# Работа в CLI

## Обзор

С помощью консольного клиента [tuna](https://tuna.am/docs/guides/install/install-cli.md) можно получать секреты, автоматически конфигурировать текущую среду или передавать конфигурацию в [env](https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D1%80%D0%B5%D0%B4%D1%8B) нужного приложения. Кроме того, из консоли доступно управление самой структурой — проектами, окружениями, конфигурациями и сервисными ключами.

| Команды                                                                                                                                                                                                                      | Для чего                                              |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| [`list`](#list), [`get`](#get), [`set`](#set), [`delete`](#delete), [`upload`](#upload)                                                                                                                                      | Чтение и изменение секретов в конфигурации            |
| [`run`](#%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)                                                                                                                  | Запуск приложения с секретами в переменных окружения  |
| [`download`](#download), [`substitute`](#substitute)                                                                                                                                                                         | Выгрузка секретов в файл или шаблон                   |
| [`setup`](#setup), [`open`](#open)                                                                                                                                                                                           | Привязка каталога к конфигурации, открытие в браузере |
| [`projects`](#%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B), [`environments`](#%D0%BE%D0%BA%D1%80%D1%83%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F), [`configs`](#%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D0%B8) | Управление структурой и сервисными ключами            |

Для получения секретов через консольное приложение tuna или [API](https://tuna.am/docs/guides/api.md) необходим [API-ключ](https://my.tuna.am/api_keys) пользователя или [Сервисный ключ конфигурации](https://tuna.am/docs/secrets.md#%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BB%D1%8E%D1%87%D0%B8). При локальной разработке удобнее использовать глобальные API-ключи пользователя, но для работы в CI/CD, Production и других общих средах мы **настоятельно рекомендуем** использовать Сервисный ключ конфигурации.

## Почему tuna secrets лучше direnv при локальной разработке?

[direnv](https://direnv.net) - прекрасная утилита для локальной конфигурации, но она всё таки заставляет следить за локальным файлом `.env` и если на Production появились новые переменные, вы можете не сразу узнать об этом и потратить часы на бессмысленную отладку. В tuna secrets предусмотрена [автоматическая синхронизация секретов](https://tuna.am/docs/secrets.md#%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-%D1%81%D0%B5%D0%BA%D1%80%D0%B5%D1%82%D0%BE%D0%B2-%D0%B2-%D0%BE%D0%BA%D1%80%D1%83%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F%D1%85), что значительно упрощает этот процесс.

## Почему tuna secrets лучше Gitlab/Github/Bitbucket Variables?

На самом деле причина тут та же, что и с direnv. Например, у вас есть специальный проект предназначенный для сборки и публикации приложения с помощью [goreleaser](https://goreleaser.com/) и он может требовать множество ключей и токенов для разных внешних интеграций, при этом для разных окружений и использовать для этого Gitlab/Github/Bitbucket Variables неудобно, а ещё неудобнее следить за этим, плюс сложность в разграничении прав. В tuna secrets доступ к окружениям регулируется RBAC и вы можете точечно распределять доступ только нужным людям/роботам/скриптам. А обновив секрет или добавив новый, он сразу начнёт работать в CI.

## Авторизация

Для доступа к секретам понадобится [Сервисный ключ](https://tuna.am/docs/secrets.md#%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BB%D1%8E%D1%87%D0%B8) определённой конфигурации или пользовательский [API-ключ](https://my.tuna.am/api_keys). Его можно передать через флаг `--api-key`, переменную окружения `TUNA_API_KEY` или сохранить в конфиг файл `tuna config save-api-key <your-key>`

Ключи различаются не только областью действия, но и набором доступных команд:

| Ключ                                 | Что можно                                                                                                                                     |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Сервисный ключ конфигурации (`tst_`) | Читать секреты своей конфигурации: `run`, `download`, `list`, `get`. С доступом `write` — ещё `set`, `delete` и `upload` в ту же конфигурацию |
| Пользовательский API-ключ (`tak_`)   | Всё то же плюс управление структурой: `projects`, `environments`, `configs` и выпуск сервисных ключей                                         |

Обращение сервисным ключом к чужой конфигурации возвращает `403`, а к командам управления структурой — `401`. Поэтому в CI/CD держите сервисный ключ конфигурации, а проекты и конфигурации заводите пользовательским ключом.

## Проект и конфигурация

Почти каждая команда работает в контексте проекта и конфигурации. Их можно задать тремя способами, в порядке приоритета:

1. флагами `--project` (`-p`) и `--config` (`-c`);
2. переменными окружения `TUNA_SECRETS_PROJECT` и `TUNA_SECRETS_CONFIG`;
3. привязкой каталога к конфигурации — командой [`setup`](#setup).

Флаг `--scope` (по умолчанию `.`) указывает, для какого каталога брать привязку. Подробнее — в разделе про [`setup`](#setup).

```shell
tuna secrets list --project api-php --config stage
tuna secrets list -p api-php -c stage
tuna secrets list                              # проект и конфигурация из привязки каталога

```

## Формат вывода

Клиент разделяет данные и служебные сообщения:

* данные идут в `stdout`;
* статусные сообщения, предупреждения и ошибки — в `stderr`.

Поэтому вывод любой команды можно перенаправлять в файл и передавать по конвейеру, не боясь поймать в него лишние строки:

```shell
tuna secrets list --only-names > names.txt
tuna secrets list | grep API_

```

### Таблицы

Человеку команды печатают таблицу без рамок: заголовки капсом, пустая ячейка — `-`, даты в формате RFC3339 в вашей локальной временной зоне. Такую таблицу удобно резать `awk` и она не разъезжается в узком терминале.

```shell
$ tuna secrets list
NAME             VALUE          TYPE    NOTE  UPDATED
JKLNRFEDUILNNIF  hmac:cea41705  string  -     2026-09-17T11:56:51+04:00

```

### Отпечатки значений

В колонке `VALUE` по умолчанию печатается не значение секрета, а его отпечаток вида `hmac:cea41705` — HMAC-SHA256 от значения на ключе команды.

Одинаковые значения дают одинаковый отпечаток, а из отпечатка нельзя восстановить значение. Поэтому отпечатки можно сравнивать между конфигурациями («в stage и prod действительно разные пароли к базе?») и печатать в логи CI, не раскрывая секрет.

Так выглядит сравнение двух окружений: `API_TOKEN` и `API_ENABLED` в dev и prod совпадают, `API_DB` и `LOG_LEVEL` различаются — и всё это видно, не раскрывая ни одного значения.

***

Настоящие значения показывают [`list --values`](#list) и [`get`](#get):

```shell
$ tuna secrets list --values
NAME             VALUE                             TYPE    NOTE  UPDATED
JKLNRFEDUILNNIF  vcTpFuptQLtJ0beoTQA13rHNeAaGNtjs  string  -     2026-09-17T11:56:51+04:00

```

### JSON и YAML

Флаг `-o` (`--output`) со значением `json` или `yaml` есть у всех подкоманд `tuna secrets`, включая команды записи: `set`, `delete`, `upload`, `create`, `rename`, `lock` и остальные печатают в машинном виде то, что получилось в результате.

```shell
$ tuna secrets set FOO=bar2 -o json
{
  "FOO": {
    "id": 42,
    "name": "FOO",
    "value": "bar2",
    "fingerprint": "hmac:b274b135",
    "value_type": "string",
    "visibility": "masked",
    "override": false,
    "note": "",
    "can_be_converged": false,
    "can_be_promoted": false,
    "reminder": null,
    "created_at": "2026-09-18T06:43:10Z",
    "updated_at": "2026-09-18T06:43:10Z"
  }
}

```

В машинном выводе секрет приходит со всеми полями и **с настоящим значением** — в отличие от таблицы, где по умолчанию стоит отпечаток. Помните об этом, перенаправляя такой вывод в файл или в лог. Исключение — секрет с видимостью `restricted`: сервер не отдаёт ни значение, ни отпечаток, поэтому оба поля приходят пустыми.

Пустой результат — это всегда валидный `[]` или `{}`, а не пустой вывод, так что парсер в скрипте не придётся защищать отдельной проверкой.

### Сырые значения для скриптов

Флаг `--plain` у команд [`get`](#get) и [`configs tokens create`](#%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BB%D1%8E%D1%87%D0%B8) печатает только само значение, **без перевода строки в конце**. Это удобно для подстановки в другую команду:

```shell
$ docker login -u robot -p "$(tuna secrets get REGISTRY_PASSWORD --plain)" registry.example.com

```

### Ошибки

Ошибки печатаются в `stderr`, код возврата в этом случае — `1`:

```shell
$ tuna secrets list -p test -c nonexistent
Error: Failed to list secrets in test/nonexistent: Config does not exist
  Code: NotFound
  Request ID: 83d3961b1452ab3c7174965acbfbd711

```

С `-o json` ошибка тоже становится JSON — его можно разобрать и, например, приложить `request_id` к обращению в поддержку:

```json
{"code":"NotFound","error":"Failed to list secrets in test/nonexistent: Config does not exist","request_id":"83d3961b1452ab3c7174965acbfbd711"}

```

## Работа с секретами

### List

Список секретов конфигурации. Алиас — `ls`.

| Флаг           | Описание                              |
| -------------- | ------------------------------------- |
| `--values`     | Показывать значения вместо отпечатков |
| `--only-names` | Только имена, одной колонкой          |

```shell
$ tuna secrets list
NAME             VALUE          TYPE    NOTE  UPDATED
JKLNRFEDUILNNIF  hmac:cea41705  string  -     2026-09-17T11:56:51+04:00

```

В записи видно, как отпечаток `LOG_LEVEL` меняется вместе со значением, а `list --values` показывает уже само значение:

***

### Get

Значения одного или нескольких секретов. В отличие от `list`, `get` показывает значения сразу.

| Флаг      | Описание                                                   |
| --------- | ---------------------------------------------------------- |
| `--plain` | Только значения, без таблицы и без перевода строки в конце |

```shell
$ tuna secrets get API_TOKEN API_DB
NAME       VALUE                             TYPE    NOTE  UPDATED
API_DB     postgres://postgres@db:5432/api   string  -     2026-09-17T11:56:51+04:00
API_TOKEN  PwZayIp9NpNntNYd2jkAAQgViM6dXdIm  string  -     2026-09-17T11:56:51+04:00

```

Если запрошенного секрета в конфигурации нет, команда завершится ошибкой и не напечатает ничего — так опечатка в имени не превратится в пустую переменную.

### Set

Запись одного или нескольких секретов.

| Флаг               | Описание                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `--type`           | Тип значения: `string`, `json`, `boolean`, `integer`, `decimal`, `email`, `url`, `yaml`, `xml`, `date`, `datetime` |
| `--note`           | Комментарий к секрету                                                                                              |
| `--visibility`     | Видимость значения в веб-интерфейсе: `masked` или `unmasked`                                                       |
| `--no-interactive` | Не спрашивать значение интерактивно, а завершиться ошибкой                                                         |

Значение можно передать четырьмя способами:

```shell
$ tuna secrets set API_TOKEN=PwZayIp9NpNntNYd2jkAAQgViM6dXdIm   # inline
$ tuna secrets set API_TOKEN PwZayIp9NpNntNYd2jkAAQgViM6dXdIm   # отдельным аргументом
$ openssl rand -hex 32 | tuna secrets set API_TOKEN             # из stdin
$ tuna secrets set API_TOKEN                                    # интерактивно

```

Интерактивный ввод включается только для одного имени без `=`, когда в stdin ничего не пришло; с `--no-interactive` вместо него будет ошибка. Ввести можно и многострочное значение, например приватный ключ: ввод заканчивается пустой строкой, а следом строкой с одной точкой.

За один вызов можно записать несколько секретов, флаги `--type`, `--note` и `--visibility` применяются ко всем сразу:

```shell
$ tuna secrets set API_ENABLED=true API_DEBUG=false --type boolean

```

В ответ команда печатает записанные секреты:

```shell
$ tuna secrets set FOO=bar
NAME  VALUE          TYPE    NOTE  UPDATED
FOO   hmac:bd154f88  string  -     2026-09-18T10:43:10+04:00

```

Не используйте видимость restricted

Флаг `--visibility` принимает и значение `restricted`, но пользоваться им сейчас не стоит: сервер перестаёт отдавать значение такого секрета вообще всем — в том числе `tuna secrets run`, — а `tuna secrets download` для всей конфигурации начинает отвечать `403`. Вернуть значение после этого нельзя.

Если кто-то изменил тот же секрет между чтением и записью, команда сообщит об этом и завершится ошибкой, не затирая чужое изменение. В этом случае достаточно повторить вызов.

### Delete

Удаление одного или нескольких секретов. Алиас — `rm`.

```shell
$ tuna secrets delete FOO
NAME       VALUE          TYPE    NOTE  UPDATED
API_TOKEN  hmac:3e96898b  string  -     2026-09-18T10:43:09+04:00
DB_HOST    hmac:829a119e  string  -     2026-09-18T10:43:09+04:00
DB_PORT    hmac:620ef1fa  string  -     2026-09-18T10:43:09+04:00

```

Команда печатает конфигурацию в том виде, в каком она осталась после удаления.

Если хотя бы одного из перечисленных секретов в конфигурации нет, не удаляется ничего.

### Upload

Загрузка секретов из файла `.env` или `.json`.

```shell
$ tuna secrets upload .env
NAME       VALUE          TYPE    NOTE  UPDATED
API_TOKEN  hmac:1ea77bae  string  -     2026-09-18T10:43:11+04:00
DB_HOST    hmac:829a119e  string  -     2026-09-18T10:43:09+04:00
DB_PORT    hmac:620ef1fa  string  -     2026-09-18T10:43:09+04:00
REDIS_URL  hmac:3df78001  string  -     2026-09-18T10:43:11+04:00

```

Как и `delete`, команда печатает конфигурацию целиком — уже после слияния.

Содержимое файла **дополняет** конфигурацию: секреты с совпадающими именами перезаписываются, остальные остаются на месте. Полной заменой конфигурации `upload` не является.

## Запуск приложений

Команда `run` запускает ваше приложение, передавая ему секреты в переменных окружения. Сами секреты при этом не попадают ни в файл, ни в историю команд.

```shell
tuna secrets run -- yarn start

```

***

### Что передаётся процессу

| Флаг                 | Описание                                                                                                                                     |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `--command`          | Выполнить строку через оболочку: `--command "echo $HOME && env"`                                                                             |
| `--only-secrets`     | Передать только секреты и минимум системных переменных (`PATH`, `HOME`, `PS1`, `USER`, `SHELL`, `TMPDIR`)                                    |
| `--preserve-env`     | Список имён через запятую, для которых приоритет у уже существующего значения в окружении; без значения флаг означает `true` — сохранить все |
| `--name-transformer` | Преобразовать имена: `upper-camel`, `camel`, `lower-snake`, `lower-kebab`, `tf-var`, `dotnet-env`                                            |
| `--forward-signals`  | Пробрасывать сигналы дочернему процессу; по умолчанию включено, когда `stdout` не терминал                                                   |

`tuna` возвращает код выхода дочернего процесса, поэтому обёртку можно ставить в CI-джобу, не ломая её логику.

```shell
$ tuna secrets run --project api-php --config stage -- yarn start
$ tuna secrets run --scope /home/user/src/api -- yarn start
$ tuna secrets run --only-secrets -- env

```

### Автоматический перезапуск при изменении секрета

Если передать флаг `--watch`, клиент подписывается на изменения конфигурации и перезапускает процесс с новыми значениями.

```shell
tuna secrets run --watch -- yarn start

```

Лучше всего работу продемонстрирует данное видео:

**VK Видео**

**YouTube**

В видео мы запускаем вот такую команду:

```shell
tuna secrets run --watch -- bash -c 'while true; do echo -n "$(date -Is) [pid: $$] ==> " ; echo ${API_TOKEN} ; sleep 1; done'

```

### Секреты файлом вместо переменных окружения

Некоторые приложения читают конфигурацию только из файла. Флаг `--mount` создаёт временный именованный канал (named pipe), кладёт в него секреты и передаёт путь дочернему процессу в переменной `TUNA_SECRETS_MOUNT_PATH`. В переменные окружения секреты в этом режиме не попадают.

Канал создаётся по указанному пути с правами `0600`, но значения живут только в памяти клиента: каждое открытие файла на чтение получает полный снимок и EOF, записанного файла с секретами на диске не появляется. После выхода процесса канал удаляется.

```shell
$ tuna secrets run --mount ./secrets.json --mount-format json -- ./app --config "$TUNA_SECRETS_MOUNT_PATH"

```

| Флаг                | Описание                                                                            |
| ------------------- | ----------------------------------------------------------------------------------- |
| `--mount`           | Путь к создаваемому каналу                                                          |
| `--mount-format`    | Формат содержимого: `env` (по умолчанию), `env-no-quotes`, `json`, `yaml`, `docker` |
| `--mount-template`  | Файл-шаблон, который рендерится в канал вместо фиксированного формата               |
| `--mount-max-reads` | Сколько раз файл можно прочитать, `0` — без ограничения                             |

***

Флаг `--mount-max-reads N` удаляет канал после N чтений — удобно, когда секреты читает ровно один процесс при старте.

к сведению

Именованные каналы не поддерживаются в Windows, там `--mount` работать не будет.

Если клиент убить сигналом `SIGKILL`, канал останется на диске и следующий запуск откажется стартовать: `mount path already exists`. Такой файл нужно удалить вручную.

### Офлайн-режим

`run` умеет хранить последние успешно полученные секреты в зашифрованном файле и использовать их, когда API недоступен — приложение не встанет из-за проблем с сетью.

| Флаг                           | Описание                                                                                                                         |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `--fallback`                   | Путь к файлу; по умолчанию вычисляется из адреса API, ключа, проекта и конфигурации                                              |
| `--fallback-only`, `--offline` | Читать только файл, не обращаясь к API                                                                                           |
| `--fallback-readonly`          | Использовать файл, но не обновлять его                                                                                           |
| `--no-fallback`                | Не читать и не писать файл                                                                                                       |
| `--fallback-passphrase`        | Парольная фраза; по умолчанию вычисляется из ключа, проекта и конфигурации. Также читается из `TUNA_SECRETS_FALLBACK_PASSPHRASE` |

Если API ответил, что доступ запрещён или конфигурация не найдена (`401`, `403`, `404`), файл удаляется: устаревшая копия секретов после отзыва доступа опаснее, чем остановка приложения. При сетевых ошибках и `5xx` клиент, наоборот, читает файл и печатает предупреждение.

Парольная фраза по умолчанию выводится из API-ключа, проекта и конфигурации — значит, после ротации ключа старый файл расшифровать уже нельзя. Если файл должен пережить ротацию, задайте фразу явно: флаг `--fallback-passphrase` имеет приоритет над переменной `TUNA_SECRETS_FALLBACK_PASSPHRASE`.

Старые файлы можно убрать командой `run clean`:

```shell
$ tuna secrets run clean --dry-run
Would delete 0 fallback file(s)

$ tuna secrets run clean --max-age 720h
$ tuna secrets run clean --all

```

По умолчанию удаляются файлы старше 14 дней (`--max-age 336h`).

Первый запуск ходит в API и попутно пишет файл, второй с `--offline` обходится без сети:

***

## Выгрузка секретов

### Download

Получение секретов конфигурации в файл или на стандартный вывод.

```shell
tuna secrets download

```

***

| Флаг                 | Описание                                                                                                    |
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
| `--format`           | `json` (по умолчанию), `yaml`, `env`, `env-no-quotes`, `docker`                                             |
| `--no-file`          | Печатать на стандартный вывод вместо записи в файл                                                          |
| `--name-transformer` | Преобразовать имена: `upper-camel`, `camel`, `lower-snake`, `lower-kebab`, `tf-var`, `dotnet`, `dotnet-env` |
| `--scope`            | Каталог, привязку которого использовать                                                                     |

Настройки можно задать и [переменными окружения](https://tuna.am/docs/guides/environment-variables.md#%D1%81%D0%B5%D0%BA%D1%80%D0%B5%D1%82%D1%8B).

#### Передача проекта и конфигурации

Флаги `--project` и `--config` принимают на вход имя (алиас) проекта и конфигурации. По умолчанию конфигурация сохранится в файл `tuna.json` в текущем каталоге.

```shell
$ tuna secrets download --project api-php --config stage
Downloaded secrets to tuna.json

$ cat tuna.json
{
  "API_DB": "postgres://postgres:postgres@localhost:5432/postgres",
  "API_TOKEN": "PwZayIp9NpNntNYd2jkAAQgViM6dXdIm"
}

```

#### Передача формата

Можно указать, в каком формате вам нужен файл.

**json**

```shell
$ tuna secrets download --project api-php --config stage --format json
Downloaded secrets to tuna.json

$ cat tuna.json
{
  "API_DB": "postgres://postgres:postgres@localhost:5432/postgres",
  "API_TOKEN": "PwZayIp9NpNntNYd2jkAAQgViM6dXdIm"
}

```

**yaml**

```shell
$ tuna secrets download --project api-php --config stage --format yaml
Downloaded secrets to tuna.yaml

$ cat tuna.yaml
API_DB: postgres://postgres:postgres@localhost:5432/postgres
API_TOKEN: PwZayIp9NpNntNYd2jkAAQgViM6dXdIm

```

**env**

```shell
$ tuna secrets download --project api-php --config stage --format env
Downloaded secrets to tuna.env

$ cat tuna.env
API_DB="postgres://postgres:postgres@localhost:5432/postgres"
API_TOKEN="PwZayIp9NpNntNYd2jkAAQgViM6dXdIm"

```

Неизвестный формат — ошибка, а не молчаливая выгрузка «чего-нибудь».

#### Формат docker

Формат `docker` печатает пары `KEY=value` без кавычек — то же, что `env-no-quotes`. Такой файл подходит для `docker run --env-file`, где кавычки стали бы частью значения:

```shell
$ tuna secrets download --format docker --no-file
API_TOKEN=s3cr3t-token-value
DB_HOST=db.internal
DB_PORT=5432

$ tuna secrets download --format docker tuna.env
$ docker run --env-file tuna.env myapp

```

#### Преобразование имён

Флаг `--name-transformer` меняет стиль имён — полезно, когда потребитель секретов ждёт не `SCREAMING_SNAKE_CASE`:

```shell
$ tuna secrets download --no-file --format json --name-transformer camel
{
  "apiDb": "postgres://postgres:postgres@localhost:5432/postgres",
  "apiToken": "PwZayIp9NpNntNYd2jkAAQgViM6dXdIm"
}

```

Трансформер `dotnet` есть только у `download`: он даёт имена вида `Section:Key`, а двоеточие в имени переменной окружения недопустимо. Поэтому у `tuna secrets run` его нет — для .NET там `dotnet-env` с двойным подчёркиванием.

#### Свой файл

В команде можно передать путь и определить свой файл.

```shell
$ tuna secrets download --project api-php --config stage foo-bar.json
Downloaded secrets to foo-bar.json

```

#### Вывод в stdout

Флаг `--no-file` отключает запись в файл и печатает конфигурацию на стандартный вывод.

```shell
$ tuna secrets download --project api-php --config stage --no-file --format env
API_TOKEN="PwZayIp9NpNntNYd2jkAAQgViM6dXdIm"
API_DB="postgres://postgres:postgres@localhost:5432/postgres"

```

### Substitute

Рендер файла-шаблона со значениями секретов. Подходит для конфигов, у которых свой формат: `nginx.conf`, `application.yml`, `.ini` и подобных.

Шаблон — это [text/template](https://pkg.go.dev/text/template) Go, данными служит набор пар имя-значение:

config.tmpl

```gotemplate
database_url = {{ .DB_URL }}
api_key = {{ index . "API-KEY" }}
extra = {{ index . "FEATURES" | fromjson | tojson }}

```

```shell
$ tuna secrets substitute config.tmpl --output config.ini
$ tuna secrets substitute config.tmpl | kubectl apply -f -

```

***

Без `--output` результат печатается на стандартный вывод. С `--output` файл пишется атомарно и с правами `0600`.

Обращение вида `{{ index . "DB-NAME" }}` нужно для имён, которые не являются идентификаторами Go. Кроме стандартных функций доступны `tojson` и `fromjson`.

warning

Опечатка в имени секрета — не ошибка: `text/template` подставит на это место `<no value>`. Проверяйте результат рендера, особенно в CI.

## Настройка окружения

### Setup

Привязка каталога к проекту и конфигурации — после неё `--project` и `--config` можно не передавать. Привязки хранятся в [конфиг файле](https://tuna.am/docs/guides/config-file.md) клиента.

```shell
tuna secrets setup

```

Без флагов команда предложит выбрать проект и конфигурацию из списка.

#### Настройка без меню

Как и в случае с `download`, в `setup` можно передать `--project` и `--config`:

```shell
$ tuna secrets setup --project api-php --config stage
SCOPE               PROJECT  CONFIG
/home/user/src/api  api-php  stage

```

***

Флаг `--no-interactive` (или переменная `TUNA_SECRETS_NO_INTERACTIVE`) запрещает спрашивать недостающие значения — команда завершится ошибкой. Это то, что нужно в CI и в скриптах установки.

#### Настройка каталога, отличного от текущего

Если вы хотите настроить не текущий каталог, передайте нужный путь во флаг `--scope`:

```shell
tuna secrets setup --project api-php --config stage --scope /home/user/src/api

```

#### Файл tuna.yaml в репозитории

Чтобы новому разработчику хватило одной команды `tuna secrets setup`, положите в корень репозитория файл `tuna.yaml`:

tuna.yaml

```yaml
setup:
  - project: api-php
    config: dev

```

Для монорепозитория можно описать несколько привязок, у каждой записи свой `path` относительно корня:

tuna.yaml

```yaml
setup:
  - project: api-php
    config: dev
    path: backend
  - project: ui
    config: dev
    path: frontend

```

Файл читается только тогда, когда проект и конфигурация не заданы флагами или переменными окружения, и только в каталоге `--scope` — вверх по дереву клиент не поднимается.

#### Как выглядит привязка

Результат `setup` попадает в конфиг файл клиента:

```shell
$ cat ~/.config/tuna/tuna.yml
secrets:
    scoped:
        /home/user/src/api:
            project: api-php
            config: stage

```

Находясь в `/home/user/src/api`, проект и конфигурацию передавать не нужно. Из другого каталога — либо передайте их явно, либо укажите `--scope`:

```shell
$ pwd
/home/user/src/worker

$ tuna secrets download --format env --no-file --scope /home/user/src/api
API_TOKEN="PwZayIp9NpNntNYd2jkAAQgViM6dXdIm"
API_DB="postgres://postgres:postgres@localhost:5432/postgres"

```

### Open

Открыть текущую конфигурацию в веб-интерфейсе:

```shell
tuna secrets open

```

Если браузер открыть не удалось, команда напечатает ссылку. С `-o json` она печатает только адрес.

## Управление структурой

Проекты, окружения и конфигурации можно создавать и менять прямо из консоли — те же действия, что и в [веб-интерфейсе](https://my.tuna.am/secrets).

### Проекты

```shell
$ tuna secrets projects
SLUG  NAME  DESCRIPTION  CONFIGS  SECRETS  CREATED
test  test  -            5        12       2026-09-17T11:20:40+04:00

```

| Команда                     | Описание                                           |
| --------------------------- | -------------------------------------------------- |
| `projects`                  | Список проектов                                    |
| `projects get [project]`    | Информация о проекте                               |
| `projects create [name]`    | Создать проект: `--slug`, `--description`          |
| `projects update [project]` | Изменить: `--name`, `--slug`, `--description`      |
| `projects delete [project]` | Удалить; без аргумента предложит выбрать из списка |

```shell
$ tuna secrets projects create "API PHP" --slug api-php --description "Основное API"

```

### Окружения

```shell
$ tuna secrets environments --project api-php
SLUG   NAME         PERSONAL CONFIGS  CREATED
dev    Development  yes               2026-09-18T10:43:08+04:00
stage  Staging      no                2026-09-18T10:43:09+04:00
prod   Production   no                2026-09-18T10:43:09+04:00

```

| Команда                           | Описание                                |
| --------------------------------- | --------------------------------------- |
| `environments`                    | Список окружений проекта                |
| `environments get ENVIRONMENT`    | Информация об окружении                 |
| `environments create NAME`        | Создать: `--slug`, `--personal-configs` |
| `environments rename ENVIRONMENT` | Переименовать: `--name`, `--slug`       |
| `environments delete ENVIRONMENT` | Удалить окружение                       |

Алиасы команды — `envs` и `environment`.

### Конфигурации

```shell
$ tuna secrets configs --project api-php
SLUG       ENVIRONMENT  TYPE       LOCKED  CREATED                    LAST FETCH
dev        dev          original   yes     2026-09-17T11:20:40+04:00  2026-09-17T16:20:35+04:00
prod/fghd  prod         duplicate  no      2026-09-17T11:55:14+04:00  2026-09-17T16:17:42+04:00

```

Колонка `TYPE` различает основную конфигурацию окружения (`original`) и её копию (`duplicate`), `LAST FETCH` показывает, когда секреты забирали в последний раз — по ней видно, пользуется ли кто-нибудь конфигурацией.

| Команда                   | Описание                                              |
| ------------------------- | ----------------------------------------------------- |
| `configs`                 | Список конфигураций проекта, фильтр — `--environment` |
| `configs get [config]`    | Информация о конфигурации                             |
| `configs create [name]`   | Создать в окружении: `--environment`, `--locked`      |
| `configs clone [config]`  | Скопировать внутри окружения: `--name`                |
| `configs rename [config]` | Переименовать: `--name`                               |
| `configs lock [config]`   | Запретить изменения                                   |
| `configs unlock [config]` | Снять запрет                                          |
| `configs delete [config]` | Удалить конфигурацию                                  |
| `configs logs`            | История изменений: `--page`, `--page-size`            |

#### История изменений

```shell
$ tuna secrets configs logs
TIME                       ACTOR  ACTION         SECRET
2026-09-17T16:20:37+04:00  Женя   secret_delete  CLAUDE_ML

```

к сведению

История изменений конфигурации сейчас доступна только за последние трое суток — более старые записи удаляются автоматически. Это текущее ограничение, а не настройка тарифа.

#### Сервисные ключи

[Сервисный ключ](https://tuna.am/docs/secrets.md#%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BB%D1%8E%D1%87%D0%B8) ограничен одной конфигурацией — это то, что стоит использовать в CI/CD вместо пользовательского API-ключа.

| Команда                    | Описание                                                            |
| -------------------------- | ------------------------------------------------------------------- |
| `configs tokens`           | Список ключей конфигурации                                          |
| `configs tokens create`    | Выпустить ключ: `--description`, `--access`, `--max-age`, `--plain` |
| `configs tokens revoke ID` | Отозвать ключ по идентификатору                                     |

| Флаг `create`   | Описание                                                                        |
| --------------- | ------------------------------------------------------------------------------- |
| `--access`      | `read` (по умолчанию) или `write`                                               |
| `--max-age`     | Срок жизни в формате Go-длительности, например `720h`; без него ключ бессрочный |
| `--description` | Описание, по которому ключ потом можно узнать в списке                          |
| `--plain`       | Напечатать только сам ключ, без таблицы и без перевода строки                   |

Значение ключа показывается один раз — при создании:

```shell
$ tuna secrets configs tokens create --description ci --access read --max-age 720h
Save this token - it will not be shown again
ID  DESCRIPTION  ACCESS  EXPIRES                    CREATED                    TOKEN
5   ci           read    2026-10-18T10:43:31+04:00  2026-09-18T10:43:31+04:00  tst_...

$ tuna secrets configs tokens
ID  DESCRIPTION  ACCESS  EXPIRES                    CREATED
6   ci-plain     read    2026-10-18T10:43:31+04:00  2026-09-18T10:43:31+04:00
5   ci           read    2026-10-18T10:43:31+04:00  2026-09-18T10:43:31+04:00

```

Предупреждение «Save this token» идёт в `stderr`, поэтому в скрипте ключ можно забрать прямо в переменную:

```shell
$ TUNA_API_KEY=$(tuna secrets configs tokens create --description "gitlab ci" --access read --max-age 720h --plain)

```
