# Tuna Documentation > Documentation for the tuna.am tunneling service ## Aup ### Acceptable Use Policy **Revision of: 13 August 2026** #### 1. General Provisions 1.1. This Policy defines what use of the Tuna platform services is acceptable and applies to all users regardless of their plan. 1.2. The Policy forms an integral part of the [Terms of Service](https://tuna.am/en/tos.md) (clause 12.1). Terms are used in the meanings defined by the Terms of Service. 1.3. The Policy covers the actions of the Customer, the members of its Organization and any persons to whom the Customer has granted access to the services or to the results of their operation. 1.4. The Customer is responsible for compliance with this Policy by all such persons as for its own actions. #### 2. General Requirements 2.1. Use of the services must be lawful under the law of the Republic of Armenia, the law of the Customer's country and the law of the country where the infrastructure used is located. 2.2. The Customer must hold the rights to the data and content published or processed through the services, as well as to the domain names and TLS certificates used. 2.3. The Customer must not create risks to the operability of the platform, its infrastructure or other users. #### 3. Prohibited Activity ##### 3.1. Unlawful and harmful content It is prohibited to place, distribute or transmit through the services: * materials whose circulation is prohibited by applicable law; * child sexual abuse material — such reports are handled with priority and result in immediate blocking; * materials inciting violence, terrorism or hatred; * content infringing copyright and related rights, trademarks or other third-party rights; * personal data distributed without a legal basis. ##### 3.2. Attacks and malware It is prohibited to use the services for: * distributing malware, including viruses, trojans, ransomware, exploits and loaders; * phishing and spoofing of websites, sign-in forms and payment pages of any organization; * spoofing, interception and substitution of third-party traffic; * operating command and control infrastructure for malware; * denial-of-service attacks, port scanning, password brute-forcing and other attacks against any systems; * circumventing third-party security controls and filtering systems. ##### 3.3. Spam and unsolicited mailings It is prohibited to use the services to send unsolicited messages, including through SMTP tunnels and webhooks, or to host infrastructure serving such mailings. ##### 3.4. Abuse of resources It is prohibited to: * circumvent plan limits, including by registering multiple accounts, automatically recreating resources and using technical means to bypass billing; * use the services for cryptocurrency mining or other computations aimed at extracting value from the Provider's infrastructure resources; * use tunnels and gateways as an anonymizing proxy or VPN for routing third-party traffic unrelated to the Customer's own resources; * use the services to operate file-sharing networks and distribute traffic-heavy content unrelated to the development and operation of the Customer's own applications; * generate load that materially exceeds what is typical for the selected plan and interferes with other users. ##### 3.5. Actions affecting the platform It is prohibited to: * conduct load testing, scanning or penetration testing against the Provider's infrastructure without prior written consent; * exploit discovered vulnerabilities; vulnerabilities should be reported to ; * interfere with the operation of the platform, decompile or modify the client software beyond what is expressly permitted by law; * present Tuna services as your own unless this is provided by the terms of your plan or a separate agreement. #### 4. Service-Specific Rules 4.1. **Tunnels and gateways** are intended for providing access to the Customer's own resources — applications it develops or operates. Providing access through them to third-party infrastructure without the consent of its owner is prohibited. 4.2. For free accounts, an interstitial warning page is shown for browser requests. Bypassing this page is permitted only via the header described in the [documentation](https://tuna.am/en/docs/abuse.md) and only for the Customer's own requests. 4.3. Addresses of dynamic domains contain the source IP address — an anti-fraud measure described in the [Abuse](https://tuna.am/en/docs/abuse.md) section. Actions aimed at circumventing it are treated as a breach of this Policy. 4.4. **Passwords and Secrets** are intended for storing the Customer's credentials and configurations. Restrictions on the composition of stored data are set out in Section 14 of the [Terms of Service](https://tuna.am/en/tos.md). 4.5. **Meetings, boards and reports** are not intended for processing special categories of personal data. Recording and publishing meeting materials is permitted only where there is a legal basis and participants have been notified. 4.6. **Monitors** must target resources owned by the Customer or resources whose owner has consented to being checked. Using monitors to scan third-party systems is prohibited. #### 5. Abuse Reports 5.1. Reports are sent to , stating the problematic address, a description of the violation and supporting materials. The submission and handling procedure is described in the [Abuse and Complaints](https://tuna.am/en/docs/abuse.md) section. 5.2. The reporter receives confirmation that the report has been accepted. The review period depends on the nature of the violation; reports involving an immediate security threat are handled with priority. 5.3. Organizations that regularly submit reports can use the [API](https://tuna.am/en/docs/guides/api.md) integration. #### 6. Enforcement 6.1. In the event of a breach of this Policy, the measures provided for in clause 12.6 of the [Terms of Service](https://tuna.am/en/tos.md) apply: a demand to remedy the breach, restriction or suspension of a resource, service or account, removal of content placed in breach of the rules, termination of the agreement and blocking of the account. 6.2. The measure is chosen proportionately to the nature of the breach, its consequences and whether it is repeated. 6.3. Immediate blocking without prior notice applies to breaches that pose an immediate threat to security or to third parties: phishing, distribution of malware, child sexual abuse material, attacks on third-party systems and spam. 6.4. In all other cases, the Customer is sent a notice stating the grounds and is given a reasonable period to remedy the breach. #### 7. Appeals 7.1. The Customer may submit an objection to , attaching explanations and supporting materials. 7.2. The objection is reviewed within 10 business days. If the breach is not confirmed, access is restored. 7.3. Where the agreement is terminated on the grounds set out in this Policy, amounts paid are not refundable (clause 12.9 of the [Terms of Service](https://tuna.am/en/tos.md)). #### 8. Changes to the Policy 8.1. The Provider may amend this Policy in accordance with Section 22 of the [Terms of Service](https://tuna.am/en/tos.md). The current revision is published at [/aup/](https://tuna.am/en/aup.md) with the date indicated. --- ## Blog ### Blog * [Telegram channel](https://t.me/tuna_am) * [RSS](https://tuna.am/en/blog/rss.xml) * [Archive](https://tuna.am/en/blog/archive) New features, releases and engineering deep dives from the Tuna tunneling service. [All](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features "Announcements of new Tuna features")|[tuna-cli releases](https://tuna.am/en/blog/tags/releases "What changed in new client versions")|[Guides](https://tuna.am/en/blog/tags/guides "How to solve a specific task with Tuna")|[Security](https://tuna.am/en/blog/tags/security "How we protect data and what to do about vulnerabilities")|[Under the hood](https://tuna.am/en/blog/tags/engineering "Engineering deep dives into how Tuna works")|[News](https://tuna.am/en/blog/tags/news "Service news: plans, payments, scheduled maintenance") 1. #### [Docs that are easy for agents to read: llms.txt and Markdown copies of pages](https://tuna.am/en/blog/docs-for-ai-agents.md) October 28 · [Under the hood](https://tuna.am/en/blog/tags/engineering) Every page of the Tuna docs has a Markdown copy, and the table of contents for the whole documentation lives in llms.txt. Here's why agents need this, how it works on our side, which pitfalls we ran into and how to do the same on your own Docusaurus site. [read →](https://tuna.am/en/blog/docs-for-ai-agents.md) 2. #### [🗞️ Что нового в Tuna — тёмная тема, адреса для файрвола и переезд Linux-пакетов](https://tuna.am/en/blog/whats-new-october-2026.md) October 27 · [News](https://tuna.am/en/blog/tags/news) Linux-пакеты tuna теперь только в code.tuna.am, в личном кабинете появилась тёмная тема, а список адресов Tuna для корпоративного файрвола можно получить через API. Плюс исправления в CLI. 3. #### [🛡 What to patch now: PHP, GitLab, Node.js, Brotli and eMudhra](https://tuna.am/en/blog/security-october-2026.md) October 26 · [Security](https://tuna.am/en/blog/tags/security) A rundown of October's vulnerabilities — PHP2Shell, two 10-out-of-10 flaws in GitLab, code execution through the Brotli decoder and the guessable keys of the eMudhra certificate authority. Which versions to install, what to do if you can't update right now, and where to read more. 4. #### [🦎 Tunnels are now highly available](https://tuna.am/en/blog/high-availability.md) October 15 · [New features](https://tuna.am/en/blog/tags/features), [Under the hood](https://tuna.am/en/blog/tags/engineering) We've spread traffic entry points and tunnel nodes across different servers, data centers and countries. A single server failure no longer means downtime, you don't need to choose a region, and Telegram webhooks reach tunnels wherever the client runs. 5. #### [🕵️ AIOps: our experience rolling out HolmesGPT across three projects](https://tuna.am/en/blog/holmesgpt-aiops.md) October 7 · [Under the hood](https://tuna.am/en/blog/tags/engineering) We connected the open-source SRE agent HolmesGPT to Alertmanager in six Kubernetes clusters. Here's how it works, what an alert investigation costs, how we chose models, what to do about sensitive data and which pitfalls we ran into. 6. #### [📒 1C tunnel — your infobase on the internet with a single command](https://tuna.am/en/blog/1c-tunnel.md) October 6 · [New features](https://tuna.am/en/blog/tags/features) The new tuna 1c command publishes a 1C:Enterprise infobase and makes it reachable through the web client and the thin client — no web server to install and configure, no port forwarding and no public IP. 7. #### [☸️ Kubernetes tunnel — temporary cluster access with a single link](https://tuna.am/en/blog/kubernetes-tunnel.md) October 5 · [New features](https://tuna.am/en/blog/tags/features) The new tuna kubernetes command gives a colleague or contractor access to your cluster with plain kubectl — no VPN, no exposing the API server to the internet and no sharing your credentials. Access can be limited by namespace, resources and lifetime. 8. #### [🎁 Бесплатный тариф без таймера и с постоянным поддоменом](https://tuna.am/en/blog/free-plan-permanent-subdomain.md) October 2 · [New features](https://tuna.am/en/blog/tags/features) На бесплатном тарифе HTTP-туннели больше не ограничены 30 минутами, а у каждого пользователя появился свой постоянный поддомен в зоне tuna.am — он выдаётся автоматически и не меняется между запусками. 9. #### [🧑‍💻 Terraform-провайдер для Tuna](https://tuna.am/en/blog/terraform-provider.md) October 1 · [New features](https://tuna.am/en/blog/tags/features) Домены, доменные зоны, шлюзы, TCP-порты, IP-политики, сертификаты и временные токены Tuna теперь можно описывать кодом в Terraform и OpenTofu. А API-ключи получили права по продуктам. 10. #### [🖥️ tuna-cli 0.36 — интерфейс в терминале и большое обновление](https://tuna.am/en/blog/cli-v0-36-tui.md) September 30 · [tuna-cli releases](https://tuna.am/en/blog/tags/releases) Туннели в tuna-cli теперь показывают полноэкранный интерфейс с лентой запросов, метриками и просмотром сессий. А ещё — unix-сокеты, политика по ссылке, данные событий в trigger, новый CLI секретов и подсказки по обновлению. 11. #### [🗝️ Секреты целиком из консоли — большое обновление tuna secrets](https://tuna.am/en/blog/secrets-cli.md) September 24 · [New features](https://tuna.am/en/blog/tags/features) В tuna-cli 0.35 команда tuna secrets научилась читать и менять отдельные секреты, управлять проектами и окружениями, отдавать секреты файлом без записи на диск, работать без сети и сравнивать значения между окружениями, не раскрывая их. 12. #### [💳 Оплата международными картами — подключили PayPro Global](https://tuna.am/en/blog/paypro-global.md) August 10 · [News](https://tuna.am/en/blog/tags/news) Теперь подписку в Tuna можно оплатить картой Visa или Mastercard, выпущенной за пределами России. Платежи в долларах обрабатывает PayPro Global, российские карты по-прежнему принимает ЮKassa. 13. #### [🔐 Ключи доступа (passkeys) — вход в Tuna без пароля](https://tuna.am/en/blog/webauthn-passkeys.md) July 15 · [New features](https://tuna.am/en/blog/tags/features), [Security](https://tuna.am/en/blog/tags/security) Вход в аккаунт по биометрии или PIN — без пароля и одноразовых кодов. Работает с 1Password, Bitwarden, iCloud Keychain, Windows Hello и аппаратными ключами YubiKey. 14. #### [Кто может зайти в ваш внутренний сервис? 5 способов закрыться авторизацией](https://tuna.am/en/blog/apps-access-methods.md) July 2 · [Guides](https://tuna.am/en/blog/tags/guides), [Security](https://tuna.am/en/blog/tags/security) Как ограничить доступ к внутренним веб-сервисам: VPN, Basic auth, IP-фильтры, прокси с авторизацией и Tuna Apps 15. #### [🔐 Безопасность Секретов и отличие от Паролей - мастер-ключ, шифрование и общая архитектура.](https://tuna.am/en/blog/secrets-security.md) June 3 · [Under the hood](https://tuna.am/en/blog/tags/engineering), [Security](https://tuna.am/en/blog/tags/security) Технический разбор - где живёт мастер-ключ, как шифруются секреты и что произойдёт, если утечёт база данных 16. #### [🆕 Приложения — Zero Trust доступ к вашим сервисам](https://tuna.am/en/blog/apps.md) May 29 · [New features](https://tuna.am/en/blog/tags/features) Защищённый reverse proxy с авторизацией, политиками доступа и SSO 17. #### [Не приходят webhooks от api.telegram.org в туннель в RU зоне](https://tuna.am/en/blog/tunnel-location.md) May 13 · [Guides](https://tuna.am/en/blog/tags/guides) Устранение проблем в работе с telegram ботами 18. #### [🆕 Boards, Meets and other platform updates](https://tuna.am/en/blog/boards-meets.md) March 24 · [New features](https://tuna.am/en/blog/tags/features) New services - Boards and Meets, integrations with task trackers and improvements in tuna-cli 19. #### [New plan names](https://tuna.am/en/blog/tariff-rename.md) February 20 · [News](https://tuna.am/en/blog/tags/news) We renamed the subscription plans — Hobby became Starter, Developer became Pro, and Team became Legend 20. #### [PROXY Protocol support in TCP tunnels](https://tuna.am/en/blog/release-v0-30-0.md) January 28 · [tuna-cli releases](https://tuna.am/en/blog/tags/releases) The tuna console client now supports PROXY Protocol for TCP tunnels — pass the original client IP and port to your application. ← newer1 / 3[older →](https://tuna.am/en/blog/page/2) --- ### 1C tunnel — your infobase on the internet with a single command [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) October 6 · 6 min Your 1C infobase lives on an office computer, but you need to work with it from home or on a business trip, or show it to a client. 1C:Enterprise is an accounting and business automation platform widely used in Russia, and an infobase is where it keeps a company's data. The usual options are remote desktop, a VPN or publishing the infobase on a web server. The last one is the most convenient for users — the infobase opens right in the browser — but also the most hassle: you need to install Apache or IIS, plug the 1C module into it, forward a port on the router, get a public IP and issue a certificate. We've added the **`tuna 1c`** command to tuna-cli 0.37, and it does all of this for you. One command, and the infobase is available at an HTTPS address: ```shell tuna 1c "My infobase" --basic-auth="login:password" ``` #### How it works 1C has no HTTP server of its own: the web client and the thin client over HTTP are served by the web server extension module, which is loaded into Apache or IIS. `tuna 1c` takes care of the whole chain: 1. finds the installed 1C platform and its web server extension module; 2. on the first run, downloads Apache httpd into its cache; 3. starts it on `127.0.0.1` and publishes the infobase in it; 4. opens an [HTTP tunnel](https://tuna.am/en/docs/tunnels/http.md) to that web server. The web server listens only on the local address, and it's the tunnel that exposes the infobase to the outside. Stop the command, and the publication is gone along with the tunnel. #### Quick start Pick the 1C installation and the infobase in the wizard once: ```shell tuna 1c setup ``` The wizard saves your choice to the [configuration file](https://tuna.am/en/docs/guides/config-file.md), and from then on you can publish the infobase without arguments: ```shell tuna 1c --basic-auth="login:password" ``` You can also specify the infobase explicitly — by its name in the 1C launcher list, the directory of a file infobase or the connection string of a client/server infobase: ```shell tuna 1c "My infobase" tuna 1c "C:\Bases\MyBase" tuna 1c "Srvr=localhost;Ref=MyBase" ``` #### Web client and thin client Open the tunnel address in a browser — tuna redirects to `/1c/` and takes you to the web client: ![1C web client opened at the tunnel address](/docs/img/tuna_1c_web_client.png) For the thin client, the header of the [terminal interface](https://tuna.am/en/docs/tunnels/tui.md) has a `1C client` line with a ready-made launch parameter: ```shell 1cv8c.exe ENTERPRISE /WS"https:///1c" ``` You can also add the same address to the launcher list as an infobase located on a web server. So that users don't have to change the link and the `/WS` parameter after every restart, set a permanent address: a subdomain (`--subdomain=my-1c`) or a custom domain (`--domain=1c.example.com`). #### Your infobase is on the internet — lock it down `tuna 1c` opens the infobase to the whole internet, and only the 1C login window stands between a stranger and your data. That's why it's worth locking down the publication at the tunnel level as well — then an unwanted request never even reaches 1C: | Flag | What it does | | ------------------------------- | --------------------------------------------------------------- | | `--basic-auth="login:password"` | Asks for a login and password before the request reaches 1C | | `--key-auth="my-secret-key"` | Lets through only requests with the key in the `X-Token` header | | `--cidr-allow="203.0.113.0/24"` | Allows access only from the specified subnets | | `--rate-limit=20` | Limits the request rate | If you run the command without `--basic-auth` and `--key-auth`, tuna shows a warning. Also check the passwords of the infobase users themselves: an account without a password, especially one with administrator rights, is an open door. All requests to the infobase show up in the [Request Inspector](https://tuna.am/en/docs/tunnels/http/inspect.md), so you always know who accessed it and when. #### What you need * **A tuna [subscription](https://tuna.am/#pricing).** You can't publish an infobase on the free plan. * **Windows x64.** The command exists only in the tuna build for Windows x64, while the 1C platform itself can be either 32-bit or 64-bit. * **The "Web server extension modules" component** in the 1C installation. It may be unselected by default — the documentation has a [guide with screenshots](https://tuna.am/en/docs/tunnels/1c.md#web-module). * **A 1C license that allows connections through a web server.** The training edition won't work, but the free [developer license](https://tuna.am/en/docs/tunnels/1c.md#community) is enough to try it out. If tuna can't find 1C or the infobase, `tuna 1c discover` shows which installations, modules and infobases tuna sees on the computer. #### Who it's for * **Remote employees** — an accountant or a manager opens the infobase from home in a browser, without a VPN or remote desktop. * **1C developers and implementation consultants** — show a client your customization right from your own computer, without setting up a separate test environment. * **Small offices** — access to the infobase without a public IP, router configuration or a web server of your own. * **Integrations** — you can edit the `default.vrd` publication file by hand: for example, enable OData or HTTP services and debug data exchange with an external system. #### The command is new We tested it on Windows 11 with the 32-bit 1C 8.3.27 platform and a file infobase. Client/server infobases on a production cluster, the 64-bit platform and 1C 8.5 haven't been tested yet — see the full list in the [documentation](https://tuna.am/en/docs/tunnels/1c.md#limitations). If something doesn't work for you, [let us know](https://t.me/tuna_support) and we'll help you sort it out. `tuna 1c` is available with a [subscription](https://tuna.am/#pricing) starting with **tuna-cli 0.37.0**. All flags and details are in the [documentation](https://tuna.am/en/docs/tunnels/1c.md), and you can update the client by following the [update guide](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🕵️ AIOps: our experience rolling out HolmesGPT across three projects](https://tuna.am/en/blog/holmesgpt-aiops.md)[older →☸️ Kubernetes tunnel — temporary cluster access with a single link](https://tuna.am/en/blog/kubernetes-tunnel.md) --- ### Приложения — Zero Trust доступ к вашим сервисам [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) May 29 · 5 min Мы рады представить новый компонент платформы Tuna — **Приложения**. Это защищённый reverse proxy, который закрывает любой ваш веб-сервис авторизацией, SSO и гранулярным контролем доступа — без единой строчки изменений в самом сервисе. [Перейти к разделу Приложения в документации](https://tuna.am/en/docs/apps.md) ![Главная страница приложений](/docs/img/apps/main.png) #### Зачем это нужно? Представьте: у вас есть внутренний сервис — админка, staging-окружение, Grafana, внутренний API. Вы хотите дать к нему доступ только нужным людям, из нужных стран, с подтверждением через корпоративный SSO. Раньше это означало возню с nginx, oauth2-proxy, сертификатами и списками доступа. С **Приложениями** Tuna проверяет каждый входящий запрос: кто пользователь, как он аутентифицировался, откуда пришёл и имеет ли право видеть запрашиваемый путь. Всё это настраивается через веб-интерфейс за пару минут. Что можно сделать: * закрыть любой веб-сервис авторизацией без изменений в коде; * гранулярно разграничить доступ — например, `/admin/*` только для администраторов из России, а `/api/health` открыть без аутентификации; * подключить корпоративный SSO (Google, Yandex, GitHub, GitLab, VK, любой OIDC-совместимый провайдер); * требовать MFA или ручное одобрение администратора для чувствительных операций; * опубликовать сервис без публичного IP через туннель — достаточно одной команды; * выдать машинный доступ через сервисные токены для CI/CD, мониторинга и автоматизации. #### Два режима доставки трафика При создании приложения вы выбираете, как Tuna будет доходить до вашего сервиса. | Режим | Когда использовать | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Прокси** | Сервис уже доступен по сети — в Kubernetes, облачной VM или той же сети, что и нода. Можно указать несколько upstream с балансировкой по весам | | **Туннель** | Сервис без публичного IP — за NAT, на dev-машине или в корпоративной сети. Бэкенд сам подключается к Tuna через клиент-агент | В режиме туннеля после создания приложения вы получаете готовую команду: ```shell tuna app <имя> --url=http://127.0.0.1:8080 ``` Запустите её на машине с сервисом — и приложение станет доступно через защищённый домен. #### Политики доступа — сердце системы Каждый запрос проходит через цепочку **политик**. Первая политика, чей шаблон пути совпал, определяет судьбу запроса: пропустить, потребовать MFA, запросить одобрение или отказать. По умолчанию доступ запрещён. ![Список политик приложения](/docs/img/apps/policies_list.png) Каждая политика — это **паттерн пути** (glob-шаблон вроде `/admin/*`) и **действие**: * **Разрешить** — доступ при прохождении правил; * **Требовать MFA** — то же плюс второй фактор (TOTP или код на email); * **Запретить** — безусловная блокировка; * **Пропустить** — путь без аутентификации (health check, webhook-эндпоинты). Внутри политики настраиваются **правила** — *кто именно* получит доступ. Их можно комбинировать по режимам OR / AND / NOT и собирать из разных типов: | Тип правила | Пример | | ------------------------------ | ----------------------- | | Команда / Email / Email RegExp | `.*@company\.com$` | | IP / CIDR | `10.0.0.0/8` | | Страна (GeoIP) | `RU`, `US`, `DE` | | Способ входа | `oidc`, `otp`, `mfa` | | Провайдер | `google` | | Service Token | машинная аутентификация | Например, чтобы открыть `/admin/*` только участникам команды и только из России, достаточно одной политики с правилами «Команда» (or) и «Страна: RU» (and). ##### Тестирование без риска Прежде чем применять конфигурацию, её можно проверить инструментом **Тест политик** — смоделировать запрос с любыми параметрами и увидеть, какое решение примет система. Без создания реальных сессий. ![Тестирование политик](/docs/img/apps/policy_test.png) #### Вход через корпоративный SSO Способы входа комбинируются — пользователь увидит их все на странице входа: * **Email OTP** — одноразовый код на email, удобно для внешних подрядчиков; * **Аккаунты tuna.am** — вход через личный аккаунт; * **Провайдеры команды** — внешние Identity Provider (Google, Yandex, GitHub, GitLab, VK, любой OIDC). ![Страница входа с провайдерами](/docs/img/apps/auth_page_providers.png) #### Ещё больше контроля **Запросы доступа** — для чувствительных ресурсов включите ручное одобрение. Пользователь объяснит причину, администратор одобрит доступ на нужный срок (от 15 минут до бессрочного). **Сервисные токены** — машинная аутентификация для CI/CD, мониторинга и скриптов. Пара Client ID + Client Secret, передаётся в заголовках. Поддерживается ротация с grace-периодом — обновляйте секрет без простоя. ```shell curl -H 'Tuna-Access-Client-Id: sat_...' \ -H 'Tuna-Access-Client-Secret: ...' \ https://your-app.example.com/ ``` **Сессии** — пользователи видят свои активные сессии, администраторы — сессии всей команды с полной информацией (IP, страна, User-Agent, последняя активность). Любую сессию можно отозвать, а кнопка «Закрыть все сессии» завершает их разом. **Гибкие настройки прокси** — заголовки запроса и ответа, переписывание путей, CORS, лимит размера тела, TLS до upstream и клиентский сертификат (mTLS). #### Полный аудит Все действия с приложениями записываются в [аудит-лог](https://my.tuna.am/team_audit_events?lang=en): создание и изменение приложений, входы пользователей, одобрения и отклонения запросов доступа, отзыв сессий. Каждое событие содержит детали — кто, когда, откуда и что именно произошло. ![Аудит-лог событий приложений](/docs/img/apps/audit_events.png) #### Попробуйте прямо сейчас Перейдите в раздел [Приложения](https://my.tuna.am/team_apps?lang=en), нажмите **+ Добавить приложение** — и закройте свой первый сервис авторизацией за пару минут. Подробное описание всех возможностей — в [документации](https://tuna.am/en/docs/apps.md): * [Политики доступа](https://tuna.am/en/docs/apps/policies.md) * [Запросы доступа](https://tuna.am/en/docs/apps/access_requests.md) * [Сервисные токены](https://tuna.am/en/docs/apps/service_tokens.md) * [Сессии](https://tuna.am/en/docs/apps/sessions.md) * [Настройки приложения](https://tuna.am/en/docs/apps/settings.md) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🔐 Безопасность Секретов и отличие от Паролей - мастер-ключ, шифрование и общая архитектура.](https://tuna.am/en/blog/secrets-security.md)[older →Не приходят webhooks от api.telegram.org в туннель в RU зоне](https://tuna.am/en/blog/tunnel-location.md) --- ### Кто может зайти в ваш внутренний сервис? 5 способов закрыться авторизацией [Blog](https://tuna.am/en/blog.md)|[Guides](https://tuna.am/en/blog/tags/guides)|[Security](https://tuna.am/en/blog/tags/security) July 2 · 6 min Внутренние сервисы иногда нужно открывать — сотруднику, подрядчику или заказчику. Например, открыть staging, служебную панель, внутреннюю админку или API для интеграций. Или HTTP-сервис без собственной авторизации, вроде Prometheus или Alertmanager. Возникает задача: не встраивать логин в каждый сервис отдельно, а поставить перед ним слой доступа и решить, кого пускать, каким способом и к каким путям. Способов решить эту задачу несколько, и каждый хорош для своей ситуации. Разберём от самых простых к более гибким по контролю. В конце — сравнительная таблица. [Перейти к разделу Приложения в документации](https://tuna.am/en/docs/apps.md) ![Главная страница приложений](/docs/img/apps/policies_list.png) #### Способ 1. VPN: не выставлять сервис наружу вообще Классика: сервис живёт в приватной сети, доступ — только через VPN. ![VPN](/blog/img/2026-07-02-apps-access-methods/VPN.webp) **Когда работает.** В компании уже есть поднятый и поддерживаемый VPN, все пользователи — штатные сотрудники с настроенными клиентами, и никого извне пускать не нужно. **Ограничения:** * Контроль на уровне сети, а не на уровне пользователя и пути. Настроить так, чтобы разработчики имели полный доступ, а дизайнеры — только к /dashboards, не получится. * VPN кто-то должен поднимать и поддерживать. Избыточен для небольших проектов. Разворачивать всё это ради одной Grafana и пяти человек — стрельба из пушки по воробьям. #### Способ 2. Basic auth: логин-пароль на прокси Самый быстрый вариант: повесить HTTP basic auth на nginx или прямо на туннель — в Tuna это делается флагом при запуске: ```bash tuna http 3000 --basic-auth="user:password" ``` ![Basic auth](/blog/img/2026-07-02-apps-access-methods/basic_auth.png) **Когда работает.** Нужно на день-два показать демо заказчику или прикрыть тестовый стенд от случайных глаз и ботов. Одна пара логин-пароль, ноль настройки. **Ограничения.** * Не видно, кто заходил. Пароль один на всех, его пересылают в чатах — персональной привязки нет, и вы не узнаете, кто именно был внутри. * Доступ нельзя отозвать у одного человека — только сменить пароль сразу всем. * Нет ролей, второго фактора и журнала. Basic auth просто спрашивает пароль, но не дает разграничить права или посмотреть историю входов. По сути это замок для честных людей: подходит для демо на пару дней, но не для постоянного доступа к админке. #### Способ 3. IP-фильтры: пускать только со своих адресов Дать доступ списку IP или подсетей на файрволе, в nginx или на туннеле. ![IP filters](/blog/img/2026-07-02-apps-access-methods/ip_filter.png) **Когда работает.** Доступ нужен из офиса со статическим адресом или с конкретных серверов (мониторинг, CI). Для машинного трафика с известных адресов — нормальное решение. **Ограничения:** * Когда люди работают из дома, с мобильного интернета и из поездок, и их адреса меняются. * Нет персональной привязки. Фильтр отвечает на вопрос, откуда пришли, но не кто пришёл. #### Способ 4. Собрать самому: nginx + oauth2-proxy Полноценная авторизация перед сервисом без правок его кода: реверс-прокси, перед ним oauth2-proxy, OIDC-провайдер, сертификаты. ![Nginx + oauth2-proxy](/blog/img/2026-07-02-apps-access-methods/OAuth2_Proxy_horizontal.svg) **Когда работает.** У вас есть DevOps-ресурс, нестандартные требования и желание полностью контролировать каждый слой. Так работают многие инфраструктурные команды. **Ограничения:** * Выше порог входа. До запуска нужно связать прокси с провайдером, разобраться с cookie-доменами и редиректами, настроить обновление сертификатов. * Нужна поддержка. Связку нужно обслуживать и переделывать при каждой смене требований. * Тонкие правила собираются вручную. Чтобы открыть путь только одной группе пользователей и только с MFA, нужно использовать несколько инструментов. Для одного-двух внутренних сервисов такие трудозатраты редко окупаются. #### Способ 5. Zero Trust реверс-прокси: готовый слой авторизации Идея та же, что в способе 4 — проверять каждый запрос до того, как он дойдёт до приложения, — но всю инфраструктуру авторизации берёт на себя готовая платформа. Этот подход называют [Zero Trust](https://en.wikipedia.org/wiki/Zero_trust_architecture). По нему работают корпоративные продукты вроде Cloudflare Access, а в **Tuna** это сервис [Приложения](https://tuna.am/docs/apps/). ![Zero Trust](/docs/img/apps/auth_page.png) Как это выглядит на практике. Вы создаёте приложение в веб-интерфейсе и описываете правила доступа: какие пути кому открыты, где требовать второй фактор, кого не пускать вовсе. Проверка происходит на стороне Tuna — до вашего сервиса доходят только запросы, прошедшие все правила. В коде приложения ничего менять не нужно. Несколько вещей, которые в самосборном варианте потребовали бы отдельных инструментов, здесь есть из коробки: * **разные способы входа под разных людей** — корпоративный SSO для команды (Google, Yandex, GitHub, GitLab, VK или любой OIDC-провайдер) и одноразовый код на email для внешнего подрядчика, которому не нужно заводить аккаунт; * **правила на уровне путей** — например, служебный `/api/health` оставить открытым для проверок, а всё остальное закрыть; правила комбинируются по email, команде, стране, IP и способу входа; * **управление доступом** — режим запроса доступа с ручным одобрением (человек объясняет, зачем ему вход, а вы решаете, пускать ли), видимость активных сессий и отзыв доступа в любой момент: в разделе сессий видно, кто сейчас внутри, и любую сессию можно закрыть точечно или все разом; * **сервисные токены** для CI/CD и мониторинга — машинный трафик ходит отдельно от человеческого и так же подчиняется правилам; * **журнал и сессии** — в аудит-лог пишется, кто и откуда заходил, кто менял правила и выдавал доступ; видно историю входов и действий администраторов. Если у сервиса нет публичного IP — он на ноутбуке, за NAT, в закрытом контуре — его можно опубликовать через туннель одной командой: ```bash tuna app my-service --url=http://127.0.0.1:8080 ``` Подробный разбор настройки — в [документации](https://tuna.am/docs/apps/), анонс с обзором возможностей — [в блоге](https://tuna.am/blog/apps/). **Ограничения:** * Это решение для веб-сервисов и HTTP API. Доступ к базе данных или SSH-доступ к серверам — отдельные задачи, для них есть [TCP-туннели с IP-фильтрами](https://tuna.am/docs/tunnels/tcp/) и [Бастион](https://tuna.am/docs/bastion/). * Как любой SaaS, платформа — это внешняя зависимость: если политика компании требует держать контроль доступа строго внутри контура, ваш путь — способ 4. #### Сравнение | | Известен пользователь | Правила доступа | MFA | Отзыв доступа | Сложность запуска | Поддержка | | ---------------------------- | ------------------------- | ---------------------- | -------------- | ------------------------ | ----------------- | -------------------- | | VPN | частично (на уровне сети) | нет | зависит от VPN | вручную, профиль | средняя–высокая | постоянная | | Basic auth | нет | нет | нет | нельзя точечно | минуты | нулевая | | IP-фильтры | нет (только откуда) | нет | нет | правкой списка | минуты | рутина со списками | | nginx + oauth2-proxy | да | да, собирается вручную | да | вручную | дни | на вашей стороне | | Zero Trust proxy (Tuna Apps) | да | да, из интерфейса | да | в один шаг из интерфейса | минуты | на стороне платформы | #### Итог: как выбрать Показываете демо на пару дней → хватит basic auth на туннеле. Пускаете только серверы и офис со статическим IP → достаточно IP-фильтров. Вся команда штатная, VPN уже работает и есть кому его обслуживать → оставайтесь на VPN. Нужен контроль на уровне пользователей и путей, видно, кто заходил, и доступ нужно забирать точечно — но нет ресурса собирать и поддерживать связку прокси с OIDC — берите Zero Trust proxy. А если требования запрещают выносить авторизацию из своего контура → собирайте способ 4 и закладывайте время на поддержку. #### Попробуйте прямо сейчас Перейдите в раздел [Приложения](https://my.tuna.am/team_apps?lang=en), нажмите **+ Добавить приложение** — и закройте свой первый сервис авторизацией за пару минут. Подробное описание всех возможностей — в [документации](https://tuna.am/en/docs/apps.md): * [Политики доступа](https://tuna.am/en/docs/apps/policies.md) * [Запросы доступа](https://tuna.am/en/docs/apps/access_requests.md) * [Сервисные токены](https://tuna.am/en/docs/apps/service_tokens.md) * [Сессии](https://tuna.am/en/docs/apps/sessions.md) * [Настройки приложения](https://tuna.am/en/docs/apps/settings.md) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Guides](https://tuna.am/en/blog/tags/guides), [Security](https://tuna.am/en/blog/tags/security) [← newer🔐 Ключи доступа (passkeys) — вход в Tuna без пароля](https://tuna.am/en/blog/webauthn-passkeys.md)[older →🔐 Безопасность Секретов и отличие от Паролей - мастер-ключ, шифрование и общая архитектура.](https://tuna.am/en/blog/secrets-security.md) --- ### Новый инспектор запросов, шлюзы, IP политики и бастион [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) March 26, 2025 · 3 min В этом релизе мы привнесли множество новшеств, переработали инспектор запросов, ещё больше сосредоточились на безопасности и продолжаем развивать функции туннелей. #### Новый инспектор запросов Инспектор с более интуитивным дизайном, чтением тела запроса и функцией повтора, нас очень много просили и вот мы рады его представить. А для большего удобства мы встроили инструменты разработчика вроде **Base64 Encode/Decode** или **URL Encode/Decode** прямо в инспектор. *Если вы не нашли нужный инструмент, [напишите нам](mailto:info@tuna.am) и мы рассмотрим запрос.* #### Шлюзы [Шлюзы](https://tuna.am/en/docs/gateways.md) - это новая функция дополняющая функции туннелей и предназначена для создания всегда доступного облачного балансировщика или WEB сервера с гибкими политиками обработки трафика. ![gateways-main](/en/assets/images/main-19b8b7724c36ce8fe4c771d5447f65ea.png) Туннели это хорошо, но бывает окружение состоит из нескольких сервисов часть из которых запущено у вас, у вашего коллеги и в тестовом контуре. Но благодаря гибким [политикам трафика](https://tuna.am/en/docs/traffic-policy.md) вы можете распределять запросы в нужные точки, управлять заголовками, авторизацией, временем работы и много другое. > **Info.** На текущий момент функция доступна только в тарифе **Легенда**, но позже мы планируем это и для **Профи**. #### IP политики Если вы хотите быть уверенным, что все туннели и шлюзы доступны только для определённых подсетей, вы можете создать глобальные на команду IP политики и запретить или разрешить те или иные подсети по CIDR. ![team_ip_policies_1](/en/assets/images/team_ip_policies_1-b6efb1916fbb84bbbcc4e979cba39ceb.png) > **Info.** [Функция](https://tuna.am/en/docs/team/ip_policies.md) рассчитана только на использование в **Командах**. #### Бастион Кардинально новый функционал нацеленный на безопасный доступ к серверам и простую настройку. [Бастион](https://tuna.am/en/docs/bastion.md) - это SSH-сервер с ролевой мандатной моделью доступа, использующий принципы [нулевого доверия (Zero Trust)](https://en.wikipedia.org/wiki/Zero_trust_architecture). В отличие от классических SSH-серверов, бастион делает акцент на идентификацию пользователей, временные сертификаты и ролевую модель доступа, обеспечивая повышенную безопасность доступа к вашим серверам в купе с простой настройки. ![bastion](/en/assets/images/scheme-aeeed643ed226b44f550ed63551bc21f.png) ##### Зачем? Обычно предоставляя доступ коллегам к серверам по SSH вы просите их публичный RSA ключ и кладёте на сервер в `~/.ssh/authorized_keys` к нужному пользователю. Да, само по себе подключение безопасно, но без специальной подготовки и автоматизации инфраструктуры вы не можете узнать кто и когда подключался, отозвать доступ в любой момент, вы не знаете насколько надёжно сотрудники хранят свои приватные ключи, и не сделали ли они закладки чтобы иметь доступ даже в случае номинального отзыва доступа. С бастионом же, имеет ли пользователь доступ контролируется централизованно. Пользователь не может подключиться даже если незаметно положил свой ключ в `~/.ssh/authorized_keys` в обход вас, так как бастион использует иной подход. В первую очередь пользователю нужно пройти авторизацию на портале Tuna, получить временный сертификат и уже с ним подключаться к серверу. > **Info.** [Функция](https://tuna.am/en/docs/bastion.md) рассчитана только на использование в **Командах**. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerОшибка ERR_NGROK_9040. Ngrok закрыл доступ для всех российских IP-адресов](https://tuna.am/en/blog/err-ngrok-9040.md)[older →🔐 Менеджер паролей - зачем мы его сделали и как он устроен](https://tuna.am/en/blog/password-manager.md) --- ### Boards, Meets and other platform updates [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) March 24 · 4 min This is a big release! We've added two new major services - Boards and Meets, new features in Monitors and Reports, improved the request inspector, and made improvements to sshd tunnels. #### Boards The headliner of the release! [![Boards](/blog/img/2026-03-24/boards.png)](https://my.tuna.am/boards?lang=en) Like many of our tools, [Tuna Boards](https://my.tuna.am/boards?lang=en) grew out of an internal tool. Its first versions were extremely primitive and simple, but for us that was always enough. Here's what the project looked like historically ... ![Plato history 1](/blog/img/2026-03-24/boards_history_1.png) ![Plato history 2](/blog/img/2026-03-24/boards_history_2.png) ![Plato history 3](/blog/img/2026-03-24/boards_history_3.png) That's why we never used other alternatives like Miro and always tried to use our own internal product. If you've noticed, any diagrams and signature drawings are made exactly in this tool. Today we're pleased to introduce **Boards** as part of our developer platform. Here you can sketch out an infrastructure diagram, plan new features, design new UX and UI, and also use the built-in voice chat, timer and other features. *** #### Meets Video calls have long been an integral part of any online meeting; every self-respecting developer hates these damned calls, but now you can do it on our platform! Meet [Tuna Meets](https://my.tuna.am/meet?lang=en). [![Meets](/blog/img/2026-03-24/meets.png)](https://my.tuna.am/meet?lang=en) The functionality is fairly standard: there's a presenter mode, presentations, background blur and so on. The interface is fully adapted for mobile devices, so you can take calls both at your computer and on the go. *** #### Integrations with task trackers After we launched [Reports](https://tuna.am/en/docs/reports.md) and [Monitors](https://tuna.am/en/docs/monitors.md), it immediately became clear that it would be convenient, for an incident in monitors or for a new report, to be able to create a task in GitLab, Jira or Yandex.Tracker with one click. So we built integrations with the main popular task trackers so you can quickly create tasks in your favorite tracker. ![Integrations 1](/blog/img/2026-03-24/intagrations_1.png) ![Integrations 2](/blog/img/2026-03-24/intagrations_2.png) ![Integrations 3](/blog/img/2026-03-24/intagrations_3.png) *** #### Filter in the request inspector The request inspector now has a filter bar for requests by many parameters: request type, response status, headers, IP address and much more. To start using it, update to the latest version of [tuna-cli](https://tuna.am/en/docs/guides/update.md). [![Filters in the request inspector](/blog/img/2026-03-24/inspector_filter.png)](https://tuna.am/en/docs/tunnels/http/inspect) *** #### Improvements in SSHd tunnels We're not forgetting to develop [tunnels](https://tuna.am/en/docs/tunnels/ssh.md) either. 1. Added support for **Windows 11+**, the user gets a PWSH terminal. 2. Auto-detection of *$HOME*, *$SHELL* and the profile settings of the user under whom the SSHd tunnel is launched. 3. Switched to the asciinema format in ssh-sessions. So sessions now record everything, including work in text editors, launching TUI utilities, etc. Recordings can be watched in the terminal, embedded on a website with a standard player, or converted to GIF using the `tuna ssh-session export` command. **Asciinema player** **GIF** ![ssh_session_asciinema](/blog/img/2026-03-24/ssh_session_asciinema.gif) *** #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerНе приходят webhooks от api.telegram.org в туннель в RU зоне](https://tuna.am/en/blog/tunnel-location.md)[older →New plan names](https://tuna.am/en/blog/tariff-rename.md) --- ### tuna-cli 0.36 — интерфейс в терминале и большое обновление [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) September 30 · 6 min За последние два релиза консольный клиент **tuna-cli** изменился сильнее, чем за весь предыдущий год. Главное — у туннелей появился **полноэкранный интерфейс в терминале**: вместо бегущего текстового лога вы видите ленту запросов, метрики, сессии баз данных и SSH и можете разобрать любой запрос, не выходя из консоли. Обновиться до **0.36.1** можно по [инструкции](https://tuna.am/en/docs/guides/update.md), а сам клиент теперь подскажет нужную команду — об этом ниже. #### Интерфейс в терминале Интерфейс включается сам, когда вы запускаете `tuna http`, `tuna tcp`, `tuna ssh`, `tuna postgres`, `tuna redis` или `tuna kubernetes` в терминале. В шапке всегда видны аккаунт, адрес туннеля и локальный адрес, куда он ведёт, ссылка на [инспектор](https://tuna.am/en/docs/tunnels/http/inspect.md) и статус подключения. Если туннель запущен с ограничениями доступа или опциями, в шапке появляются строки **Access** и **Options** — например, `basic auth (1) · cidr deny 203.0.113.0/24` и `rate limit 20/s · cors`, так что видно, с какими настройками туннель работает прямо сейчас. Пароли и ключи не показываются, только их количество. Основная часть экрана зависит от типа туннеля. ##### HTTP: лента запросов и метрики Новые запросы появляются сверху, над лентой — счётчики: всего запросов, rpm, p50 и p90 времени ответа, число ответов 4xx и 5xx. По `Enter` открываются детали запроса с заголовками и отформатированным JSON-телом, `r` повторяет запрос, а `/` фильтрует ленту по методу, пути или статусу. Отлаживать вебхук или API стало заметно удобнее: не нужно переключаться в браузер, чтобы понять, что именно пришло. ##### Postgres и Redis: сессии и запросы Слева список сессий, справа — события выбранной: запросы с подсветкой, число строк в ответе, ошибки. `y` копирует запрос в буфер обмена. Видно, какие запросы шлёт ваше приложение или коллега, подключившийся к базе через туннель. ##### SSH: живой просмотр и запись сессий Для `tuna ssh` интерфейс показывает инструкцию для подключения и ленту сессий, включая отказы в доступе. `Enter` на активной сессии открывает её просмотр **в реальном времени**, на завершённой — воспроизведение записи. ##### Горячие клавиши | Клавиша | Действие | | ------------------ | -------------------------------------------------------- | | `c` | Скопировать адрес туннеля | | `i` | Открыть инспектор в браузере | | `Q` | Показать QR-код адреса — удобно открыть сайт на телефоне | | `l` | Показать или скрыть лог | | `↑` `↓`, `g` / `G` | Навигация по ленте | | `Ctrl+C` | Остановить туннель и выйти | Полный список клавиш для каждого типа туннеля — в [документации](https://tuna.am/en/docs/tunnels/tui.md). ##### Если нужен старый добрый лог Интерфейс не мешает автоматизации: он не включается, если вывод перенаправлен в файл или пайп, туннель запущен [как сервис](https://tuna.am/en/docs/tunnels/guides/service.md), задана переменная `CI` или `NONINTERACTIVE`, а также когда tuna запускает ИИ-агент — Claude Code, Cursor и другие. Вернуть текстовый лог вручную можно флагом `--log=stdout` или переменной `TUNA_TUI=0`: ```shell tuna http 8080 --log=stdout ``` #### Туннели ##### Unix-сокеты `tuna http` и `tuna tcp` умеют публиковать приложение, которое слушает не порт, а [unix-сокет](https://tuna.am/en/docs/tunnels/http.md#unix-socket) — например, gunicorn или php-fpm за прокси: ```shell tuna http unix:///run/gunicorn.sock ``` Инспектор, повтор запросов и политики трафика работают так же, как с портом. Через [TCP-туннель](https://tuna.am/en/docs/tunnels/tcp.md#unix-socket) можно, например, временно открыть Docker API — только обязательно ограничьте доступ своим IP через `--cidr-allow`: доступ к `docker.sock` равносилен root-доступу к хосту. ##### Политика трафика по ссылке и сжатие ответов [Политику трафика](https://tuna.am/en/docs/traffic-policy.md) для `tuna http` теперь можно загрузить по https-ссылке — одна политика в репозитории на все машины команды: ```shell tuna http 8080 --policy-url=https://gitlab.com/example/policies/-/raw/main/policy.yml ``` Новое действие [`compress_response`](https://tuna.am/en/docs/traffic-policy.md#compress_response) сжимает ответ приложения в `br`, `zstd`, `gzip` или `deflate`, если оно не сделало этого само. Ответ сжимается на лету, поэтому SSE и потоковые ответы продолжают работать. Заодно политика стала строже: если файл из `--policy-file` не найден, политика не парсится или у аккаунта нет подписки Team, туннель **не запустится** — раньше клиент выводил предупреждение и продолжал работу. Подробнее — в разделе [Ошибки политики](https://tuna.am/en/docs/traffic-policy.md#policy-errors). ##### Trigger получает данные события [`tuna trigger`](https://tuna.am/en/docs/tunnels/trigger.md) раньше просто запускал команду на входящий вебхук или письмо. Теперь команда получает само событие: тело запроса или письмо приходит на stdin, а метод, путь, query и заголовки — в переменных окружения `TUNA_REQUEST_*` и `TUNA_MAIL_*`. Так можно, например, запускать деплой только по push в `main`: deploy.sh ```shell #!/bin/sh set -e [ "$TUNA_REQUEST_HEADER_X_GITLAB_EVENT" = "Push Hook" ] || exit 0 jq -e '.ref == "refs/heads/main"' > /dev/null || exit 0 /opt/app/deploy.sh ``` SMTP-триггер теперь срабатывает на каждое принятое письмо, а не один раз на соединение, а локальный адрес SMTP-сервера задаётся флагом `--listen-address`. ##### Проверка подписи вебхуков GitHub и Sentry `--verify-webhook=github` и `--verify-webhook=sentry` теперь проверяют HMAC-SHA256 от тела запроса так, как описано в документации провайдеров. Раньше настоящие доставки от GitHub и Sentry получали `401` — если вы из-за этого отключали проверку, самое время включить её обратно. #### Секреты в консоли В tuna-cli 0.35 сильно вырос [CLI секретов](https://tuna.am/en/docs/secrets/cli.md): работа с отдельными секретами, управление проектами и окружениями, секреты файлом без записи на диск, офлайн-режим и отпечатки значений. Об этом — [отдельный пост](https://tuna.am/en/blog/secrets-cli.md). #### Мелочи, которые экономят время * **Подсказка по обновлению.** Когда выходит новая версия, клиент показывает готовую команду обновления для вашего способа установки: brew, winget, apt, dnf, zypper, скрипт установки или Docker. `tuna version` показывает способ установки и проверяет наличие новой версии. * **Подсветка справки.** В `--help` выделены команды, флаги и примеры. * **Подтверждение удаления.** Перед удалением доменов, портов, туннелей, API-ключей, секретов и проектов клиент переспрашивает. Пропустить вопрос — флаг `--yes`. * **`tuna whoami`** показывает, под каким аккаунтом авторизован клиент. #### Что проверить при обновлении * **Удаление в скриптах требует `--yes`.** Если команду удаления, например `tuna domain clear` или `tuna secrets delete`, запускают без терминала — в CI или cron, — без флага `--yes` она сразу завершится ошибкой. * **API-ключи выпускаются только в личном кабинете.** Команды `tuna api-key create` и `tuna session terminate` удалены: сервер больше не позволяет выпускать ключи и завершать веб-сессии по API-ключу. Ключи создаются в [личном кабинете](https://my.tuna.am/api_keys?lang=en). * **Новый репозиторий пакетов.** Пакеты для Linux переехали в реестр `code.tuna.am`, а пакет называется `tuna-cli`. На Debian и Ubuntu обычный `apt upgrade` сам переключит репозиторий, на RHEL, Fedora и openSUSE репозиторий нужно один раз сменить вручную — команды есть в [инструкции по обновлению](https://tuna.am/en/docs/guides/update.md). * **Сроки поддержки старых версий.** Для пользователей без подписки версии **0.32.x** перестают работать 1 октября 2026 года, **0.33.x** — 1 ноября, **0.34.x** — 1 декабря, **0.35.x** — 1 января 2027 года. Полная таблица — в [политике обновления](https://tuna.am/en/docs/guides/update.md). Полный список изменений по версиям — в [журнале изменений](https://tuna.am/en/changelog.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newer🧑‍💻 Terraform-провайдер для Tuna](https://tuna.am/en/blog/terraform-provider.md)[older →🗝️ Секреты целиком из консоли — большое обновление tuna secrets](https://tuna.am/en/blog/secrets-cli.md) --- ### Docs that are easy for agents to read: llms.txt and Markdown copies of pages [Blog](https://tuna.am/en/blog.md)|[Under the hood](https://tuna.am/en/blog/tags/engineering) October 28 · 8 min More and more often, documentation is read by an agent rather than a person. A developer asks Claude Code or Cursor to "expose my localhost:3000 with tuna behind a password", and the agent heads to tuna.am for the answer. What it finds there is an HTML page: menus, scripts, styles, icons, and somewhere in the middle, the command it needs. We made sure agents don't have to dig through the markup. Every page of our documentation has a Markdown copy, and the table of contents for the whole documentation fits in a single file. Replace the last slash in the address with `.md`, and you'll see the page the way an agent sees it: ```text https://tuna.am/en/docs/tunnels/http/ → https://tuna.am/en/docs/tunnels/http.md ``` > **TL;DR** > > * The table of contents — [tuna.am/en/llms.txt](https://tuna.am/en/llms.txt): over 250 pages in one list, with links and descriptions. The whole documentation in a single file — [llms-full.txt](https://tuna.am/en/llms-full.txt). > * Every documentation page and blog post has a Markdown copy: replace the last slash in the address with `.md`. The Russian version is at the same address without the `/en/` prefix. > * An agent that asks for Markdown with the `Accept: text/markdown` header gets it at the page's usual address. > * Want an agent to work with Tuna using our docs? Give it the link to `llms.txt`. If your agent works with Tuna and gets something in the docs wrong, [let us know](https://tuna.am/en/docs/support.md). Read on for why agents need all this, how it works on our side and how to do the same on your own Docusaurus site. #### Why agents need Markdown In HTML, the [HTTP tunnel](https://tuna.am/en/docs/tunnels/http.md) page weighs 88 KB, while its Markdown copy is 23 KB. The difference — almost four times — is menus, scripts, classes and service attributes. Agents don't need any of it, but it still costs tokens and clutters the context. Markdown is a native format for language models: they understand headings, lists, tables and code blocks with a language tag without any hints. Agents prefer it too: in [Vercel's study](https://vercel.com/kb/guide/make-your-documentation-readable-by-ai-agents), 65% of agents' web requests asked for Markdown. #### What llms.txt is and who actually reads it [llms.txt](https://llmstxt.org/) was proposed by Jeremy Howard in September 2024. It's a Markdown file at the root of a site: a heading with the name, a short description in a blockquote, and sections with lists of links. By convention, the `Optional` section holds secondary material that an agent can skip when its context is running low. The proposal also describes a convention for Markdown copies: a page has a version with `.md` in its address, and the HTML points to it with ``. An excerpt from our file: ```markdown # Tuna Documentation > Documentation for the tuna.am tunneling service ## Blog - [📒 1C tunnel — your infobase on the internet with a single command](https://tuna.am/en/blog/1c-tunnel.md): The new tuna 1c command publishes a 1C:Enterprise infobase… ``` Search and training crawlers hardly ever request these files. According to Ahrefs data, [as reported by PPC Land](https://ppc.land/llms-txt-adoption-rises-8-8x-but-97-of-files-get-zero-ai-requests/), 97% of llms.txt files across 137,000 domains got zero requests from AI systems in May 2026. In [dejan.ai's logs](https://dejan.ai/blog/crawler-census-robots-llms-okf/), over 20 days, crawlers from Google, Anthropic and OpenAI requested robots.txt 1,150 times and llms.txt four times. But that's fine: from the start, the author of the proposal expected the file to be useful when answering a question, not for training models. llms.txt isn't a way into search results — it's an entry point for an agent that's been pointed at your documentation. #### How it works on our side Markdown copies are built from the finished HTML, not from the sources. That way they include everything a person sees, embedded components included. The Tuna docs are built with Docusaurus. The Markdown copies are produced by the [@signalwire/docusaurus-plugin-llms-txt](https://github.com/signalwire/docusaurus-plugins/tree/main/packages/docusaurus-plugin-llms-txt) plugin. After the build, it takes the finished HTML of each page, turns it into Markdown and puts it next to the page, and along the way builds llms.txt and llms-full.txt for both locales. **Why from HTML and not from the sources.** Our documentation leans heavily on MDX — more than a hundred `.mdx` files, with and without `import`. A source-based converter would have dropped the components, leaving our pages less useful to agents. **Why an alpha version of the plugin.** The stable 1.2.2 broke every internal link with our `trailingSlash: true` setting: `/docs/foo/` turned into `/docs/foo/.md`. Version 2.0.0-alpha.7 fixes this and adds hooks for custom rehype plugins, without which nothing else would have been possible. After the build, a script adds a link to each page's copy in its ``, so an agent finds it even without knowing the `.md` convention. And nginx serves the copies with the right type and, if an agent asks for Markdown with the `Accept` header, serves the copy at the page's usual address. #### Pitfalls Turning on the plugin is easy. The first Markdown out of the converter looked decent — until we started actually reading it. **Anchors in headings.** Docusaurus adds an anchor link `#` to every heading. In Markdown, it carried over as is, so we cut it out. **Code blocks without a language.** Docusaurus keeps the block's language on `
`, while the converter looks for it on ``. More than 700 code blocks lost their `shell`, `yaml` or `ini` tag, so we move the class from `
` to ``.

**Tabs that ran together.** Tab labels ended up as a separate list, and the contents of all tabs followed one after another. On blocks with commands for AMD64 and ARM64, an agent would have to guess. Now each label sits inside its own tab:

```markdown
**Windows**

### Installation using winget

```

**Excessive escaping.** The Markdown generator escaped every `_` and `&`, so the `TUNA_TOKEN` variable turned into `TUNA\_TOKEN`. Links were the worst: an agent takes a link with `\&` in its UTM parameters literally and breaks it. We remove escaping where, per CommonMark, the character can't become markup: `_` inside a word and `&` not followed by a character reference.

**React comments.** During server-side rendering, React inserts `` between adjacent text nodes, and the converter carried them over as is: `user:pass`. We cut them out.

**Admonitions that lost their meaning.** Note, Tip and Caution boxes are blocks with an icon and a heading in HTML. In Markdown, all that remained was a lone line reading "note" above ordinary paragraphs, and an agent couldn't tell a warning from the text around it. Now an admonition becomes a blockquote with a heading:

```markdown
> **Note.** All flags with hints are in the built-in help:

```

**nginx didn't know about `.md`.** Without a MIME type, the file came back as `application/octet-stream`, and the browser downloaded the page instead of showing it. We added the type.

#### Try it yourself

```shell
curl https://tuna.am/en/llms.txt

```

```shell
curl https://tuna.am/en/docs/tunnels/http.md

```

```shell
curl -H "Accept: text/markdown" https://tuna.am/en/docs/tunnels/http/

```

Or give an agent a task:

```text
Read https://tuna.am/en/llms.txt, find out how to expose a local
server with tuna, and open localhost:3000 behind a password.

```

#### How to do the same on your own Docusaurus site

**1. Add the plugin.** If your config has `trailingSlash: true`, use the 2.x branch:

docusaurus.config.ts

```ts
plugins: [
  [
    '@signalwire/docusaurus-plugin-llms-txt',
    {
      markdown: {
        enableFiles: true,
        // An agent can open absolute links without knowing the base URL.
        relativePaths: false,
        includeDocs: true,
        includeBlog: true,
        includePages: true,
        excludeRoutes: ['**/search', '**/tags/**', '**/page/**'],
        beforeDefaultRehypePlugins: [rehypeCleanup],
      },
      llmsTxt: {
        enableLlmsFullTxt: true,
        siteTitle: 'Project documentation',
        siteDescription: 'A short summary of what the project is',
      },
    },
  ],
],

```

**2. Read your Markdown yourself.** Anchors, code block languages, tabs, admonitions, escaping — every documentation site has its own set of junk. The easiest place to fix it is your own rehype plugin in `beforeDefaultRehypePlugins`, while the page is still HTML.

**3. Teach your web server to serve Markdown.** Add the line `text/markdown md;` to `mime.types`. In your nginx config, add the charset and copy selection by `Accept`:

nginx

```nginx
charset utf-8;
charset_types text/markdown text/plain;

map $http_accept $markdown_requested {
    default           0;
    "~*text/markdown" 1;
}

map "$markdown_requested$uri" $markdown_twin {
    default              /.no-markdown-twin;
    "~^1(?/.+)/$"  $page.md;
}

map $markdown_requested $markdown_vary {
    default "";
    1       Accept;
}

map $markdown_requested $markdown_cache_control {
    default "";
    1       "private, no-store";
}

server {
    # ...
    location ~ /$ {
        try_files $markdown_twin ${uri}index.html =404;
        add_header Vary $markdown_vary;
        add_header Cache-Control $markdown_cache_control;
    }
}

```

If your site sits behind a CDN, check that it doesn't cache responses with `no-store`, or set up a separate rule for requests with `Accept: text/markdown`.

**4. Link to the copy in ``.** For us, a short post-build script does this: for every `index.html` that has a `.md` next to it, it inserts ``.

#### Leave feedback

If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en).

![](/blog/img/2024-08-22/feedback.gif)

#### News

We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything.

#### Help

We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways:

* [Email](mailto:info@tuna.am)
* [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy)

Categories: [Under the hood](https://tuna.am/en/blog/tags/engineering)

[older →🗞️ Что нового в Tuna — тёмная тема, адреса для файрвола и переезд Linux-пакетов](https://tuna.am/en/blog/whats-new-october-2026.md)


---

### Ошибка ERR_NGROK_9040. Ngrok закрыл доступ для всех российских IP-адресов

[Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news)

April 2, 2025 · 1 min

Пользователи массово столкнулись с проблемами в работе ngrok в России — при попытке запустить туннель отображается ошибка `ERR_NGROK_9040`: "We do not allow agents to connect to ngrok from your IP address". Это связано с санкциями, которые запрещают предоставление сервиса пользователям из России.

```sh
ERROR:  authentication failed: We do not allow agents to connect to ngrok from your IP address (198.51.100.55). 
ERROR: 
ERROR:  ERR_NGROK_9040 
ERROR:  https://ngrok.com/docs/errors/err_ngrok_9040  
ERROR:

```

В качестве решения проблемы с ошибкой `ERR_NGROK_9040` мы предлагаем воспользоваться нашим сервисом [Tuna](https://my.tuna.am/sign_up/?utm_source=blog&utm_medium=organic&utm_campaign=err_ngrok_9040&lang=en) – простой интерфейс, понятный функционал, в целом, всё, как надо.

[![demo](/en/assets/images/demo-5f3d687ab6fd177c90ef62d6fd811a1a.webp)](https://my.tuna.am/sign_up/?utm_source=blog&utm_medium=organic&utm_campaign=err_ngrok_9040&lang=en)

Categories: [News](https://tuna.am/en/blog/tags/news)

[← newerОбновление клиента tuna 0.23.0 и инспектор 1.1.0](https://tuna.am/en/blog/inspector-live-updates.md)[older →Новый инспектор запросов, шлюзы, IP политики и бастион](https://tuna.am/en/blog/bastion-inspect-geteway.md)


---

### Бесплатный тариф без таймера и с постоянным поддоменом

[Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features)

October 2 · 3 min

У бесплатного тарифа Tuna было два ограничения, на которые жаловались чаще всего: туннель жил только **30 минут**, а адрес при каждом запуске был новым. Оба ограничения мы убрали:

* HTTP-туннель на бесплатном тарифе работает **без ограничения по времени** — сколько нужно;
* у каждого пользователя есть **собственный постоянный поддомен** вида `brave-otter-4821.ru.tuna.am`, который не меняется между запусками.

#### Туннели без таймера

Раньше туннель на бесплатном тарифе отключался через полчаса, и его приходилось перезапускать — а вместе с ним менялся и адрес. Для быстрой демонстрации этого хватало, для чего-то длиннее — уже нет.

Теперь таймера нет: туннель работает, пока вы его не остановите. Одновременно на бесплатном тарифе по-прежнему доступен один HTTP-туннель.

#### Постоянный поддомен для каждого

Динамический адрес вида `4l7mqf-212-49-103-2.ru.tuna.am` хорош для разовой отладки, но неудобен там, где адрес нужно куда-то записать: в настройки вебхука, в колбэк платёжной системы, в закладку браузера или в сообщение коллеге. Поэтому теперь у каждого аккаунта есть один постоянный поддомен — бесплатно и на любом тарифе.

Как он устроен:

* **имя выдаётся автоматически** в формате `<прилагательное>-<существительное>-<4 цифры>`, например `brave-otter-4821`;
* **одно имя во всех локациях**: `brave-otter-4821.ru.tuna.am` и `brave-otter-4821.nl.tuna.am`;
* **закреплён за вашим аккаунтом навсегда**: удалить его или передать другому пользователю нельзя, `tuna domain clear` его пропускает;
* **выбрать или сменить имя нельзя** — поддомен с любым свободным именем, например `billing.ru.tuna.am`, по-прежнему доступен по [подписке](https://tuna.am/#pricing).

> **Если у вас подписка.** Бесплатный поддомен есть и на платных тарифах, и он **не расходует лимит доменов подписки**: он идёт в дополнение к поддоменам, которые входят в ваш тариф. При смене тарифа — в любую сторону — поддомен сохраняется.

#### Где найти свой поддомен

Поддомен показан в разделе [Домены](https://my.tuna.am/domains?lang=en) личного кабинета с меткой «Бесплатный», рядом лежит готовая команда запуска. Ещё его подсказывает сам клиент, если запустить туннель без флагов:

```text
Your permanent free domain: brave-otter-4821.ru.tuna.am. Use it with --subdomain=brave-otter-4821

```

Если вы зарегистрировались раньше, поддомен появится при первом открытии раздела **Домены** или при первом запуске туннеля без флагов.

#### Как им пользоваться

По умолчанию туннель, как и раньше, получает динамический адрес — постоянный поддомен нужно указать явно:

```shell
tuna http 8080 --subdomain=brave-otter-4821

```

В другой локации:

```shell
tuna http 8080 --subdomain=brave-otter-4821 --location=nl

```

Поддомен привязан к аккаунту, поэтому клиент должен быть авторизован **вашим** токеном. Сохраните его один раз командой `tuna login` или передайте через переменную окружения `TUNA_TOKEN` — например, в Docker и CI. Без токена или с токеном другого аккаунта поддомен не заработает.

Постоянный адрес особенно пригодится, чтобы:

* принимать вебхуки GitLab, GitHub или Telegram-бота без перенастройки после каждого перезапуска;
* показывать заказчику сайт по одной и той же ссылке;
* держать на телефоне закладку на свой dev-сервер.

Все подробности, включая таблицу видов адресов и тарифов, — в [документации по HTTP-туннелям](https://tuna.am/en/docs/tunnels/http.md#free-subdomain).

#### Что остаётся по подписке

* поддомены с любым свободным именем и собственные домены;
* несколько туннелей одновременно и остальные типы туннелей — TCP, SSH, Postgres, Redis, Kubernetes и другие;
* защита туннеля паролем, ключом и фильтрами по IP, [политики трафика](https://tuna.am/en/docs/traffic-policy.md), [шлюзы](https://tuna.am/en/docs/gateways.md) и прочие возможности платных тарифов.

Сравнить тарифы можно на [главной странице](https://tuna.am/#pricing).

#### Leave feedback

If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en).

![](/blog/img/2024-08-22/feedback.gif)

#### News

We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything.

#### Help

We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways:

* [Email](mailto:info@tuna.am)
* [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy)

Categories: [New features](https://tuna.am/en/blog/tags/features)

[← newer☸️ Kubernetes tunnel — temporary cluster access with a single link](https://tuna.am/en/blog/kubernetes-tunnel.md)[older →🧑‍💻 Terraform-провайдер для Tuna](https://tuna.am/en/blog/terraform-provider.md)


---

### Helm chart для запуска tuna в Kubernetes

[Blog](https://tuna.am/en/blog.md)|[Guides](https://tuna.am/en/blog/tags/guides)

February 5, 2025 · 3 min

Мы подготовили Helm chart для запуска tuna в Kubernetes.
В этой статье мы опишем случаи применения когда tuna может помочь вам в работе с приложениями в Kubernetes. #### Обзор В большинстве случаев вы можете обойтись штатными инструментами **kubectl**, опубликовать сервис просто создав Ingress, создать Service LoadBalancer у облачного провайдера для публикации доступа к базе данных, ну или просто запустить port-forward. Но не всегда штатные инструменты позволяют решать наши запросы достаточно быстро, удобно или дёшево. ##### Подключение репозитория Прежде чем начать, [установите Helm](https://helm.sh/docs/intro/install/).
Подключите репозиторий и обновите манифесты: ```shell helm repo add tuna https://releases.tuna.am/charts/ helm repo update tuna ``` ##### Основные значения (values) * `type` - [тип туннеля](https://tuna.am/en/docs/tunnels.md), передаётся как 1й аргумент к команде, доступные значения - `http, tcp, ssh`. * `to` - адрес назначения, это обязательный аргумент для типов `http, tcp`. Значение `to` передаётся в [переменную](https://tuna.am/en/docs/guides/environment-variables.md) `TUNA_PORT` Вы можете указать адрес сервиса в вашем кластере, например - `my-service:80` или `postgres-postgresql.postgres.svc:5432`. * `token` - ваш [токен](https://my.tuna.am/token?lang=en) для создания туннелей, если передать его в это поле, то он будет сохранён в секрет и передан в tuna как переменная окружения `TUNA_TOKEN`. Но это не обязательно, вы можете и просто передать его в `env.TUNA_TOKEN=` или в `args[0]='--token='`. Также скорее всего вы захотите задать дополнительные конфигурации: * `env: {}` - Конфигурация через [переменные окружения](https://tuna.am/en/docs/guides/environment-variables.md). * `args: []` - Конфигурация через [аргументы командной строки](https://tuna.am/en/docs/tunnels.md). Более подробно читайте в [отдельном разделе](https://tuna.am/en/docs/tunnels/examples/kubernetes.md). ##### Публикация сервиса Базовый пример это просто публикация сервиса. Например вы хотите получить доступ какой то админке или метрикам. Tuna также поможет открыть доступ к сервису в minikube в серой сети. ```shell helm install tuna --set to=my-service:80 --set token= tuna/tuna ``` ##### Доступ к базе данных и другим TCP ресурсам Допустим у нас есть Redis или MongoDB или Postgres, и нужно открыть доступ к нему всем на какой то промежуток времени. Создать Service LoadBalancer можно, но это дороже. Создать роль и раздать всем разработчикам доступ в Kubernetes, чтобы каждый сам запуска у себя kubectl port-forward тоже можно, но это значительно дольше и ещё всех надо научить. C Tuna же организовать это можно самостоятельно буквально за 5 минут, раздать всем адрес подключения и вот уже все могут подключаться к вашим TCP приложениям. Создайте `values.yml` файл: ```yaml type: tcp to: postgres-postgresql.postgres.svc:5432 token: ``` Запустите приложение: ```shell helm install tuna -f values.yml tuna/tuna ``` ##### SSH доступ Вы можете запустить Tuna с встроенным SSHd сервером и подключиться в контейнер, напрямую через ssh. Добавьте привилегии `securityContext` если нужно установить дополнительное ПО в контейнере, или используйте свой образ. Создайте `values.yml` файл: ```yaml type: ssh token: securityContext: capabilities: {} readOnlyRootFilesystem: false runAsNonRoot: false runAsUser: 0 ``` Запустите приложение: ```shell helm install tuna -f values.yml tuna/tuna ``` #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Guides](https://tuna.am/en/blog/tags/guides) [← newerДополнительные лицензии на увеличение лимитов](https://tuna.am/en/blog/licenses.md)[older →Доменные зоны (wildcard), Менеджер паролей и tuna 0.21.2](https://tuna.am/en/blog/release-pwd-wildcard.md) --- ### Tunnels are now highly available [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features)|[Under the hood](https://tuna.am/en/blog/tags/engineering) October 15 · 12 min ![Animation: one of three data centers is on fire, while visitor requests go through the other two and down a tunnel to a home lab, and the site keeps working](/blog/img/2026-10-15-high-availability/cover-en.svg) From day one, Tuna tunnels ran on a simple setup. A tunnel address pointed straight at a specific server, and that server did everything at once: it accepted traffic from the internet, held client connections and passed one to the other. That made sense for a launch. But Tuna has grown. Today we have more than a thousand tunnels online at any given moment, and our tunnel nodes accept tens of thousands of connections a day. For a service like that, the "everything on one server" design was no longer good enough. If a server went down for a reboot or lost its network, every tunnel on it went down with it. On October 14, we finished a migration we'd been preparing since the summer, and **our users' tunnels are now truly highly available**. It's no exaggeration to say this is the biggest change to Tuna's infrastructure since launch. No server and no data center is a single point of failure anymore. There's nothing you need to do. Addresses and certificates moved over on their own. > **TL;DR** > > * The point where traffic enters Tuna and the node your client connects to are now separate servers. A failure of either one doesn't take tunnels down. > * Entry points and tunnel nodes run in several data centers operated by two providers in Russia, Germany and the Netherlands. > * A request arrives at the entry point closest to the sender, not wherever the client is connected. That's why Telegram webhooks reach the tunnel no matter where the client runs. > * You no longer need to choose a region for new names, ports and domains. > * Free subdomains move from `ru.tuna.am` and `nl.tuna.am` to the `a.tuna.free` zone, and the old address stops working after the move. > * High availability works for HTTP and TCP tunnels, for subdomains in the new zones and for custom domains connected via CNAME. #### How it used to work The first version of the architecture had two regions, `ru` and `nl`. A tunnel's DNS name pointed directly at the IP of a tunnel node. The same node also ran a [Traefik](https://traefik.io/traefik) frontend that accepted requests from the internet and passed them into the tunnel. ![Animation of the old setup: the CLI gets a server address from the API and opens a tunnel to tuna-node; visitor requests go through Traefik on the same server; when the server fails, all tunnels become unavailable](/blog/img/2026-10-15-high-availability/before-en.svg) Traefik and tuna-node used to live on the same server. If it went down, the tunnels on it became unavailable. This setup had three weak spots, and we felt all three firsthand: * **A server failure hit all of its clients.** A kernel update, a provider outage or problems in the data center made every tunnel on the node unavailable until it came back. * **You had to choose a region.** Where traffic came in and where the client connected were tightly coupled: a tunnel in the `ru` region accepted traffic only through a Russian server. * **Availability depended on the route to a single server.** If the path from the sender to the node broke, the tunnel became unreachable for that sender. That's how, this spring, [Telegram webhooks](https://tuna.am/en/blog/tunnel-location.md) stopped reaching tunnels in the `ru` region. There was only one workaround: switch the tunnel to another region and change the webhook URL. #### Why now We'd known about the weak spots of the old setup for a long time, but this summer we really felt them. In June and July, the data center in the Netherlands that hosted our `nl` region node had several major outages. Each time, the node was down for days, and with it went the sites of every client whose tunnels ran through it. Technically, we could have moved the tunnels to another server, but that would only have partly solved the problem. Tunnels on our subdomains would have moved right away. But more than half of the tunnels on that node ran on custom domains like `example.com` connected via an A record. A new server would have had a new IP address, and every one of those clients would have had to update their DNS. That would most likely have taken longer than the data center's recovery, so waiting was the more sensible choice. That was the first wake-up call. After it, we sat down with diagrams and plans for what highly available tunnels should look like, and got to work. We didn't want to rush, so we tested everything in a staging environment. We took nodes down, switched off entry points and watched how quickly clients reconnected. Then, between October 8 and 11, drones damaged three Yandex data centers, and the Vladimir facility [had to be shut down completely](https://www.kommersant.ru/doc/9023925) (in Russian). Along with Yandex's own services, the websites of companies hosted in its cloud went down too. It was clear we couldn't put this off any longer, especially since by then the new architecture had been thoroughly tested. The switchover on October 14 took almost a whole day and finished at night, and in all that time tunnels were unavailable for about five minutes. #### How it works now We split the service into two layers, and each of them now runs on its own servers. **Entry points** accept traffic from the internet and terminate TLS. They now run [Envoy](https://www.envoyproxy.io/) instead of Traefik. The entry points are grouped into a Russian group and a European group. DNS sends requests from Russia to the Russian group and requests from all other countries to the European one, and within a group it picks the closest healthy entry point. **Tunnel nodes** hold client connections. There are several of them too, in Russia and Europe. The server assigns each client to a node close to it, taking load into account, and the entry points know where each tunnel currently is and deliver requests there. ![Animation of the new setup: RU and EU entry points are separate from tunnel nodes; a request from Russia and a Telegram webhook come in through different entry points and reach the node with the tunnel; when a node fails, the CLI reconnects to a neighboring one, and when an entry point fails, traffic moves to a neighboring one](/blog/img/2026-10-15-high-availability/after-en.svg) Entry points are now separate from nodes, and a failure of either means reconnecting within seconds rather than downtime. What this means in practice: * **An entry point failure is barely noticeable.** DNS drops the failed entry point, and traffic moves to a neighboring one within a few seconds. * **A tunnel node failure no longer means downtime.** The client moves to another node on its own, and the tunnel address stays the same. This usually takes seconds, or up to a minute when hundreds of tunnels move at once. * **It no longer matters where your client runs.** A Telegram webhook arrives at the entry point closest to Telegram and goes from there to the node your client is connected to, whether your client is in Russia or outside it. The problem from the spring post is now solved by the architecture, not by a workaround. #### No more choosing a region Since where traffic comes in is no longer tied to the node the client connects to, choosing a region doesn't make sense anymore. Random names, including on the free plan, and reserved names are now issued in the `a.2na.am` zone, and TCP addresses look like `a.2na.am:`. You don't need to choose a region for new addresses, and the dashboard no longer offers that choice. Reserved names and TCP ports in `ru.tuna.am` and `nl.tuna.am` stay where they are, and so do custom domains with a CNAME to these zones. They all keep working as before and now also go through the new entry points. The `--location` flag and the `TUNA_LOCATION` variable are now needed only for them: they tell the client which of the old zones to look in for a reserved name or port. They have no effect on the new zones. ##### Free subdomains There's now one free permanent subdomain, in the `a.tuna.free` zone, instead of a pair in `ru` and `nl`. Old free subdomains move there automatically. Once neither name has carried a tunnel for 24 hours and neither is attached to an App or a gateway, the pair in `ru.tuna.am` and `nl.tuna.am` is replaced with a single name in `a.tuna.free`. The subdomain stays the same if it's available there: `free-example-1234.ru.tuna.am` becomes `free-example-1234.a.tuna.free`. If it's taken, you get a random one. While the names are in use, they stay put. The old names are deleted during the move and can't be restored. The old address returns 404, and running a tunnel with `--domain=free-example-1234.ru.tuna.am` fails with `Domain does not exist`. If the old address is in QR codes, bookmarks or webhook settings, replace it with the new one. You can find the new name in the dashboard. To run a tunnel on the new name, the `-s` (`--subdomain`) flag with just the subdomain is enough, with no zone and no `--location`: ```bash tuna http 127.0.0.1:5192 -s=free-example-1234 ``` We don't plan to change names in `a.tuna.free`, so you can treat them as permanent. #### Where high availability applies * **Subdomains in the `a.2na.am` and `a.tuna.free` zones** and TCP ports, including SSH, Postgres and Redis over TCP. We've also tested WebSocket, HTTP/2 and SSE. * **Reserved names and TCP ports in the old `ru.tuna.am` and `nl.tuna.am` zones**. These zones themselves now point to `a.2na.am` via CNAME. * **Custom subdomains** like `app.example.com` or `www.example.com` connected via a CNAME to `a.2na.am` or to the old `ru.tuna.am` and `nl.tuna.am` zones. ngrok and Cloudflare Tunnel work the same way. A CNAME points to the infrastructure as a whole, not to a specific server. * **A team's custom domain zone** connected via a `*.` CNAME to `a.2na.am`, `ru.tuna.am` or `nl.tuna.am`. If your domain or zone is already connected via a CNAME to `ru.tuna.am` or `nl.tuna.am`, you don't need to change anything. #### Where high availability does not apply Domains connected via an A record aren't highly available, because an A record points to specific addresses. For a subdomain, switching it to a CNAME is enough. But an apex domain like `example.com`, meaning the domain itself without `www` or any other subdomain, can't be switched that way, because the DNS standard doesn't allow a CNAME there. Such a domain works, but it isn't fully fault-tolerant, and the dashboard warns you about it. We recommend moving your site to a subdomain with a CNAME: a landing page to `www.example.com`, a user dashboard or web app to `app.example.com` or `my.example.com`. On the apex domain, set up a permanent 301 or 308 redirect using a [gateway](https://tuna.am/en/docs/gateways.md) with the [`redirect`](https://tuna.am/en/docs/traffic-policy.md#redirect) action. The redirect itself won't be fault-tolerant, but the site behind it will be. Browsers remember permanent redirects, so visitors who have been to the site before go straight to the subdomain from then on. #### Other things we've left behind The minimal setup had been piling up problems for two years, and the migration fixed many of them at once: * a single server going down no longer takes down the tunnels of all its clients; * Telegram webhooks reach tunnels no matter where the client runs; * a custom domain connected via CNAME is no longer tied to a single IP; * if the DNS check for your custom domain fails, the dashboard tells you why right away, such as an address that isn't ours, an extra AAAA record or a missing record, instead of staying silent until the next day; * you don't need to choose a region, and there's one free subdomain instead of two. #### You can now host your server at home The Yandex data center story is a good reminder that any data center can one day disappear entirely, without warning. If your project is reachable from the internet through a service that lives on one server in one data center, your project is exactly as available as that server. That's no longer the case with Tuna. Entry points and tunnel nodes are spread across different data centers and countries, and losing one of them won't stop our users' traffic. So if your project isn't high-load and you've long wanted to host it at home in a home lab, on a mini PC or an old laptop, now is a good time. On Tuna's end, your site will stay available. The only things between you and real high availability are your home power and your internet provider, and those don't depend on us. A UPS protects you from power outages, and a backup connection from provider problems. #### What you need to do Reserved names, ports and custom domains work as before, and you don't need to move to the new domain. If you used a free subdomain in `ru.tuna.am` or `nl.tuna.am`, replace the old address with the new one in `a.tuna.free` wherever it's saved. See the details [above](#free-subdomains). We also recommend three things: * **Update the client** to the latest version. Versions older than 0.28 don't notice a hung node and wait for it to come back instead of reconnecting. Version 0.35.0 has a bug that prevents reconnecting. * **Switch your domain from an A record to a CNAME** pointing to `a.2na.am`. Old A records keep working, but only a CNAME gives you fault tolerance. We've covered what to do with a domain like `example.com` [above](#where-high-availability-does-not-apply). * **Update your IP allowlists** if you've added a node or tunnel address to them. A TCP address now has several IPs. If something doesn't work the way you expect, reach out in our [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) or email . #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features), [Under the hood](https://tuna.am/en/blog/tags/engineering) [← newer🛡 What to patch now: PHP, GitLab, Node.js, Brotli and eMudhra](https://tuna.am/en/blog/security-october-2026.md)[older →🕵️ AIOps: our experience rolling out HolmesGPT across three projects](https://tuna.am/en/blog/holmesgpt-aiops.md) --- ### AIOps: our experience rolling out HolmesGPT across three projects [Blog](https://tuna.am/en/blog.md)|[Under the hood](https://tuna.am/en/blog/tags/engineering) October 7 · 34 min ![Slack window: a KubernetesJobFailed alert from Alertmanager with a Holmes investigation below it, including a verdict and verification steps](/blog/img/2026-10-07-holmesgpt-aiops/slack-window.png) An example of an alert and its Holmes investigation in Slack. An alert lands in the chat with no context. "Pod is restarting", "queue is full", "Job failed" — and the on-call engineer always starts the same way: open a dashboard, find the Pod, scroll through the logs, try to remember what was deployed the day before. This first-pass investigation almost always follows the same pattern, and that's exactly the part we handed over to an agent. We've deployed [HolmesGPT](https://github.com/HolmesGPT/holmesgpt), an open-source SRE agent, in six Kubernetes clusters: Tuna's own cluster and five clusters belonging to clients we provide managed DevOps for. There are two client projects: one runs in a Russian cloud, the other is an international project based outside Russia that runs on two clouds. Along the way, we had to write our own tool, [holmes-alert-bridge](https://gitlab.com/yuccastream/holmes-alert-bridge), because the open-source version of HolmesGPT has no ready-made Alertmanager integration. The bridge receives an alert, filters out the noise, passes the alert to the agent along with context and brings the investigation back to the chat; we've open-sourced the code. Now, a few minutes after an alert fires, a hypothesis appears next to it: what's happening, why and what to check. Below is how it works, what it costs and what we learned in the first month. > **TL;DR** > > * HolmesGPT is an open-source SRE agent from the CNCF Sandbox: it queries Kubernetes, metrics and logs on its own and comes up with a hypothesis about what caused an alert. > * The open-source version has no built-in push integration with Alertmanager, so we wrote and open-sourced [holmes-alert-bridge](https://gitlab.com/yuccastream/holmes-alert-bridge). > * Our agent only reads and only forms hypotheses. It can't fix anything — we didn't let it. > * An investigation takes 2–3 minutes and costs a few rubles or 10–30 cents, depending on the model. > * Before rolling it out, check your logs for passwords, tokens and personal data: everything the agent reads goes to the model provider. #### Why we need it An alert rule only knows what its expression says: a metric crossed a threshold. It doesn't know why, and the annotation advising you to "raise the limit" was written once to cover every case. The on-call engineer gets the signal and spends the first few minutes gathering context before they can start thinking. HolmesGPT covers exactly this stretch. When an alert comes in, the engineer doesn't start the investigation from scratch: they already have a theory of what's going on, a list of what has been checked and two or three steps to confirm or rule out the theory. Across recent episodes, the median time from the alert firing to a finished report in the chat is about four minutes, including Alertmanager's grouping delay. Here's what it looks like in one real episode in Tuna — we go through it in detail below, in the [case studies](#tcp-listen-queue). The chart is built in our Grafana from ordinary metrics: the expression from the alert rule, the `ALERTS` series and the bridge counters. ![Grafana chart: a spike in dropped connections, the alert's pending and firing states, the period when Holmes was working and the moment the hypothesis appeared in the chat](/blog/img/2026-10-07-holmesgpt-aiops/alert-timeline.png) Top: the expression from the alert rule and its threshold (dashed line). Bottom: the alert state, Holmes at work and what the on-call engineer sees in the chat at that moment. | Time | What happened | | -------- | --------------------------------------------------------------------------------------------------------- | | 14:34:49 | the metric crossed the threshold and the alert went into `pending`: the rule has `for: 5m` | | 14:39:49 | the alert fired; 10 seconds later, Alertmanager sent a message to the chat and a copy to the bridge | | 14:39:59 | Holmes started investigating | | 14:42 | the spike ended and the alert resolved | | 14:44:07 | the investigation and its hypothesis were posted to the chat — 4 minutes 17 seconds after the alert fired | Just over nine minutes passed from the start of the problem to the hypothesis, and five of them were spent waiting on the rule itself. By then the alert had already resolved, but now it's clear what happened and whether anything needs to be done. We made three decisions right away. **Hypotheses only, no auto-remediation.** HolmesGPT itself can do more: give it write permissions and describe actions in skills, and it will not only look for the cause but fix it too. We leave this off on purpose: the agent only has read permissions, and its answer is labeled as a hypothesis, not a diagnosis. **Our quality figure is just an estimate, and we say so.** In our experience, the hypothesis turns out to be correct in 90–95% of cases. That's a gut feeling from on-call shifts, not a measurement: we haven't started systematically comparing investigations against the actual causes of incidents yet. **Every alert costs money.** This is an unexpectedly useful side effect. Once a recurring alert shows up as a line item on the bill, you're much more motivated to fix it for good instead of turning a blind eye to it for the hundredth time. The three setups differ, which makes them easy to compare: | | Tuna | Project in a Russian cloud | International project on two clouds | | ---------------------------- | ------------------------------------ | --------------------------------------------------------- | ----------------------------------- | | Clusters | 1 | 2 | 3 | | Model | DeepSeek V4.1 Flash | DeepSeek V4.1 Flash | Claude Sonnet 5 | | How it's connected | [OpenRouter](https://openrouter.ai/) | [Selectel AI Router](https://promo.selectel.ru/ai-router) | directly via the model provider | | Where the investigation goes | Telegram | Slack | Slack | #### What HolmesGPT is [HolmesGPT](https://holmesgpt.dev) is an agent for investigating problems in cloud infrastructure. It's licensed under Apache 2.0, was created at Robusta.dev and is developed with substantial involvement from Microsoft; since October 2025, it has been part of the CNCF Sandbox. It has about 3,500 stars on GitHub, and releases come out every one to two weeks — thirteen in the last four months. We use versions 0.41–0.42. Inside is a classic agent loop: the model gets a question, decides which tool to call, reads the result, calls the next one — and so on until it has an answer. A single alert takes twenty to thirty tool calls, and tangled cases come close to a hundred. It's built from four parts: * **Toolsets** — sets of tools: Kubernetes (resources, Pod logs, metrics), Prometheus, VictoriaLogs, ArgoCD, Grafana, network reachability checks and others. You can describe your own toolset as a YAML file with scripts, write it in Python or plug it in as an MCP server. * **Skills** — what used to be called runbooks: a `SKILL.md` file with a name and a description. The agent sees only the descriptions and loads the full text when it needs it. This is where knowledge about your project lives. * **Model** — anything litellm can reach: Anthropic, OpenAI, Azure, Bedrock, Gemini, Ollama, OpenRouter and any OpenAI-compatible API. * **Ways to run it** — a CLI with an interactive console, an HTTP server with a Helm chart, a Kubernetes operator (still in alpha) and a Python SDK. ![HolmesGPT architecture: alerts and questions as input, the model and runbooks on top, observability data at the bottom, root cause analysis as output](/blog/img/2026-10-07-holmesgpt-aiops/holmesgpt-architecture.png) Diagram from the [HolmesGPT README](https://github.com/HolmesGPT/holmesgpt), Apache 2.0 license. ##### How we deploy it We run it inside Kubernetes, with **a separate installation per cluster**. That way there's no need for complex authorization: the agent runs under its cluster's ServiceAccount with read-only permissions and sees nothing outside that cluster. You can also run Holmes on a dedicated server — from the CLI or docker-compose with a kubeconfig — but running it in the cluster is simpler. A minimal `values.yaml`: ```yaml additionalEnvVars: - name: MODEL # key of the modelList entry value: fast - name: OVERRIDE_MAX_CONTENT_SIZE # context window, see "Pitfalls" value: "400000" - name: OPENROUTER_API_KEY valueFrom: secretKeyRef: name: holmes-llm key: OPENROUTER_API_KEY modelList: fast: model: openrouter/deepseek/deepseek-v4.1-flash api_key: "{{ env.OPENROUTER_API_KEY }}" temperature: 0.5 extra_body: provider: zdr: true # see "Data and security" toolsets: kubernetes/core: enabled: true kubernetes/logs: enabled: true prometheus/metrics: enabled: true # + config with the Prometheus URL victorialogs: enabled: true # + config with the URL and credentials skills: enabled: true # explicitly disable anything that can write or reach the outside world bash: enabled: false internet: enabled: false robusta: enabled: false enableTelemetry: false ``` ```bash helm repo add robusta https://robusta-charts.storage.googleapis.com helm upgrade --install holmes robusta/holmes -n holmes --create-namespace -f values.yaml ``` Resource usage is modest: Holmes takes about half a gigabyte of memory for us, with spikes caused by tools like `kubectl` on large lists. ##### Not just alerts You can also ask the agent questions by hand — handy when there's no alert but there is a question. Use the CLI (`holmes ask "..."`) or query the installation in the cluster directly: ```bash kubectl -n holmes port-forward svc/holmes-holmes 8080:80 curl -s localhost:8080/api/chat -H 'Content-Type: application/json' \ -d '{"ask": "Why is the checkout Pod in the shop namespace restarting?"}' | jq -r .analysis ``` In the console, it looks like this: ![HolmesGPT console session: kubectl tool calls and the agent's final answer](/blog/img/2026-10-07-holmesgpt-aiops/holmesgpt-cli-demo.gif) Recording from the [HolmesGPT documentation](https://holmesgpt.dev): the agent calls tools and puts together an answer. #### A bridge to Alertmanager Here we were in for a surprise. The open-source version of HolmesGPT can only *pull* alerts: the `holmes investigate alertmanager` command reads the active alerts once and investigates them. The HTTP server has no webhook receiver; an alert trigger for the operator is still only planned; and automatic investigations in the Robusta product go through their SaaS platform. What we needed was simple: Alertmanager sends a webhook, and a couple of minutes later an investigation shows up in the same chat. That's how [holmes-alert-bridge](https://gitlab.com/yuccastream/holmes-alert-bridge) came about — a small Go service under the Apache 2.0 license. ![holmes-alert-bridge logo](/blog/img/2026-10-07-holmesgpt-aiops/holmes-alert-bridge-logo.svg) The alert's path: the regular notification goes to the chat as before, and a copy goes to the bridge and from there, if it passes the filters, to the agent. Almost everything the bridge does is about protecting the budget and everyone's nerves: * **Responds with 200 right away.** Holmes takes minutes to think, and Alertmanager won't wait that long. The investigation runs in the background. * **Allowlist by alert name.** With an empty list, the service refuses to start: investigating everything means a bill from the provider for every flapping alert. * **Deduplication by episode.** An episode is the alert name, namespace and start time. Repeated deliveries of the same episode are ignored. For failed CronJobs, the key is the CronJob name; otherwise, every new run would be a new episode. * **Throttling.** By default, no more than two investigations per hour and ten per day. This is a spending cap, not performance tuning. * **A single worker.** Investigations run strictly one at a time, and the service always has exactly one replica. * **A timeout that cuts the stream.** The agent's response is read as a stream, so after 15 minutes the bridge drops the connection, and Holmes stops at the end of its current step instead of finishing the job in the background at your expense. * **State in a database.** Deduplication and throttling marks survive a restart: SQLite by default, or PostgreSQL or MySQL if you already have one. * **Delivery.** Slack, Telegram, email or any webhook. The agent's Markdown is converted to the channel's markup, and a long investigation isn't truncated but split into parts. Below the investigation are the duration, cost, model and a link to the full record in VictoriaLogs. How much this filters out: in Tuna, Alertmanager delivered 645 alerts to the bridge in the first month, and 41 of them reached the agent — deduplication, the allowlist and throttling took care of the rest. The picture is the same in the other setups: over 90% of deliveries don't trigger an investigation. Here's what a finished report looks like — it's the same picture as at the beginning of the post: ![Slack window: a KubernetesJobFailed alert from Alertmanager with a Holmes investigation below it, including a verdict and verification steps](/blog/img/2026-10-07-holmesgpt-aiops/slack-window.png) An example message ##### Installation You need a working HolmesGPT installation in the cluster and a list of alerts worth investigating. ```bash helm repo add holmes-alert-bridge \ https://gitlab.com/api/v4/projects/yuccastream%2Fholmes-alert-bridge/packages/helm/stable helm upgrade --install holmes-alert-bridge holmes-alert-bridge/holmes-alert-bridge \ -n holmes -f values.yaml ``` ```yaml config: alertAllowlist: - KubernetesPodCrashLooping - KubernetesJobFailed - InstanceDown holmesUrl: http://holmes-holmes.holmes.svc:80 holmesModel: fast # key from the Holmes modelList promptLang: ru clusterName: prod environment: production costCurrency: USD maxPerHour: 2 maxPerDay: 10 telegram: chatId: "-1001234567890" victoriaLogs: uiUrl: https://logs.example.com streamFilter: '{app="holmes-alert-bridge",cluster="prod"}' existingSecret: holmes-alert-bridge-secrets # TELEGRAM_BOT_TOKEN and the rest ``` On the Alertmanager side, add a receiver and a route with `continue: true` so the investigation supplements the regular notification instead of replacing it: ```yaml receivers: - name: holmes webhook_configs: - url: http://holmes-alert-bridge.holmes.svc:8080/webhook send_resolved: false route: routes: - receiver: holmes matchers: - severity =~ "warning|critical" continue: true ``` If your routing tree has no explicit catch-all, `continue` can take delivery away from alerts that used to fall through to the root receiver. In that case, it's easier to add the bridge to your existing receivers as a second entry in `webhook_configs` — that's what we did in four of the six clusters, leaving all the filtering to the allowlist. Everything else — channels, secrets, custom prompt and message templates — is covered in [INSTALL.md](https://gitlab.com/yuccastream/holmes-alert-bridge/-/blob/master/INSTALL.md). #### Models and budget ##### How we chose The pilot started in the Russian cloud project, and the very first run — on `glm-5.2` — was a wake-up call: a single question used up more than a million input tokens, and the investigation cost dozens of rubles. The agent takes dozens of turns, and every turn resends the entire accumulated history to the model. So we held a selection round: eight models, 52 runs in three waves, including a task with a known answer. We looked at inexpensive models first: monitoring shouldn't cost more than the thing it's watching. We added a couple of higher-tier models for comparison. | Model | Runs | Failures | Known-answer task | Cost per run | | ---------------------------- | ---- | -------- | ----------------- | ------------ | | `qwen3.7-flash` | 6 | 1 | 0 of 1 | ≈ 3 ₽ | | `gpt-5.6-luna` | 4 | 0 | 0.5 of 2 | ≈ 6 ₽ | | **`deepseek-v4-flash-0731`** | 16 | 0 | 4 of 4 | ≈ 16 ₽ | | `minimax-m3` | 6 | 1 | 0 of 1 | ≈ 24 ₽ | | `gemini-3.7-flash` | 4 | 1 | 1 of 1 | ≈ 41 ₽ | | `longcat-2.0` | 4 | 0 | 0 of 2 | ≈ 42 ₽ | | `glm-5.2` | 6 | 0 | 2 of 2 | ≈ 54 ₽ | | `deepseek-v4-pro-0813` | 6 | 0 | 2 of 2 | ≈ 77 ₽ | Cost per run is an upper estimate that ignores caching: with caching, an investigation is several times cheaper. Half a point means a partially correct answer. The sample is small: 4 to 16 runs per model. What we learned: * **You can't compare models on a single run.** A price gap that looked like an order of magnitude on the first run shrank to a factor of several over fifty runs: the spread within a single model turned out to be larger than the difference between models. * **A cheap model doesn't mean a cheap investigation.** Half of the candidates failed the known-answer task, and in the same way: they confidently reported that the component in question wasn't in the cluster. It was a managed autoscaler, which really isn't visible among the Pods. * **`deepseek-v4-flash` won**: 16 runs without a single failure and the correct answer in all four attempts. It's not the cheapest model on the list, but both cheaper models failed the known-answer task. In production, the first version didn't last long. In one setup, roughly one investigation in five didn't finish within 15 minutes. In another, the model got stuck repeating tool calls and timed out, and on top of that, the provider retired its version. In mid-September, we moved to DeepSeek V4.1 Flash. Its list price is several times higher, and the average investigation got more expensive too — but investigations started to finish, and to finish quickly. A caveat: we don't have a clean before-and-after comparison. Around the same time, we adjusted the tool instructions and updated the bridge, and we didn't measure the effect of each change separately. ##### What we ended up with | | Tuna | Project in a Russian cloud | International project | | ------------------------- | ------------------- | -------------------------- | --------------------- | | Model | DeepSeek V4.1 Flash | DeepSeek V4.1 Flash | Claude Sonnet 5 | | Successful investigations | 22 of 22 | 92.9% | 15 of 15 | | Duration, median | 2–3 minutes | 2–3 minutes | about a minute | | Cost per investigation | about 10 cents | a few rubles | 25–30 cents | The window is the 30 days up to October 6, 2026; the success rate is counted after things stabilized, that is, after the model switch and the release of bridge v0.11. Ruble amounts are estimates: the model prices in our configuration didn't match the router's price list. The monthly bill is a few dollars per project: about four dollars in Tuna according to the provider's billing, four to five in the international project across three clusters and under a thousand rubles for the two clusters in the Russian cloud. In Tuna, we checked the cost estimate that the bridge takes from the Holmes response against the billing for the key — it matched to the cent. ##### Why a cheap, fast model is enough An alert investigation takes about a million tokens, of which the model itself generates around twenty thousand. Everything else is accumulated history that the agent resends on every turn. That's why three properties of a model matter more than its list price per million tokens: * **Cheap cache reads.** By our estimate, about 90% of input tokens are read from the cache. That figure is back-calculated from the totals, not taken from the billing, but the order of magnitude is clear: the economics of an investigation rest on the cache. * **A large context window.** With a 200,000-token window, Holmes started compacting the history as early as around 126,000 tokens, then requested what it had already gathered all over again — and the investigation got longer and more expensive. With a 400,000-token window, compaction stopped. * **Reliable tool calling.** A model that now and then writes a tool call as plain text or breaks the argument JSON ends up costing more than any "smart" one. Where we use a pricier model: the international project has run Claude Sonnet 5, connected directly via the provider, since day one. We didn't compare models there. That project has few alerts — most of them resolve before Alertmanager gets around to sending them — so in absolute terms, the expensive model costs the same few dollars a month. Two practical tips: * **Set a monthly limit on the provider key.** The bridge's throttling only protects the path from Alertmanager; manual questions and the agent's other clients bypass it. * **Double-check prices.** If a model is defined in `modelList` with its own `input_cost_per_token` and `output_cost_per_token`, the cost in the report is calculated from them. Our numbers didn't match the router's price list, and the amounts in the chat were wrong. #### Data and security > **Check what's in your logs first.** With an external API, everything the agent reads goes to the model provider: log lines, manifests, events, metric labels. Before rolling it out, check your applications and logs for passwords, tokens and personal data. The agent can't tell a secret from an ordinary string: log content goes into the model request as is. What we recommend doing before the first alert: **1. Audit your logs.** The agent has access to Pod logs across the whole cluster. If an application writes an auth token or a customer's email address to its log, that needs to be fixed in the application — and before you connect the agent, not after. **2. Zero Data Retention — in two places.** ZDR means the provider doesn't store your requests. In OpenRouter, it's worth enabling both in the account settings and with the `provider.zdr: true` request parameter (the example above already has it). The account setting isn't visible from the cluster configuration, and the request parameter only works if the chain of libraries actually passes it along — together they back each other up. You verify this with a live request: with ZDR enabled, a call to a provider that isn't on the ZDR list returns a `ZDR violation` error. **3. A router that actually has ZDR.** Any router with an API will do, but it's better to pick one whose providers really offer zero retention. OpenRouter publishes a [live list](https://openrouter.ai/api/v1/endpoints/zdr) of such endpoints, and it's worth checking before you choose a model: the same model can be on the list with one host and off it with another. **4. Read-only permissions.** The HolmesGPT chart enables the `bash` and `internet` toolsets by default — disable them explicitly. The chart doesn't let the agent read Secrets, but it's RBAC that should prevent changes, not a sentence in the prompt. **5. A rule in a skill isn't a technical restriction.** We tell the agent to "work with aggregates, don't quote personal identifiers", and it mostly listens. But that's text, not a barrier, and you can't rely on it for protection. **6. Lock down the Holmes API itself.** By default, `/api/chat` is open without authentication to any Pod in the cluster — and so are the agent's read permissions and your budget with the provider. Your options are NetworkPolicies (make sure your CNI enforces them) or the `HOLMES_API_KEY` variable. Here's where we honestly stand. Tuna uses OpenRouter with ZDR enabled in both places, verified with live requests. In the Russian cloud project, requests go through the Selectel AI Router: there's no separate ZDR setting there, and we rely on the provider's stated request-handling policy. In the international project, the model is connected directly via its provider: the retention policy there depends on the contract and the organization's settings, it isn't visible from the cluster configuration, and it's a separate item on our checklist. #### VictoriaLogs Logs are the agent's main data source: they account for the largest share of tool calls. We recommend [VictoriaLogs](https://docs.victoriametrics.com/victorialogs/): it's fast, and its HTTP API is simple enough for scripts and agents to use with equal ease. HolmesGPT has a built-in toolset for it. We moved to VictoriaLogs — two projects from Loki and one from Graylog — precisely so that logs would be accessible programmatically, both to the agents we work with day to day and to Holmes. As a result, the log store plays three roles for the agent at once: * a source of application logs; * the agent's own memory: a skill searches the bridge logs for past investigations; * the target of the "full investigation" link under the chat message. This is the record that the "full investigation" link points to under the message from the [TCP queue case](#tcp-listen-queue). The collector's service fields are removed, and the investigation text is truncated: ```json { "_time": "2026-10-03T10:44:06.793Z", "_msg": "investigation done", "cluster": "yucca", "alertname": "TCPListenQueueOverflow", "episode": "TCPListenQueueOverflow//2026-10-03T10:39:49Z", "starts_at": "2026-10-03T10:39:49Z", "duration": "4m7s", "cost": "0.0542736192", "cost_currency": "USD", "prompt_tokens": "1119866", "completion_tokens": "20957", "total_tokens": "1140823", "analysis": "I found a skill named **tuna-network-attacks** и использовал его для разбора…" } ``` Investigations are stored in the logs as ordinary fields, so the budget takes a single query to calculate. For example, here's how much each alert cost over the month: ```text _time:30d {app="holmes-alert-bridge"} "investigation done" | stats by (alertname) count() n, sum(cost) total, avg(cost) avg | sort by (total) desc ``` Installing the toolset isn't enough — you also need to describe the field schema to the agent. Field names depend on the collector: with vector, the namespace is in `kubernetes.pod_namespace`; with fluent-bit, it's in `kubernetes.namespace_name`. The model won't guess that. Our instructions for the logs toolset boil down to this: ```text Log fields (collector: vector): app, cluster, kubernetes.pod_namespace, kubernetes.pod_name, level, _msg A bare "phrase" is searched in _msg only. To filter by a field write field:="value". Pass `start` only. Never pass `end`, null or "now". One failed call does not mean the storage is down: fix the query and retry once. Do not name a root cause from a tail of 50 lines. Count first: ... | stats by (level, app) count() n ``` #### Prompts and skills We split our knowledge about each project into four layers. **The investigation's boundaries** are set by the request's system prompt — it's built into the bridge and comes in Russian and English. The gist: you're investigating an alert with no human in the conversation; read-only; stay within the alert's namespace; your conclusion is a hypothesis; the answer goes to the chat as a single message — a verdict, the most likely cause and two or three verification steps. **The cluster profile** is a skill the agent reads first. It's the most useful text in the whole configuration. An anonymized example, structured like ours: ```markdown --- name: prod-cluster description: How the production cluster is built, what is normal here and what is not. Read this FIRST for any investigation in this cluster. --- ## What this cluster is Managed Kubernetes, production of an online store. Node pools: `apps` (stateless services, autoscaled) and `infra` (monitoring, ingress, ArgoCD). ## Where the workloads live - `shop` — API and web frontend, the only namespace that serves customers. - `jobs` — CronJobs: exports, reports, cleanup. A single failed run is rarely urgent. - `monitoring`, `ingress`, `argocd` — infrastructure. ## Databases are outside the cluster PostgreSQL and Redis are managed services. You cannot kubectl into them: use their metrics and `tcp_check`. ## ArgoCD owns the infrastructure A manual change will be reverted. Never suggest `kubectl edit`. ## What you may read, and what you may not Application logs may contain customer data. Work with counts and aggregates, never quote e-mails, phone numbers or order contents. ## What is normal here - Nodes of the `apps` pool are replaced every night: short NotReady and evictions between 02:00 and 03:00 UTC are expected. - `jobs/report-*` retries up to three times. Only the third failure matters. ## How to fill tool parameters Time range: pass `start` only. Log fields: see the logs toolset instructions. ## Blaming an external provider is a claim you can check Before writing "the payment gateway is down", run `tcp_check` against it and compare the error rate with the previous day. ## What always deserves escalation Anything in `shop` that lasts longer than 10 minutes; any sign of data loss. ``` **Skills for alert types and services.** The skeleton is always the same: ```markdown --- name: edge-triage description: Investigate EdgeDown and EdgeHighErrorRate. The edge proxies run on plain hosts OUTSIDE Kubernetes, so kubectl is useless here. --- ## What the edge is # layer → where it runs → systemd unit → metrics job ## How the alert reaches you # the rule expression, what the labels mean ## Workflow # 4–6 steps with ready-made LogsQL and PromQL queries ## Synthesize # what the answer must include, links to dashboards ``` **A hint in the rule itself.** Alert annotations are passed to the agent in full as part of the question. So you can put a short instruction right in the rule as a separate annotation that the chat message template doesn't display: ```yaml - alert: KubernetesJobFailed annotations: summary: "Job {{ $labels.job_name }} failed" ai: >- Jobs in the `jobs` namespace are retried by the scheduler. Check whether the next run succeeded before calling it an incident. ``` What we learned while writing all this: * **Skills in English, answers in Russian.** This combination works fine for us. * **A tool that isn't named in a skill almost never gets called.** We connected ArgoCD, Grafana and TCP checks to the agent — and it barely used them. So it's worth keeping a "tools you tend to forget" section in the cluster profile and naming the right tool directly in the steps of your skills. * **Put a rule where the model is sure to read it.** The ban on passing `end: "now"` was in the logs toolset instructions, but the model kept passing it anyway — dozens of failed calls. Only a general rule in the cluster profile helped; now it's also built into the bridge's system prompt. * **Disable broken toolsets explicitly.** Built-in toolsets with the `failed` status still end up in the system prompt, and the agent tries to use them. * **"An external service is to blame" is a claim you can check.** Until the agent had a network check tool and an instruction to check for itself, investigations of a partner API outage ended with the advice to "run curl". ##### The skill that found nothing for a month This is our most instructive mistake. The agent has a "have we investigated this before" skill: before investigating, it searches the bridge logs for past investigations of the same alert. It's a good idea, and in Tuna the agent used this skill more often than any other. In two of the three setups, it spent a month searching the wrong field. The query in the skill looked for the alert name as a bare phrase, and in LogsQL a bare phrase is searched only in the message text — which contains exactly `investigation done` and nothing else: ```text {app="holmes-alert-bridge"} "investigation done" "KubernetesJobFailed" → 0 records {app="holmes-alert-bridge"} "investigation done" alertname:="KubernetesJobFailed" → 73 records ``` The agent ran hundreds of these queries and dutifully reported "no past investigations" every time — including for an episode that had been investigated nineteen times that month. There was no error: an empty response is still a response. In the third setup, the query used a field filter, and memory worked there: the agent referred to its past conclusions and even corrected them. The fix was one line. The lesson is broader: **test an agent's skills like code**. Run every query in a skill by hand at least once and make sure it finds something. #### Case studies ##### TCP listen queue overflows, but sysctl isn't the culprit On one of Tuna's servers, `TCPListenQueueOverflow` fired: the kernel was dropping incoming connections because the listening socket's queue was full. The alert description has the standard advice baked in: raise `net.core.somaxconn` or the application's backlog. ![Telegram: the TCPListenQueueOverflow alert with advice to raise net.core.somaxconn, followed by a message that it resolved](/blog/img/2026-10-07-holmesgpt-aiops/telegram-alert.png) The alert in Telegram: it fired at 14:39 and resolved three minutes later. ![Telegram: the Holmes investigation of the TCPListenQueueOverflow alert — five checked steps, a verdict and what to check](/blog/img/2026-10-07-holmesgpt-aiops/telegram-holmes-report.png) The Holmes investigation arrived at 14:44 — in the same chat, below the alert. In four minutes, for five cents, Holmes came to a different conclusion: 1. The overflow is on one host only, and there were none in the previous seven days. The SYN cookies counter barely moved, so it's not a SYN flood. 2. The listener that stopped accepting connections is `sshd`. Its journal has entries about `MaxStartups throttling` kicking in and about dropped connections — around two thousand per episode. 3. The source is two addresses and roughly 180 login attempts with nonexistent usernames in 15 minutes. 4. The host's resources are fine; the other services on it and the neighboring hosts aren't affected. 5. The alert has already resolved, and the ports are responding. Verdict: a brief loss of TCP connections caused by an SSH connection flood. The queue overflowed because `sshd` hit its own `MaxStartups` limit, not because the host ran out of resources; `net.core.somaxconn` has nothing to do with it. One more observation: fail2ban doesn't stop this kind of flood, because the connections are dropped before authentication. The advice in the alert description would have sent the on-call engineer down the wrong path. The screenshot also shows two of our own sore spots described in this post: the line "no past investigations of this alert" — that very broken skill — and the agent's boilerplate preamble at the start of the message. ##### VirusTotal: noise or signal We have an [open-source tool](https://tuna.am/en/blog/vt-telegram-bot.md) that keeps an eye on how our IP addresses, published domains and build hashes look in antivirus databases and spam lists. The goal is to react before a user gets a "your software is malicious" warning. Its hits come in two kinds. Most of the time it's noise: one or two little-known engines have flagged a tunnel server's address. Once in a while, it's a real signal. Now every hit comes with a Holmes verdict: a dedicated skill tells the agent which engines to treat as noise and which combinations of signs are a reason to call in a human. ![A VirusTotal hit message with a Holmes verdict](/blog/img/2026-10-07-holmesgpt-aiops/telegram-virustotal-mockup.png) An example message In this example, the agent first concluded that the hit on the address was noise from low-coverage engines. But it noticed a new file in the report, flagged by a sizable number of engines, that sends traffic to a tunnel subdomain — and escalated: a malicious file is probably being distributed through the tunnel, and a human needs to step in. Along the way, it checked that there were no past verdicts on this file and that the tunnel wasn't in use at the time, which means the owner has to be tracked down through their account. This path goes to Holmes directly, bypassing the bridge — one more reason to keep a limit on the provider key. ##### Two more, briefly * **The agent investigated its own failure.** After an upgrade, the new Holmes Pod went into CrashLoop, and the alert about it went to the old Pod. The old Pod found an access denied error on a new path in the secrets store, compared the environments of the two revisions and noted that the service wasn't down, because the old Pod was still alive. A minute and a half, ten cents. * **One alert, two different causes.** In the Russian cloud project, the agent linked a memory shortage on a preemptible node to a scheduled replacement of a neighboring node and found a Pod that was using noticeably more memory than it had requested. Two days later, on the same alert, it named a different cause — overlapping CronJob schedules — and explicitly wrote how this episode differed from the previous one. #### Pitfalls **A long-firing alert gets investigated again every day.** The default deduplication window is 24 hours, so if an alert keeps firing longer than that, it dutifully gets investigated again. In one setup, these repeats ate up almost half the budget, and half of the production investigations came from a handful of chronically failing CronJobs. The cure works from both ends: increase the window (`dedupWindow`) to several days and finally fix whatever has been firing for weeks — a bill for investigations is very motivating. **The agent's boilerplate in messages.** Holmes leaves placeholders in its answer for embedding charts in the Robusta UI: blocks like `<< {"type": "promql", ...} >>` and images with the `tool-image://` scheme. In a chat this looks like junk, and it showed up in more than half of the investigations. Starting with v0.11.1, the bridge strips them out before sending. The "I found a skill named…" preamble at the start of the answer is harder to get rid of: it's Holmes's own format for working with skills, and a rule in the system prompt has no effect on it. **The model breaks tool arguments.** Every now and then, the model returns a call's arguments as a string with JSON inside instead of an object. Holmes fails on this with a validation error, and the whole investigation is lost — in one setup, this was the most common reason for failed investigations. In these cases, the bridge retries the investigation right away if the budget allows, and since v0.11.1, it logs when a retry didn't happen. At the end of September, these failures stopped on their own, with no changes on our side. **Alerts on the bridge itself.** For a month, we had no rules at all on the bridge's metrics: throttled alerts and timeouts only showed up in the logs. The bare minimum: ```yaml - alert: HolmesBridgeInvestigationsFailing expr: increase(holmes_bridge_investigations_total{status!="ok"}[1h]) > 0 - alert: HolmesBridgeThrottling expr: increase(holmes_bridge_alerts_throttled_total[1h]) > 0 ``` Don't add these alerts to the bridge's own allowlist. **Prices in `modelList` break the context window.** Give a model your own prices, and Holmes 0.41 no longer knows its window size and fails on the very first tool call. The workaround is the `OVERRIDE_MAX_CONTENT_SIZE` variable. Version 0.42 no longer fails, but its default window is 200,000 tokens, so for long investigations it's still worth setting explicitly. **Tools run out of memory.** On large lists, `kubectl` ran into the per-tool-call memory limit rather than the Pod's limit. That limit is set separately, with `TOOL_MEMORY_LIMIT_MB`. **Paired rules produce two investigations.** If the allowlist has both a general rule and its specialized twin, a single incident gets investigated twice, seconds apart. Keep only one of the pair. **A "successful" investigation without a single query.** On the old model, this happened twice: the agent read a couple of skills, made no queries to the data and confidently wrote that it had no access to metrics. Formally the investigation succeeded, and it cost a fraction of a cent. An abnormally cheap investigation is a reason to take a look at it yourself. And one upside: **`group_wait` is a free filter**. In the international project, Alertmanager waits three minutes before the first notification, and out of dozens of production episodes, only a handful reached the agent — anything that lasts a minute or two resolves before then. #### What's next What we haven't measured yet — and we don't want to pretend we have: * **hypothesis quality** — 90–95% remains an estimate until we label a sample of investigations against the actual causes of incidents; * **the effect on people** — how much shorter incident investigations have become. What we'd like to add to the bridge: * a metric for money spent, so the budget is visible in Grafana alongside the other counters; * smarter handling of long-firing alerts — a growing re-investigation interval instead of a fixed window; * an API key for requests to Holmes; * investigation links not only to VictoriaLogs but also to Loki, Elasticsearch and other stores; * new delivery channels — Mattermost, Microsoft Teams, Discord. What the bridge won't have: parallel investigations, routing rules (that's Alertmanager's job) and auto-remediation. The code, chart and documentation are in the [holmes-alert-bridge repository](https://gitlab.com/yuccastream/holmes-alert-bridge). If you try it out, let us know how it went: issues and merge requests are welcome. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Under the hood](https://tuna.am/en/blog/tags/engineering) [← newer🦎 Tunnels are now highly available](https://tuna.am/en/blog/high-availability.md)[older →📒 1C tunnel — your infobase on the internet with a single command](https://tuna.am/en/blog/1c-tunnel.md) --- ### Изменение настроек tuna при обновлении Immich [Blog](https://tuna.am/en/blog.md)|[Guides](https://tuna.am/en/blog/tags/guides) October 17, 2024 · 1 min Изменение настроек по умолчанию в [Immich](https://immich.app/?utm_source=tuna.am), сервисе для самостоятельного хранения фотографий, аналог [Google Photos](https://www.google.com/photos/about/?utm_source=tuna.am). ![immich-login](/docs/img/examples/immich.png) В immich-server версии [**v1.118**](https://github.com/immich-app/immich/releases/tag/v1.118.0) изменился порт подключения по умолчанию с **3001** на **2283**, поэтому нужно поменять аргумент у tuna с `immich-server:3001` на `immich-server:2283`. > **Было** > > ```yaml > tuna: > container_name: immich_tuna > image: yuccastream/tuna:latest > command: http immich-server:3001 --location=ru --subdomain=<ваш_поддомен> --inspect=false > env_file: > - .env > restart: always > depends_on: > - immich-server > > ``` > **Стало** > > ```yaml > tuna: > container_name: immich_tuna > image: yuccastream/tuna:latest > command: http immich-server:2283 --location=ru --subdomain=<ваш_поддомен> --inspect=false > env_file: > - .env > restart: always > depends_on: > - immich-server > > ``` Этот код есть и в [примере](https://tuna.am/en/docs/tunnels/examples/immich.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Guides](https://tuna.am/en/blog/tags/guides) [← newerВыпуск tuna 0.20.0](https://tuna.am/en/blog/release-v0-20-0.md)[older →Обзор возможностей работы в Легенде](https://tuna.am/en/blog/team-review.md) --- ### Обновление клиента tuna 0.23.0 и инспектор 1.1.0 [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) April 3, 2025 · 1 min В этом обновлении tuna 0.23.0 и инспектора 1.1.0 добавлено живое обновление списка запросов. [![inspector-live-updates](/en/assets/images/inspector-live-updates-b0bdc265f57916db1473f642f39ef087.webp)](https://tuna.am/en/docs/tunnels/http/inspect.md) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerПлановые работы 14.04.2025 в регионе NL (Нидерланы)](https://tuna.am/en/blog/maintain-nl.md)[older →Ошибка ERR_NGROK_9040. Ngrok закрыл доступ для всех российских IP-адресов](https://tuna.am/en/blog/err-ngrok-9040.md) --- ### Обновления - оплата по счёту и новые функции клиента [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) August 22, 2024 · 3 min Новые функций в этом выпуске! #### Оплата по счёту для организаций ![org_invoice](/en/assets/images/org_invoice-357a47311c2c3e7f9e7097c5dc18a18b.gif) Мы добавили возможность оплаты подписки за команду по счёту для организаций в России, а также блок с сравнением всех функций тарифов. Теперь вы можете создать команду, указать реквизиты организации и мы выставим счёт на оплату, который вы можете передать бухгалтеру. Счёт придёт на почту всем участникам команды с ролью **владелец**, а также в [настройках](https://my.tuna.am/team_settings?lang=en) организации можно указать дополнительные контакты ответственных за оплату людей. #### Обновления в клиенте tuna 0.18.0 Было исправлено несколько ошибок, в частности утечка памяти при включённом инспекторе запросов в `tuna http --file-server`, а также добавлены новые команды и флаги, о них подробнее ниже. ##### tuna http --webdav ![webdav](/en/assets/images/webdav-45082cfdea1afcf6d5962db16fe2387c.png) В команде `tuna http` появился новый флаг `--webdav`. С помощью встроенного WebDAV сервера теперь вы можете предоставить доступ к каталогу на вашем компьютере, от куда можно не только скачать файлы, но и загрузить их к вам. Поддержка WebDAV есть стандартных проводниках всех современных операционных систем, поэтому для использования не нужно никакое дополнительно ПО. ##### tuna run Запуск туннеля при запуске команды. ![tuna_run](/en/assets/images/tuna_run-6efa86afeb5224d166817a19bbc239f1.gif) В этом режиме tuna выступает как обёртка (wrapper) для запуска вашего приложения. Можно одной командой запустить туннель и ваше приложение, tuna самостоятельно обнаружит порт который слушает приложение и запустит туннель, например: ```shell tuna run -- yarn start --host 0.0.0.0 ``` Мы сами используем к примеру редактируя эту документацию, в проекте с документацией есть `Makefile` в котором описан запуск проекта через `tuna run`, редактируя эту статью запускаем в консоли `make`, стартует проект с документацией в режиме разработки и к нему открывается туннель, который можно отправить коллегам на ревью этой статьи 🙂 Пример Makefile: ```shell .DEFAULT_GOAL := run .PHONY: run run: ## Запуск сервера через tuna враппер @tuna run -- yarn start --host 0.0.0.0 ``` ##### tuna detect Обнаружение портов и подготовка команд для tuna. ![tuna_detect](/en/assets/images/tuna_detect-35f718721bdbb52c4189c38be84b54a2.gif) В этом режиме tuna также выступает как обёртка (wrapper), но не стартует в последствии туннель, а просто выводит в консоль порты, что были обнаружены и предлагает команду для запуска. Это полезно если вы не знаете какой порт слушает приложение которое вам нужно запустить, пример: ```shell % tuna detect -- nc -l 1234 INFO[19:06:56] Waiting for available ports... INFO[19:06:56] 5 INFO[19:06:57] 4 INFO[19:06:57] Found port: 1234 INFO[19:06:58] 3 INFO[19:06:59] 2 INFO[19:07:00] 1 INFO[19:07:01] Here is your tunnel commands: INFO[19:07:01] 1. tuna tcp 127.0.0.1:1234 ``` #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerОбратный SSH туннель](https://tuna.am/en/blog/ssh-reverse.md)[older →Новый функционал - Дополнительные токены и управление командой](https://tuna.am/en/blog/temp-tokens.md) --- ### Kubernetes tunnel — temporary cluster access with a single link [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) October 5 · 4 min Here's a familiar scenario: a contractor needs to check logs in the staging cluster, a colleague from another team needs to help with debugging, and the cluster's API server isn't reachable from the internet. The usual choice is between "set them up with a VPN and an account" and "send them your kubeconfig" — and both options are bad. We've added a new tunnel type to tuna-cli 0.36: **Kubernetes**. One command, and the guest gets a link to a ready-made kubeconfig that works with plain `kubectl`: ```shell tuna kubernetes --context=staging --service-account=shop/debug --preset=debug --ttl=4h ``` #### How it works The client reads your local kubeconfig, starts a reverse proxy to the API server and exposes it through an HTTP tunnel with a valid TLS certificate. The guest gets a one-time link: ```shell curl -sfO https:///tuna//kubeconfig.yaml KUBECONFIG=./kubeconfig.yaml kubectl get pods ``` The downloaded kubeconfig contains a **guest token**, not your credentials. The proxy checks this token and swaps it for your real credentials, which never leave your machine. Stop the tunnel and access is gone — there's nothing to revoke. Everything `kubectl` users are used to works through the tunnel: `get`, `list`, `watch`, `logs`, `exec` with stdin, `port-forward`, `pods/proxy` and writes — `scale`, `create`, `delete`. Essentially, it's an auth proxy for the Kubernetes API that starts with a single command and lives exactly as long as you need it. #### You control the guest's permissions The first line of defense is **RBAC**. By default, the guest acts with your permissions, and the client warns you about it up front. The better way is to create a service account with minimal permissions for the guest and pass it in the `--service-account` flag. The client then issues a token for it via the TokenRequest API and renews it every hour. The second line is **the tunnel's own restrictions**. They work on top of RBAC and can only narrow permissions: | Flag | What it does | | ------------------------------------ | ---------------------------------------------------------------------- | | `--namespace=shop` | A single namespace only, no cluster-scoped resources and no `-A` | | `--read-only` | Read-only: no `exec`, `attach` or `port-forward`, but logs are allowed | | `--no-delete` | Everything except deleting | | `--deny-resource=secrets,configmaps` | Denies specific resources | | `--deny-subresource=exec,attach` | Denies subresources | Presets save you from combining restrictions by hand: | Preset | What is allowed | | ---------- | -------------------------------------------------------------------- | | `view` | Read-only, no `secrets`, `exec`, `attach`, `port-forward` or `proxy` | | `observer` | Read-only access to pods and their logs | | `debug` | Everything except deleting | | `open` | No extra restrictions | Presets and flags add up and never weaken each other: `--preset=debug --read-only` gives read-only access. #### Lifetime and one-time links * `--ttl=4h` — after four hours, the guest's `kubectl` gets `this access has expired, ask for a new link`. * `--download-limit` — how many times the kubeconfig can be downloaded, **once** by default. If the guest gets `410` on the very first try, someone else got to the config first: stop the tunnel and issue a new link. Guest tokens live only in the process memory: after the client restarts, old links stop working. #### Full visibility The tunnel runs in the new [terminal interface](https://tuna.am/en/docs/tunnels/tui.md): the header shows the service account, namespace, restrictions and access expiry, and below it, a feed of the guest's requests. The same requests show up in the [Request Inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) as regular HTTP requests, so you always know exactly what the guest did in the cluster. #### Who it's for * **Contractors and outsourced teams** — give them access to staging for the duration of the job, without creating cloud accounts. * **Support and on-call engineers** — quickly let a colleague in to look at logs and pods in `observer` mode. * **Demos and training** — show a cluster at a workshop with `--read-only` and a short `--ttl`. * **Local clusters** — kind, minikube or k3s on your laptop become reachable to a colleague without port forwarding. If you need a permanent address, use a reserved subdomain (`--subdomain=k8s-staging`) or a custom domain. You can also run the tunnel [as a service](https://tuna.am/en/docs/tunnels/guides/service.md). The Kubernetes tunnel is available with a [subscription](https://tuna.am/#pricing) starting with **tuna-cli 0.36.0**. The command has a short alias, `tuna k8s`. All flags and details are in the [documentation](https://tuna.am/en/docs/tunnels/kubernetes.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer📒 1C tunnel — your infobase on the internet with a single command](https://tuna.am/en/blog/1c-tunnel.md)[older →🎁 Бесплатный тариф без таймера и с постоянным поддоменом](https://tuna.am/en/blog/free-plan-permanent-subdomain.md) --- ### Дополнительные лицензии на увеличение лимитов [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) February 6, 2025 · 1 min Если вам недостаточно лимитов на активные туннели, поддомены, свои домены или статичные TCP порты, то теперь вы можете докупить дополнительную лицензию и увеличить эти лимиты. [![licenses](/en/assets/images/licenses-3bd87f31b597bffc40fefeb4a423b610.png)](https://my.tuna.am/subscription_update?lang=en) Одна дополнительная лицензия увеличивает все лимиты в туннелях: * **+1** активный туннель * **+1** поддомен в зоне tuna.am * **+1** свой домен * **+1** статичный TCP порт По стоимости мы продолжаем придерживаться доступных и демократичных цен. В тарифе Профи 1 дополнительная лицензия: * **99** рублей в месячной подписке * **990** рублей в годовой подписке В тарифе Легенда 1 дополнительная лицензия на 1 участника: * **199** рублей в месячной подписке * **1990** рублей в годовой подписке Покупка дополнительных лицензий доступна на странице [редактирования подписки](https://my.tuna.am/subscription_update?lang=en). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newer🔐 Менеджер паролей - зачем мы его сделали и как он устроен](https://tuna.am/en/blog/password-manager.md)[older →Helm chart для запуска tuna в Kubernetes](https://tuna.am/en/blog/helm-chart.md) --- ### Плановые работы 14.04.2025 в регионе NL (Нидерланы) [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) April 11, 2025 · 1 min ⚠️ Внимание! Плановые работы 🛠️ **14.04.2025** запланированы в регионе **NL (Нидерланы)**, ожидается недоступность от 1 до 12 часов в течении дня. Будет произведена смена IP адресов и переезд к новому провайдеру. * `193.33.194.47` > `89.19.217.251` * `62.84.101.212` > `147.45.238.190` Старый IP на адресе `193.33.194.47` продолжит работать, для обратной совместимости с пользовательскими доменами, что используют в dns А запись. По завершении работ мы объявим о дате полного отключения старого адреса, а всех владельцев пользовательскими доменов дополнительно уведомим о необходимости изменить DNS запись после проведения работ. Работы необходимо провести в рамках модернизации инфраструктуры и повышения стабильности. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newerПлановые работы 16.04.2025 в регионе RU (Россия)](https://tuna.am/en/blog/maintain-ru.md)[older →Обновление клиента tuna 0.23.0 и инспектор 1.1.0](https://tuna.am/en/blog/inspector-live-updates.md) --- ### Плановые работы 16.04.2025 в регионе RU (Россия) [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) April 14, 2025 · 1 min ⚠️ Внимание! Плановые работы 🛠️ **16.04.2025** запланированы в регионе **RU (Россия)**, ожидается недоступность от 10 до 60 минут в промежутке с 11 до 13 часов МСК. Будет произведена смена IP адресов и переезд к новому провайдеру. * `178.20.41.29` > `37.252.22.27` * `178.20.43.146` > `37.252.21.90` Старый IP на адресе `178.20.41.29` продолжит работать, для обратной совместимости с пользовательскими доменами, что используют в dns А запись. По завершении работ мы объявим о дате полного отключения старого адреса, а всех владельцев пользовательскими доменов дополнительно уведомим о необходимости изменить DNS запись после проведения работ. Работы необходимо провести в рамках модернизации инфраструктуры и повышения стабильности. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newer🆕 Новые типы туннелей - Postgres и Redis](https://tuna.am/en/blog/tunnels-pg-and-redis.md)[older →Плановые работы 14.04.2025 в регионе NL (Нидерланы)](https://tuna.am/en/blog/maintain-nl.md) --- ### Monitors - new functionality [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) January 27 · 4 min We're pleased to introduce a new component of the Tuna platform - **Monitors**. Functionality for tracking the health of your services, tunnels, jobs and infrastructure with notifications through various communication channels. [Go to Monitors section in documentation](https://tuna.am/en/docs/monitors.md) ![Monitors main page](/docs/img/monitors/main.png) #### What are Tuna Monitors? Monitors let you control the operation of your cron jobs, services and infrastructure. If a problem occurs, you'll get an immediate notification through the configured communication channels — email, Telegram, Slack and others. **Monitors** are: * Tracking of periodic jobs (backup scripts, cron jobs) * Availability checks for websites, APIs and tunnels from multiple regions * Network infrastructure monitoring (TCP, DNS, ICMP) * Automatic notifications on failures and recoveries * Integration with the tuna CLI for convenient work from the terminal #### Who is it for? 1. **DevOps engineers** — for monitoring infrastructure and automated jobs 2. **Developers** — for tracking the availability of APIs and services 3. **System administrators** — for controlling the execution of backup scripts and cron jobs 4. **Teams** — for centralized monitoring of all services with shared notifications #### 6 monitor types Tuna supports 6 monitor types, divided into two categories: ##### User monitors These monitors wait for signals from your applications and scripts: | Type | Description | | -------------- | ----------------------------------------------------------- | | **Job** | Monitoring the execution of periodic jobs on a schedule | | **Heartbeat** | Monitoring regular signals from services in closed networks | ##### External monitors These monitors independently check the availability of your resources from multiple geographic regions: | Type | Description | | --------- | ------------------------------------------------------------------ | | **HTTP** | Availability checks for websites and APIs with response validation | | **TCP** | Availability checks for TCP services (databases, mail) | | **DNS** | Checking DNS records and their correctness | | **ICMP** | Network availability checks (ping) | HTTP and TCP monitors are integrated with Tunnels. If you have active tunnels or reserved domains/ports, when creating a monitor the resource you need will be in the menu to the right of the address. ![Monitors main page](/docs/img/monitors/http_tunnel.png) #### Key features ##### Cron job monitoring Track the execution of backup scripts, syncs and other periodic operations: ```bash # Simple backup with monitoring tuna monitor exec MONITOR_ID -- pg_dump -U postgres mydb > /backups/dump.sql # In crontab 0 3 * * * tuna monitor exec MONITOR_ID -- /scripts/backup.sh ``` The system automatically sends `start`, `finish` or `fail` signals, and also captures execution logs. ##### Heartbeat monitoring For services in closed networks, IoT devices and background processes: ```bash # Sending a heartbeat every 5 minutes */5 * * * * tuna monitor ping MONITOR_ID --host=my-server ``` ##### Checks from multiple regions External monitors check the availability of your resources from different geographic locations, which makes it possible to detect routing issues or regional unavailability. ##### SSL/TLS and Whois monitoring HTTP monitors automatically check SSL certificates and track the domain expiration date so you don't miss the renewal moment. ##### Integration with notifications Monitors are integrated with the [Contacts](https://tuna.am/en/docs/team/contacts.md) and [Integrations](https://tuna.am/en/docs/team/integrations.md) systems. Set up notifications via Telegram, Slack, email or webhook. #### CLI integration The **tuna** console client is fully [integrated](https://tuna.am/en/docs/monitors/cli.md) with monitors: * `tuna monitor ping` — sending a Heartbeat signal to a monitor * `tuna monitor exec` — executing a command with automatic tracking * `tuna monitor status` — viewing monitor statuses Detailed information about each monitor type, settings and usage examples is available in the [documentation](https://tuna.am/en/docs/monitors.md). *** #### Notifications and integrations Notifications and integrations are also a big part of the new functionality, which is integrated not only with [monitors](https://tuna.am/en/docs/monitors.md) but also with [reports](https://tuna.am/en/docs/reports.md). ![Types of integrations](/docs/img/team/integrations_2.png) Popular communication channels are supported: Telegram, Slack, Discord, VK Teams, Google Chat and custom Webhooks. [Contact groups](https://tuna.am/en/docs/team/contacts.md) let you flexibly manage recipients — combine email addresses and integrations into named groups, use one group for multiple monitors, and easily add or remove recipients without changing the monitors' settings themselves. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerPROXY Protocol support in TCP tunnels](https://tuna.am/en/blog/release-v0-30-0.md)[older →🆕 Reports - new functionality](https://tuna.am/en/blog/reports.md) --- ### Менеджер паролей - зачем мы его сделали и как он устроен [Blog](https://tuna.am/en/blog.md)|[Under the hood](https://tuna.am/en/blog/tags/engineering)|[Security](https://tuna.am/en/blog/tags/security) February 20, 2025 · 7 min Когда вы доверяете свои пароли внешнему сервису, главный вопрос — можно ли ему доверять. Поэтому мы решили открыто рассказать, как изнутри устроен [Менеджер паролей](https://tuna.am/en/docs/passwords.md) Tuna: какими ключами и в каком порядке шифруются данные, что хранится у нас на сервере, а что — только у вас, и что произойдёт, если наша база данных утечёт. Цель простая — чтобы пользоваться менеджером было не страшно, потому что вы понимаете, как он работает. ![Tuna — менеджер паролей](/blog/img/2025-02-20-password-manager/00-cover.jpg) > **Коротко.** Все ключи шифрования создаются **на вашем устройстве** и никогда не покидают клиент в открытом виде. На сервер уходит только зашифрованный текст, а Мастер-ключ есть **только у вас**. Это модель нулевого знания (zero-knowledge): даже если наша инфраструктура будет скомпрометирована и утечёт вся база данных — расшифровать ваши пароли без Мастер-ключа невозможно. #### Нафига мы сделали менеджер паролей? Мы много работали по фрилансу в разных компаниях, поэтому есть насмотренность как люди меняются корпоративными паролями к сервисам, инфраструктуре и прочему. Ну так вот, если в компании нет купленного 1Password или чего-то подобного (что там Дудь рекламирует), то это просто аншлаг. * Нет единого хранилища, чаще всего это просто какой-то 1 ответственный человек, если он уйдёт, то всё... * Пароли передают в чатиках Tg или Slack * При увольнении сотрудника, если есть какая то общая учётка от внешнего сервиса, приходится почти рассылать новый пароль всем. Там ещё скоуп проблем, но это основное. В общем посмотрели мы на это всё и подумали, что у нас 99% пользователей это разработчики, наверняка у них та же самая проблема. Есть потребность хранить какие то инфраструктурные пароли, SSH ключи, офисный WI-FI и так далее. Но не всегда есть доступные инструменты для безопасного и удобного способа делиться ими с командой, заказчиками, аудиторами или просто друзьями. Так вот и решили, что надо — ну и сделали. #### Сколько стоит? Мы решили, что это будет **базовым бесплатным функционалом** в рамках нашей платформы. Безопасность — это базовая потребность (даже по пирамиде Маслоу), и мы хотим, чтобы даже самые маленькие стартапы на 5 человек могли себе позволить безопасно хранить и обмениваться паролями в компании или друзьями. Возможно позже мы введём какие то разумные ограничения или новые возможности которые будут доступны только с подпиской, но пока весь функционал доступен бесплатно всем. Вы можете зарегистрироваться, создать Команду, пригласить пользователей и завести общие каталоги с паролями без оформления подписки. #### Архитектура Что ж, ничего сложного, генерируем на клиенте какой-то ключ показываем его пользователю, нигде не сохраняем, этим ключом пользователь на клиенте шифрует пароли, в зашифрованном виде передаёт на сервер, мы храним. Когда пользователь хочет прочитать пароль, отдаём его, пользователь расшифровывает этот пароль своим ключом который есть только у него — профит. Кажется всё просто, но сложность начинается когда нам надо не только хранить пароли одного пользователя, а иметь какие то общие каталоги в командах и делиться паролями наружу одноразовой ссылкой. Тут то мы и начинаем замешивать симметричные и асимметричные ключи, шифровать одно другим и вообще становится не так всё просто. ##### Общие каталоги в команде Вообще в этом параграфе мы разберём почти полное устройство, так как будем распутывать клубок из конца в начало. Начнём с задачи — иметь общие каталоги с паролями в команде. Тут есть 2 пути: * Пароли должны быть зашифрованы 1 ключом который есть у всех. * При добавлении нового пользователя мы дублируем все пароли и шифруем его публичным ключом. Второй поход нам не подходит, так как пользователь 1 должен иметь возможность редактировать пароль, а пользователь 2 должен сразу это видеть и наоборот. Значит — пароли должны быть зашифрованы одним ключом который есть у всех. Как такое провернуть? А вот так: ![Пароли шифруются AES-ключом каталога, а сам AES-ключ — RSA-ключом пользователя](/blog/img/2025-02-20-password-manager/01-team-catalogs.jpg) В итоге пароли шифруются симметричным AES ключом, назовём его — AES-ключ каталога. Но не храним же мы его в открытом виде? Верно, этот AES-ключ каталога на самом деле зашифрован RSA-ключом пользователя. Распутываем клубок ... ![Приватный RSA-ключ шифруется Мастер-ключом, публичный хранится в открытом виде](/blog/img/2025-02-20-password-manager/02-rsa-keys.jpg) Когда вы инициируете менеджер паролей, для вашего пользователя генерируется ассиметричный RSA ключ, его приватная часть шифруется Мастер-ключом, а публичная сохраняетися в открытом виде. Итого, пользователь хранит: * свой Мастер-ключ Итого, мы храним: * публичный RSA-ключ * зашифрованный приватный RSA-ключ * зашифрованный AES-ключ каталога * зашифрованный пароли Давайте попробуем посмотреть картину в целом и собрать в кучу весь алгоритм что там вообще происходит? ![Полная схема алгоритма работы менеджера паролей](/blog/img/2025-02-20-password-manager/03-full-algorithm.jpg) Если коротко, то основная концепция в том, что на стороне сервера если что-то и хранится, то исключительно в зашифрованном виде, а на клиенте в открытом виде всё может существовать только в Runtime и перманентно тоже нигде не хранится. Чтобы проще это понять нужно пройтись по шагам: * Пользователь инициирует менеджер паролей * На клиенте (браузер) генерируется пара RSA-ключей приватный и публичный. * На клиенте генерируется Мастер-ключ, пользователь должен сохранить его себе * На клиенте приватный RSA-ключ шифруется Мастер-ключом * На сервер передаётся зашифрованный приватный RSA-ключ и публичный RSA-ключ * Пользователь создаёт новый каталог * На клиенте генерируется AES-ключ каталога * На клиенте AES-ключ каталога шифруется публичным RSA-ключом * На клиенте пароли шифруются AES-ключом каталога Когда нам надо сделать пароли общими, мы находим пользователя в команде, берём его публичный RSA-ключ, берём AES-ключ каталога расшифровываем его своим приватным RSA-ключом, шифруем публичным RSA-ключом нового пользователя и отправляем на сервер, на сервере у каталога появляется новый пользователь с AES-ключом каталога и новый пользователь теперь может расшифровать его своим приватным RSA-ключом, а затем и пароли зашифрованные AES-ключом каталога. В итоге получается примерно вот такая колбаса: ![Цепочка вложенного шифрования ключей и паролей](/blog/img/2025-02-20-password-manager/04-sausage.jpg) Фух ... ##### Делимся паролями наружу Если с пользователями внутри команды разобрались, то как делиться паролями наружу, ведь у незарегистрированных пользователей нет RSA-ключей и они вообще ничего не знают? ![Обмен паролем наружу по одноразовой ссылке](/blog/img/2025-02-20-password-manager/05-share-external.jpg) Тут мы приходим к тому нужно сделать копию пароля. Снова генерируем AES-ключ но в этот раз он будет только для 1 пароля. Зашифрованному паролю присваиваем UID и отправляем это на сервер. Что знает клиент: * UID пароля * AES-ключ которым он зашифрован Что знает сервер: * UID пароля * Зашифрованный пароль Далее клиент генерирует ссылку, где часть URI — это UID пароля, а другая AES-ключ: ```text https://my.tuna.am/password#{UID}#{AES-ключ} ``` Соответственно у себя в базе мы не храним AES-ключ и не можем расшифровать пароль, но пользователь, что перейдёт по ссылке без труда его расшифрует и посмотрит. Да, формально мы можем расшифровать пароль в момент 1-го же обращения, так как в запросе мы увидим полную ссылку, но делать этого мы конечно не будем. В целом есть идеи как улучшить этот момент, но пока всё на этапе обсуждения. Тем не менее именно в базе данных всё зашифровано. ![В базе данных всё хранится в зашифрованном виде](/blog/img/2025-02-20-password-manager/06-db-encrypted.jpg) Думаю тут можно завершить с архитектурой и перейти дальше. #### Какие планы? Мы уже получили обратную связь от CTO одного из наших клиентов, с просьбой добавить поддержку TOTP (Time-based one-time Password), думаю добавим в ближайшем релизе. Это актуально если в компании есть какой-то сервис с одним аккаунтом и вход там с подтверждением через TOTP. Думаем как сделать функционал Делимся паролями наружу более безопасным, но при этом таким же удобным. Также есть планы внедрить работу с паролями в консольную утилиту `tuna` чтобы можно было получать пароли в консоли. В целом продукт ещё в стадии Бета и мы активно собираем обратную связь. Пожалуй тут и закончим, с удовольствием отвечу на вопросы в комментариях 🤗. ![Tuna — менеджер паролей](/blog/img/2025-02-20-password-manager/07-closing.jpg) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Under the hood](https://tuna.am/en/blog/tags/engineering), [Security](https://tuna.am/en/blog/tags/security) [← newerНовый инспектор запросов, шлюзы, IP политики и бастион](https://tuna.am/en/blog/bastion-inspect-geteway.md)[older →Дополнительные лицензии на увеличение лимитов](https://tuna.am/en/blog/licenses.md) --- ### Оплата международными картами — подключили PayPro Global [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) August 10 · 3 min Пожалуй, самая долгожданная просьба в нашей поддержке наконец закрыта: подписку в Tuna теперь можно оплатить **международной картой**. Мы подключили платёжного провайдера **PayPro Global** и принимаем оплату в долларах картами Visa и Mastercard практически любой страны. ![Переключатель валюты оплаты в личном кабинете Tuna — рубли или доллары США](/blog/img/2026-08-10-paypro-global/currency.png) #### Что изменилось Раньше оплатить подписку можно было только российской картой через **ЮKassa** — и для многих наших пользователей это была стена. Аккаунт есть, тарифы понятны, а платить нечем: карта не проходит, и остаётся либо бесплатный тариф, либо просьба знакомым «оплати, я переведу». Теперь в [личном кабинете](https://my.tuna.am/subscription?lang=en) появился переключатель **валюты оплаты**: * **RUB, российский рубль** — оплата картой российского банка через ЮKassa, как и раньше; * **USD, доллар США** — оплата через PayPro Global картой любой страны. Выбранная валюта определяет и провайдера, и цены — при переключении стоимость тарифов на странице сразу пересчитывается. Например, тариф **Профи** в долларах стоит 5 USD в месяц или 50 USD в год. #### Про PayPro Global [PayPro Global](https://payproglobal.com/) — международный платёжный провайдер, который больше пятнадцати лет специализируется на подписках для SaaS. Он выступает **merchant of record**, то есть берёт на себя приём платежа, конвертацию валют, налоги и всю сопутствующую отчётность в стране покупателя. Что это значит на практике: * принимаются карты **Visa и Mastercard** большинства стран мира; * оплата проходит на защищённой странице провайдера — данные карты не попадают на серверы Tuna; * подписка списывается автоматически, продлением и уведомлениями занимается провайдер; * чек и документы об оплате приходят на почту. #### Кого мы ждали больше всего В первую очередь — наших пользователей из **стран СНГ**. Многие из вас пишут нам не первый месяц: пользуемся Tuna каждый день, хотим тариф, но заплатить физически невозможно. Спасибо, что дождались и не ушли — теперь платформа доступна вам целиком, без оговорок и обходных путей. Это касается и всех остальных, кто живёт за пределами России: разработчиков в Европе, Азии, обеих Америках — процесс для вас теперь ровно такой же, как в любом привычном SaaS-сервисе. #### Что остаётся без изменений * Оплата российскими картами через ЮKassa работает как прежде. * Действующие подписки продолжают действовать, ничего переоформлять не нужно. * [Оплата по счёту](https://tuna.am/en/blog/invoices.md) для организаций в России никуда не делась. * Набор функций тарифов одинаковый независимо от валюты оплаты. #### Как оплатить Откройте [страницу подписки](https://my.tuna.am/subscription?lang=en), выберите в блоке **«Валюта оплаты»** нужный вариант, проверьте цены и нажмите **«Оформить подписку»**. Дальше вас встретит платёжная страница выбранного провайдера. Если карта всё-таки не проходит или что-то идёт не так — [напишите нам](mailto:info@tuna.am), разберёмся. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newer🗝️ Секреты целиком из консоли — большое обновление tuna secrets](https://tuna.am/en/blog/secrets-cli.md)[older →🔐 Ключи доступа (passkeys) — вход в Tuna без пароля](https://tuna.am/en/blog/webauthn-passkeys.md) --- ### Доменные зоны (wildcard), Менеджер паролей и tuna 0.21.2 [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) January 23, 2025 · 4 min Доменные зоны, TLS сертификаты, Менеджер паролей и новые виды дистрибуции клиента tuna, улучшения безопасности и многое другие. #### Доменные зоны ##### Управление зонами В командах появилась возможность добавлять [доменные зоны](https://tuna.am/en/docs/team/domain_zones.md) и загружать свои [TLS сертификаты](https://tuna.am/en/docs/team/tls_certificates.md). ![domain_zones](/en/assets/images/domain_zones-561cf25dd9ba3a4321037fb19807bf2a.png) Владение доменной зоной уникально в рамках платформы Tuna. Если вы верифицировали владение зоной, к примеру, `tuna-dev.ru`, то с этого момента только участники вашей команды могут резервировать домены по маске `*.tuna-dev.ru`. Можно резервировать зоны второго и выше уровня, например, можно добавить отдельно `foo.tuna-dev.ru` и `bar.tuna-dev.ru`. ##### Использрование После успешного прохождения проверки владения пользователи смогут начать её использовать. На странице [управления доменами](https://my.tuna.am/domains?lang=en), пользователь увидит все доступные зоны в команде привязанные к данному региону. ![domain-add](/en/assets/images/domain-add-0ed6d8a0d3b5fd9a224cc2d3175e6b6b.png) ##### TLS сертификат Если вы добавили и указали свой [TLS сертификат](https://tuna.am/en/docs/team/tls_certificates.md), то для всех поддоменов в этой зоне будет использоваться ваш сертификат и Let's encrypt не будет выпускаться. Это значительно ускоряет добавление новых поддоменов. А также даёт вам возможность использовать любые покупные сертификаты, самоподписанные или например от [Минцифры](https://www.gosuslugi.ru/tls). ![domain_zones-tls](/en/assets/images/domain_zones-tls-8cfa29c8835ad88220c78546050962ea.png) ##### Своя зона по умолчанию Также появляется возможность использовать свою зону как зону по умолчанию в данном регионе. Это значит, что все динамически созданные [HTTP туннели](https://tuna.am/en/docs/tunnels/http.md) будут иметь домен в вашей зоне. Например при запуске `tuna http -f .` вы сразу получите адрес `4l7mqf-212-49-103-2.your-domain.ru` вместо стандартного `4l7mqf-212-49-103-2.ru.tuna.am`, т.е. не нужно указывать флаг `--domain` и использовать какой-то статичный домен, вы получаете брендированный (white label) домен *из коробки*! ![self-domain-default](/en/assets/images/self-domain-default-5e1abf79dd1706f418f58a779ca6ca50.png) > **Note.** Можно скрывать IP адреса пользователей в динамических доменах, подробнее в разделе [домены](https://tuna.am/en/docs/team/domains.md). #### Менеджер паролей Кардинально новый функционал не связанный с реверс-прокси туннелями, но тесно связанный с людьми которые пользуются реверс-прокси туннелями! ![passwords-main](/en/assets/images/main-dacca5895830a1cf52a5767b574b095a.png) Почему мы встроили **Менеджер паролей** в сервис для создания туннелей? Мы заметили, что во многих командах разработчиков и администраторов отсутствует культура безопасного хранения паролей и секретов. При этом у всех есть потребность хранить инфраструктурные пароли, SSH ключи, офисный WI-FI и так далее. Но не всегда есть доступные инструменты для безопасного и удобно способа делиться ими с командой, заказчиками, аудиторами или просто друзьями. Тогда люди начинают передавать пароли в открытом виде в чатах, письмах, записках. Наша цель это повысить культуру хранения секретов среди наших пользователей и обезопасить клиентов и от утечек, и несанкционированного доступа к их инфраструктуре. Поэтому Менеджер паролей является базовой и бесплатной функцией для всех. ![pwd-types](/en/assets/images/pwd-types-2d6a18327bbc102893246e45dd5e23fa.png) Создан для безопасного хранения и удобного обмена паролей и других видов секретов для разработчиков, администраторов их команд и не только. Мы не храним пароли в открытом виде, согласно модели [нулевого знания](https://en.wikipedia.org/wiki/Zero-knowledge_proof), **только** пользователь обладает ключами, которые могут расшифровать данные. > **Info.** В настоящий момент [Менеджер паролей](https://my.tuna.am/passwords?lang=en) является бесплатной функцией и доступен всем пользователям и командам без ограничений. Вы можете хранить пароли, SSH-ключи, лицензии, заметки, реквизиты к базам данных и прочие типы секретов. Создавать общие каталоги с секретами внутри команды и делиться ими с любыми людьми вне платформы Tuna. Подробнее все возможности описаны в [отдельном разделе](https://tuna.am/en/docs/passwords.md). #### Выпуск tuna 0.21.2 В этом выпуске нет значительных улучшений или новых функций клиента. Но по просьбам наших пользователей мы добавили новые виды дистрибуции клиента tuna. ##### MSI пакеты для Windows Теперь для пользователей Windows помимо winget и ручной установки через powershell [доступны MSI пакеты](https://tuna.am/en/docs/guides/install/install-cli.md?os=windows). ##### AUR для Arch Linux Для Arch Linux пользователей мы подготовили [репозиторий в AUR](https://aur.archlinux.org/packages/tuna.am-bin), а также статические пакеты и добавили [раздел в инструкцию](https://tuna.am/en/docs/guides/install/install-cli.md?os=linux&subos=archlinux). ##### Исправления Устранена утечка памяти при включённом [инспекторе запросов](https://tuna.am/en/docs/tunnels/http/inspect.md) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerHelm chart для запуска tuna в Kubernetes](https://tuna.am/en/blog/helm-chart.md)[older →Выпуск tuna 0.21.0](https://tuna.am/en/blog/release-v0-21-0.md) --- ### Новый функционал - Команды [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) July 25, 2024 · 2 min В этом обновлении мы добавили новый тариф для работы в командах! ![teams](/en/assets/images/teams1-27650c68c0b62decfc770ec87bbff0e1.png) Создание команд уже доступно всем пользователям, помимо возможности оплачивать подписку за всех членов команды, вы получаете расширенные лимиты относительно базового тарифа **Профи**. Сравнение лимитов: | Профи | Легенда | | ----------------------- | ---------------------------- | | 5 туннелей одновременно | **10** туннелей одновременно | | 10 статичных поддоменов | **20** статичных поддоменов | | 1 собственный домен | **10** собственных доменов | | 1 статичный TCP порт | **5** статичных TCP портов | Тариф рассчитан на использование в компаниях занимающихся разработкой сайтов и приложений. Также в скором времени мы добавим возможность оплаты по счёту. #### Кейсы применения ##### Подключение оплаты на сайте ![example1](/en/assets/images/example1-109056a20efb8309bb0faf4961ad6d5b.png) Если вам нужно тестировать оплату локально, то без обратных Webhooks это невозможно. Мы сами используем tuna для тестирования платежей с [ЮKassa](https://yookassa.ru/?utm_source=tuna.am&utm_medium=organic&utm_campaign=blog), но всё тоже самое будет верно и для других платёжных шлюзов ([CloudPayments](https://cloudpayments.ru/?utm_source=tuna.am&utm_medium=organic&utm_campaign=blog), [Stripe](https://stripe.com/?utm_source=tuna.am&utm_medium=organic&utm_campaign=blog), [2Checkout](https://www.2checkout.com/?utm_source=tuna.am&utm_medium=organic&utm_campaign=blog), и .т.д). C **tuna** вы можете создать тестовый магазин для каждого разработчика или тестировщика и внедрять и проверять работу оплаты в вашем проекте. ##### Ускорение командной разработки ![example2](/en/assets/images/example2-4ade121aee7ec540203cc4ed9b38dce4.png) При классической схеме, когда у вас есть команда backend и frontend разработчиков, порой сложно синхронизироваться при внедрении нового функционала, когда обе команды работают над 1 задачей. Backend делают бизнес-логику, frontend интерфейс сайта, а протестировать это можно или на отдельном стенде, когда UI c новой фичей смотрит в API с новой фичей. При этом все эти стенды и CI/CD пайплайны могут занимать много времени, так как на каждую итерацию запускаются тесты и прочие задачи. Намного проще, если backend-разработчик с помощью tuna публикует API со своего рабочего компьютера, в frontend-разработчик помощью tuna публикует UI. За несколько минут можно проверить, что всё работает как надо, сэкономить часы прогона тестов на CI/CD пайплайнах и выпустить в релиз новое приложение уже в среду, а не пятницу 🙂 #### Заключение Безусловно можно придумать ещё очень много примеров как и где **tuna** может улучшить или ускорить вашу разработку и мы обязательно расскажем о них в других постах нашего блога. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerВыпуск tuna 0.17.0](https://tuna.am/en/blog/release-v0-17-0.md)[older →Выпуск tuna 0.16.1](https://tuna.am/en/blog/release-v0-16-1.md) --- ### Выпуск tuna 0.16.1 [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) July 24, 2024 · 1 min В новой версии клиента есть несколько исправлений и улучшений, вот самые важные: * Исправлена работа инспектора с [Server-sent events](https://tuna.am/en/docs/tunnels/http.md#server-sent-events) Для обновления следуйте [инструкции](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerНовый функционал - Команды](https://tuna.am/en/blog/release-teams.md)[older →Welcome to Tuna](https://tuna.am/en/blog/welcome-tuna.md) --- ### Выпуск tuna 0.17.0 [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) July 31, 2024 · 2 min В этой версии клиента появились новые функции. #### Множественный запуск туннелей ![multi-tunnels](/en/assets/images/multi-tunnels-4cd333c20895595fcb5aae138d03ed59.gif) В проектах где нужно опубликовать несколько сервисов, может потребоваться запустить сразу несколько туннелей – UI, API, база данных или что-то ещё. Это может быть крайне удобно при командной разработке. Так вы можете создать файл `.tuna.yml` в корне репозитория и это сразу будет работать для всех разработчиков в команде, либо вы можете описать настройки в Readme проекта и тогда новые сотрудники легко разберутся как запустить и опубликовать проект. Подробнее о том как это настроить, описано [на отдельной странице](https://tuna.am/en/docs/tunnels/guides/multi-tunnels.md). #### Создание системных сервисов ![service](/en/assets/images/service-fc45e68f9c0ce148c0462f9ac3514c1d.gif) Создавать системные сервисы теперь стало проще. Мы добавили отдельную команду, с помощью которой вы легко можете создать фоновую службу для туннеля который должен работать всё время. Это работает для всех поддерживаемых операционных систем. Подробнее о том как это настроить, описано [на отдельной странице](https://tuna.am/en/docs/tunnels/guides/service.md). #### Флаг для CORS Для корректной работы сайта с API на стороннем домене необходимо настройки отдачу заголовков [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) и корректно обрабатывать `OPTIONS` (preflight) запросы, поэтому мы добавили специальный флаг `--cors` для команды `tuna http`. Подробнее о том как c этим работать, описано [на отдельной странице](https://tuna.am/en/docs/tunnels/http.md#cors). #### Обновление Для обновления следуйте [инструкции](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerНовый функционал - Дополнительные токены и управление командой](https://tuna.am/en/blog/temp-tokens.md)[older →Новый функционал - Команды](https://tuna.am/en/blog/release-teams.md) --- ### Выпуск tuna 0.19.0 [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) September 24, 2024 · 2 min Политики обработки трафика, QR-код и другие улучшения новой версии клиента tuna. #### Политики обработки трафика Функция позволяет описать правила и действия обработки трафика для входящих запросов и более гибко настроить взаимодействие с вашим приложением. К примеру вы можете включить базовую авторизацию только для определённого префикса, проверить наличие заголовков, отдельно обрабатывать `OPTIONS` запросы, запретить доступ к методу по подсети, ограничить доступ в определённые дни недели и многое другое. Настройка собственных фильтров с помощью мощного синтаксиса [expr-lang](https://expr-lang.org/) выведет вашу разработку на новый уровень. ![policy_debug](/docs/img/policy_debug.png) Мы постарались максимально подробно [задокументировать](https://tuna.am/en/docs/traffic-policy.md) работу с данной функцией. Сейчас это доступно только по [подписке](https://tuna.am/#pricing) в тарифе **Легенда**. #### Генерация QR кода с ссылкой Для команд `tuna http` и `tuna tcp` появился новый параметр `--qr` (переменная `TUNA_QR_CODE`). При включении этого для флага, для ссылки будет сгенерирован QR код и напечатан в консоли. Это может быть удобно если вы тестируете сайт на мобильном устройстве. ![](/docs/img/examples/qr.png) #### Адрес для инспектора запросов Ранее [инспектор HTTP запросов](https://tuna.am/en/docs/tunnels/http/inspect.md) всегда запускался на адресе `127.0.0.1`, как выяснилось это не очень удобно для тех, кто запускает **tuna** в Docker контейнере. Поэтому мы добавили возможность переопределить адрес и порт для инспектора запросов с помощью переменной окружения `TUNA_INSPECT_ADDR` или параметра `inspectAddr` в [конфигурационном файле](https://tuna.am/en/docs/guides/config-file.md). Пример для Docker Compose: ```yaml --- services: whoami: image: traefik/whoami restart: always tuna: image: yuccastream/tuna:latest command: http whoami:80 restart: always depends_on: - whoami environment: - TUNA_TOKEN=<ваш_токен> - TUNA_INSPECT_ADDR=0.0.0.0:4040 ports: - "4040:4040" ``` #### Обновление Для обновления следуйте [инструкции](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerОбзор возможностей работы в Легенде](https://tuna.am/en/blog/team-review.md)[older →Обратный SSH туннель](https://tuna.am/en/blog/ssh-reverse.md) --- ### Выпуск tuna 0.20.0 [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) November 1, 2024 · 2 min Авторизация по ключам в SSH туннелях, скрытие IP адреса в динамических HTTP туннелях, фильтры в аудит логах, улучшения безопасности и много другое в новой версии tuna. #### Авторизация по ключам в SSH туннелях Появилась возможность авторизации клиентов по SSH ключу. Теперь вы можете добавить свои [SSH ключи](https://tuna.am/en/docs/guides/ssh-keys.md) в [личном кабинете](https://my.tuna.am/public_keys?lang=en) и использовать их для авторизации в [SSH туннелях](https://tuna.am/en/docs/tunnels/ssh.md). При этом в [Командах](https://my.tuna.am/team?lang=en) вы можете также разрешить авторизоваться по ключу и другим участникам команды. ![](/docs/img/ssh-keys.png) #### Скрывать IP адрес в динамических HTTP туннелях В тарифе [Легенда](https://my.tuna.am/team?lang=en) добавили функцию скрытия IP адреса пользователя в динамических HTTP туннелях. ![](/blog/img/2024-11-01/hide_ip.png) Вместо IP адреса будет использоваться имя команды приведённое к латинице. Минимальная длина сгенерированного имени - 8 символов, если название вашей команды короче, то оно будет дополнено случайными ascii символами. Ниже пример, как это работает. Поведение по умолчанию: ![](/blog/img/2024-11-01/no_hide_ip_terminal.png) Скрывать IP адрес: ![](/blog/img/2024-11-01/hide_ip_terminal.png) #### Фильтры в аудит логах В разделе с Аудит логами теперь есть фильтры, что позволит быстрее насти нужные действия совершаемые пользователями. #### Политика обновления клиента tuna Начиная с этой версии мы вводим политику обновления клиента. Мы стремимся, чтобы пользователи всегда имели доступ к актуальной версии tuna. Поэтому ограничиваем использование устаревших версий для пользователей без подписки. При выходе новой версии клиента, предыдущая продолжить работать на протяжении примерно 6 месяцев, точная дата прекращения работы будет отражена в таблице на странице [обновление](https://tuna.am/en/docs/guides/update.md#tuna-update-policy). Пользователи с подпиской могут продолжать пользоваться любой версией без ограничений. #### Обновление Для обновления следуйте [инструкции](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerВыпуск tuna 0.21.0](https://tuna.am/en/blog/release-v0-21-0.md)[older →Изменение настроек tuna при обновлении Immich](https://tuna.am/en/blog/immich-118.md) --- ### Выпуск tuna 0.21.0 [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) December 23, 2024 · 3 min Поддержка unicode доменов, подписанный и заверенный клиент для macOS, борьба с злоупотреблениями и взаимодействие с антивирусам, улучшения безопасности и многое другие в новой версии tuna. > **🍾🎉🔥.** Это последний релиз в 2024 году, хотим выразить благодарность всем нашим пользователям, желаем счастливого Рождества и Нового года! #### Поддержка unicode доменов Мы добавили полноценную поддержку unicode доменов. Теперь вы сможете подключить домен в зоне `.РФ` или [любой другой](https://www.reg.ru/web-tools/punycode) соответствующий [RFC3492](https://datatracker.ietf.org/doc/html/rfc3492). ![](/blog/img/2024-12-23/punicode.png) #### Подписанный и заверенный бинарный файл tuna для macOS Начиная с этой версии мы [подписываем и заверяем](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution) в Apple клиент tuna для macOS. Это позволит больше не добавлять его в исключения при первом запуске. ![](/blog/img/2024-12-23/macos_notarize.png) Обратите внимание, что файл подписан и заверен на имя: ```yaml Developer ID Application: Lev Aminov (FW65BD237L) ``` Проверить локально можно с помощью команды: ```sh codesign -dv --verbose=4 $(which tuna) ``` #### Борьба с злоупотреблениями и взаимодействие с антивирусам В последние месяцы мы столкнулись с частыми злоупотреблениями. Некоторые пользователями распространяли вирусы для Android и Windows через платформу Tuna. В следствии чего наши туннельные ноды попали в чёрные списки VirusTotal, это негативно сказалось на работе других клиентов, в частности тех, кто использовал популярные антивирусы. По полученным данными от антивирусных компаний мы заблокировали несколько пользователей, которые распространяли вредоносное ПО, чем нарушали наши [условия использования](https://tuna.am/tos/). Далее последовало долгое взаимодействие со всеми антивирусными компаниями, чтобы нас исключили из чёрных списков, и вот по прошествии месяца мы снова чисты. Данная ситуация заставила нас иначе взглянуть на вопрос безопасности, в связи с чем мы внедрили несколько механизмов позволяющим минимизировать вредительство а так-же обработку входящих сообщений о злоупотреблениях. Теперь на бесплатных тарифах всем кто впервые открывает ссылку будет показываться предупреждение. ![](/blog/img/2024-12-23/html_warning.png) Также появилась отдельная страница в [документации](https://tuna.am/en/docs/abuse.md) описывающая правила работы и обработки входящих сообщений и жалоб. И несколько других доработок, улучшений и оптимизаций которые позволят сделать работу c Tuna безопасной и стабильной. #### Улучшения безопасности SSH туннелей Для SSH туннелей появился новый флаг `--password-auth` позволяющий выключить авторизацию по логин/пароль. Так теперь вы сможете подключиться к SSH серверу только с помощью [ключей](https://tuna.am/en/docs/guides/ssh-keys.md) добавленных в личном кабинете. Это значительно повышает безопасность доступа к вашей инфраструктуре и исключает возможности подбора пары логин/пароль с помощью перебора. Пример использования: ```sh tuna ssh --port=server-1 --password-auth=false ``` либо с конфигурацией через переменные окружения: ```sh export TUNA_TCP_PORT=server-1 export TUNA_SSH_PASSWORD_AUTH=false tuna ssh ``` #### Обновление Для обновления следуйте [инструкции](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerДоменные зоны (wildcard), Менеджер паролей и tuna 0.21.2](https://tuna.am/en/blog/release-pwd-wildcard.md)[older →Выпуск tuna 0.20.0](https://tuna.am/en/blog/release-v0-20-0.md) --- ### IP filters in TCP, SSH, Postgres and Redis tunnels [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) June 13, 2025 · 3 min In the console client **tuna** version v0.25.0 we added the ability to restrict IPs and subnets from which connections can be made. This increases the level of security and is especially important in applications without built-in authorization. #### New flags The `tuna tcp`, `tuna ssh`, `tuna postgres` and `tuna redis` commands have new flags and env analogs: **Flags** ```shell --cidr-allow # Reject connections that do not match specified CIDRs --cidr-deny # Reject connections matching specified CIDRs ``` **Envs** ```shell TUNA_CIDR_ALLOW # Reject connections that do not match specified CIDRs TUNA_CIDR_DENY # Reject connections matching specified CIDRs ``` #### Usage examples **TCP** You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna tcp 3389 --cidr-allow="10.0.0.1/32" ``` Or block access from specific subnets: ```shell tuna tcp 3389 --cidr-deny="10.0.0.1/32" ``` You can combine, for example specify a wide network and subtract private IPs: ```shell tuna tcp 3389 --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists separated by commas: ```shell tuna tcp 3389 --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` **SSH** You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna ssh --cidr-allow="10.0.0.1/32" ``` Or block access from specific subnets: ```shell tuna ssh --cidr-deny="10.0.0.1/32" ``` You can combine, for example specify a wide network and subtract private IPs: ```shell tuna ssh --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists separated by commas: ```shell tuna ssh --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` **Postgres** You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna postgres 5432 --cidr-allow="10.0.0.1/32" ``` Or block access from specific subnets: ```shell tuna postgres 5432 --cidr-deny="10.0.0.1/32" ``` You can combine, for example specify a wide network and subtract private IPs: ```shell tuna postgres 5432 --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists separated by commas: ```shell tuna postgres 5432 --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` **Redis** You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna redis 6379 --cidr-allow="10.0.0.1/32" ``` Or block access from specific subnets: ```shell tuna redis 6379 --cidr-deny="10.0.0.1/32" ``` You can combine, for example specify a wide network and subtract private IPs: ```shell tuna redis 6379 --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists separated by commas: ```shell tuna redis 6379 --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` #### IP policies in Team In [teams](https://tuna.am/en/docs/team.md), if [IP policies](https://tuna.am/en/docs/team/ip_policies.md) are specified, they will work in conjunction with these flags. #### Update To update, follow the [instructions](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newer🆕 Webhooks - new functionality](https://tuna.am/en/blog/webhooks.md)[older →🆕 Tuna Desktop - new GUI application](https://tuna.am/en/blog/tuna-desktop-first.md) --- ### PROXY Protocol support in TCP tunnels [Blog](https://tuna.am/en/blog.md)|[tuna-cli releases](https://tuna.am/en/blog/tags/releases) January 28 · 2 min The [console client](https://tuna.am/en/docs/guides/install/install-cli.md) **tuna** has added support for [PROXY Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) for TCP tunnels. Now you can pass the original client IP address and port directly to your application. ```shell $ tuna tcp 54321 --proxy-protocol=2 INFO[22:08:58] Welcome to Tuna INFO[22:09:00] Forwarding tcp://ru.tuna.am:22347 -> 127.0.0.1:54321 ``` [Go to documentation](https://tuna.am/en/docs/tunnels/tcp.md#proxy-protocol) #### Why do you need PROXY Protocol? When using tunnels, your application sees connections from the tunnel server rather than from the real client. PROXY Protocol solves this problem — it passes the original client IP and port in the connection header, which allows you to: * Keep correct connection logs with real IP addresses * Apply access control rules at the application level * Collect accurate analytics on user geography #### New flag The `tuna tcp` command has a new `--proxy-protocol` flag that accepts the protocol version: | Value | Format | Description | | ----- | ------ | ------------------------------------------ | | `1` | Text | Human-readable format, easy to debug | | `2` | Binary | More efficient, recommended for production | #### Usage examples ##### Basic example ```shell tuna tcp 8443 --proxy-protocol=1 # or with an environment variable TUNA_PROXY_PROTOCOL=1 tuna tcp 8443 ``` ##### Minecraft server Some game server builds, for example [Velocity](https://docs.papermc.io/velocity/configuration/#advanced-section), support PROXY Protocol. Enable it in the game config and in the tuna client: ```shell tuna tcp 45678 --proxy-protocol=2 ``` #### Compatibility PROXY Protocol is widely supported by popular server software: * **Haproxy** — supports both versions, detects automatically * **Nginx** — supports both versions, detects automatically * **Envoy** — supports both versions * Many databases and applications via a proxy layer > **Tip.** If you're not sure which version to choose — use version `2`. The binary format is more efficient and is the recommended option in most cases. #### Updating To update, follow the [instructions](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [tuna-cli releases](https://tuna.am/en/blog/tags/releases) [← newerNew plan names](https://tuna.am/en/blog/tariff-rename.md)[older →🆕 Monitors - new functionality](https://tuna.am/en/blog/monitors.md) --- ### Reports - new functionality [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) November 12, 2025 · 2 min We're pleased to introduce a new component of the Tuna platform - **Reports**. Designed for generating error reports on your website. With an embeddable script, users and QA engineers can easily show where the bug is hiding, and you get debugging information and can immediately start fixing it. [Go to Reports section in documentation](https://tuna.am/en/docs/reports.md) ![Setup](/docs/img/reports/setup.png) #### How does it work? 1. Go to your dashboard and create a project in the [Reports](https://my.tuna.am/reports?lang=en) section. 2. Add this code to your website before the closing `` tag: ```html ``` 3. Call the script with `CTRL + ALT + S` or `⌘ + ⌥ + S` and create a report. #### What does a report look like? Inside the report you will find a screenshot or video and all the debugging information from the console, network operations, user actions and a snapshot of the page DOM tree. **Info** The first tab contains general summary information about the client, device and browser. ![](/docs/img/reports/info.png) **Inspector** A snapshot of the page DOM tree, with the ability to select an element. *** > **Note.** This tab is only available for reports with a screenshot. Reports with video cannot capture a DOM tree snapshot since it can change over time. **Console** A list of all events from the browser console. ![](/docs/img/reports/console.png) **Network** A list of all XHR requests with the option to look into the details. **Actions** User actions on the page. ![](/docs/img/reports/actions.png) **Comments** You can also leave comments and reactions to comments. ![](/docs/img/reports/message.png) #### Video overview **YouTube** **VK Video** *** #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🆕 Monitors - new functionality](https://tuna.am/en/blog/monitors.md)[older →🆕 Webhooks (SMTP, traffic policies, tunnel replay), TOTP in passwords, client distribution improvements](https://tuna.am/en/blog/webhooks-smtp.md) --- ### Secrets - New Feature [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) May 9, 2025 · 4 min We are pleased to introduce a new component of the Tuna platform - **Secrets**. Designed for centralized and secure management of confidential settings and environment variables. It's a powerful alternative to `.env` files, variables in GitLab/GitHub CI, and other local storage solutions. [Go to the Secrets section in the documentation](https://tuna.am/en/docs/secrets.md) #### 🔐 What are Tuna Secrets? The secrets service is designed to store settings for your applications that you run - locally, in CI/CD, or in your test and production environments. It is an excellent alternative to `.env` files and environment variables in CI/CD systems. **Secrets** are: * A single source of configuration for local development, CI/CD, staging, and production environments. * Convenient management interface - via web, CLI, and API. * Support for RBAC, comments, reminders, and change auditing. * Flexible work with projects, environments, and configurations. * All secrets are encrypted, keys are not stored in the database. A database leak will not lead to data disclosure. #### 👥 Who is it for? 1. **Developers** — to simplify environment setup and reduce "magic" values in code. 2. **DevOps engineers** — for centralization and automation of configurations. 3. **Teams** — for secure and controlled shared access to confidential data. #### 🎯 Key Features We have implemented a structure that allows for effective secret management. In this video, you'll see a brief overview of the capabilities: **VK Video** **YouTube** *** Hierarchy: projects → environments → configurations. We follow a common structure, so secret storage is divided by projects, within projects there are environments, and within environments there are main or child configurations. Inside configurations, there are many functions for working directly with secrets and access to them, auditing, and more. When working in a [Team](https://tuna.am/en/docs/team.md), the roles section allows you to granularly distribute participant access to the needed environments in projects. What else is in secrets? * **Configuration duplication** - create unique configurations for each user. * **Variable types** — you can specify bool, int, and other types. * **Generator and comments** — convenient addition and annotation of secrets. * **Reminders** — email notifications about the need to update a secret. * **Environment comparison** — find differences between configurations. * **Secret hiding** — exclude unique prod variables from comparison. * **Service keys** — API keys for read/write access to a specific configuration. * **IP restriction** — access control by CIDR. * **Change history** — full audit of all actions. #### ⚙️ CLI Integration Naturally, the **tuna** console client is fully [integrated](https://tuna.am/en/docs/secrets/cli.md): * `tuna secrets download` — Getting secrets with the ability to save to a file (`json`, `yaml`, `env`) or output to *stdout*. * `tuna secrets setup` — Setting up a directory for scope configuration. * `tuna secrets run` — Automatic application configuration with passing environment variables directly to the process. And with the `--watch` flag, the application will restart automatically when a secret is changed in the personal account. **VK Video** **YouTube** #### 🛡️ Access Management and Security * **RBAC in team** — Provide access to environments precisely by users. * **Service keys** — Issue unique keys for specific configurations with *read* or *read and write* restrictions. Perfect for use in CI/CD. * **Allowed IP addresses and subnets** — Restrict access to secrets by specifying allowed IP addresses and subnets in CIDR format. * **Change history** — Full audit of all actions and changes within configurations allows tracking who changed secrets and when. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🆕 Tuna Desktop - new GUI application](https://tuna.am/en/blog/tuna-desktop-first.md)[older →🆕 Новые типы туннелей - Postgres и Redis](https://tuna.am/en/blog/tunnels-pg-and-redis.md) --- ### Секреты целиком из консоли — большое обновление tuna secrets [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) September 24 · 4 min До недавнего времени [секреты](https://tuna.am/en/docs/secrets.md) в консоли умели немного: скачать конфигурацию, привязать каталог и запустить приложение с переменными окружения. Всё остальное — завести секрет, поправить значение, создать окружение — приходилось делать в личном кабинете. В **tuna-cli 0.35** `tuna secrets` стал полноценным инструментом: секретами, проектами, окружениями и сервисными ключами теперь можно управлять целиком из терминала, а вывод команд удобно разбирать в скриптах и CI. #### Работа с отдельными секретами Появились команды для чтения и записи секретов по одному: ```shell tuna secrets list # список, значения скрыты tuna secrets get API_TOKEN DB_URL # значения конкретных секретов tuna secrets set API_ENABLED=true --type boolean openssl rand -hex 32 | tuna secrets set API_TOKEN tuna secrets delete OLD_KEY tuna secrets upload .env # дополнить конфигурацию из файла ``` `set` принимает значение аргументом, из stdin или интерактивно — так в историю шелла не попадёт ничего лишнего. Можно задать тип значения, комментарий и видимость в веб-интерфейсе. `upload` дополняет конфигурацию из `.env` или `.json`, не трогая остальные секреты. #### Сравнить окружения, не раскрывая значений В таблицах вместо значения секрета теперь печатается его **отпечаток** — `hmac:cea41705`. Одинаковые значения дают одинаковый отпечаток, а восстановить значение из отпечатка нельзя. Так можно ответить на вопрос «а в stage и prod точно разные пароли к базе?», ничего не раскрывая, и спокойно печатать таблицы в логи CI: Настоящие значения показывают `list --values` и `get`. #### Новый разработчик — одна команда Положите в корень репозитория файл `tuna.yaml`: tuna.yaml ```yaml setup: - project: api-php config: dev ``` Теперь новому человеку в команде достаточно выполнить `tuna secrets setup` — и `tuna secrets run -- yarn start` запустит приложение с актуальными секретами. Для монорепозитория можно описать несколько привязок, каждую со своим каталогом. #### Секреты файлом, но без файла на диске Некоторые приложения читают конфигурацию только из файла. Раньше приходилось выгружать секреты через `download` и помнить, что файл потом надо удалить. Флаг `--mount` решает это иначе: клиент создаёт **именованный канал** (named pipe) и отдаёт через него секреты в нужном формате — `env`, `json`, `yaml` или по вашему шаблону. Значения живут только в памяти клиента, файла с секретами на диске не появляется, а после выхода процесса канал удаляется. ```shell tuna secrets run --mount ./secrets.json --mount-format json -- ./app --config ./secrets.json ``` С `--mount-max-reads 1` канал исчезает сразу после того, как приложение прочитало его при старте. #### Работа без сети `tuna secrets run` сохраняет последние полученные секреты в **зашифрованный** файл и использует его, если API недоступен, — приложение не встанет из-за проблем с сетью. Если же сервер ответил, что доступ отозван или конфигурации больше нет, копия удаляется: устаревшие секреты после отзыва доступа опаснее, чем остановка приложения. С `--offline` клиент вообще не обращается к API: #### Шаблоны конфигов `tuna secrets substitute` рендерит файл-шаблон со значениями секретов. Подходит для конфигов со своим форматом — `nginx.conf`, `application.yml`, `.ini` — и для манифестов Kubernetes: ```shell tuna secrets substitute config.tmpl --output config.ini tuna secrets substitute deployment.tmpl | kubectl apply -f - ``` Опечатка в имени секрета — ошибка с указанием места в шаблоне, а не молчаливая пустая строка в конфиге. #### Удобно в скриптах и CI Вывод всех команд `tuna secrets` переделан с расчётом на автоматизацию: * данные идут в stdout, статус и ошибки — в stderr, так что вывод можно смело перенаправлять в файл или пайп; * `-o json` и `-o yaml` есть у каждой подкоманды, включая команды записи, а ошибки в этом режиме тоже приходят JSON'ом; * `get --plain` печатает голое значение для подстановки: `docker login -p "$(tuna secrets get REGISTRY_PASSWORD --plain)"`; * `tuna secrets run` возвращает код выхода дочернего процесса, поэтому обёртку можно ставить прямо в CI-джобу. Готовые примеры для GitLab CI и GitHub Actions есть в [документации](https://tuna.am/en/docs/secrets/examples.md). #### Управление структурой Проекты, окружения, конфигурации и сервисные ключи теперь тоже создаются из консоли: ```shell tuna secrets projects create "API PHP" --slug api-php tuna secrets environments create Staging --slug stage --project api-php tuna secrets configs clone stage --name stage-feature-x --project api-php tuna secrets configs tokens create --description ci --access read --max-age 720h --project api-php --config stage ``` `tuna secrets open` открывает текущую конфигурацию в веб-интерфейсе — когда всё-таки нужен кабинет. #### Что проверить при обновлении * Удаление секретов, конфигураций, окружений, проектов и отзыв сервисных ключей с tuna-cli 0.36 спрашивают подтверждение. В CI и скриптах добавьте флаг `--yes`, иначе команда завершится ошибкой. * Если вы разбирали вывод `tuna secrets` в скриптах, проверьте их: строки лога вида `INFO[...]` заменены таблицами, а для машинного разбора теперь есть `-o json`. Полное описание команд и флагов — на странице [Работа в CLI](https://tuna.am/en/docs/secrets/cli.md). Обновить клиент можно по [инструкции](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🖥️ tuna-cli 0.36 — интерфейс в терминале и большое обновление](https://tuna.am/en/blog/cli-v0-36-tui.md)[older →💳 Оплата международными картами — подключили PayPro Global](https://tuna.am/en/blog/paypro-global.md) --- ### Безопасность Секретов и отличие от Паролей - мастер-ключ, шифрование и общая архитектура. [Blog](https://tuna.am/en/blog.md)|[Under the hood](https://tuna.am/en/blog/tags/engineering)|[Security](https://tuna.am/en/blog/tags/security) June 3 · 9 min Когда вы отдаёте свои пароли, токены и ключи доступа во внешний сервис, главный вопрос — можно ли ему доверять. Поэтому мы решили показать, как устроена безопасность [Tuna Секретов](https://tuna.am/en/docs/secrets.md) изнутри: где хранится мастер-ключ, каким алгоритмом шифруются данные и — самое важное — что произойдёт, если нашу инфраструктуру взломают и утечёт вся база данных. > **Коротко.** Значения секретов шифруются **до** записи в базу алгоритмом **AES-256-GCM**. Ключ, которым их можно расшифровать, в базе **не лежит**. Утёкший дамп БД без мастер-ключа — это бесполезный набор зашифрованных данных. #### Пароли и секреты: две разные модели безопасности В Tuna есть два хранилища для чувствительных данных — [Менеджер паролей](https://tuna.am/en/docs/passwords.md) и [Секреты](https://tuna.am/en/docs/secrets.md). Они намеренно построены по-разному, потому что решают разные задачи. ![Сравнение моделей: пароли шифруются на клиенте, секреты — на сервере](/blog/img/2026-06-03-secrets-security/00-pwd-vs-secrets.png) **Менеджер паролей — модель нулевого знания (zero-knowledge).** Ключи шифрования создаются на вашем устройстве и никогда не покидают клиент — браузер или приложение. Шифрование и расшифровка происходят только на вашей стороне, а на сервер уходит уже зашифрованный текст. Принцип, заложенный в основу: *компрометация нашей инфраструктуры не должна приводить к компрометации ваших паролей* — расшифровать их можно лишь там, где есть мастер-ключ, а он есть только у вас. Обратная сторона — данные доступны только владельцу мастер-ключа: автоматически выдать пароль скрипту или пайплайну, не раскрывая его, в такой модели невозможно. **Секреты — серверное шифрование.** Здесь мастер-ключ находится на стороне платформы, и шифрование выполняет сервер. Это **осознанный компромисс**, и сделан он ради главного сценария секретов — автоматизации. Секрет должен сам, без участия человека, попадать в приложение при запуске, в CI/CD-пайплайн, в скрипт развёртывания. Запуск приложения с подгрузкой переменных, сервисные ключи для CI, автоматический перезапуск при изменении значения — всё это работает именно потому, что сервер способен выдать секрет по запросу авторизованного клиента, не требуя мастер-пароль каждый раз. Платой за удобство становится доверие к инфраструктуре платформы — технически сервер может расшифровать секрет. Поэтому всё, что описано дальше, — про то, как мы это доверие оправдываем. | | Пароли | Секреты | | ------------------------------ | ------------------------- | --------------------------------- | | Модель | Нулевое знание | Серверное шифрование | | Где находится ключ | Только у клиента | На стороне платформы | | Где идёт шифрование | На клиенте | На сервере | | Может ли Tuna прочитать данные | Нет | Да, для авторизованных операций | | Автоматизация (CI/CD, скрипты) | Ограничена | Полноценная | | Главный сценарий | Хранение и обмен паролями | Доставка конфигурации приложениям | #### Модель доверия секретов Раз сервер умеет расшифровывать секреты, главный принцип звучит так: **база данных не должна содержать ничего, чем можно расшифровать секреты**. Даже если у злоумышленника окажется полный дамп таблиц, в нём не должно быть достаточно материала, чтобы получить исходные значения. Достигается это классической схемой **конвертного шифрования** (envelope encryption) — двухуровневой иерархией ключей. #### Конвертное шифрование: два уровня ключей ![Иерархия ключей: мастер-ключ оборачивает ключ команды, ключ команды шифрует значения секретов](/blog/img/2026-06-03-secrets-security/01-key-hierarchy.png) В системе два уровня ключей: 1. **Мастер-ключ платформы (KEK, Key Encryption Key).** Хранится на стороне Tuna и в базу данных не попадает. Сам по себе он ничего пользовательского не шифрует — его единственная задача «оборачивать» (защищать) ключи команд. 2. **Ключ команды (DEK, Data Encryption Key).** Для каждой команды генерируется свой случайный ключ — криптографическим генератором случайных чисел. Именно им шифруются значения секретов этой команды. Ключ команды не хранится в открытом виде: он зашифрован мастер-ключом и в таком «обёрнутом» виде лежит в базе. Чтобы им воспользоваться, сервер сначала разворачивает его мастер-ключом в памяти. Получается цепочка зависимостей: **значение секрета** защищено **ключом команды**, а **ключ команды** защищён **мастер-ключом**. Разорвав любое звено, дальше пройти нельзя. Любопытно, что [менеджер паролей](https://tuna.am/en/blog/password-manager.md) построен на той же идее конвертного шифрования — с двумя принципиальными отличиями: «конверт» там запечатывается асимметрично, на паре открытого и закрытого ключей, а корневой ключ остаётся на стороне клиента, а не сервера. Схема одна и та же, по-разному распределено лишь доверие: секреты настроены на автоматизацию, пароли — на нулевое знание. #### Где и как хранится мастер-ключ Это основной для всей схемы вопрос, поэтому разберем каждое место хранения: * **Не в базе данных.** Мастер-ключ принципиально отделён от данных, которые он защищает. В дампе базы его нет. * **Не в коде и не в репозитории.** Он не зашит в исходники, а передаётся сервису извне — из защищённого хранилища конфигурации платформы. * **Только в памяти процесса.** Сервер держит ключ в оперативной памяти на время работы и не пишет его на диск в открытом виде. * **Полноценный 256-битный ключ.** Этого достаточно, чтобы перебор был вычислительно невозможен. Главный вывод: **доступ к базе данных и доступ к мастер-ключу — это два разных уровня доступа** с разными правами. Компрометация одного из них не означает автоматически компрометацию второго. #### Жизненный цикл секрета: запись и чтение Шифрование и расшифровка встроены в слой работы с данными и происходят прозрачно — приложение никогда не пишет открытое значение в базу и никогда не читает его оттуда напрямую. ![Последовательность сохранения и чтения секрета: шифрование перед записью, расшифровка после чтения](/blog/img/2026-06-03-secrets-security/02-lifecycle.png) **При сохранении** значение шифруется ключом команды ещё до записи. Для каждого значения берётся свой случайный одноразовый вектор (nonce) — поэтому даже два одинаковых секрета выглядят в базе по-разному. В хранилище попадает только зашифрованный текст. **При чтении** происходит обратный процесс: сервер достаёт шифр, разворачивает ключ команды и расшифровывает данные. Открытое значение существует только в памяти процесса ровно столько, сколько нужно, чтобы отдать его авторизованному клиенту. Мы используем режим **GCM** (Galois/Counter Mode) — это *аутентифицированное* шифрование. Помимо конфиденциальности оно гарантирует целостность: если кто-то изменит хотя бы один байт зашифрованных данных в базе, расшифровка не вернёт «мусор», а честно провалится с ошибкой проверки. Подделать или незаметно подменить значение в обход сервера не получится. В транзите всё дополнительно защищено TLS — между вашим клиентом и API данные идут по зашифрованному каналу. #### Что лежит в базе зашифровано, а что открыто Разбор того, какие данные и в каком виде реально хранятся в базе: | Зашифровано (AES-256-GCM) | В открытом виде | | ------------------------------- | ------------------------------------------- | | Значения секретов | Имена переменных | | Заметки к секретам | Типы значений | | Ключ команды (в обёрнутом виде) | Уровень видимости | | | Структура: проекты, окружения, конфигурации | | | Метки времени создания и изменения | Да, **метаданные — это тоже информация**. Даже не видя значений, по именам переменных можно судить о том, какими сервисами и технологиями вы пользуетесь. Сами секреты при этом остаются зашифрованными. Но это снова небольшой компромисс в угоду удобству и быстроте работы. #### Что будет, если утечёт база данных Это сценарий, ради предотвращения которого вся схема и строилась. Представим худшее: злоумышленник получил полный дамп нашей базы. ![Модель угроз при утечке базы: без мастер-ключа расшифровать значения невозможно](/blog/img/2026-06-03-secrets-security/03-db-leak.png) **Что у него на руках:** * Зашифрованные значения секретов — шифр AES-256-GCM, статистически неотличимый от случайного шума. * Ключ команды — но он сам зашифрован мастер-ключом. * Метаданные — имена, типы, структура проектов. **Чего у него нет:** * **Мастер-ключа.** Его нет в базе — он на стороне сервера, куда дамп базы доступа не даёт. Цепочка обрывается на первом же шаге. Чтобы расшифровать значение, нужен ключ команды. Чтобы развернуть ключ команды — нужен мастер-ключ. Мастер-ключа в дампе нет, а взломать AES-256 перебором невозможно. **Значения секретов остаются защищены.** Злоумышленник видит лишь каркас: имена и структуру, но не содержимое. ##### Границы защиты Никакая схема не защищает от всего, и важно понимать её рамки: * **Если утечёт И база, И мастер-ключ одновременно** — защита падает. Именно поэтому они хранятся раздельно, с разными правами доступа: чтобы одна уязвимость не вскрывала сразу оба уровня. * **Метаданные видны** при утечке базы — об этом сказано выше. * **Реакция на инцидент.** При подозрении на компрометацию правильный шаг — ротация: смена мастер-ключа, перевыпуск [сервисных ключей](https://tuna.am/en/docs/secrets.md) и обновление самих секретов в их первоисточниках. Сервисные ключи можно выпускать с ограниченным сроком жизни именно на такой случай. #### Дополнительные уровни защиты Шифрование — это фундамент, но не единственный слой. Доступ к секретам ограничен ещё на нескольких уровнях: * **Скрытые значения (Restricted).** Секрет с такой видимостью не отдаётся через API и не попадает в выгрузки — даже авторизованному пользователю показывается только факт его существования. * **Сервисные ключи с узким доступом.** Для CI/CD выпускаются ключи, привязанные к конкретной конфигурации, с правами только на чтение или на чтение и запись, и с опциональным сроком действия. Утечка такого ключа не открывает доступ ко всем секретам команды. * **Ограничение по IP (Trusted IP).** Доступ к конфигурации можно ограничить списком разрешённых адресов и подсетей в формате CIDR. * **Полный аудит.** Все изменения внутри конфигураций журналируются — видно, кто и когда менял каждый секрет. #### Что в планах Сейчас Tuna Секреты — это централизованное и зашифрованное хранилище. Логичный следующий шаг — сделать его **источником истины**, из которого секреты автоматически синхронизируются туда, где они нужны. ![Планы: синхронизация секретов из Tuna в Vault, Kubernetes Secrets, GitHub и GitLab CI/CD](/blog/img/2026-06-03-secrets-security/04-future-sync.png) В планах — интеграции для односторонней синхронизации: * **HashiCorp Vault** — отправка секретов в Vault, чтобы Tuna стала удобным интерфейсом управления, а приложения продолжали читать их привычным способом. * **Kubernetes Secrets** — синхронизация конфигурации напрямую в секреты нужного namespace кластера. * **GitHub Actions** — публикация в encrypted secrets репозитория или организации для использования в пайплайнах. * **GitLab CI/CD** — синхронизация в CI/CD variables проектов и групп. Идея в том, чтобы вы редактировали секрет в одном месте, а он автоматически и безопасно доходил до всех ваших окружений — без копирования значений между десятком панелей. #### Итог Безопасность Tuna Секретов держится на нескольких простых и проверенных принципах: * **Две модели под две задачи** — пароли шифруются на клиенте (мы их не видим), секреты — на сервере, ради автоматизации. * **AES-256-GCM** — аутентифицированное шифрование значений до записи в базу. * **Конвертное шифрование** — мастер-ключ защищает ключи команд, ключи команд защищают данные. * **Мастер-ключ отделён от данных** — его нет ни в базе, ни в коде, и утёкший дамп БД бесполезен. * **Слои доступа сверху** — видимость, сервисные ключи, ограничение по IP и аудит. Мы не делаем вид, что защита абсолютна, и открыто показываем её границы — потому что доверие строится на прозрачности, а не на маркетинговых обещаниях. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Under the hood](https://tuna.am/en/blog/tags/engineering), [Security](https://tuna.am/en/blog/tags/security) [← newerКто может зайти в ваш внутренний сервис? 5 способов закрыться авторизацией](https://tuna.am/en/blog/apps-access-methods.md)[older →🆕 Приложения — Zero Trust доступ к вашим сервисам](https://tuna.am/en/blog/apps.md) --- ### What to patch now: PHP, GitLab, Node.js, Brotli and eMudhra [Blog](https://tuna.am/en/blog.md)|[Security](https://tuna.am/en/blog/tags/security) October 26 · 16 min Mid-October was rough. Within two weeks, PHP, GitLab and Node.js all shipped emergency fixes. An out-of-bounds write turned up in the reference Brotli decoder and spread from there into Node.js, Bun, Python libraries and proxies. And the private keys of the eMudhra certificate authority turned out to be crackable in a fraction of a second. Here's what happened, which versions to install and what to do if you can't update right now — all in one place. > **TL;DR** > > | What | Update to | How urgent | > | ------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------- | > | PHP | 8.5.12, 8.4.27, 8.3.36, 8.2.35 | Today: unauthenticated code execution, attacks are already underway | > | GitLab (self-managed) | 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, 18.11.13 | Today: rated 10 out of 10, every version since 7.0 is affected | > | Node.js | 26.12.1, 24.22.1, 22.23.4 | Today: code execution through Brotli plus nine more vulnerabilities | > | Brotli in libraries and proxies | libbrotli 1.2.1, Python Brotli 1.2.1, Bun 1.4.4, Envoy, Istio | This week: check your dependencies and rebuild images | > | eMudhra certificates | up-to-date ca-certificates in the OS, Go 1.27.3 or 1.26.10 | This week: update your root certificate stores | > | nginx | 1.30.6, or 1.31.7 and later | If you use `alias` with `rewrite`, SSI with AIO, or HTTP/3 | #### Why this matters for tunnels A tunnel makes a local service reachable from the internet, so everything below applies to it too. A test site on PHP, a Node.js dev server or a home lab GitLab behind a tunnel is just as vulnerable as a production server. It just tends to get updated less often. Two habits go a long way toward reducing the risk: 1. **Update your local environment along with production.** A vulnerable PHP on a developer's laptop, exposed through a tunnel, is just as much an entry point for an attack. 2. **Don't open a service to everyone if only two people need it.** You can lock a tunnel down with a [password](https://tuna.am/en/docs/tunnels/http.md#basic-auth), a [key in a header](https://tuna.am/en/docs/tunnels/http.md#key-auth) or a [subnet allowlist](https://tuna.am/en/docs/tunnels/http.md#cidr). A request that fails the check never reaches your app. For more complex rules, there are [traffic policies](https://tuna.am/en/docs/traffic-policy.md) and [Apps](https://tuna.am/en/docs/apps.md) with SSO sign-in. We compared all the options in our post ["5 ways to put authorization in front of a service"](https://tuna.am/en/blog/apps-access-methods.md). ```shell tuna http 8080 --basic-auth="anna:s3cret" --cidr-allow="203.0.113.0/24" ``` #### PHP2Shell: unauthenticated code execution **What happened**: On October 21, PHP shipped an out-of-band release fixing two critical vulnerabilities in the interpreter itself. Both are rated 9.2 on CVSS 4.0, and both can be exploited without authentication: * **CVE-2026-109618.** If a request hits `memory_limit` at the wrong moment, internal data is left half-initialized, and request cleanup then treats it as valid. The result is memory corruption. * **CVE-2026-109619.** Request state outlives the memory it lives in and gets picked up by the next request on the same worker. This is a use-after-free. **Who is affected**: Every way of running PHP for the web is vulnerable: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. mod_php is the worst case, because the attack fires before any application code runs. The bug lives in the interpreter, so any site on a vulnerable version is at risk — WordPress, Bitrix, Laravel or custom-built. You need to update PHP itself; updating your CMS won't help. **Why it's urgent**: The CVE IDs leaked on October 20, and the release had to ship a day ahead of schedule. Amazon spotted the first exploitation attempt against its honeypots as early as October 17, four days before the fix, and now reports broad exploitation in the wild. Public exploits are out as well. Government agencies are sounding the alarm too: the UK's [NCSC](https://www.ncsc.gov.uk/news/critical-security-issue-affecting-php) advises taking systems offline if you can't patch or mitigate them, and on October 22 Russia's NCIRCC told critical infrastructure operators to update urgently and to cut off from external access or shut down any servers they can't patch in time. **What to do**: 1. Update to **8.5.12, 8.4.27, 8.3.36 or 8.2.35**, or install your distribution's package with the backported fix. After updating, restart all workers: php-fpm, Apache. If PHP runs in a container, rebuild your app image on top of the official [php](https://hub.docker.com/_/php) image with a fixed version, such as `php:8.4.27-fpm` or `php:8.3.36-apache`. 2. PHP 8.1 and older won't get official releases. The PHP team has published minimal patches for the 7.4–8.1 branches, but you have to build them from source. Otherwise, your options are moving to a supported branch or turning to an LTS vendor. Keep in mind that PHP 8.2 reaches end of life on December 31, 2026. 3. If you can't update right away, apply the temporary mitigations from the PHP advisory. They narrow the attack paths but don't fully close the vulnerabilities. In `php.ini` or the FPM pool settings — but not via `ini_set()` and not in `.user.ini`: ```ini ; CVE-2026-109619 session.upload_progress.enabled = 0 ``` In the PHP-FPM pool — via `php_admin_value`, so the application can't override it: ```ini ; CVE-2026-109618 php_admin_value[memory_limit] = -1 ``` Also cap the request size: `post_max_size = 8M` for sites without file uploads and `32M` for sites with them. On PHP 8.5, also set `fatal_error_backtraces = 0`. Restart the workers after making changes. > **Turned off upload progress? Leave it off.** On October 23, PHP published another advisory, CVE-2026-115560, also related to upload progress. The scenario is unusual: the application has to switch `session.serialize_handler` at runtime. The fix will come in a regular release, and until then it's best to keep `session.upload_progress.enabled = 0`. **Learn more**: * [PHP advisory of October 21](https://www.php.net/archive/2026.php#2026-10-21-5) and [advisory of October 23](https://www.php.net/archive/2026.php#2026-10-23-1) * [PHP Foundation post](https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/) and GitHub advisories: [CVE-2026-109618](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w), [CVE-2026-109619](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp) * [Tenable FAQ](https://www.tenable.com/blog/php2shell-cve-2026-109618-cve-2026-109619-frequently-asked-questions-about-critical-php-remote) and [Rapid7 analysis](https://www.rapid7.com/blog/post/etr-cve-2026-109618-and-cve-2026-109619-critical-unauthenticated-remote-code-execution-in-php/) * [NCSC alert](https://www.ncsc.gov.uk/news/critical-security-issue-affecting-php) and the [NCIRCC demand](https://habr.com/ru/news/1098146/) (news on Habr, in Russian) #### GitLab: two 10-out-of-10 vulnerabilities in one month **What happened**: On October 12, GitLab fixed CVE-2026-97229, rated 10.0 on CVSS. A specially crafted regular expression in a CI/CD configuration could execute code on the server without authentication. Versions from 19.2 onward were affected. On October 21, another out-of-band release followed. Its headline vulnerability, **CVE-2026-110264**, is also rated 10.0: an integer overflow in the regular expression compiler once again allows unauthenticated code execution. It affects Community and Enterprise Edition across every version since 7.0, which dates back to 2014. The same release fixed 11 more vulnerabilities, including takeover of LDAP accounts through OmniAuth auto-linking (8.2) and OAuth/OIDC discovery cache poisoning (7.5). > **Updated on October 12? That's not enough.** The October 12 release fixed a different vulnerability. Only the October 21 release protects against CVE-2026-110264. **What to do**: * Self-managed: update to **19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10 or 18.11.13**. There are no fixes for versions older than 18.11; you'll need to move to a supported branch. * If you're far behind, you can't jump straight to the latest version: GitLab requires going through mandatory intermediate versions. [Upgrade Path](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) helps you plan the route. Enter your current version, and the tool shows which versions to go through to reach the latest, what breaking changes they contain and which features will start behaving differently. * GitLab in Docker is updated by changing the tag of the [gitlab/gitlab-ce](https://hub.docker.com/r/gitlab/gitlab-ce) or [gitlab/gitlab-ee](https://hub.docker.com/r/gitlab/gitlab-ee) image: `19.5.1-ce.0`, `19.5.1-ee.0` and so on. Pin the version explicitly instead of using `latest`, or you'll skip the intermediate versions. * On a single-node installation, the migrations in this release can cause downtime, so plan the update. Multi-node installations can be updated without downtime using the standard procedure. * GitLab.com and GitLab Dedicated are already patched; there's nothing you need to do. **Learn more**: * [19.5.1 release of October 21](https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-5-1-released/) and [19.4.2 release of October 12](https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-4-2-released/) * [Plan your upgrade path](https://docs.gitlab.com/update/upgrade_paths/) — GitLab documentation * [SecurityOnline coverage](https://securityonline.info/gitlab-critical-patch-release-cve-2026-110264/) #### Brotli: a vulnerability in a decoder that's almost everywhere **What happened**: CVE-2026-111497 is an out-of-bounds write in the reference [google/brotli](https://github.com/google/brotli) decoder, versions 1.0.3 through 1.2.0. A specially crafted compressed stream makes the decoder write past its allocated memory. The impact ranges from a process crash and a memory leak to code execution. It's rated 9.2 on CVSS 4.0. The vulnerability doesn't have a settled name yet: it goes by Node2Shell, Broccoli2Shell and HTTP2Shell. **Why it's dangerous**: Brotli decompression is often on by default, and developers may not even know it: * **Node.js.** The built-in `fetch()` asks HTTPS servers for Brotli on its own and decompresses the responses. Express 5 and body-parser 2.x decompress request bodies with `Content-Encoding: br`. * **Python.** urllib3, requests, httpx and aiohttp decompress Brotli if the `Brotli` or `brotlicffi` package is installed in the environment. You don't need to import it in your code — it's enough for it to be installed as a dependency. * **Bun, Envoy and Istio** with the Brotli decompression filter, and **curl** with the `--compressed` flag. nginx and Apache don't decompress incoming Brotli by default. Update first the services that fetch user-supplied URLs (webhooks, link previews, scrapers) and the services that accept compressed request bodies. **What to do**: | Component | Fixed versions | Docker Hub image | | ------------------------- | ------------------------------ | ------------------------------------------------------------- | | libbrotli (google/brotli) | 1.2.1, 1.1.1, 1.0.10 | base image package | | Python: Brotli | 1.2.1 | — | | Python: brotlicffi | 1.2.1.0 | — | | Node.js | 26.12.1, 24.22.1, 22.23.4 | [node](https://hub.docker.com/_/node) | | Bun | 1.4.4 | [oven/bun](https://hub.docker.com/r/oven/bun) | | Envoy | 1.40.0, 1.39.3, 1.38.6, 1.37.8 | [envoyproxy/envoy](https://hub.docker.com/r/envoyproxy/envoy) | | Istio | 1.31.2, 1.30.6, 1.29.9 | [istio/proxyv2](https://hub.docker.com/r/istio/proxyv2) | | Go: andybalholm/brotli | v1.2.7 | — | * The system libbrotli is updated with your distribution's package. Check whether the fix has landed for your base image, then rebuild your image. A statically compiled copy can only be updated by rebuilding. That includes executables built with `bun build --compile`. * In containers, pin the fixed version in the tag explicitly or pull the image again with `docker pull`. A floating tag like `node:24` in your local cache may still point to an old build. * Brotli is also embedded in other projects: .NET, PHP and Ruby extensions, ClickHouse, DuckDB. According to [OpenNet](https://www.opennet.ru/opennews/art.shtml?num=66537) (in Russian), not all of them had shipped fixes when the news came out. Keep an eye on the advisories for your stack. * As a stopgap, you can reduce the risk by disabling Brotli decompression or rejecting requests with `Content-Encoding: br` at the proxy. This is not a substitute for updating. **Learn more**: * [Wiz analysis](https://www.wiz.io/blog/cve-2026-111497-brotli-rce-node-js) with a table of affected products * [Aikido analysis](https://www.aikido.dev/blog/python-packages-vulnerable-node2shell-brotli-cve-2026-111497) of Python libraries and automatic decompression #### Node.js: nine more vulnerabilities On October 21, **Node.js 26.12.1, 24.22.1 and 22.23.4** came out. Besides Brotli, the release fixes nine more vulnerabilities. The most important ones: * **CVE-2026-105483 and CVE-2026-106069** (High). The hostname check against a TLS certificate can be bypassed by writing an IP address in a non-standard form: with fullwidth digits or leading zeros. * **CVE-2026-105482** (High). `dns.resolve*()` truncates a name at a NUL byte, so `attacker.example\0.trusted.example` resolves as `attacker.example`. This is dangerous for applications that check a host against an allowlist and then resolve it. * **CVE-2026-106074** (Medium). The entropy cache of `crypto.randomUUID()` and `crypto.randomInt()` ends up in snapshots (`--build-snapshot`) and single executable applications (SEA). Every process started from the same snapshot produces the same UUIDs. Updating Node.js alone isn't enough here: snapshots and SEAs built with a vulnerable version need to be rebuilt. The same release includes updates to OpenSSL, c-ares, undici and the bundled root certificate store, which drops the compromised eMudhra certificates (more on those below). Node.js 20 and older are no longer supported and won't get fixes. The fixed versions are already available in the official [node](https://hub.docker.com/_/node) image: `26.12.1`, `24.22.1` and `22.23.4`. **Learn more**: * [Node.js advisory](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) * [How to find vulnerable runtimes — Qualys](https://blog.qualys.com/product-tech/2026/10/23/node-js-patches-brotli-remote-code-execution-flaw-find-vulnerable-runtimes-before-attackers-do) * [Aikido on Node2Shell](https://www.aikido.dev/blog/node2shell-nodejs-brotli-rce-cve-2026-111497) — how `fetch()` and Express end up decompressing Brotli #### eMudhra: certificate authority keys cracked in under a second **What happened**: On October 17, a researcher going through the CCADB certificate authority database discovered that the private keys of eight intermediate certificates from the Indian CA eMudhra could be recovered from the public ones in under a second. Each key turned out to be a number below 256, and one was literally 4. Six of these certificates were meant for issuing TLS certificates for websites, and two for code signing. The chains led to root certificates trusted by Apple, Google, Microsoft and Mozilla. That means anyone with these keys could issue a valid certificate for any domain and intercept HTTPS traffic. **The response**: Chrome and Firefox blocked the certificates on the day of the report, Apple on October 19, and Microsoft on the 20th. Mozilla stopped trusting the root certificate that signed the compromised intermediates. Google is removing the emSign ECC Root CA – G3 root from Chrome. On October 22, eMudhra announced it was leaving the global TLS certificate business. Around 30,000 customer certificates will need to be reissued. No forged certificates issued with these keys have been reported so far. According to the Indian authorities, Indian digital signature certificates (DSC) and the eSign service are not affected. **What to do**: Browsers and operating systems are already protected; installing updates is enough. It's trickier with software that has its own root certificate store: * **Servers and containers.** Update the `ca-certificates` package. Rebuild your images on a fresh base image — [debian](https://hub.docker.com/_/debian), [ubuntu](https://hub.docker.com/_/ubuntu), [alpine](https://hub.docker.com/_/alpine) — and check that it ships the updated `ca-certificates`. * **Go.** Versions 1.27.3 and 1.26.10, released October 19, reject chains that go through the compromised certificates (CVE-2026-111943). The block lives in the standard library, so Go applications need to be rebuilt with the new compiler version. In a multi-stage Docker build, bump the [golang](https://hub.docker.com/_/golang) image to `1.27.3` or `1.26.10`. * **Node.js.** The updated bundled root certificate store is part of the October 21 release. * **Java, Python certifi, mTLS stores and certificate bundles embedded in applications.** Check each one separately. * **Sites with an eMudhra certificate.** Reissue the certificate with a different CA. > **Don't turn off certificate verification.** If something stops loading over HTTPS after the updates, don't "fix" it with `--insecure`, `InsecureSkipVerify` or `NODE_TLS_REJECT_UNAUTHORIZED=0`. That opens the door to exactly the attack the block protects against. **Learn more**: * [Incident report in Mozilla Bugzilla](https://bugzilla.mozilla.org/show_bug.cgi?id=2083449) * [Cloudflare analysis](https://blog.cloudflare.com/emudhra-certificate-compromise/) with advice for servers, containers and mTLS * [Scott Helme's analysis](https://scotthelme.co.uk/eight-bits-of-trust-emudhras-ca-keys-cracked-in-milliseconds/) and the [Help Net Security timeline](https://www.helpnetsecurity.com/2026/10/21/emudhra-certificates-private-keys-compromised/) * [BleepingComputer coverage](https://www.bleepingcomputer.com/news/security/mozilla-google-apple-block-compromised-emudhra-ca-certificates/) #### What else came out in October * **nginx 1.30.6 and 1.31.7** (October 14). Five vulnerabilities that only show up with specific configurations: `alias` combined with `rewrite`, evaluation of string expressions with variables, SSI combined with AIO, HTTP/3, and TLS session ticket key rotation. nginx itself rates them as medium, but if you recognize your config, update. The full list is in the [nginx security advisories](https://nginx.org/en/security_advisories.html). The fixed versions are available in the official [nginx](https://hub.docker.com/_/nginx) image: `1.30.6` (stable) and `1.31.7` or later (mainline). * **Go 1.27.2 and 1.26.9** (October 8). Security fixes in `crypto/tls`, `net/http`, `html/template`, `net/textproto`, `os` and the `go` command. Details are in the [Go release history](https://go.dev/doc/devel/release). * **Collabora Online in Nextcloud** (CVE-2026-110447). OS command execution through the built-in CODE server — the `richdocumentscode` app before version 26.4.303. The vulnerability is already being used in attacks to plant web shells on Nextcloud servers. On October 23, CISA added it to its [Known Exploited Vulnerabilities catalog](https://www.cisa.gov/news-events/alerts/2026/10/23/cisa-adds-one-known-exploited-vulnerability-catalog-0). Update the app to 26.4.303 or remove it — just disabling it is not enough ([more on BleepingComputer](https://www.bleepingcomputer.com/news/security/hackers-exploit-collabora-rce-flaw-to-backdoor-nextcloud-servers/)). #### What we've done Tuna users' data is safe, and there's no threat to it. The base images of our server applications have already been updated. New versions of tuna-cli and Tuna Desktop are coming out this week — watch the [changelog](https://tuna.am/en/changelog.md). #### Checklist 1. Find every PHP installation — production, test, local — and update it. If you can't, apply the temporary mitigations. 2. Update self-managed GitLab to the October 21 release. If you're on an old version, plan the route with [Upgrade Path](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/). 3. Update Node.js, Bun and the `Brotli` and `brotlicffi` Python packages. Rebuild container images, snapshots and SEAs. 4. Update `ca-certificates`, rebuild Go applications with Go 1.27.3 or 1.26.10, and check your own certificate stores. 5. Check your nginx configuration and update Collabora in Nextcloud. 6. Lock down anything published through a tunnel that not everyone needs with a password, a key or an IP allowlist. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Security](https://tuna.am/en/blog/tags/security) [← newer🗞️ Что нового в Tuna — тёмная тема, адреса для файрвола и переезд Linux-пакетов](https://tuna.am/en/blog/whats-new-october-2026.md)[older →🦎 Tunnels are now highly available](https://tuna.am/en/blog/high-availability.md) --- ### Обратный SSH туннель [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) September 6, 2024 · 1 min В этом выпуске мы исправили ошибки, улучшили стабильность платформы, а также добавили новую экспериментальную функцию. #### Обратный SSH туннель > **Внимание.** Функция на стадии BETA тестирования, в настоящий момент поддерживается создание только HTTP туннелей с динамическим поддоменом. Обратный SSH туннель `ssh -R` - это альтернативный механизм создания туннеля без установки и использования клиента **tuna**. Для создания туннеля используется стандартный SSH клиент, как правило, он присутствует во всех Unix подобных ОС. Может быть крайне полезным, когда вам нужно открыть доступ к какому либо ресурсу, но нет возможности установить полноценный клиент, или это избыточно. ```shell ssh -R 443:localhost:8080 ssh-ru.tuna.am http ``` ![](/docs/img/ssh-r.gif) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerВыпуск tuna 0.19.0](https://tuna.am/en/blog/release-v0-19-0.md)[older →Обновления - оплата по счёту и новые функции клиента](https://tuna.am/en/blog/invoices.md) --- ### New plan names [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) February 20 · 1 min We've updated the names of our subscription plans. Functionality and pricing remain the same — only the names have changed. #### What's changed? | Before | After | | --------- | ----------- | | Hobby | **Starter** | | Developer | **Pro** | | Team | **Legend** | The new names better reflect the level of capabilities of each plan and the user's growth path on the platform. #### Why did we rename? The old name **Team** was misleading users — many thought the plan was intended exclusively for team use. In reality there are no such restrictions, and this plan can be used solo, getting expanded limits and all the platform's features. The new name **Legend** removes this misunderstanding. #### Switching from Pro to Legend Previously, to switch to the **Legend** plan you had to create a separate [team](https://tuna.am/en/docs/team.md) — now this is no longer required. You can switch from **Pro** to **Legend** directly from your [dashboard](https://my.tuna.am/subscription?lang=en) without any additional steps, keeping all your resources and settings. #### What stays the same? * Subscription pricing * Limits and features of each plan * Additional licenses * All active subscriptions continue to work If you have any questions — [get in touch](mailto:info@tuna.am). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newer🆕 Boards, Meets and other platform updates](https://tuna.am/en/blog/boards-meets.md)[older →PROXY Protocol support in TCP tunnels](https://tuna.am/en/blog/release-v0-30-0.md) --- ### Обзор возможностей работы в Легенде [Blog](https://tuna.am/en/blog.md)|[Guides](https://tuna.am/en/blog/tags/guides) October 11, 2024 · 4 min В этой статье я подробно расскажу о фишках и преимуществах при работе с Tuna в команде: что вы получите, кейсы применения и чего ждать в будущем. ![team](/en/assets/images/Screenshot_20241010_135041-c84b39aa1b1dd6d30b303e89b384103f.png) #### Для кого тариф Легенда? ☝ Нет никаких ограничений в количестве участников в команде и вы можете иметь команду на себя одного, но в тарифе Легенда, вы получите увеличенные лимиты и расширенные возможности. | Новичок | Профи | Легенда | | ---------------------- | ----------------------- | ------------------------------------------------------------- | | 1 туннель одновременно | 5 туннелей одновременно | **10** туннелей одновременно | | 0 статичных поддоменов | 10 статичных поддоменов | **20** статичных поддоменов | | 0 собственных доменов | 1 собственный домен | **10** собственных доменов | | 0 статичных TCP портов | 1 статичный TCP порт | **5** статичных TCP портов | | - | - | [Политики трафика](https://tuna.am/en/docs/traffic-policy.md) | Поэтому, если вы уже пользуетесь Tuna на тарифе **Профи** и вам недостаточно, к примеру, 1-го статично TCP порта, а они используются не только для проброса [TCP трафика](https://tuna.am/en/docs/tunnels/tcp.md), но и для [SSH туннелей](https://tuna.am/en/docs/tunnels/ssh.md) и для [Trigger туннелей](https://tuna.am/en/docs/tunnels/trigger.md), то самое время перейти на тариф **Легенда**. #### Преимущества для компаний 🏢 ##### Оплата по счёту 🫰 Помимо возможности централизованной оплаты за всех участников, в Легенде доступна оплата банковским переводом, что часто является ключевым ограничением для закупок услуг компаниями или ИП. * Добавьте E-mail бухгалтера в [контакты команды](https://my.tuna.am/team_settings?lang=en) * Укажите реквизиты [организации](https://my.tuna.am/team_organization?lang=en) для выставления счёта * Дождитесь пока администратор подтвердит реквизиты (в рабочее время обычно не дольше 10-ти минут) * На странице управления [подпиской](https://my.tuna.am/subscription?lang=en) укажите платёжное средство - **Банковский перевод** * Нажмите купить подписку (сформируется счёт и отправится всем контактам из 1-го пункта) * После поступления денег подписка автоматически активируется ![team_settings](/en/assets/images/Screenshot_20241010_154647-e0fbba0fd21219da1ef3e42ab1efb21b.png) ![team_organization](/en/assets/images/Screenshot_20241010_154821-eafa66a5ef8cf957f07dc5d15043fe04.png) ![subscription](/en/assets/images/Screenshot_20241010_151909-90d4a24d3b959f659100021690316fa1.png) ##### RBAC 🙄 *Ролевая модель распределения прав доступа* В компаниях всегда нужно разграничивать права доступа, поэтому RBAC, как правило, является фундаментальной. ![members](/en/assets/images/Screenshot_20241010_160215-9f7acb55125aa211fead1d2be999646b.png) Какие роли есть у нас: * **Основатель** - создатель команды. Имеет все привилегии, может приглашать/удалять членов команды, назначать роли всем кроме основателя, управлять подпиской, менять настройки команды, просматривать аудит логи, просматривать и управлять ресурсами членов команды (туннели/порты/домены), просматривать статистику. Участник с такой ролью может быть только 1, его роль нельзя изменить и его нельзя удалить. * **Владелец** - также имеет все привилегии в команде, но таких участников может быть много, может приглашать/удалять членов команды, назначать роли всем кроме основателя, управлять подпиской, менять настройки команды, просматривать аудит логи, просматривать и управлять ресурсами членов команды (туннели/порты/домены), просматривать статистику. * **Администратор** - может приглашать членов команды, удалять Участников и администраторов, назначать роли Участникам и Администраторам, просматривать и управлять ресурсами членов команды (туннели/порты/домены), просматривать статистику. * **Пользователь** - знает только название команды в которой он состоит и может использовать свой токен в этой команде. Помимо этого вы можете указать будет ли пользователь учитываться в подписке. Можно добавить пользователя, но отключить его, т.е. не платить за него. Когда это полезно? Например, руководитель создаёт команду и приглашает в нее разработчиков, покупает подписку на количество разработчиков, но не платит за себя, так как сам туннелями он не пользуется, при этом имеет полный контроль. ##### Статистика и управление ресурсами 📊 Вы можете просматривать статистику по тем ресурсам, что используют участники, сколько занято портов, доменов, сколько было создано туннелей и так далее. Мы также будем развивать и добавлять новые метрики, дополнять текущие. ![team_statistics](/en/assets/images/Screenshot_20241010_164530-4ff71fea993773f81d589779873d2f8d.png) Ещё можно посмотреть все запущенные сейчас туннели, ссылки и их авторов, а также удалить активный туннель любого участника. Можно видеть и удалять домены и порты. Вы всегда сможете проконтролировать кто и что публикует, на каком домене или порту. ![team_tunnels](/en/assets/images/Screenshot_20241010_164856-046be4fcd02c6d6e657683335166a1ef.png) ![team_domains](/en/assets/images/Screenshot_20241010_164902-9bfbeb970dc4b1f959b5c947d0fea138.png) ![team_tcp_ports](/en/assets/images/Screenshot_20241010_164908-e9b5a8f248fc87fd3550ccf2a3bebdeb.png) ##### Аудит логи 🧐 Помимо ресурсов, что используются в реальном времени можно посмотреть историю создания/удаления/изменения ресурсов, ролей и других событий в команде. ![team_audit_events](/en/assets/images/Screenshot_20241010_165029-bd62b21605209e084f5428c9f3ab9d09.png) Сейчас есть лишь базовые события, но функционал будет развиваться под потребности пользователей. #### Выводы * Тариф с командами полезен всем, не только компаниям * Компании могут оплачивать по счёту * Расширенные лимиты и функции * Командная разработка может ускориться и стать проще #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Guides](https://tuna.am/en/blog/tags/guides) [← newerИзменение настроек tuna при обновлении Immich](https://tuna.am/en/blog/immich-118.md)[older →Выпуск tuna 0.19.0](https://tuna.am/en/blog/release-v0-19-0.md) --- ### Новый функционал - Дополнительные токены и управление командой [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) August 8, 2024 · 2 min Новые функций в этом выпуске! #### Дополнительные токены ![temp-tokens](/en/assets/images/temp-tokens-75bbb4bde8675bcf5f03aaf1a23bb10b.png) Мы добавили возможность создавать [дополнительные токены](https://my.tuna.am/token?lang=en) с различными ограничениями - по типу создаваемых туннелей HTTP/TCP, по времени действия, по количеству активных туннелей и количеству туннелей которые можно создать. Это очень удобно если вы хотите чтобы ваш друг/коллега/клиент запустил **tuna** у себя и предоставил вам доступ к ресурсу, но при этом вы не хотите после этого менять ваш основной токен, так как он настроен в вашем окружении. При создании токена мы сразу предложим несколько примеров использования, к примеру вот такой: ```shell bash <(curl -sSL https://get.tuna.am) && tuna ssh --token <временный_токен> ``` Ваш друг/коллега/клиент запускает 1 команду у себя, открывает вам доступ по SSH, вы оказываете поддержку, а дальше просто удаляете временный токен или он удалится сам по истечении времени жизни. #### Статистика и управление ресурсами команды ![team-stats](/en/assets/images/team-stats-44e346fcaa9ce2d033e8667c4fa68bf3.gif) В этом разделе улучшили отображение и управление ресурсами в команде. Приглашения и управление ролями участников, статистика используемых ресурсов, просмотр и управление туннелями, доменами и TCP портами. Всё это сделает работу с командой более прозрачной и комфортной. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerОбновления - оплата по счёту и новые функции клиента](https://tuna.am/en/blog/invoices.md)[older →Выпуск tuna 0.17.0](https://tuna.am/en/blog/release-v0-17-0.md) --- ### Terraform-провайдер для Tuna [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) October 1 · 3 min Когда туннелей, доменов и шлюзов становится больше десятка, настраивать их кликами в личном кабинете уже неудобно: сложно повторить конфигурацию для второго окружения, непонятно, кто и когда что поменял. Поэтому мы выпустили **Terraform-провайдер** [`yuccastream/tuna`](https://registry.terraform.io/providers/yuccastream/tuna/latest/docs) — теперь инфраструктуру Tuna можно хранить в git рядом с остальным кодом, ревьюить в merge request'ах и применять через `terraform apply`. ```hcl terraform { required_providers { tuna = { source = "yuccastream/tuna" } } } resource "tuna_domain" "preview" { subdomain = "myapp-preview" location = "ru" } ``` #### Что можно описать | Ресурс | Что описывает | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `tuna_domain` | [Зарезервированный домен](https://tuna.am/en/docs/team/domains.md): поддомен встроенной зоны, своей зоны или собственный домен | | `tuna_domain_zone` | [Доменная зона](https://tuna.am/en/docs/team/domain_zones.md) | | `tuna_tls_certificate` | [TLS-сертификат](https://tuna.am/en/docs/team/tls_certificates.md) для доменной зоны | | `tuna_gateway` | [Шлюз](https://tuna.am/en/docs/gateways.md) с политикой трафика | | `tuna_endpoint` | [TCP-порт](https://tuna.am/en/docs/team/tcp_ports.md) | | `tuna_ip_policy`, `tuna_ip_policy_rule` | [IP-политика](https://tuna.am/en/docs/team/ip_policies.md) и её правила | | `tuna_public_key` | [Публичный SSH-ключ](https://tuna.am/en/docs/team/ssh_public_keys.md) | | `tuna_temporal_token` | Временный токен для запуска туннелей | Источник данных `tuna_tunnels` возвращает список туннелей аккаунта. Существующие ресурсы из личного кабинета можно подтянуть в state через `terraform import`. Провайдер работает с Terraform 1.11+ и с OpenTofu. Приватный ключ TLS-сертификата передаётся через write-only атрибут и не попадает в state. #### Пример: временные туннели для CI Типичный сценарий — preview-окружения в CI. Terraform резервирует домен и выпускает временный токен с ограничениями, а CI-задача поднимает туннель с этим токеном. Основной API-ключ в CI при этом не попадает: ```hcl resource "tuna_temporal_token" "ci" { description = "CI preview" duration = "24h" allow_http = true allow_tcp = false limit_by_active_tunnels = 5 limit_by_created_tunnels = 100 } output "ci_tunnel_token" { value = tuna_temporal_token.ci.token sensitive = true } ``` ```bash terraform apply export TUNA_TOKEN="$(terraform output -raw ci_tunnel_token)" tuna http 8080 --subdomain=myapp-preview ``` #### Пример: шлюз с политикой из файла [Шлюз](https://tuna.am/en/docs/gateways.md) — это домен с политикой трафика, который работает без запущенного клиента. Политика лежит в соседнем файле и меняется так же, через ревью: ```hcl resource "tuna_domain" "api" { subdomain = "myapp-api" location = "ru" } resource "tuna_gateway" "api" { domain_id = tuna_domain.api.id policy = file("${path.module}/policy.yaml") } ``` Ещё несколько сценариев — своя доменная зона с wildcard-сертификатом, доступ по IP только из офиса — есть в [документации](https://tuna.am/en/docs/guides/terraform.md). #### API-ключи с правами по продуктам Провайдеру нужен API-ключ, и отдавать ему полный доступ к аккаунту незачем. Поэтому вместе с провайдером API-ключи получили **скоупы**: ключ можно ограничить туннелями, мониторингом, секретами, бастионом, отчётами или командой — на чтение или на запись. Для Terraform достаточно `tunnels:write`. Заодно мы закрыли для API-ключей всё, что может навредить аккаунту при утечке ключа: * выпуск новых API-ключей, приглашения в команду и смена ролей участников; * безопасность аккаунта: пароль, MFA, WebAuthn, токен туннелей, привязки провайдеров входа; * оплата и подписка: способ оплаты, смена тарифа, получатели счетов, удаление команды; * продление и завершение веб-сессий, [пароли](https://tuna.am/en/docs/passwords.md) и mesh. Всё это делается только из [личного кабинета](https://my.tuna.am/api_keys?lang=en). Ключ, выпущенный без скоупов, получает доступ `read_api` — только на чтение. #### С чего начать 1. Выпустите в [личном кабинете](https://my.tuna.am/api_keys?lang=en) API-ключ со скоупом `tunnels:write`. 2. Передайте его провайдеру через `export TUNA_API_KEY=...`. 3. Опишите первый ресурс и выполните `terraform init && terraform apply`. Справочник по всем ресурсам и атрибутам — в [Terraform Registry](https://registry.terraform.io/providers/yuccastream/tuna/latest/docs), обзор и сценарии — в [нашей документации](https://tuna.am/en/docs/guides/terraform.md). Это первая версия провайдера: если вам не хватает какого-то ресурса, [напишите нам](mailto:info@tuna.am) — расширим. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🎁 Бесплатный тариф без таймера и с постоянным поддоменом](https://tuna.am/en/blog/free-plan-permanent-subdomain.md)[older →🖥️ tuna-cli 0.36 — интерфейс в терминале и большое обновление](https://tuna.am/en/blog/cli-v0-36-tui.md) --- ### Tuna Desktop - new GUI application [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) May 26, 2025 · 3 min We are excited to introduce a new client for creating tunnels, now as a desktop application with an interface. ![desktop_main](/en/assets/images/main-3eefd7c74a66295ba24ca72f724f2433.png) #### 🤷‍♂️ Why? During local development, it often happens that there is a tunnel with a static address that needs to be always accessible. For example, when testing our payments in [YooKassa](https://tuna.am/en/docs/tunnels/examples/yookassa.md), we have a test store for each developer, where a static HTTP notification address is specified. Opening a separate terminal each time and typing `tuna http -l ru -s test-yookassa-1 8080` becomes tedious, and I always forget to do it in advance, which leads to creating multiple invoices or waiting for the payment system to resend the notification. We also know that sometimes access to a local resource needs to be opened not only for developers or system administrators, and the command line is not the most user-friendly interface. Considering several factors, we decided to create a GUI application with a maximally simple interface, so even an accountant could use it comfortably. #### 🎯 Key Features Currently, at the time of the first release, the application has minimal features: * [HTTP tunnels](https://tuna.am/en/docs/tunnels/http.md) * [TCP tunnels](https://tuna.am/en/docs/tunnels/tcp.md) Even this functionality is significantly limited compared to the console version, all in favor of simplicity. However, in the future, we plan to expand the functionality, for example, by adding a file server and basic authorization. ![desktop_list](/docs/img/desktop/list.png) ![desktop_icon](/docs/img/desktop/icon.png) ![desktop_setting](/docs/img/desktop/setting.png) #### 🧑‍💻 Supported Platforms We support all popular platforms: Windows, macOS, and Linux.
Below are the links for downloading and installation: **Windows** * [Installer (requires admin rights)](https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_installer_latest.zip) * [Portable version](https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_portable_latest.zip) **macOS** * [Universal (Apple M and Intel)](https://releases.tuna.am/desktop/latest/tuna-desktop_darwin-universal_latest.dmg) **Linux** * [Installation guide](https://tuna.am/en/docs/guides/install/install-desktop.md?os=linux) *** For macOS and Windows, we plan to publish through official app stores later. > **Note.** If you have suggestions for improvement, we would be happy to discuss them in our [Telegram chat](https://telegram.me/+Kgbtt0_gJwI5ZmEy) or via [e-mail](mailto:info@tuna.am) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newerIP filters in TCP, SSH, Postgres and Redis tunnels](https://tuna.am/en/blog/release-v0-25-0.md)[older →🆕 Secrets - New Feature](https://tuna.am/en/blog/secrets.md) --- ### Не приходят webhooks от api.telegram.org в туннель в RU зоне [Blog](https://tuna.am/en/blog.md)|[Guides](https://tuna.am/en/blog/tags/guides) May 13 · 2 min В последнее время многие пользователи сообщают об одной и той же проблеме: webhooks от `api.telegram.org` перестали приходить в туннели в регионе RU. В этом посте — как быстро обойти проблему, переключив туннель в другой регион. #### Проблема Симптомы следующие: * бот, зарегистрированный через `setWebhook`, перестал получать обновления; * запросы от `api.telegram.org` не доходят до туннеля в регионе RU — их не видно ни в логах клиента, ни в [инспекторе](https://my.tuna.am/tunnels?lang=en); * при этом туннель работает: запросы из браузера и от других сервисов проходят штатно. Со стороны нашего сервиса и хостинг-провайдера в России никаких ограничений нет — туннель работает штатно, мы видим установленное соединение. Проблема где-то в магистрали между инфраструктурой Telegram и нашими RU-нодами; вероятнее всего, это ограничение со стороны РКН. Повлиять на это с нашей стороны мы не можем, но регион подключения у туннеля — это параметр клиента, и сменить его — дело пары секунд. #### Решение — сменить регион туннеля Самый быстрый способ — поднять туннель в другом регионе, например `nl` (Нидерланды). Полный список доступных регионов и все способы переопределения — в [документации](https://tuna.am/en/docs/tunnels/guides/locations.md). ##### Tuna Desktop При создании туннеля выберите регион в выпадающем списке **Регион**: ![Выбор региона в Tuna Desktop](/en/assets/images/td-0c10e1fdb48b465987307614651ad7fd.png) После сохранения туннель поднимется в выбранном регионе — webhooks от Telegram начнут приходить. ##### Командная строка Передайте регион аргументом `--location` (или коротким `-l`): ```shell tuna http 8080 --location=nl tuna http 8080 -l nl ``` Чтобы не указывать флаг каждый раз, регион можно задать по умолчанию одним из трёх способов. **Команда `tuna config save-location`** — сохранит значение в [конфигурационный файл](https://tuna.am/en/docs/guides/config-file.md): ```shell tuna config save-location nl ``` **Параметр в [конфигурационном файле](https://tuna.am/en/docs/guides/config-file.md)** напрямую: ```yaml location: nl ``` **[Переменная окружения](https://tuna.am/en/docs/guides/environment-variables.md)** `TUNA_LOCATION`: ```shell TUNA_LOCATION=nl ``` #### Что дальше После смены региона у туннеля изменится URL — не забудьте обновить адрес webhook'а в настройках вашего Telegram-бота (метод [`setWebhook`](https://core.telegram.org/bots/api#setwebhook) с новым URL). Если и после этого webhooks не приходят — напишите нам в [Telegram-сообщество](https://telegram.me/+Kgbtt0_gJwI5ZmEy) или на . #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Guides](https://tuna.am/en/blog/tags/guides) [← newer🆕 Приложения — Zero Trust доступ к вашим сервисам](https://tuna.am/en/blog/apps.md)[older →🆕 Boards, Meets and other platform updates](https://tuna.am/en/blog/boards-meets.md) --- ### Новые типы туннелей - Postgres и Redis [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) April 30, 2025 · 2 min Мы расширяем возможности [Туннелей](https://tuna.am/en/docs/tunnels.md) и добавили новые типы - [Postgres](https://tuna.am/en/docs/tunnels/postgres.md) и [Redis](https://tuna.am/en/docs/tunnels/redis.md), теперь с поддержкой инспекции запросов! Ранее для предоставления доступа к любым базам данных нужно было открывать доступ с помощью [TCP туннеля](https://tuna.am/en/docs/tunnels/tcp.md), но теперь для **Postgres** и **Redis** мы добавили возможность запускать специализированный тип туннеля. При этом появляется аудит и инспекция запросов которые вы отправляете в базу. #### \*\*Postgres [Postgres туннель](https://tuna.am/en/docs/tunnels/postgres.md) позволяет предоставить доступ к базе данных PostgreSQL и отслеживать выполняемые SQL запросы в инспекторе. *** Слева отображается список сессий, а справа вывод команд, которые были выполнены внутри сессий. ![web_postgres](/docs/img/inspect/web_postgres.webp) #### \*\*Redis [Redis туннель](https://tuna.am/en/docs/tunnels/redis.md) аналогично позволяет предоставить доступ к базе данных Redis и отслеживать выполняемые запросы в инспекторе. *** Слева отображается список сессий, а справа вывод команд, которые были выполнены внутри сессий. ![web_redis](/docs/img/inspect/web_redis.webp) *** #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🆕 Secrets - New Feature](https://tuna.am/en/blog/secrets.md)[older →Плановые работы 16.04.2025 в регионе RU (Россия)](https://tuna.am/en/blog/maintain-ru.md) --- ### Telegram bot for VirusTotal monitoring [Blog](https://tuna.am/en/blog.md)|[Guides](https://tuna.am/en/blog/tags/guides) September 11, 2025 · 2 min We're publishing another Open-Source tool that we use ourselves and want to share with everyone. This time it's a [Telegram bot for monitoring domains and IP addresses in VirusTotal databases](https://gitlab.com/yuccastream/vt-telegram-bot). #### Overview Even in the early stages, we faced the use of our platform by scammers. They distribute malware, send spam, publish phishing sites, and engage in other misconduct. Therefore, we implemented quite a lot of tools for analysis and early detection of such violations. But this doesn't provide 100% protection - sometimes tunnel nodes still end up in antivirus databases, and here our task is to learn about this as quickly as possible, block the violator, and promptly contact the antivirus company to remove our domains, subdomains, and IP addresses from quarantine databases. Fortunately, there's such a wonderful resource as [VirusTotal](https://www.virustotal.com) - essentially an aggregator. In it, you can check a resource (domain, IP, file) for presence in any antivirus databases. Everything is very simple and visual, looks something like this: ![](/en/assets/images/vt-367db5466c61048a8e61b72928436bfe.png) VirusTotal has a public API, also a CLI utility, ready-made clients for some languages, and if you register and get an API Key, you can automate everything quite well, which will allow you to learn about presence in databases as quickly as possible. Which is what we did. But keep in mind that the free tier has some limitations on the number of requests, but for a dozen domains/IPs it will definitely be enough. ![](/en/assets/images/vt-quota-10b95ad6cd0ad82e502e8ef191c15f17.png) #### How to use? In the [repository](https://gitlab.com/yuccastream/vt-telegram-bot) you'll find binary builds and docker images for deployment, as well as configuration instructions. We'll be happy to receive feedback and merge requests. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [Guides](https://tuna.am/en/blog/tags/guides) [← newer🆕 Webhooks (SMTP, traffic policies, tunnel replay), TOTP in passwords, client distribution improvements](https://tuna.am/en/blog/webhooks-smtp.md)[older →🆕 Webhooks - new functionality](https://tuna.am/en/blog/webhooks.md) --- ### Ключи доступа (passkeys) — вход в Tuna без пароля [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features)|[Security](https://tuna.am/en/blog/tags/security) July 15 · 4 min Пароли всем надоели: их придумывают, забывают, пересылают в чат «на пару минут», а потом меняют после очередной утечки. В Tuna появился способ забыть о них насовсем — вход по **ключам доступа (passkeys)**. Приложили палец, взглянули в камеру или ввели PIN — и вы уже в аккаунте. Ни пароля, ни кода из приложения-аутентификатора. ![Кнопка «Войти с ключом доступа» на странице входа Tuna](/blog/img/2026-07-15-webauthn/login.png) #### Что такое ключ доступа Ключ доступа (passkey) — это пара криптографических ключей, которая приходит на смену паролю. Приватная половина никогда не покидает ваше устройство, менеджер паролей или аппаратный брелок, а на серверах Tuna оседает только **публичный** ключ. Красть на нашей стороне попросту нечего: сам по себе публичный ключ для входа бесполезен, в отличие от базы паролей. И ещё одна приятная деталь: каждый ключ намертво привязан к домену `tuna.am`. Подсунуть его на фишинговый сайт-двойник не выйдет — браузер даже не предложит ключ на чужом адресе. Пароль так не умеет: его можно выманить поддельной страницей или подобрать по утёкшей базе. #### Чем это удобнее пароля * **Нечего запоминать и вводить.** Логин — это один клик и касание пальцем, а не строчка из 16 символов со спецзнаками. * **Не выманить фишингом.** Ключ срабатывает только на настоящем домене Tuna — фальшивая страница уйдёт ни с чем. * **Один шаг вместо двух.** Passkey сам по себе сочетает два фактора: владение устройством плюс подтверждение личности (биометрия или PIN). Поэтому при входе по ключу Tuna **не спрашивает отдельный одноразовый код** — двухфакторность уже соблюдена. #### Где может жить ваш ключ WebAuthn — это открытый стандарт (FIDO2), а не проприетарная фишка одного вендора. Поэтому Tuna дружит практически с любым современным хранилищем ключей. А их, по большому счёту, два семейства. **Синхронизируемые passkeys** — ключ живёт в вашем менеджере паролей или облачной связке и сам подтягивается на все ваши устройства. Сменили телефон — ключи уже там. Сюда относятся: * **менеджеры паролей** — 1Password, Bitwarden, Dashlane, NordPass, Proton Pass, а для любителей держать всё локально — KeePassXC; * **встроенные хранилища ОС** — iCloud Keychain на Apple, Google Password Manager на Android и в Chrome, Windows Hello на Windows. **Аппаратные ключи (FIDO2)** — отдельный физический брелок, из которого приватный ключ не извлекается в принципе. Максимальная защита для тех, кому важно, чтобы секрет не покидал железку: * **YubiKey** от Yubico — де-факто отраслевой стандарт, версии под USB-A, USB-C и NFC (на скриншоте выше как раз добавлен такой — «Yubico 1»); * **Google Titan**, **Feitian**, **Nitrokey** (открытая прошивка), **Token2** и другие совместимые с FIDO2 ключи. Один брелок хранит десятки, а современные модели — и сотни ключей от разных сервисов, так что заводить отдельный «на Tuna» не нужно. #### Как добавить ключ Откройте [Профиль](https://my.tuna.am/profile?lang=en) и найдите блок **«Ключи доступа (passkeys)»** рядом с настройками двухфакторной аутентификации. Нажмите **«Добавить ключ»**, а дальше всё сделает браузер: предложит сохранить passkey в менеджер, связку ключей или на аппаратный брелок и попросит подтвердить создание. ![Раздел «Ключи доступа (passkeys)» в профиле Tuna со списком ключей](/blog/img/2026-07-15-webauthn/settings.png) К одному аккаунту можно привязать до **10 ключей** — например, по одному на рабочий ноутбук, телефон и физический YubiKey в связке. Каждый ключ виден в списке с датой добавления и временем последнего входа, его легко переименовать или удалить. #### Как войти по ключу На странице входа под основной кнопкой появилась кнопка **«Войти с ключом доступа»**. Нажимаете её, выбираете ключ, подтверждаете биометрией или PIN — и вы внутри. Никаких почты, пароля и кода из приложения. А если в прошлый раз вы заходили именно так, рядом с кнопкой подсветится бейдж **«Последний вход»** — маленькая подсказка, чтобы не гадать, каким способом вы обычно пользуетесь. #### Безопасность * На серверах Tuna хранится только публичный ключ — приватная часть остаётся у вас. * Привязка к домену `tuna.am` защищает от фишинга. * Каждый вход требует подтверждения пользователя — биометрии или PIN. * Счётчик использований помогает выявить попытку клонирования ключа. При этом никто не заставляет бросать привычное: пароль и вход через Google, Yandex или Mail.ru работают как раньше. Ключи доступа их не отменяют, а дополняют — а каким способом входить, решаете вы. #### Попробуйте прямо сейчас Загляните в [Профиль](https://my.tuna.am/profile?lang=en), откройте блок **«Ключи доступа (passkeys)»** и нажмите **«Добавить ключ»** — следующий вход в Tuna займёт одно касание. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features), [Security](https://tuna.am/en/blog/tags/security) [← newer💳 Оплата международными картами — подключили PayPro Global](https://tuna.am/en/blog/paypro-global.md)[older →Кто может зайти в ваш внутренний сервис? 5 способов закрыться авторизацией](https://tuna.am/en/blog/apps-access-methods.md) --- ### Webhooks - new functionality [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) August 27, 2025 · 1 min In this update, we added new functionality - [Webhooks](https://my.tuna.am/webhooks?lang=en). A feature that allows you to easily see what's in the request sent by any service. In particular, understand what information actually comes in the request body, headers, source IP, and so on. [Go to dashboard](https://my.tuna.am/webhooks?lang=en) #### Overview *** A new section [Webhooks](https://my.tuna.am/webhooks?lang=en) has appeared in the dashboard. Here you can create links for testing, debugging, and creating mocks. This is quite convenient when you want to understand why a service's webhook documentation doesn't match reality - just specify the link and everything will become clear. You'll see all headers and request body in a convenient format. In the [documentation](https://tuna.am/en/docs/webhooks.md) section, you'll find more detailed information on managing webhooks and examples. #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🛠 Telegram bot for VirusTotal monitoring](https://tuna.am/en/blog/vt-telegram-bot.md)[older →IP filters in TCP, SSH, Postgres and Redis tunnels](https://tuna.am/en/blog/release-v0-25-0.md) --- ### Webhooks (SMTP, traffic policies, tunnel replay), TOTP in passwords, client distribution improvements [Blog](https://tuna.am/en/blog.md)|[New features](https://tuna.am/en/blog/tags/features) October 13, 2025 · 2 min In this update - new features in Webhooks. A small but important update in the Password Manager. Improvements and enhanced security in client distribution. #### Webhooks ##### Traffic Policy In addition to a simple response, you can now describe complex webhook processing rules, imitating an API response for example. It's important to note that this will work even on the free plan, and you can try traffic policies for free. ![Screenshot with traffic policy in webhook](/docs/img/webhooks/reply_tp.png) ##### Resend request to tunnel If you have running tunnels, you can forward the received request to them. We thought this could be convenient and useful when debugging something in the early stages. ![Screenshot with request forwarding to tunnel](/docs/img/webhooks/resend_to_tunnel.png) #### TOTP in Password Manager Long-awaited TOTP (Time-based one-time) passwords have appeared. This means that if you have 1 account from a third-party service for the team, now you can safely enable 2FA. ![GIF with TOTP](/docs/img/passwords/totp.webp) #### Client distribution improvements ##### Security We focus on security, so we recommend checking that you're using our official client builds. Now all build artifacts, archives, packages, installers, checksum files, and docker images are signed with our digital signature. * Public **GPG** key for verification is distributed via the link - * Public [Cosign](https://docs.sigstore.dev/) key for Docker images verification - ##### Distribution for ALT Linux Added distribution support for the Russian ALT Linux, more details in the [installation instructions](https://tuna.am/en/docs/guides/install.md). *** #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [New features](https://tuna.am/en/blog/tags/features) [← newer🆕 Reports - new functionality](https://tuna.am/en/blog/reports.md)[older →🛠 Telegram bot for VirusTotal monitoring](https://tuna.am/en/blog/vt-telegram-bot.md) --- ### Welcome to Tuna [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) July 16, 2024 · 1 min This is the first post in our blog. Here we will talk about new features, write notes about using the service, discuss examples from the documentation in more detail, and much more. ![base-usage](/en/assets/images/base-usage-6a21784aabebe0224f319ce91049c863.png) #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newerВыпуск tuna 0.16.1](https://tuna.am/en/blog/release-v0-16-1.md) --- ### Что нового в Tuna — тёмная тема, адреса для файрвола и переезд Linux-пакетов [Blog](https://tuna.am/en/blog.md)|[News](https://tuna.am/en/blog/tags/news) October 27 · 4 min Мы обновили платформу и выпустили tuna CLI 0.38. О крупных новинках — подключении корневых доменов, запросах на изменение секретов и Tuna Mesh в личном кабинете — расскажем отдельными постами в ближайшие дни. Здесь — остальное: изменение, которое требует действий, тёмная тема, адреса для файрвола и исправления. #### Требуется действие: Linux-пакеты только в code.tuna.am Linux-пакеты `tuna-cli` и `tunad` теперь публикуются только в репозитории `code.tuna.am`. В старый репозиторий `repo.tuna.am` новые версии больше не попадают, а переходный пакет `tuna.am` больше не выпускается. Если вы ставили tuna на Linux через `apt`, `dnf` или `zypper`, проверьте, из какого репозитория приходят обновления: хосты на старом репозитории новых версий больше не получат. Проще всего запустить установщик ещё раз: он сам заменит репозиторий и обновит пакет. ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` Подключить репозиторий вручную можно по [инструкции по установке](https://tuna.am/en/docs/guides/install/install-cli.md). Если на RHEL, Fedora или openSUSE вы подключали `repo.tuna.am` вручную, удалите его файл из `/etc/yum.repos.d/` или `/etc/zypp/repos.d/`: установщик старый репозиторий там не убирает. Tuna Desktop для Linux пока публикуется и в `repo.tuna.am`. Он тоже переедет в `code.tuna.am`, но позже, и об этом мы предупредим отдельно. Пользователей macOS, Windows и Docker переезд не касается. #### Тёмная тема В личном кабинете появилась тёмная тема. Переключатель — в меню пользователя внизу боковой панели, пункт **Тема**. Вариант **Системная** следует настройке операционной системы. ![Меню пользователя: пункт «Тема» с вариантами «Системная», «Светлая» и «Тёмная»](/blog/img/2026-10-27-whats-new/theme-menu.webp) ![Раздел «Секреты» личного кабинета в тёмной теме](/blog/img/2026-10-27-whats-new/dark-secrets.webp) По умолчанию кабинет остаётся светлым. Выбор хранится в браузере, поэтому на другом устройстве тему придётся выбрать заново. В мобильной версии переключателя пока нет. #### Адреса Tuna для корпоративного файрвола Если в вашей сети исходящий трафик разрешён только на известные адреса, для tuna приходилось выяснять их вручную. Теперь полный список адресов есть на странице [Работа tuna в корпоративной сети](https://tuna.am/en/docs/guides/corporate-network.md#firewall-addresses) и в открытом методе API, авторизация не нужна: ```shell curl -fsS https://api.tuna.am/v1/system/ips ``` В ответе — адреса ячеек, куда приходит трафик к туннелям и своим доменам, и адреса нод, к которым подключается CLI. По этому методу можно автоматически обновлять правила файрвола. Команда `tuna diagnose` теперь берёт адреса оттуда же. Она проверяет доступ к каждой ячейке и каждой ноде и показывает цепочку DNS, по которой видно, в какой регион вас направил GeoDNS: ```shell DNS: api.tuna.am: alt-lb.tuna.am. → eu-lb.tuna.am. [147.45.149.16, 194.31.173.118] a.2na.am: edge-eu-a.2na.am. [2.28.136.177, 89.19.217.251] TCP connectivity: tuna edge a 2.28.136.177:443: ok [41.53925ms] tuna edge a 89.19.217.251:443: ok [38.112708ms] tuna node DE node-1-de.2na.am:443: ok [52.81425ms] tuna node RU node-1-ru.2na.am:443: ok [34.079541ms] ``` Если что-то не подключается, пришлите этот вывод в [поддержку](https://tuna.am/en/docs/support.md): так мы быстрее найдём причину. #### Исправления * **HTTP-туннели снова ждут ответа до трёх часов.** После переезда на новую инфраструктуру туннель отвечал `504`, если приложение молчало дольше минуты, и мешал отлаживать запрос на точке останова. Теперь, как и раньше, соединение держится до трёх часов простоя. * **`--rate-limit=N` пропускает ровно N запросов.** Раньше флаг и действие `rate_limit` в [политике трафика](https://tuna.am/en/docs/traffic-policy.md) пропускали на один запрос меньше, а `--rate-limit=1` не пропускал ни одного. * **Переподключение не затягивается.** Пауза перед переподключением росла до 16 секунд после серии обрывов. Теперь она сбрасывается после сессии длиной от 30 секунд. * **Понятная причина ошибки.** Если сервер несколько раз подряд отвечал ошибкой, CLI писал только `giving up after 3 attempt(s)`. Теперь в логе есть текст ошибки сервера. * **Инспектор запросов.** В настройках инспектора блок «Заголовки ответа» показывает заголовки из `--response-header`, а не заголовки запроса. * **Homebrew.** При установке на macOS больше нет предупреждения `Calling postflight is deprecated`. Полный список изменений клиента — в [журнале изменений](https://tuna.am/en/changelog/cli-v0.38.0.md). Обновите клиент, чтобы получить исправления: ```shell tuna update ``` Версия 0.37 на бесплатном тарифе будет работать до 1 марта 2027 года, сроки поддержки остальных версий — на странице [Обновление](https://tuna.am/en/docs/guides/update.md). #### Leave feedback If you enjoy using Tuna, or on the contrary you are not happy with something, please [leave feedback](https://my.tuna.am/?show_feedback=1&lang=en). ![](/blog/img/2024-08-22/feedback.gif) #### News We announce new features and posts on our [Telegram channel](https://t.me/tuna_am). Subscribe so you don't miss anything. #### Help We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) Categories: [News](https://tuna.am/en/blog/tags/news) [← newer🤖 Docs that are easy for agents to read: llms.txt and Markdown copies of pages](https://tuna.am/en/blog/docs-for-ai-agents.md)[older →🛡 What to patch now: PHP, GitLab, Node.js, Brotli and eMudhra](https://tuna.am/en/blog/security-october-2026.md) --- ## Changelog ### Changelog #### [tuna-cli v0.38.0](https://tuna.am/en/changelog/cli-v0.38.0.md) October 22, 2026 > **Action required.** The tuna-cli and tunad Linux packages are now published only to `code.tuna.am`; new versions no longer reach the old `repo.tuna.am` repository. Hosts that installed tuna from it will no longer get updates. Run the installer again: it switches the host to the new repository and updates the package. > > ```shell > sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" > > ``` > > Or add the repository manually following the [installation guide](https://tuna.am/en/docs/guides/install/install-cli.md). > > If you added `repo.tuna.am` manually on RHEL, Fedora or openSUSE, delete its file from `/etc/yum.repos.d/` or `/etc/zypp/repos.d/`: the installer doesn't remove the old repository there. * \[FEATURE] tuna diagnose checks every Tuna edge and node from the published address list (cli) * \[ENHANCEMENT] Publish Linux packages to code.tuna.am only and retire the tuna.am meta-package (cli) * \[BUGFIX] Fixes reconnect delay growing after repeated disconnects (cli) * \[BUGFIX] Shows the server error after retries are exhausted (cli) * \[BUGFIX] Fixes rate limit letting through one request less than configured (cli) * \[BUGFIX] Remove deprecated postflight hook from Homebrew casks (cli) * \[BUGFIX] Request inspector shows --response-header values in Settings → Response headers (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.25.0 June 13, 2025 * \[FEATURE] TCP allow and deny CIDRs (Added functionality to restrict connections to TCP tunnels by IP address) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.26.0 August 27, 2025 * \[ENHANCEMENT] Refactoring **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.27.0 September 3, 2025 * \[FEATURE] Prints additional json fields (cli) * \[FEATURE] Adds tunnel name to json logs (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.28.0 December 18, 2025 * \[ENHANCEMENT] Reworked reconnection mechanism for network failures **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.30.0 January 28, 2026 * \[FEATURE] PROXY Protocol support in TCP tunnels * \[BUGFIX] Fix monitor User-Agent **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.31.0 February 9, 2026 * \[FEATURE] Add support riscv64 arch (cli) * \[BUGFIX] Skip HTTPS redirect for WebSocket upgrade requests (cli) * \[FEATURE] Reports in tunnels http --capture-key (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna v0.32.0 March 23, 2026 * \[BUGFIX] Multiple fixes and improvements * \[FEATURE] Asciinema format ssh sessions (cli) * \[FEATURE] Add support mips, mipsle, armv6, armv5 archs **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.33.0 April 13, 2026 * \[FEATURE] SSH session upload (cli) * \[BUGFIX] Fix tuna start args check (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.34.0 May 27, 2026 * \[FEATURE] Adds Apps (node server ui) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.35.0 September 24, 2026 * \[FEATURE] Secrets CLI: list, get, set, delete, upload, projects, environments, configs, service tokens, substitute, open (cli) * \[FEATURE] Secrets run --mount and offline mode, value fingerprints, -o json|yaml for all secrets commands (cli) * \[FEATURE] Adds whoami command (cli) * \[FEATURE] Unix socket upstreams for tcp and http tunnels (cli) * \[FEATURE] Adds compress_response policy action (cli) * \[FEATURE] Removes api-key create and session terminate commands, api-key del deletes a key (cli) * \[BUGFIX] (#222) Fix empty request body (cli) * \[BUGFIX] Sets correct record-session value (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.35.1 September 24, 2026 * \[BUGFIX] Fix package upgrade from the old repository (cli) * \[BUGFIX] Fix SSH hang after connection loss caused by golang.org/x/crypto v0.52.0 (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.36.0 September 30, 2026 * \[FEATURE] TUI Interface (cli) * \[FEATURE] Adds Kubernetes API tunnel (cli) * \[FEATURE] Free static subdomains (cli) * \[FEATURE] Passes request and message data to tuna trigger commands (cli) * \[FEATURE] Adds --policy-url flag for http tunnels (cli) * \[FEATURE] Highlight flags, commands, code examples (cli) * \[FEATURE] Confirmation prompts for delete commands, --yes to skip (cli) * \[ENHANCEMENT] Show the upgrade command for the detected install method (cli) * \[BUGFIX] Fixes GitHub and Sentry webhook signature verification (cli) * \[BUGFIX] Inspector records the real status of rejected upgrade requests (WebSocket, kubectl exec) (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.36.1 September 30, 2026 * \[BUGFIX] Fixes CLI build for FreeBSD (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.37.0 October 6, 2026 * \[FEATURE] Publish 1C:Enterprise infobases (cli) * \[ENHANCEMENT] Update Upgrade connection tests (cli) * \[BUGFIX] Fix brew upgrade command (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-cli v0.38.0 October 22, 2026 > **Action required.** The tuna-cli and tunad Linux packages are now published only to `code.tuna.am`; new versions no longer reach the old `repo.tuna.am` repository. Hosts that installed tuna from it will no longer get updates. Run the installer again: it switches the host to the new repository and updates the package. > > ```shell > sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" > > ``` > > Or add the repository manually following the [installation guide](https://tuna.am/en/docs/guides/install/install-cli.md). > > If you added `repo.tuna.am` manually on RHEL, Fedora or openSUSE, delete its file from `/etc/yum.repos.d/` or `/etc/zypp/repos.d/`: the installer doesn't remove the old repository there. * \[FEATURE] tuna diagnose checks every Tuna edge and node from the published address list (cli) * \[ENHANCEMENT] Publish Linux packages to code.tuna.am only and retire the tuna.am meta-package (cli) * \[BUGFIX] Fixes reconnect delay growing after repeated disconnects (cli) * \[BUGFIX] Shows the server error after retries are exhausted (cli) * \[BUGFIX] Fixes rate limit letting through one request less than configured (cli) * \[BUGFIX] Remove deprecated postflight hook from Homebrew casks (cli) * \[BUGFIX] Request inspector shows --response-header values in Settings → Response headers (cli) **Tags:** * [cli](https://tuna.am/en/changelog/tags/cli) --- ### tuna-desktop v0.1.5 June 13, 2025 * \[BUGFIX] Fix client termination (Fixed issue where application closes after 30 minutes of tunnel operation on free plan) **Tags:** * [desktop](https://tuna.am/en/changelog/tags/desktop) --- ### tuna-desktop v0.1.6 July 10, 2025 * \[FEATURE] New version notification **Tags:** * [desktop](https://tuna.am/en/changelog/tags/desktop) --- ### tuna-desktop v0.2.0 December 18, 2025 * \[ENHANCEMENT] Reworked reconnection mechanism for network failures **Tags:** * [desktop](https://tuna.am/en/changelog/tags/desktop) --- ## Docs ### Welcome 🐟 **Tuna** is a platform for developers and their teams, aimed at accelerating development, simplifying team collaboration, and ensuring security. #### Network ##### 🚇 Tunnels Functionality that allows you to use the console utility *tuna* to create reverse proxy [HTTP](https://tuna.am/en/docs/tunnels/http.md), [TCP](https://tuna.am/en/docs/tunnels/tcp.md) and [SSHd](https://tuna.am/en/docs/tunnels/ssh.md) tunnels. You can get direct access from the internet to locally running websites or applications. Just run a short command in the console, receive a link in response, and your friend from anywhere in the world can follow it to access the website running on your PC in the local network, all without VPN, "white" IP requirements, or port forwarding on the router. #### [\*\*HTTP](https://tuna.am/en/docs/tunnels/http.md) [Provide access to a website, API, GraphQL, Websocket server](https://tuna.am/en/docs/tunnels/http.md) #### [\*\*TCP](https://tuna.am/en/docs/tunnels/tcp.md) [Provide access to a database, SSH, RDP, game server](https://tuna.am/en/docs/tunnels/tcp.md) #### [\*\*SSHd](https://tuna.am/en/docs/tunnels/ssh.md) [Provide access via SSH without an SSH server](https://tuna.am/en/docs/tunnels/ssh.md) #### [\*\*Trigger](https://tuna.am/en/docs/tunnels/trigger.md) [Perform actions upon event occurrence](https://tuna.am/en/docs/tunnels/trigger.md) #### [\*\*Postgres](https://tuna.am/en/docs/tunnels/postgres.md) [Access PostgreSQL with SQL query inspection](https://tuna.am/en/docs/tunnels/postgres.md) #### [\*\*Redis](https://tuna.am/en/docs/tunnels/redis.md) [Access Redis with query inspection](https://tuna.am/en/docs/tunnels/redis.md) #### [\*\*Kubernetes](https://tuna.am/en/docs/tunnels/kubernetes.md) [Temporary kubectl access to a cluster without a VPN](https://tuna.am/en/docs/tunnels/kubernetes.md) #### [📒 1C](https://tuna.am/en/docs/tunnels/1c.md) [Publish a 1C:Enterprise infobase without setting up a web server](https://tuna.am/en/docs/tunnels/1c.md) #### [📦 Installation](https://tuna.am/en/docs/guides/install.md) [Start with installation and initial client setup — it will take no more than a minute](https://tuna.am/en/docs/guides/install.md) ##### 🪝 Webhooks Functionality that allows you to easily find out what is in the request sent by some service. In particular, understand what information actually arrives in the request body, headers, source IP, and so on. #### [👀 Product overview](https://tuna.am/en/docs/webhooks.md) [Debugging incoming HTTP requests](https://tuna.am/en/docs/webhooks.md) ##### 🚦 Gateways A function that complements tunnels and is designed to create an always-available cloud load balancer or web server with flexible traffic processing policies. Tunnels are great, but sometimes the environment consists of several services, some of which are running on your machine, your colleague's machine, and in the test environment. Thanks to flexible [traffic policies](https://tuna.am/en/docs/traffic-policy.md), you can distribute requests to the right points, manage headers, authorization, working hours, and much more. #### [👀 Product overview](https://tuna.am/en/docs/gateways.md) [An always-available cloud load balancer with flexible traffic processing policies](https://tuna.am/en/docs/gateways.md) ##### 📡 Monitors Functionality for monitoring CronJobs, Heartbeat, ICMP, DNS, HTTP and TCP applications with notification support via various communication channels. #### [👀 Product overview](https://tuna.am/en/docs/monitors.md) [Monitoring of jobs and services](https://tuna.am/en/docs/monitors.md) #### Security ##### 🔑 Passwords The password manager works in the web interface of the personal account, designed for secure storage and convenient sharing of passwords within work teams and beyond. We do not store passwords in plain text, following the [Zero-knowledge proof model](https://en.wikipedia.org/wiki/Zero-knowledge_proof) — **only** the user has the keys that can decrypt the data. #### [👀 Product overview](https://tuna.am/en/docs/passwords.md) [Securely store and share your passwords for infrastructure and services with your team.](https://tuna.am/en/docs/passwords.md) ##### 🔒 Secrets The secrets service is designed to store settings for your applications that you run — locally, in CI/CD, or in your test and production environments. It is an excellent alternative to `.env` files and environment variables in CI/CD systems. #### [👀 Product overview](https://tuna.am/en/docs/secrets.md) [Configure applications and environments simply and securely](https://tuna.am/en/docs/secrets.md) ##### 🐡 Bastion An SSH server with a role-based mandatory access model, using [Zero Trust principles](https://en.wikipedia.org/wiki/Zero_trust_architecture). Designed for secure infrastructure access, it provides centralized control and auditing of SSH sessions. Unlike classic SSH servers, the bastion emphasizes user identification, temporary certificates, and a role-based access model, ensuring enhanced security for accessing your servers. [](https://tuna.am/en/docs/bastion.md) #### [👀 Product overview](https://tuna.am/en/docs/bastion.md) [An SSH server with a role-based mandatory access model, using ](https://tuna.am/en/docs/bastion.md)[Zero Trust principles](https://en.wikipedia.org/wiki/Zero_trust_architecture). #### Collaboration ##### 🪲 Reports Functionality for collecting errors and creating comprehensive reports on your website. Using an embeddable script, users and QA engineers can easily show where the bug is hiding, while you get debug information and can immediately start fixing it. #### [👀 Product overview](https://tuna.am/en/docs/reports.md) [Collect bugs from your site with screenshots and debug information](https://tuna.am/en/docs/reports.md) ##### 🖌️ Boards Infinite boards for collaborative planning, documentation and brainstorming. Stickers, shapes, arrows, timers, comments and built-in calls help the team quickly throw out ideas, hold retros and brainstorms without switching between a dozen services. #### [👀 Product overview](https://tuna.am/en/docs/boards.md) [Infinite boards with shapes, stickers and calls for the team](https://tuna.am/en/docs/boards.md) ##### 🎥 Meets Online meetings service right inside Tuna. Call your team, share your screen and discuss tasks in a single workspace. #### [👀 Product overview](https://tuna.am/en/docs/meet.md) [Online meetings and screen sharing inside the platform](https://tuna.am/en/docs/meet.md) #### We are on external platforms #### [🎣 Habr](https://habr.com/ru/companies/tuna/articles/) [Our blog on habr.com](https://habr.com/ru/companies/tuna/articles/) #### [📣 Telegram](https://t.me/tuna_am) [Tuna news on our Telegram channel](https://t.me/tuna_am) #### [🪼 VC](https://vc.ru/u/525007-evgeniy-medvedev) [Our blog on vc.ru](https://vc.ru/u/525007-evgeniy-medvedev) #### Help We value our users and thoroughly review all inquiries. If you encounter issues with tuna, be sure to contact us using one of the following methods: * [Email](mailto:info@tuna.am) * [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) --- ### Abuse and Complaints #### Reporting Violations If you want to report abuse or illegal activity prohibited by our [Terms of Service](https://tuna.am/tos/) on a site hosted via tuna, please send an email to with the problematic URL and a description of the type of violation you observed. Please support your report with screenshots, videos, or other evidence of the violation. You will receive an email confirmation that your report has been received and is under review. The list of prohibited activities and the enforcement procedure are described in the [Acceptable Use Policy](https://tuna.am/en/aup.md). #### Fighting Online Abuse At tuna, we employ a multifaceted strategy to combat misuse of our services. We have invested significant resources in automated systems that flag suspicious account activity and third-party reports in real time. We complement these measures with manual moderation and tools that allow us to remove malicious and illegal content from our services with minimal risk of spreading it to the public network. #### Browser Warning To prevent phishing attacks through our platform, tuna adds an interstitial page for free accounts receiving requests from browsers. When a user first visits an endpoint, tuna displays a page notifying them that the content is provided via tuna and warns them not to enter sensitive information if the link sender cannot be trusted. Users see this page only once for each endpoint, and its display can be completely bypassed by adding the `tuna-skip-browser-warning` header to your request. #### Anonymity Using tunneling through tuna allows you to hide the real source IP address. However, this anonymity may encourage the use of tuna for hosting malicious content. To mitigate this risk, tuna applies the following controls: * Creating tunnels and publishing applications is only available after registering an account. * Source IP addresses for all dynamic HTTP tunnels are included in the generated URL. URLs are formed as follows: `${random}-${origin-ip}.${location}.tuna.am`. For example, the URL `4l7mqf-212-49-103-2.ru.tuna.am` has a source IP address of `212.49.103.2`. #### API Integration If you are a representative of a fraud prevention company, we have simplified and accelerated the content removal request process by providing direct integration via [API](https://tuna.am/en/docs/guides/api.md). If you plan to send a significant number of abuse reports, contact us directly at to request access to integrate with our [API](https://tuna.am/en/docs/guides/api.md) for submitting abuse reports. --- ### Приложения Представьте: у вас есть внутренний сервис — админка, staging, Grafana, внутренний API — и вы хотите дать к нему доступ только нужным людям, из нужных стран, с подтверждением через корпоративный SSO. Приложения в Tuna решают именно эту задачу. Приложения — это защищённый reverse proxy, который проверяет каждый входящий запрос: кто пользователь, как он аутентифицировался, откуда пришёл и имеет ли право видеть запрашиваемый путь. ![apps-main](/en/assets/images/main-90152e8c052b1e7bdcedf49998a8c57c.png) Что можно сделать: * закрыть любой веб-сервис авторизацией за пару минут, без изменений в коде самого сервиса; * гранулярно разграничить доступ — например, `/admin/*` только для администраторов из России, а `/api/health` открыть без аутентификации; * подключить корпоративный SSO (Google, Yandex, GitHub, GitLab, VK, любой OIDC-совместимый провайдер); * требовать MFA или ручное одобрение администратора для чувствительных операций; * опубликовать сервис без публичного IP через туннель — достаточно запустить одну команду; * выдать машинный доступ через сервисные токены для CI/CD, мониторинга и автоматизации. #### Режимы работы При создании приложения нужно выбрать, как Tuna будет доставлять трафик до вашего сервиса. ##### Прокси Tuna нода обращается к upstream напрямую по URL. Этот режим подходит, когда сервис уже доступен по сети — запущен в Kubernetes, в облачной VM или просто в той же сети, что и нода. ![Создание приложения в режиме прокси](/docs/img/apps/new_proxy_filled.png) Можно указать несколько upstream-адресов — трафик будет распределяться по весам. Например, при весах 3 и 1 первый upstream получит 75% запросов, второй — 25%. ##### Туннель Бэкенд сам подключается к Tuna через клиент-агент. Идеальный вариант, когда сервис без публичного IP — за NAT, на dev-машине или в корпоративной сети. ![Создание приложения в режиме туннеля](/docs/img/apps/new_tunnel.png) После создания приложения в режиме туннеля на странице деталей появится готовая команда: ![Команда для запуска туннеля](/docs/img/apps/tunnel_command.png) ```shell tuna app <имя> --url=http://127.0.0.1:8080 ``` Запустите её на машине с сервисом — клиент установит исходящее соединение к ноде, и приложение станет доступно через защищённый домен. > **Tip.** Для быстрой проверки без реального бэкенда добавьте флаг `--hello-world` — Tuna запустит встроенный тестовый сервер. #### Создание приложения > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. Перейдите в раздел [Приложения](https://my.tuna.am/team_apps?lang=en) и нажмите **+ Добавить приложение**. | Поле | Описание | | ------------- | ------------------------------------------------------------ | | **Имя** | Уникальное имя внутри команды, будет использоваться в CLI | | **Режим** | Прокси или Туннель | | **Upstreams** | Адреса бэкенда — только для режима Прокси | | **Домен** | Домен, через который приложение будет доступно пользователям | #### Основные параметры После создания откроется страница деталей приложения с основными настройками. ![Основные параметры приложения](/docs/img/apps/edit_params.png) * **Длительность сессии** — сколько живёт сессия после входа. По истечении пользователь проходит логин заново. По умолчанию — 24 часа. * **Таймаут неактивности** — сессия сгорает, если пользователь не делал запросов дольше указанного времени. По умолчанию отключено. #### Провайдеры Здесь выбираются способы входа в приложение. Можно комбинировать несколько вариантов — пользователь увидит их все на странице входа. ![Провайдеры авторизации](/docs/img/apps/providers_section.png) * **Email OTP** — пользователь вводит email и получает одноразовый код. Удобно для внешних подрядчиков и разовых доступов. * **Аккаунты tuna.am** — вход через личный аккаунт на tuna.am. * **Провайдеры команды** — внешние Identity Provider (Google, Yandex, GitHub, GitLab, VK, любой OIDC), настроенные на странице [Провайдеры](https://my.tuna.am/team_identity_providers?lang=en). Снимите галочку «Все провайдеры команды», чтобы выбрать конкретные. ![Список провайдеров команды](/docs/img/apps/identity_providers.png) #### Обзор Блок **Обзор** на странице деталей наглядно визуализирует текущую конфигурацию: слева — кто может войти, в центре — через какие политики проходит запрос, справа — куда направляется трафик. ![Обзор приложения](/docs/img/apps/overview.png) Это удобный способ одним взглядом убедиться, что всё настроено как задумано. #### Страница входа Когда пользователь открывает защищённое приложение без активной сессии, он попадает на страницу входа. Набор кнопок формируется автоматически из подключённых провайдеров. ![Страница входа](/docs/img/apps/auth_page.png) Если в команде настроены внешние провайдеры, каждый из них отображается отдельной кнопкой «Войти через ...»: ![Страница входа с провайдерами](/docs/img/apps/auth_page_providers.png) > **Info.** Внешний вид страницы входа можно кастомизировать — добавить логотип через [настройки приложения](https://tuna.am/en/docs/apps/settings.md). #### Пользовательский каталог Участники команды видят все доступные им приложения в виде карточек на странице [Приложения](https://my.tuna.am/apps?lang=en). Клик по карточке открывает приложение в новой вкладке. В боковом меню пользователю также доступны [Запросы доступа](https://tuna.am/en/docs/apps/access_requests.md) и [Сессии](https://tuna.am/en/docs/apps/sessions.md) — для управления собственными запросами и активными сессиями. #### Аудит Все действия с приложениями записываются в [аудит-лог](https://my.tuna.am/team_audit_events?lang=en): создание и изменение приложений, входы пользователей, одобрения и отклонения запросов доступа, отзыв сессий. Каждое событие содержит детали — кто, когда, откуда и что именно произошло. ![Аудит-лог событий приложений](/docs/img/apps/audit_events.png) --- ### Запросы доступа Иногда доступ к ресурсу нужно выдавать не автоматически, а только после ручного подтверждения. Для этого в [политике](https://tuna.am/en/docs/apps/policies.md) есть опция **Требовать одобрение администратора** — пользователь не получит доступ, пока администратор не одобрит его запрос. #### Как это выглядит ##### 1. Пользователь объясняет причину После аутентификации, вместо доступа к приложению, пользователь видит форму, где нужно описать, зачем ему нужен доступ. Это помогает администратору принять обоснованное решение. ![Форма причины запроса](/docs/img/apps/approval_purpose.png) ##### 2. Ожидание решения После отправки запроса пользователь видит страницу ожидания. Вкладку можно оставить открытой — она обновится автоматически, когда администратор примет решение. ![Ожидание одобрения](/docs/img/apps/approval_pending.png) ##### 3. Администратор принимает решение Все запросы появляются в разделе [Запросы доступа](https://my.tuna.am/team_apps_access_requests?lang=en). Можно фильтровать по статусу — только ожидающие, одобренные или отклонённые. ![Список запросов доступа](/docs/img/apps/access_requests.png) При клике на запрос открывается детальная карточка со всем контекстом: ![Детали запроса на доступ](/docs/img/apps/access_request_detail.png) Администратор видит, кто запрашивает доступ, откуда, к какому пути, какая политика сработала и какую причину указал пользователь. Остаётся выбрать **длительность доступа** (от 15 минут до бессрочного) и нажать **Одобрить** или **Отклонить**. > **Info.** Одобренный доступ действует ограниченное время. После истечения пользователю потребуется отправить новый запрос. #### Мои запросы Пользователи могут отслеживать статус своих запросов на странице [Приложения → Запросы доступа](https://my.tuna.am/apps_access_requests?lang=en). --- ### Политики доступа > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. Политики — это сердце системы контроля доступа в приложениях. Каждый входящий запрос проходит через цепочку политик, и первая политика, чей шаблон пути совпал, определяет судьбу запроса: пропустить, потребовать MFA, запросить одобрение или отказать. По умолчанию доступ запрещён — если ни одна политика не совпала, запрос будет отклонён. ![Список политик приложения](/docs/img/apps/policies_list.png) #### Как устроена политика Каждая политика задаётся тремя ключевыми параметрами: | Параметр | Описание | | ---------------- | --------------------------------------------------------------------- | | **Имя** | Понятное название — «Admin panel», «Public API», «Monitoring» | | **Паттерн пути** | Glob-шаблон URL-пути: `*` — все пути, `/admin/*` — всё внутри /admin/ | | **Действие** | Что произойдёт, если путь совпал | ##### Действия **Разрешить** — пользователь получит доступ, если пройдёт проверку правил. Самое частое действие. **Требовать MFA** — работает как «Разрешить», но дополнительно потребует подтверждение вторым фактором (TOTP-приложение или код на email). Используйте для чувствительных разделов — финансы, удаление данных, управление пользователями. ![Политика с действием Требовать MFA](/docs/img/apps/policy_edit.png) **Запретить** — безусловная блокировка. Правила даже не проверяются — запрос отклоняется сразу. Это полезно, когда нужно закрыть путь для всех, а затем создать allow-политику с более высоким приоритетом для избранных. ![Политика с действием Запретить](/docs/img/apps/policy_deny.png) **Пропустить** — путь доступен вообще без аутентификации. Страница входа не показывается. Типичные кандидаты: health check, webhook-эндпоинты, публичные API. ![Политика с действием Пропустить](/docs/img/apps/policy_bypass.png) ##### Дополнительные параметры * **Длительность сессии** — можно задать свой TTL сессии для конкретной политики, отличный от общего у приложения. * **Активна** — выключенная политика сохраняется, но пропускается при оценке. Удобно для временного отключения без удаления. * **Требовать одобрение администратора** — перед получением доступа пользователь заполнит форму с причиной запроса, а администратор вручную одобрит или отклонит. Подробнее — в разделе [Запросы доступа](https://tuna.am/en/docs/apps/access_requests.md). #### Правила Внутри каждой политики с действием «Разрешить» или «Требовать MFA» настраиваются правила — они определяют, *кто именно* получит доступ. ![Создание политики с правилами](/docs/img/apps/policy_new.png) ##### Режимы правил Правила группируются по режимам, и режимы комбинируются между собой: | Режим | Логика | Что значит | | -------------- | ------ | ------------------------------------------------ | | **Любое (or)** | OR | Достаточно совпадения хотя бы одного правила | | **Все (and)** | AND | Должны совпасть все правила одновременно | | **Исключить** | NOT | Если хоть одно правило совпало — доступ запрещён | Доступ предоставляется, когда: совпало хотя бы одно **Любое**, совпали все **Все**, и ни одно **Исключить** не сработало. ##### Типы правил | Тип | Описание | Пример значения | | ----------------- | ---------------------------------------------------------------------------------------------- | -------------------- | | **Команда** | Все активные участники команды | — | | **Email** | Конкретный email-адрес | `alice@company.com` | | **Email RegExp** | Регулярное выражение по email | `.*@company\.com$` | | **Service Token** | Машинная аутентификация по [сервисному токену](https://tuna.am/en/docs/apps/service_tokens.md) | Client ID | | **IP / CIDR** | IP-адрес или подсеть | `10.0.0.0/8` | | **Способ входа** | Метод аутентификации | `oidc`, `otp`, `mfa` | | **Провайдер** | Конкретный Identity Provider | `google`, `*` | | **Страна** | ISO-код страны (определяется по GeoIP) | `RU`, `US`, `DE` | ##### Пример Допустим, вы хотите, чтобы к `/admin/*` имели доступ только участники команды, и только из России. Создайте политику: * **Имя**: admin * **Паттерн**: `/admin/*` * **Действие**: Разрешить * **Правила**: * Любое (or): Команда — все участники * Все (and): Страна — `RU` Результат: чтобы попасть в админку, нужно быть участником команды **и** заходить из России. #### Приоритет и порядок Политики оцениваются сверху вниз по номеру — чем меньше номер, тем выше приоритет. Как только путь запроса совпал с шаблоном политики, она применяется и остальные не проверяются. Порядок можно менять перетаскиванием в интерфейсе. > **Warning.** При создании приложения автоматически создаётся политика **Default** с паттерном `*` и наименьшим приоритетом. Она отвечает за все пути, не покрытые другими политиками. Удалять её не рекомендуется — без неё все непокрытые пути будут молча отклоняться. #### Обзор Блок **Обзор** на странице деталей визуализирует полную картину: кто входит, через какие политики проходит и куда попадает. ![Обзор с провайдерами и MFA](/docs/img/apps/overview_providers.png) #### Тестирование политик Прежде чем применять конфигурацию, её можно проверить. Инструмент **Тест политик** позволяет смоделировать запрос с любыми параметрами — email, страна, IP, метод входа, статус MFA — и увидеть, какое решение примет система. ![Тестирование политик](/docs/img/apps/policy_test.png) Результат покажет, какая политика совпала, какие правила сработали и итоговое решение. Тестер не создаёт реальных сессий — это безопасный способ отладки. --- ### Сервисные токены > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. Не все запросы к приложению приходят от людей. CI/CD пайплайны, мониторинг, скрипты автоматизации — им тоже нужен доступ, но логиниться через браузер они не могут. Для этого существуют сервисные токены — пара Client ID + Client Secret, которая позволяет машинам аутентифицироваться в защищённых приложениях. #### Создание токена Перейдите в раздел [Сервисные токены](https://my.tuna.am/team_apps_service_tokens?lang=en) и нажмите **+ Добавить токен**. ![Создание сервисного токена](/docs/img/apps/service_tokens.png) Укажите: * **Имя** — понятное название, отражающее назначение: «CI/CD pipeline», «Prometheus», «Backup script». * **Срок действия** — от 1 дня до 1 года. После истечения токен перестанет работать. #### Использование После создания Tuna покажет Client ID и Client Secret. **Сохраните секрет** — он показывается только один раз и больше не доступен через интерфейс. ![Реквизиты созданного токена](/docs/img/apps/service_token_created.png) Для аутентификации передавайте оба значения в заголовках запроса: ```shell curl -H 'Tuna-Access-Client-Id: sat_...' \ -H 'Tuna-Access-Client-Secret: ...' \ https://your-app.example.com/ ``` #### Привязка к политикам Сам по себе токен не даёт доступа — он лишь идентифицирует клиента. Чтобы токен мог пройти через политику, добавьте правило с типом **Service Token** и укажите Client ID нужного токена. Типичный сценарий: создать отдельную политику (например, «Monitoring» на путь `/api/health`) с правилом Service Token — и мониторинг получит доступ только к нужному endpoint, без доступа к остальному приложению. #### Ротация Если секрет скомпрометирован или просто пришло время его обновить, токен можно ротировать. При ротации генерируется новый секрет, а старый продолжает работать в течение grace-периода (до 60 минут), чтобы вы могли обновить секрет во всех сервисах без простоя. --- ### Сессии Каждый раз, когда пользователь проходит аутентификацию в приложении, создаётся сессия. Она живёт до истечения срока или до момента, когда администратор её отзовёт. #### Мои сессии Каждый пользователь видит свои активные сессии на странице [Приложения → Сессии](https://my.tuna.am/apps_sessions?lang=en). ![Список сессий пользователя](/docs/img/apps/sessions.png) Для каждой сессии отображается приложение, IP-адрес, страна (по GeoIP), время создания и истечения. Нажмите на корзину, чтобы завершить сессию — при следующем запросе потребуется повторная аутентификация. #### Сессии команды Администраторы видят сессии всех пользователей в разделе [Сессии](https://my.tuna.am/team_apps_sessions?lang=en). При клике на строку открывается детальная карточка с полной информацией о сессии. ![Сессии команды с деталями](/docs/img/apps/sessions_admin.png) В карточке видно: | Поле | Описание | | ------------------------ | ----------------------------------------- | | **Приложение** | К какому приложению привязана сессия | | **Email** | Адрес пользователя | | **Тип** | Внутренний (участник команды) или внешний | | **IP** | IP-адрес, с которого была создана сессия | | **Страна** | Страна по GeoIP | | **Создана / Истекает** | Время жизни сессии | | **Последняя активность** | Когда был последний запрос | | **User-Agent** | Браузер и ОС клиента | Администратор может отозвать любую сессию, а кнопка **Закрыть все сессии** на странице деталей приложения завершает все активные сессии разом. --- ### Настройки > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. На вкладке **Настройки приложения** собраны параметры, которые влияют на то, как прокси обрабатывает запросы к upstream — заголовки, пути, TLS, CORS и внешний вид страницы входа. ![Все настройки приложения](/docs/img/apps/settings_all.png) #### Видимость Определяет, видят ли участники команды это приложение в [пользовательском каталоге](https://my.tuna.am/apps?lang=en). ![Настройка видимости](/docs/img/apps/settings_visibility.png) Если выключить — приложение исчезнет из каталога, но продолжит работать по своему адресу. Полезно для внутренних сервисов, к которым обращаются только через API или автоматизацию. #### Логотип и страница блокировки ![Логотип и страница блокировки](/docs/img/apps/settings_logo.png) * **Ссылка на логотип** — URL изображения, которое появится на странице входа вместо стандартного. Помогает пользователям сразу понять, куда они попали. * **Адрес страницы блокировки** — вместо стандартной страницы «доступ запрещён» пользователь будет перенаправлен по этому адресу. Например, на корпоративную страницу поддержки. #### Заголовок Host ![Настройка заголовка Host](/docs/img/apps/settings_host.png) * **Принудительно установить заголовок Host** — по умолчанию upstream получает Host из своего URL. Если ваш бэкенд ожидает конкретный Host (например, nginx с виртуальными хостами), укажите его здесь. * **Передать оригинальный Host из запроса клиента** — upstream увидит тот же Host, что ввёл пользователь в браузере. Перекрывает Host Override. #### Путь Управление URL-путём запроса перед проксированием. ![Настройка пути](/docs/img/apps/settings_path.png) * **Вырезать префикс** — удаляет указанный префикс из пути. Пример: при значении `/api/v1` запрос `/api/v1/users` пойдёт на upstream как `/users`. * **Добавить префикс** — приклеивает префикс к пути перед отправкой на upstream. Применяется после вырезания. > **Info.** Комбинация: вырезать `/api/v1` + добавить `/internal` → запрос `/api/v1/users` пойдёт на upstream как `/internal/users`. #### Лимит размера тела запроса ![Лимит размера тела запроса](/docs/img/apps/settings_body.png) Максимальный размер тела HTTP-запроса от клиента (upload) в байтах. По умолчанию — 32 МиБ (33554432 байт). Если ваш сервис принимает большие файлы, увеличьте значение. `0` — без ограничения. #### CORS Если ваш фронтенд живёт на другом домене и обращается к приложению через fetch/XMLHttpRequest — включите CORS. Preflight-запросы (OPTIONS) при этом проходят без авторизации. ![CORS выключен](/docs/img/apps/settings_cors.png) После включения появляются параметры: ![CORS включён](/docs/img/apps/settings_cors_enabled.png) | Параметр | Описание | | ------------------------- | --------------------------------------------------------------------------------------------- | | **Разрешённые источники** | Домены, с которых разрешены запросы. По одному на строку, `*` — все | | **Разрешённые методы** | HTTP-методы: GET, POST, PUT, DELETE и др. | | **Разрешённые заголовки** | Заголовки, которые клиент может отправлять | | **Разрешить Cookies** | Позволяет передавать cookies и заголовки авторизации. Нельзя комбинировать с `*` в источниках | | **Кэш Preflight** | Сколько секунд браузер может кэшировать ответ на OPTIONS | #### Заголовки запроса и ответа Добавьте пользовательские заголовки к запросам (при передаче на upstream) и ответам (перед отправкой клиенту). Типичные примеры: `X-Custom-Header` для идентификации источника, `Strict-Transport-Security` для HSTS. ![Пользовательские заголовки](/docs/img/apps/settings_headers.png) #### TLS до upstream Настройки TLS-соединения между прокси и вашим бэкендом. ![TLS до upstream](/docs/img/apps/settings_tls.png) * **Не проверять TLS сертификат upstream** — включайте только для тестовых окружений или бэкендов с самоподписанным сертификатом. ##### Клиентский сертификат (mTLS) Если ваш upstream требует взаимную аутентификацию (mTLS), прокси может предъявить клиентский сертификат. Это позволяет upstream убедиться, что запрос пришёл именно через Tuna, а не напрямую. * **Клиентский сертификат (PEM)** — сертификат, который прокси предъявляет upstream. * **Приватный ключ (PEM)** — ключ к сертификату. * **Корневой CA (PEM, опционально)** — корневой сертификат, если upstream использует собственный CA. --- ### Bastion SSH server with role-based mandatory access control model, using [Zero Trust](https://en.wikipedia.org/wiki/Zero_trust_architecture) principles. Unlike classic SSH servers, bastion emphasizes user identification, temporary certificates, and role-based access model, providing enhanced security for accessing your servers coupled with simple configuration. ![bastion-main](/en/assets/images/main-d010947b01a00aaabab74362acf8b84f.png) #### Overview > **Info.** Currently, this functionality is in open beta testing. It is available to all users with a [Team](https://tuna.am/en/docs/team.md) subscription. The format and monetization may change significantly in the future. ##### What is bastion in the context of SSH access? Instead of traditional SSH access `ssh user@host` based on passwords or RSA keys, users authenticate through our [tuna client](https://tuna.am/en/docs/guides/install/install-cli.md) on our portal. Then on the bastion server, certificate-based authentication works, which is a standard OpenSSH feature and the most secure of all, but historically has been the most complex to configure. We take all the complexity upon ourselves, leaving you only with security. After authorization on the portal, the client generates RSA keys and transmits the public part to the API for delivery to bastion servers. The client also generates a temporary PKI certificate which is signed by the CA in the Tuna portal. With the RSA key and certificate, you connect to the server, the server verifies that the certificate is valid, signed by the correct CA, and that the user for whom the certificate was issued has sufficient permissions for access considering roles and policies. Bastion servers in turn also have temporary certificates signed by the CA, and when establishing a connection, the client also verifies the server's validity in the CA, so you're confident that your server is your server and not something fake. As a result, we know nothing about the private keys of servers and clients, they are generated and stored locally, which means we have no ability to connect to your infrastructure, nor do attackers in case of leaks on our side, but we act as a third-party custodian of the CA that signs certificates and authenticates users. Key principles: * Role-Based Access Control (RBAC) model — access to SSH servers is managed through roles and policies, not manually through authorized_keys files * Zero Trust — verifies not only user credentials, but also context - Who is requesting access? Which server and for what purpose? What are the session parameters (time, source, MFA policy)? * Passwordless authentication — instead of static passwords, in addition to SSH keys, PKI certificates are used that automatically expire. * (coming soon) Audit and observability — every SSH session is recorded, allowing you to view user actions in real time. * (coming soon) SSO integration (OAuth, OpenID, SAML, LDAP) — users can authenticate through corporate services such as Google Workspace or Okta. ##### How does it work in practice? ![main-scheme](/en/assets/images/scheme-aeeed643ed226b44f550ed63551bc21f.png) 1. You run **tuna-bastion** on your server and register it in the Tuna API. An RSA key pair is generated, along with a 30-day certificate signed by the CA allocated to the team in the Tuna API. The private key is stored locally and not transmitted anywhere, the Tuna API doesn't know it, meaning we have no access to your servers. 2. In the control panel [my.tuna.am](https://my.tuna.am/team?lang=en) you create a role describing the access model and apply it to the needed user. 3. The user runs the command `tuna bastion login username` on their machine. An RSA key pair is generated, along with a 12-hour certificate signed by the CA allocated to the team in the Tuna API, similar to the server. But this only happens if the user has sufficient permissions to connect with that login. 4. The user runs the command `tuna bastion ssh username@hostname` and if their certificate is valid, the server certificate is valid, the user has sufficient permissions and no restrictions are imposed, then the session is established. *** #### [🗃Guides](https://tuna.am/en/docs/bastion/guides.md) [3 items](https://tuna.am/en/docs/bastion/guides.md) #### [🗃Administrator](https://tuna.am/en/docs/bastion/admin.md) [5 items](https://tuna.am/en/docs/bastion/admin.md) #### [🗃User](https://tuna.am/en/docs/bastion/user.md) [3 items](https://tuna.am/en/docs/bastion/user.md) --- ### Administrator Description of functionality for bastion administrators. #### [📄️Servers](https://tuna.am/en/docs/bastion/admin/servers.md) [Management of your bastion servers in this section](https://tuna.am/en/docs/bastion/admin/servers.md) #### [📄️Access Requests](https://tuna.am/en/docs/bastion/admin/access_requests.md) [User access requests to servers](https://tuna.am/en/docs/bastion/admin/access_requests.md) #### [📄️Sessions](https://tuna.am/en/docs/bastion/admin/sessions.md) [List of active sessions of all users in the team](https://tuna.am/en/docs/bastion/admin/sessions.md) #### [📄️Locks](https://tuna.am/en/docs/bastion/admin/locks.md) [Locks for restricting user access to servers](https://tuna.am/en/docs/bastion/admin/locks.md) #### [📄️Certificates](https://tuna.am/en/docs/bastion/admin/certificates.md) [CA, user and server certificates](https://tuna.am/en/docs/bastion/admin/certificates.md) --- ### Access Requests [This section](https://my.tuna.am/team_bastion_access_requests?lang=en) displays access requests to servers from team users. ![admin_access_request](/en/assets/images/admin_access_request-08b4eeff536b70961d844f024ee6a93a.png) The administrator will see the user's name and email, the server for which access is requested, the login and the reason specified by the user. How users create requests is described [here](https://tuna.am/en/docs/bastion/user/access_requests.md). > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### Certificates [In this section](https://my.tuna.am/team_bastion_certificates?lang=en) you will see CA certificate fingerprints, as well as all signed server and user certificates. ![admin_certificates](/en/assets/images/admin_certificates-d5b9974f3b3e56d0f0f9a603e5dc8bfb.png) Separate CAs are used for servers and users. All servers verify that user certificates are signed by the CA for users, and users vice versa verify that server certificates are signed by the CA for servers. The CA is generated uniquely for each team when the first server is added. > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. #### Certificate Rotation #### CA > **Warning.** Team CA certificate rotation is not yet available but is planned in the near future. ##### Servers > **Info.** By default issued for 30 days If the bastion server is active, the certificate is automatically rotated as the expiration date approaches. ##### Clients > **Info.** By default issued for 12 hours If the client session is active and the connection works through the `tuna bastion ssh` wrapper rather than directly through the native `ssh-client`, then as the expiration date approaches, the certificate is also automatically rotated. --- ### Locks [In this section](https://my.tuna.am/team_bastion_certificates?lang=en) you can create locks to quickly restrict user access to servers. ![admin_locks](/en/assets/images/admin_locks-656695338bc34e8ddbc10bcf29da849d.png) If you want to deny access to a server and group of servers by label, login, or specific user. #### When is this needed? Many examples can be imagined, here are some: * You have a former employee who needs to have access to all infrastructure immediately revoked. * You want to restrict access to production servers during the New Year holiday moratorium. * Maintenance work on one server and you need to close access to it for all users. #### Example You cannot add a lock without any conditions, you need to fill in at least one condition: * Server * Role * User * Login In this example, user *Zhenya* will be prohibited from logging into any servers, and all current sessions will be immediately closed. ![admin_lock_add](/en/assets/images/admin_lock_add-c5240e11102893ce4e5b3d035ba4b57c.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### Servers [In this section](https://my.tuna.am/team_bastion_servers?lang=en) you manage your bastion servers: * Adding * Deleting * Displaying list with settings and status ![bastion-main](/en/assets/images/main-d010947b01a00aaabab74362acf8b84f.png) #### Adding a server Click the **Add server** button, you will see a short instruction on installing the bastion server on your host using a script, as well as registration and server startup. > **Note.** Installation and configuration of `tuna-bastion` is described in more detail [here](https://tuna.am/en/docs/bastion/guides/setup.md). ![](/docs/img/bastion/admin_server_add.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### Sessions [This section](https://my.tuna.am/team_bastion_sessions?lang=en) displays a list of all active sessions of all users in the team. ![admin_sessions](/en/assets/images/admin_sessions-0721c0cbd6012b2adeae5561741f8599.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### Guides Guides for bastion administration and usage #### [📄️Installation](https://tuna.am/en/docs/bastion/guides/install.md) [Installation of bastion server](https://tuna.am/en/docs/bastion/guides/install.md) #### [📄️Setup](https://tuna.am/en/docs/bastion/guides/setup.md) [Configuration and launch of tuna-bastion server](https://tuna.am/en/docs/bastion/guides/setup.md) #### [📄️Usage](https://tuna.am/en/docs/bastion/guides/usage.md) [Request access, authorization and connection to bastion server](https://tuna.am/en/docs/bastion/guides/usage.md) --- ### Installing tuna-bastion #### Using script Run the command in the terminal: ```shell curl -sSLf https://releases.tuna.am/bastion/get.sh | sh ``` > **Info.** The script automatically detects the distribution and installs `tuna-bastion` through the package manager. #### Through package manager **Debian/Ubuntu** For Debian and Debian-based distributions (Ubuntu, Mint and others), run the following commands in the terminal: ```shell sudo apt update sudo apt install -y ca-certificates curl gnupg curl -fsSL https://code.tuna.am/api/packages/tuna/debian/repository.key | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/tuna-forgejo.gpg echo 'deb [signed-by=/usr/share/keyrings/tuna-forgejo.gpg] https://code.tuna.am/api/packages/tuna/debian stable main' | sudo tee /etc/apt/sources.list.d/tuna-forgejo.list sudo apt update sudo apt install -y tuna-bastion ``` **SUSE/OpenSUSE** For SUSE and OpenSUSE, run the following commands in the terminal: ```shell sudo zypper addrepo --repo https://code.tuna.am/api/packages/tuna/rpm/stable.repo sudo zypper install -y tuna-bastion ``` **RHEL/Fedora** For RHEL and derivative distributions (CentOS, Rocky, AlmaLinux and others), as well as Fedora, run the following commands in the terminal: ```shell sudo curl -fsSL https://code.tuna.am/api/packages/tuna/rpm/stable.repo -o /etc/yum.repos.d/tuna-forgejo.repo sudo dnf install -y tuna-bastion ``` --- ### Setup #### Registration After [installation](https://tuna.am/en/docs/bastion/guides/install.md) you need to register the server. At this point, the server generates SSH keys locally and signs its certificates in the Tuna API. ```sh tuna-bastion enroll --key ``` > **Note.** How to get the key for server registration is described [here](https://tuna.am/en/docs/bastion/admin/servers.md). If all required keys are not specified, you will see a console menu: ```sh ? Enroll Key (--key) 3678287f-93ea-4ebe-8478-4d9e9f28d95d ? Data directory (--data-dir) /var/lib/tuna-bastion/data ? Listen SSH port (--ssh-port) 993 ? Advertise Address (--advertise-address) 1.2.3.4:993 ? Hostname (--hostname) server-1 ? Labels (--labels) env=testing,region=am,service=ssh Successfully enrolled node: 688fa3e2-035e-40fb-88e6-2869b6025014 ``` * **Enroll Key** - used only during registration * **Data directory** - directory where SSH keys and certificates will be saved (default `/var/lib/tuna-bastion/data`) * **Listen SSH port** - port that the `tuna-bastion` server will listen on locally. By default we suggest using port `993` (IMAPs) as it is usually open in corporate firewalls by default. * **Advertise Address** - address that will be registered in the API and for which a certificate will be issued. * **Hostname** - server name, will be recorded in the API and certificate, cannot be changed. * **Labels** - server labels, labels can be used to configure access roles, they can be overridden in the config file `/etc/tuna-bastion/config.yaml` if necessary. #### Launch Start the server ```sh systemctl enable --now --no-block tuna-bastion.service ``` If everything is fine, you will see that the server is now available on [this page](https://my.tuna.am/team_bastion_servers?lang=en). In case of problems, check the server logs. ```sh journalctl -o short -n 100 -f -u tuna-bastion.service ``` --- ### Usage After [launching](https://tuna.am/en/docs/bastion/guides/setup.md) bastion servers, you can connect to them using the [tuna](https://tuna.am/en/docs/guides/install/install-cli.md) client. #### Authorization Before proceeding, select the server you need access to, you can view the list of servers [here](https://tuna.am/en/docs/bastion/user/servers.md). If you don't have access, request it from team administrators. Next, you need to authorize by specifying the login of the end user on the server, in our example this is `root`, although we recommend using a dedicated user. ```sh $ tuna bastion login root INFO[12:01:27] Open in your browser: https://my.tuna.am/bastion_login/c95e812f-e1ef-4f32-a8e3-4fec7ddb7406 INFO[12:01:27] Waiting for approval... ``` At this point you will need to confirm that you are you by following the link and authorizing on the Tuna portal. This is part of the [Zero Trust](https://tuna.am/en/docs/bastion.md) principle. ![](/docs/img/bastion/user_auth.png) If everything is fine, a 12-hour certificate will be signed for your user, with this certificate the user will be able to connect to bastion servers to which they have been granted access using [roles](https://tuna.am/en/docs/team/roles.md). All keys and certificate will be saved locally. ```sh INFO[12:03:03] Keys directory: /home/jidckii/.tuna/bastion/keys ``` #### Connection With a valid certificate, you can view the list of available servers in the console: ```sh $ tuna bastion ls INFO[12:58:24] t480s (env=testing, region=am, service=ssh) ``` Or connect immediately: ```sh tuna bastion ssh root@t480s ``` > **Info.** Login `root` is given as an example, you may have your own user. If there are sufficient permissions and no locks, an active session will appear both for the [user](https://tuna.am/en/docs/bastion/user/sessions.md) and in the [administrator](https://tuna.am/en/docs/bastion/admin/sessions.md) section. You can also generate ssh_config and use it to connect through the native `ssh-client`, for example: ```sh $ tuna bastion ssh_config root@t480s Host t480s Hostname 127.0.0.1 Port 1234 User root CertificateFile /home/example/.tuna/bastion/keys/root-cert.pub UserKnownHostsFile /home/example/.tuna/bastion/keys/known_hosts IdentityFile /home/example/.tuna/bastion/keys/id_rsa ``` Save it to a local file and use it for connection ```sh tuna bastion ssh_config root@t480s > t480s.ssh_config ``` ```sh ssh -F t480s.ssh_config t480s ``` > **Important.** After the certificate expires, you will need to authorize again with `tuna bastion login` and issue a new certificate. But if the client session is active and the connection works through the `tuna bastion ssh` wrapper rather than directly through the native `ssh-client`, then as the expiration date approaches, the certificate is also automatically rotated. --- ### User Description of functionality for bastion users #### [📄️Servers](https://tuna.am/en/docs/bastion/user/servers.md) [Bastion servers in your team](https://tuna.am/en/docs/bastion/user/servers.md) #### [📄️Access Requests](https://tuna.am/en/docs/bastion/user/access_requests.md) [Access requests to servers in the team](https://tuna.am/en/docs/bastion/user/access_requests.md) #### [📄️Sessions](https://tuna.am/en/docs/bastion/user/sessions.md) [Your sessions to bastion servers](https://tuna.am/en/docs/bastion/user/sessions.md) --- ### Access Requests [In this section](https://my.tuna.am/bastion_access_requests?lang=en) you can see all your access requests and their status. ![user_access_request](/en/assets/images/user_access_request-c4d4df9857e284efa1dce04f24a607e5.png) You can create a request on the [servers](https://tuna.am/en/docs/bastion/user/servers.md) page. --- ### Servers [In this section](https://my.tuna.am/bastion_servers?lang=en) you can see all available bastion servers in your team and request access to a server from the administrator if you don't have a permitting role. ![user_servers](/en/assets/images/user_servers-ce39643ed9351764cd22f2ca71f26973.png) Also in this section you can request access to a server: ![user_server_req_access](/en/assets/images/user_server_req_access-2a38a340696cd3527c3e530caccde969.png) You will need to specify the user login on the server and the reason that the team administrator will see: ![user_server_req_access_2](/en/assets/images/user_server_req_access_2-ce2523b5db38d86ffa55174bb0fb6fb3.png) Or view the connection command: ![user_server_cmd_view](/en/assets/images/user_server_cmd_view-f9a71b3eb72f216352b6d983c09f15f4.png) ![user_server_cmd_view_2](/en/assets/images/user_server_cmd_view_2-efbaef7431070d17a91fc3375fae9edb.png) --- ### Sessions [In this section](https://my.tuna.am/bastion_sessions?lang=en) displays a list of all active sessions of all users in the team. ![user_sessions](/en/assets/images/user_sessions-fc16c879c45d817b2fabac6d1a46d84a.png) --- ### Boards [Go to personal account](https://my.tuna.am/boards?lang=en) Online whiteboards for your team: brainstorm ideas, draw architecture and have calls — all in one place. *** #### Overview **Boards** is an infinite canvas where you can draw diagrams, collect sticky notes, leave comments, have voice calls and work together as a whole team. > **What you can do on boards** > > * Design and document architecture and infrastructure > * Run brainstorms, retros and task planning > * Have voice calls right inside the board window > * Leave comments and discuss decisions > * Present results to your team > * Export the outcome to PNG, JPG, PDF or SVG #### Service structure The **Boards** service contains individual **boards**, each of which is an independent infinite canvas. Boards are available to every member of your Tuna team — all changes are synced in real time. #### Creating your first board 1. Go to the [Boards](https://my.tuna.am/boards?lang=en) section in your personal account. 2. Click the **+ Create new** button. 3. Set a board name — it will help you tell it apart from the others. 4. Open the board and invite your colleagues — they only need to open the same section. *** #### Board tools ##### Shapes and diagrams Use shapes, lines and arrows to draw architecture, flows and any kind of diagram. The library offers both basic geometric shapes and ready-made icon sets (for example, **Agentic Workflows**, **Yandex Cloud** and others) — handy for describing infrastructure. ![Library of shapes and icons on a board](/docs/img/boards/figurs.png) ##### Sticky notes A classic tool for brainstorms and retros. Sticky notes can be moved, grouped, resized and recolored, and you can add text inside them. ![Sticky notes of different colors and sizes](/docs/img/boards/sticker.png) ##### Text and fonts Add text blocks to the canvas, choose the font, style (bold, italic, underline, strikethrough), text color and highlight color. ![Font and text formatting options](/docs/img/boards/fonts.png) ##### Images and media You can add pictures and videos to a board — for example, screenshots, photos or short recordings. It's handy for referring to a specific screen or showing how a bug reproduces. ![Images and videos on a board](/docs/img/boards/pic.png) ##### Emoji Any object on the board can be enriched with an emoji — a quick way to mark status, mood or priority. ![Adding emoji to a board](/docs/img/boards/emoji.png) *** #### Collaboration ##### The whole team on one board Several people can work on the same board at the same time. Cursors of other participants are visible in real time — just like sitting next to each other. All changes appear instantly. ##### Comments Leave comments right on board elements — a question won't get lost, and the discussion stays attached to a specific object. Comments support threaded replies and can be resolved once the question is answered. ![Comments on a board element](/docs/img/boards/comments.png) ##### Activity and threads The **Activity** panel collects all discussions from the board — a convenient way to see what colleagues have written and quickly jump to the relevant thread. ![Activity panel with threads](/docs/img/boards/activity.png) ##### Reactions Quick wordless feedback: add reactions — like, heart, fire and others — to board elements or messages from teammates. ![Reactions on a board](/docs/img/boards/reactions.png) ##### Voice chat One click — and you're already in a shared voice room with your team. No need to spin up a separate Zoom, Google Meet or Telegram call: the discussion happens right inside the board window. ![Voice chat on a board](/docs/img/boards/voice-chat.png) ##### Timer Start a shared timer for a brainstorm or retro — every participant sees the countdown at the same time. You can pause it or quickly add +1 / +5 minutes. ![Timer for board participants](/docs/img/boards/timer.png) ##### Presentation mode Switch to presentation mode to walk the team through the relevant parts of the board without extra scrolling and zooming. The board turns into a slideshow with pre-selected areas. *** #### Exporting a board Save the result of your session in one of the formats: * **PNG** — for publications and chats * **JPG** — for lightweight images * **PDF** — for reports and printing * **SVG** — for further editing in vector editors Share the file with those who weren't at the session, or attach it to a task. *** #### Pricing Boards are included in the Tuna subscription and don't require a separate payment. The functionality is available on the **Developer** and **Legend** plans — see the [pricing](https://tuna.am/#pricing) page for details. --- ### Gateways A gateway is an always available cloud load balancer or WEB server with flexible traffic processing policies. ![gateways-main](/en/assets/images/main-19b8b7724c36ce8fe4c771d5447f65ea.png) #### Overview You can reserve your own or use a domain in the tuna.am zone and create a gateway for this domain. Gateway descriptions use [traffic policies](https://tuna.am/en/docs/traffic-policy.md). After saving, the settings will be applied and requests will start being processed by the node to which the domain and accordingly the gateway is assigned. #### When is this useful? Tunnels are great, but sometimes the environment consists of several services, some of which are running on your machine, on your colleague's machine, and in a test environment. But thanks to flexible [traffic policies](https://tuna.am/en/docs/traffic-policy.md) you can distribute requests to the right points, manage headers, authorization, working hours, and much more. #### How to create a Gateway? 1. Reserve a domain in the [domains section](https://my.tuna.am/domains?lang=en) that you plan to use, for this article's example it will be `demo.stage.tuna.am` 2. Go to the [Gateways section](https://my.tuna.am/gateways?lang=en) and click **Add gateway**, select the previously reserved domain. 3. Describe the policy using [traffic policies](https://tuna.am/en/docs/traffic-policy.md) syntax and click **Create**. If the syntax is correct, the gateway will be immediately available and you can start working. #### Example Imagine a demo stand available only on weekdays, the main part of which is in a test environment, and 2 microservices are behind developers' tunnels. ```yaml rules: - expressions: - now().Weekday().String() in ["Saturday", "Sunday"] actions: - type: custom_response config: statusCode: 200 content: We don't work on weekends! headers: content-type: "text/plain; charset=utf-8" - expressions: - hasPrefix(req.URL.Path, "/admin") actions: - type: reverse_proxy config: url: "https://valera-dev.run.tuna.am" - expressions: - hasPrefix(req.URL.Path, "/checkout") actions: - type: reverse_proxy config: url: "https://alena-dev.run.tuna.am" - actions: - type: reverse_proxy config: url: "https://testing.example.com" ``` So on weekends you will get the message `We don't work on weekends!`, and on weekdays all main requests will go to the test environment at `https://testing.example.com`, everything behind `/admin` will go to the tunnel `https://valera-dev.run.tuna.am`, and `/checkout` to `https://alena-dev.run.tuna.am`. --- ### Guides Here you will find useful notes and recipes that will help in difficult times. Have something to share? Write to us at . #### [🗃Installation](https://tuna.am/en/docs/guides/install.md) [2 items](https://tuna.am/en/docs/guides/install.md) #### [📄️Update](https://tuna.am/en/docs/guides/update.md) [Updating the client to the latest version](https://tuna.am/en/docs/guides/update.md) #### [📄️API](https://tuna.am/en/docs/guides/api.md) [API overview and access](https://tuna.am/en/docs/guides/api.md) #### [📄️Autocompletion](https://tuna.am/en/docs/guides/completion.md) [Command autocompletion by pressing TAB](https://tuna.am/en/docs/guides/completion.md) #### [📄️Configuration File](https://tuna.am/en/docs/guides/config-file.md) [Configuration through a configuration file](https://tuna.am/en/docs/guides/config-file.md) #### [📄️Configuration Priority](https://tuna.am/en/docs/guides/config-ordering.md) [Rules for overriding settings](https://tuna.am/en/docs/guides/config-ordering.md) #### [📄️Using tuna in a corporate network](https://tuna.am/en/docs/guides/corporate-network.md) [Connection diagnostics and tuna addresses for firewall rules](https://tuna.am/en/docs/guides/corporate-network.md) #### [📄️Environment Variables](https://tuna.am/en/docs/guides/environment-variables.md) [List of environment variables used with tuna](https://tuna.am/en/docs/guides/environment-variables.md) #### [📄️Removal](https://tuna.am/en/docs/guides/remove.md) [Removing the client](https://tuna.am/en/docs/guides/remove.md) #### [📄️SSH Keys](https://tuna.am/en/docs/guides/ssh-keys.md) [SSH keys management page](https://tuna.am/en/docs/guides/ssh-keys.md) #### [📄️Terraform](https://tuna.am/en/docs/guides/terraform.md) [Manage tuna domains, gateways, tokens and policies with Terraform and OpenTofu](https://tuna.am/en/docs/guides/terraform.md) #### [📄️Troubleshooting](https://tuna.am/en/docs/guides/troubleshooting.md) [Diagnostics and troubleshooting when working with tuna](https://tuna.am/en/docs/guides/troubleshooting.md) --- ### API #### Overview We provide a programmatic interface to all resources available to the user. The API is available at `https://api.tuna.am` for all registered users. All resources are collected on a [separate page](https://my.tuna.am/swagger/?lang=en), where you can also test the operation of a method of interest by specifying your API key in SwaggerUI. #### Quick Start Before you begin, you need to create an API key, which can be done on a [separate page](https://my.tuna.am/api_keys?lang=en). For authentication, the key must be passed in the HTTP header `Authorization: Bearer {API-key}` > **Note.** The deprecated variant `Authentication: Bearer {API-key}` is supported but may be removed in the future. Please use the current header. ##### Example with cURL To get the user description, execute a request using curl: ```bash curl -H "Authorization: Bearer {API-key}" https://api.tuna.am/v1/user ``` ##### Example with tuna ```bash tuna config save-api-key "{API-key}" tuna domain list ``` #### Errors ##### HTTP Status Codes Errors usually return a `4xx` response code. Less frequently, you may receive `5xx` errors - this means the error is related to the tuna API, not your code. ##### Error Format All errors returned by tuna have the following format: ```json { "code": "BadRequest", "error": "Incorrect identifier: strconv.ParseInt: parsing \"foobar\": invalid syntax", "request_id": "785f3f7e174cc6dc2c2d359bed4d921d" } ``` Errors always contain the `code` field and optionally text in the `error` field. --- ### Autocompletion Autocompletion is a convenient command line feature for displaying suggestions when pressing the TAB button. **PowerShell** ```powershell tuna completion powershell | Out-String | Invoke-Expression ``` To test, type `tuna` and press the TAB button several times. To save settings for future sessions, follow these steps: 1. Open PowerShell as **Administrator** and run the command to allow running custom scripts in the shell: ```powershell Set-ExecutionPolicy RemoteSigned ``` Answer the question with: A (Yes to All). 2. Open a new PowerShell window as your user and run the commands one by one: ```powershell if (!(Test-Path -Path $PROFILE.CurrentUserAllHosts)) { New-Item -ItemType File -Path $PROFILE.CurrentUserAllHosts -Force } Add-Content $PROFILE.CurrentUserAllHosts "tuna completion powershell | Out-String | Invoke-Expression" ``` Or you can edit the file using a text editor: ```shell notepad $PROFILE.CurrentUserAllHosts ``` Adding the line: ```powershell tuna completion powershell | Out-String | Invoke-Expression ``` **Zsh** ```shell source <(tuna completion zsh) ``` To save settings for future sessions, add to the `~/.zshrc` file: ```shell if command -v tuna &>/dev/null; then eval "$(tuna completion zsh)" fi ``` **Bash** ```shell source <(tuna completion bash) ``` To save settings for future sessions, add to the `~/.bashrc` file: ```shell if command -v tuna &>/dev/null; then eval "$(tuna completion bash)" fi ``` **Fish** ```shell tuna completion fish | source ``` To save settings for future sessions, add to the `~/.config/fish/completions/tuna.fish` file: ```shell tuna completion fish ``` --- ### Configuration File Some basic settings can be set through a configuration file in yaml format. #### Default Path On Windows, the file is usually stored at - `$env:USERPROFILE\AppData\Local\tuna\tuna.yml`, on Unix-like operating systems (macOS, Linux, FreeBSD) - `${HOME}/.config/tuna/tuna.yml`. #### Saving Basic Settings Some settings can be defined with special commands: Save token: ```shell tuna config save-token ``` Set [region](https://tuna.am/en/docs/tunnels/guides/locations.md): ```shell tuna config save-location nl ``` Set [API key](https://tuna.am/en/docs/guides/api.md): ```shell tuna config save-api-key ``` #### File Example Example of basic settings: ```yaml --- version: "1" token: tt_*** apiKey: tak_*** location: nl inspectAddr: "0.0.0.0" ``` #### Using a Different File Path You can explicitly specify the path to the file using the `--config` flag: ```shell tuna --config /tmp/tuna.yml http 8080 ``` Or through the `TUNA_CONFIG` environment variable: ```shell export TUNA_CONFIG=/tmp/tuna.yml tuna http 8080 ``` #### Multiple Tunnel Startup The file can store a description for [multiple tunnel startup](https://tuna.am/en/docs/tunnels/guides/multi-tunnels.md), example: ```yaml --- tunnels: web: tags: [frontend, ui] commandLine: tuna http localhost:5173 --subdomain=web php: tags: [backend, php, api] commandLine: tuna http 8080 --subdomain=api python: tags: [backend, python, api] commandLine: tuna http 9000 --subdomain=new-api postgres: tags: db commandLine: tuna tcp 5432 --port=postgres mysql: tags: db commandLine: tuna tcp 3306 --port=mysql ``` #### Traffic Policies The file can store rule descriptions for [traffic policies](https://tuna.am/en/docs/traffic-policy.md), example: ```yaml --- rules: - name: robots.txt expressions: - hasPrefix(req.URL.Path, "/robots.txt") actions: - name: step 1 type: custom_response config: statusCode: 200 content: | User-agent: * Disallow: / headers: content-type: "text/plain" - name: Mock for banks expressions: - hasPrefix(req.URL.Path, "/banks") actions: - type: custom_response config: statusCode: 403 content: | Forbidden headers: content-type: "text/plain; charset=utf-8" - expressions: - hasPrefix(req.URL.Path, "/v1") actions: - type: reverse_proxy config: url: "http://127.0.0.1:7070" ``` #### Checking Syntax Correctness ```shell tuna config check ``` --- ### Configuration Priority Configuration can be done through a configuration file, environment variables, and command line arguments. > **Configuration reading priority:** > > 1. [Configuration file](https://tuna.am/en/docs/guides/config-file.md) > 2. [Environment variables](https://tuna.am/en/docs/guides/environment-variables.md) > 3. Command line arguments --- ### Using tuna in a corporate network Most often there should be no problems with this, as corporate firewalls allow outgoing connections, however there may be restrictions on connection ports that may interfere with tuna operation. If you encounter errors when creating a tunnel, run the `tuna diagnose` command to get diagnostic information. The report will help you better understand where exactly the problem is. ```shell Please wait, testing connectivity... TLS connectivity: Google.com: ok [1.46189625s] Yandex.ru: ok [1.246520375s] tuna API: ok [1.484186875s] DNS: api.tuna.am: alt-lb.tuna.am. → eu-lb.tuna.am. [147.45.149.16, 194.31.173.118] a.2na.am: edge-eu-a.2na.am. [2.28.136.177, 89.19.217.251] TCP connectivity: tuna edge a 2.28.136.177:443: ok [41.53925ms] tuna edge a 89.19.217.251:443: ok [38.112708ms] tuna node DE node-1-de.2na.am:443: ok [52.81425ms] tuna node RU node-1-ru.2na.am:443: ok [34.079541ms] ``` As a rule, tuna uses port `443` to connect to nodes when creating a tunnel. If the `TCP connectivity` checks fail, contact your network administrators to resolve the issue. #### Which addresses to allow in the firewall tuna CLI connects to the API and to nodes on port `443`. If your firewall only lets through allowed addresses, open access to: * `api.tuna.am` and `my.tuna.am`, the API and the dashboard: the CLI calls the API on every run; * nodes: the CLI connects to them when it brings up a tunnel; * cells, if tunnel addresses are opened from the same network: cells receive the traffic to tunnels and [custom domains](https://tuna.am/en/docs/tunnels/guides/connect-self-domain.md). The table lists the addresses of nodes and cells. The addresses of `api.tuna.am` and `my.tuna.am` are not in it. The current address list in JSON: Allow every address in the list, not just the ones that resolve from your office. `api.tuna.am`, `my.tuna.am` and the cell zones are served by GeoDNS, which returns different load balancers for Russia and for other regions. The `DNS` section of the `tuna diagnose` output shows where GeoDNS sent your network: the region is given by the last name in the CNAME chain. For example, `edge-ru-a.2na.am.` means Russia, and `edge-eu-a.2na.am.` means other regions. The addresses are currently fixed. If they change, we will try to notify you by email in advance. Either way, the current list is always available from the [API method](#ips-api), so you can check for changes yourself. If your firewall supports FQDN-based rules, allow access by name, and you won't have to update the rules when the addresses change. ##### Fetching the addresses automatically The list is available from a public API method that requires no authorization: ```shell curl -fsS https://api.tuna.am/v1/system/ips | jq -r '.ingress_cells[].ips[], (.nodes[].ip // empty)' | sort -u ``` The response has two arrays: * `ingress_cells` are cells. `ips` holds the addresses that receive traffic to tunnels, and `zone` is the cell's main zone, which CNAME records of custom domains point to. Take the addresses of all cells regardless of `status`: cells in the `draining` status still receive traffic. * `nodes` are nodes. `address` is the host and port the CLI connects to, `ip` is the node's address, and `region` is the node's country as an ISO 3166-1 alpha-2 code, such as `DE`. The `ip` and `region` fields may be missing. Without `ip`, allow access by the name from `address`. The server refreshes the response every 5 minutes, so polling the method more often makes no sense. The method supports `ETag`: with the `If-None-Match` header it returns `304` if the list hasn't changed, which makes it convenient to check for changes on a schedule. The limit is one request per second from a single IP address; above it, the server responds with `429`. The method is described in [Swagger](https://my.tuna.am/swagger/?lang=en), tag `SystemIPs`. --- ### Environment Variables List of [environment variables](https://en.wikipedia.org/wiki/Environment_variable) supported by the **tuna** client. #### Global | Name | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------- | | TUNA_CONFIG | Path to the [configuration file](https://tuna.am/en/docs/guides/config-file.md) | | TUNA_LOG | Path to the log file: /file/to/file.log, stdout, stderr (default "stdout") | | TUNA_LOG_LEVEL | Logging level: debug, info, warn, error, fatal (default "info") | | TUNA_LOG_FORMAT | Logging format: text, json (default "text") | | TUNA_TUI | Turn the [terminal interface](https://tuna.am/en/docs/tunnels/tui.md) on (`1`) or off (`0`) | | TUNA_NO_PROMPT | Never ask questions such as delete confirmations: a command without `--yes` fails, as it does without a terminal | #### Common | Name | Description | | ------------- | --------------------------------------------------------------------------------------- | | TUNA_PORT | Port or destination address (e.g., `8080` or `10.0.0.10:5432`) | | TUNA_TOKEN | Token for creating a tunnel | | TUNA_LOCATION | Connection [region](https://tuna.am/en/docs/tunnels/guides/locations.md) (`nl` or `ru`) | | TUNA_API_KEY | [API key](https://tuna.am/en/docs/guides/api.md) | #### For HTTP tunnels | Name | Description | | -------------------------- | ---------------------------------------------------------------------------------------------------------- | | TUNA_BASIC_AUTH | Add basic authentication (e.g., user:pass) | | TUNA_CIDR_ALLOW | Reject connections that do not match the specified CIDR | | TUNA_CIDR_DENY | Reject connections that match the specified CIDR | | TUNA_CORS | Enable CORS headers and automatic OPTIONS request handling | | TUNA_DOMAIN | Reserved domain | | TUNA_FILE_SERVER | Path to directory for publishing files via HTTP | | TUNA_FILE_SERVER_SPA | Path to directory for publishing SPA site | | TUNA_HTTPS_REDIRECT | Redirect requests to HTTPS (`true` or `false`) | | TUNA_INSPECT | Enable or disable [request inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) (`true` or `false`) | | TUNA_INSPECT_ADDR | Set address for the request inspector | | TUNA_KEY_AUTH | Add authentication key passed in `X-Token` header | | TUNA_POLICY_FILE | Path to configuration file with rules (default `.tuna.yml`) | | TUNA_POLICY_URL | [URL](https://tuna.am/en/docs/traffic-policy.md#policy-url) of a rules file in YAML or JSON (https only) | | TUNA_QR_CODE | Generate and display QR code | | TUNA_RATE_LIMIT | Limit the number of requests to your application (e.g., 20 for 20 requests/sec) | | TUNA_REQUEST_HEADER | Add headers to pass to local service (e.g., `host:tuna.am`) | | TUNA_RESPONSE_HEADER | Add headers to pass to client (e.g., `env:test`) | | TUNA_SUBDOMAIN | Reserved subdomain | | TUNA_TLS_SKIP_VERIFY | Skip TLS verification | | TUNA_VERIFY_WEBHOOK | Verify if webhooks are signed by this provider (e.g., gitlab) | | TUNA_VERIFY_WEBHOOK_SECRET | Secret used by the provider to sign webhooks, if any | | TUNA_WEBDAV | Path to directory for publishing files via WebDAV | #### For TCP tunnels | Name | Description | | --------------- | ------------------------------------------------------- | | TUNA_TCP_PORT | Reserved port number or alias | | TUNA_QR_CODE | Generate and display QR code | | TUNA_CIDR_ALLOW | Reject connections that do not match the specified CIDR | | TUNA_CIDR_DENY | Reject connections that match the specified CIDR | #### For SSH tunnels | Name | Description | | ---------------------- | -------------------------------------------------------------------------------------- | | TUNA_TCP_PORT | Reserved port number or alias | | TUNA_SSH_AUTH | Set [static username/password](https://tuna.am/en/docs/tunnels/ssh.md#static-password) | | TUNA_SSH_PASSWORD_AUTH | Enable or disable login/password authentication | | TUNA_RECORD_SESSION | [SSH session recording](https://tuna.am/en/docs/tunnels/ssh.md#ssh-session-recording) | | TUNA_CIDR_ALLOW | Reject connections that do not match the specified CIDR | | TUNA_CIDR_DENY | Reject connections that match the specified CIDR | #### For TRIGGER tunnels | Name | Description | | -------------------------------- | ------------------------------------------------------------------- | | TUNA_TCP_PORT | Reserved port number or alias | | TUNA_TRIGGER_SMTP_USERNAME | Username for SMTP connection | | TUNA_TRIGGER_SMTP_PASSWORD | User password for SMTP connection | | TUNA_TRIGGER_SMTP_LISTEN_ADDRESS | Local address of the trigger SMTP server (default "127.0.0.1:1025") | | TUNA_TRIGGER_WEBHOOK_SECRET | Secret to be verified against `X-Webhook-Secret` header value | #### For Postgres/Redis tunnels | Name | Description | | ----------------- | ---------------------------------------------------------------------------------------------------------- | | TUNA_TCP_PORT | Reserved port number or alias | | TUNA_QR_CODE | Generate and display QR code | | TUNA_INSPECT | Enable or disable [request inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) (`true` or `false`) | | TUNA_INSPECT_ADDR | Set address for the request inspector | | TUNA_CIDR_ALLOW | Reject connections that do not match the specified CIDR | | TUNA_CIDR_DENY | Reject connections that match the specified CIDR | #### For Kubernetes tunnels | Name | Description | | ------------------------- | --------------------------------------------------------------------- | | KUBECONFIG | Path to the kubeconfig (a single file) | | TUNA_K8S_CONTEXT | Kubeconfig context (`current-context` by default) | | TUNA_K8S_SERVICE_ACCOUNT | Service account for the guest (`[namespace/]name`) | | TUNA_K8S_NAMESPACE | Restrict access to a single namespace | | TUNA_K8S_PRESET | Restriction preset: `view`, `observer`, `debug`, `open` | | TUNA_K8S_READ_ONLY | Allow read requests only | | TUNA_K8S_NO_DELETE | Deny deleting resources | | TUNA_K8S_DENY_RESOURCE | Denied resources (for example, `secrets,configmaps`) | | TUNA_K8S_DENY_SUBRESOURCE | Denied subresources (`exec`, `attach`, `portforward`, `log`, `proxy`) | | TUNA_K8S_TTL | Access lifetime (for example, `4h`, `0` means no limit) | | TUNA_K8S_DOWNLOAD_LIMIT | How many times the kubeconfig can be downloaded (`0` means no limit) | #### For 1C publication The [HTTP tunnel](#for-http-tunnels) variables for access, address and the inspector work as well. | Name | Description | | ------------------- | ----------------------------------------------------------------------------------------- | | TUNA_1C_HOME | 1C:Enterprise installation directory | | TUNA_1C_PLATFORM | 1C platform bitness: `x64` or `x86` | | TUNA_1C_PORT | Local port of the web server (a random free port by default) | | TUNA_1C_PUBLISH_DIR | Directory with the [publication](https://tuna.am/en/docs/tunnels/1c.md#publish-dir) files | #### Service creation and management | Name | Description | | ----------------------------- | -------------------------------------------------------- | | TUNA_SERVICE_NAME | Service name | | TUNA_SERVICE_INSTALL_USER | User under which the service will run | | TUNA_SERVICE_INSTALL_PASSWORD | Password when specifying a different user (Windows only) | #### Multiple tunnel startup | Name | Description | | ----------------------- | ------------------------------------------------------------- | | TUNA_START_TUNNELS_FILE | Path to configuration file with tunnels (default `.tuna.yml`) | | TUNA_START | Tunnel name to start | | TUNA_START_TAGS | Tunnel tags to start | | TUNA_START_ALL | Start all tunnels | #### Port detection | Name | Description | | ------------------- | ------------------------------------------ | | TUNA_DETECT_SECONDS | Time interval allocated for port detection | #### Secrets | Name | Description | | -------------------------------- | ------------------------------------------------------------------------------------------------------------- | | TUNA_SECRETS_SCOPE | Directory configuration for scope (default `.`) | | TUNA_SECRETS_PROJECT | Project alias | | TUNA_SECRETS_CONFIG | Configuration alias | | TUNA_SECRETS_FORMAT | Output format (json, yaml, env, env-no-quotes or docker) (default `json`) | | TUNA_SECRETS_NO_FILE | Print to stdout (default `false`) | | TUNA_SECRETS_WATCH | Automatically restart process when secrets change (default `false`) | | TUNA_SECRETS_NO_INTERACTIVE | Fail instead of prompting for project and config in `tuna secrets setup` (default `false`) | | TUNA_SECRETS_FALLBACK_PASSPHRASE | Passphrase for the `tuna secrets run` offline fallback file | | TUNA_SECRETS_MOUNT_PATH | Path to the secrets file with `tuna secrets run --mount`. The client sets this variable for the child process | #### Working with direnv [direnv](https://direnv.net) is an environment variable manager for the terminal. It allows you to automatically load and unload environment variables depending on the current directory – this allows you to have environment variables specific to each project you work on. Before each prompt, direnv checks for the presence of a `.envrc` file in the current and parent directories, and if the file exists, it is loaded into a bash subshell, and all exported variables are then captured by direnv and become available to the shell. Before using direnv, you need to install it. Installation instructions are available in the [official documentation](https://direnv.net/#getting-started). After that, create a `.envrc` file in your project directory with tuna settings, for example: ```shell export TUNA_PORT=8080 export TUNA_SUBDOMAIN=my-project ``` When navigating to the project directory and running the `tuna http` command, a tunnel will be created with the settings from the `.envrc` file: ```shell % tuna http INFO[13:00:00] Welcome to Tuna INFO[13:00:00] Account: test (Paid till 05.04.2024) INFO[13:00:00] Web Interface: http://127.0.0.1:4040 INFO[13:00:00] Forwarding https://my-project.ru.tuna.am -> 127.0.0.1:8080 ``` --- ### Installation We strive to make working with Tuna comfortable for everyone, both experienced users and beginners, so we support 2 clients: 1. Console utility **tuna-cli** - has a full set of features and integrations with the platform. A Swiss army knife in the hands of an experienced developer. 2. Desktop application **Tuna Desktop** - basic functionality for creating tunnels. Maximum simple installation, configuration and launch. Even your accountant can handle it. [![Console with running tuna cli](/docs/img/getting-started/cli.png "I'm a hardcore senior, emacs is my home")](https://tuna.am/en/docs/guides/install/install-cli.md)[![Running Tuna Desktop](/docs/img/getting-started/desktop.png "I'm a vibe-coder, click-click")](https://tuna.am/en/docs/guides/install/install-desktop.md) 🖌 Security and authenticity verification All builds, signature files, public keys are available at the link - We focus on security, so we recommend verifying that you are using our official client builds. Starting from tuna cli version **0.27.4** and Tuna Desktop **v0.1.11**, all build artifacts, archives, packages, installers, checksum files and docker images are signed with our digital signature. * Public **GPG** key for verification is distributed at the link - * Public [Cosign](https://docs.sigstore.dev/) key for Docker images verification - All tuna cli artifacts are signed with a **GPG** key, docker images using **cosign**. Tuna Desktop artifacts for Linux are also signed with a **GPG** key, for Windows and macOS using **cosign** and contain a `.bundle` file for easy verification. > **Warning.** We strongly recommend verifying that you are using original build artifacts and our repositories. --- ### tuna-cli #### Installation **Windows** ##### Installation using winget Open PowerShell and run the command: ```shell winget install --id yuccastream.tuna ; powershell ``` ##### Installing the MSI package Download the latest version of tuna using this [link](https://releases.tuna.am/tuna/latest/tuna_x64.msi) and run the installation by double-clicking the file. [![Installer for Windows x64 (requires administrator privileges)](/docs/img/getting-started/badge-msi-installer.svg "Installer for Windows x64 (requires administrator privileges)")](https://releases.tuna.am/tuna/latest/tuna_x64.msi) 🖌 Authenticity verification Starting from version **0.27.4**, all artifacts are signed using [cosign](https://docs.sigstore.dev/). To verify the originality of the archive, [install the cosign client](https://docs.sigstore.dev/cosign/system_config/installation/), download the public key, and perform the verification. ```shell winget install GnuPG.Gpg4win Invoke-WebRequest -Uri "https://releases.tuna.am/tuna/latest/tuna_x64.msi" -OutFile "tuna_x64.msi" Invoke-WebRequest -Uri "https://releases.tuna.am/tuna/latest/tuna_x64.msi.sig" -OutFile "tuna_x64.msi.sig" Invoke-WebRequest -Uri "https://releases.tuna.am/tuna.asc" -OutFile "tuna.asc" gpg --import tuna.asc gpg --verify tuna_x64.msi.sig tuna_x64.msi ``` ##### Manual installation Open PowerShell and run the commands one by one: **AMD64** ```powershell winget install GnuPG.Gpg4win cd $env:TEMP Invoke-WebRequest -Uri "https://releases.tuna.am/tuna/latest/tuna_windows_amd64.zip" -OutFile "$env:TEMP\tuna_windows_amd64.zip" Invoke-WebRequest -Uri "https://releases.tuna.am/tuna/latest/tuna_windows_amd64.zip.sig" -OutFile "$env:TEMP\tuna_windows_amd64.zip.sig" Invoke-WebRequest -Uri "https://releases.tuna.am/tuna.asc" -OutFile "$env:TEMP\tuna.asc" gpg --import tuna.asc gpg --verify tuna_windows_amd64.zip.sig tuna_windows_amd64.zip Expand-Archive -Path "$env:TEMP\tuna_windows_amd64.zip" -DestinationPath "$env:TEMP\tuna" -Force Copy-Item -Path "$env:TEMP\tuna\tuna.exe" -Destination "$env:USERPROFILE\AppData\Local\Microsoft\WindowsApps" -Force ``` **ARM64** ```powershell winget install GnuPG.Gpg4win cd $env:TEMP Invoke-WebRequest -Uri "https://releases.tuna.am/tuna/latest/tuna_windows_arm64.zip" -OutFile "$env:TEMP\tuna_windows_arm64.zip" Invoke-WebRequest -Uri "https://releases.tuna.am/tuna/latest/tuna_windows_arm64.zip.sig" -OutFile "$env:TEMP\tuna_windows_arm64.zip.sig" Invoke-WebRequest -Uri "https://releases.tuna.am/tuna.asc" -OutFile "$env:TEMP\tuna.asc" gpg --import tuna.asc gpg --verify tuna_windows_arm64.zip.sig tuna_windows_arm64.zip Expand-Archive -Path "$env:TEMP\tuna_windows_arm64.zip" -DestinationPath "$env:TEMP\tuna" -Force Copy-Item -Path "$env:TEMP\tuna\tuna.exe" -Destination "$env:USERPROFILE\AppData\Local\Microsoft\WindowsApps" -Force ``` ##### Verify the installation To make sure the installation was successful, run the command: ```bash tuna help ``` **macOS** ##### Installation using brew To install brew, follow the [instructions](https://brew.sh/), then run the command in the terminal: ```bash brew install yuccastream/tap/tuna ``` If you previously installed **tuna** *manually* or using the *script*, remove the previous version: ```bash sudo rm -f /usr/local/bin/tuna ``` ##### Installation using script Run the command in the terminal: **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` ##### Manual installation Open the terminal and run the following commands: **ARM64** ```shell cd $(mktemp -d) curl -sfSL "https://releases.tuna.am/tuna/latest/tuna_darwin_arm64.tar.gz" | tar -f - -xz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **AMD64** ```shell cd $(mktemp -d) curl -sfSL "https://releases.tuna.am/tuna/latest/tuna_darwin_amd64.tar.gz" | tar -f - -xz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` *** > **Info.** The binary file is [signed and notarized](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution) with Apple under the name: `Developer ID Application: Lev Aminov (FW65BD237L)` You can verify locally using the command: > > ```sh > codesign -dv --verbose=4 $(which tuna) > > ``` ##### Verify the installation To make sure the installation was successful, run the command: ```bash tuna help ``` **Linux** Supported architectures | Architecture | Description | | ------------------- | ------------------------------------------------------------------------------------ | | **AMD64** (x86_64) | Standard PCs and servers | | **ARM64** (aarch64) | Raspberry Pi 3/4/5, Apple Silicon (in Linux VM), ARM servers | | **ARMv7** | Raspberry Pi 2, most 32-bit ARM SBCs | | **ARMv6** | Raspberry Pi 1, Pi Zero / Zero W | | **ARMv5** | Older embedded devices | | **MIPS** | Routers and embedded devices (big-endian) | | **MIPS LE** | Routers and embedded devices (little-endian) | | **RISC-V 64** | RISC-V boards (e.g., [NanoKVM](https://tuna.am/en/docs/tunnels/examples/nanokvm.md)) | ##### Installation using script Run the command in the terminal: **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` > **Tip.** The script will detect your distribution, add the repository, and install the package if possible. Otherwise, it will download the binary file and install it to a PATH executable directory. ##### Manual installation **Debian/Ubuntu** For Debian and based distributions (Ubuntu, Mint, and others), run the following commands in the terminal: ```shell sudo apt update sudo apt install -y ca-certificates curl gnupg curl -fsSL https://code.tuna.am/api/packages/tuna/debian/repository.key | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/tuna-forgejo.gpg echo 'deb [signed-by=/usr/share/keyrings/tuna-forgejo.gpg] https://code.tuna.am/api/packages/tuna/debian stable main' | sudo tee /etc/apt/sources.list.d/tuna-forgejo.list sudo apt update sudo apt install -y tuna-cli ``` **SUSE/OpenSUSE** For SUSE and OpenSUSE, run the following commands in the terminal: ```shell sudo zypper addrepo --repo https://code.tuna.am/api/packages/tuna/rpm/stable.repo sudo zypper install -y tuna-cli ``` **RHEL/Fedora** For RHEL and based distributions (CentOS, Rocky Linux, AlmaLinux, as well as Fedora), run the following commands in the terminal: ```shell sudo curl -fsSL https://code.tuna.am/api/packages/tuna/rpm/stable.repo -o /etc/yum.repos.d/tuna-forgejo.repo sudo dnf install -y tuna-cli ``` **ALT Linux** For [ALT Linux](https://www.basealt.ru/alt-workstation-k?utm_source=tuna.am&utm_medium=organic&utm_campaign=docs), run the following commands in the terminal: ```shell sudo apt-get update sudo apt-get install -y curl apt-repo sudo rpm --import https://releases.tuna.am/tuna.asc sudo apt-repo add rpm https://code.tuna.am/api/packages/tuna/alt/stable.repo $(arch) classic sudo apt-repo add rpm https://code.tuna.am/api/packages/tuna/alt/stable.repo noarch classic sudo apt-get update sudo apt-get install -y tuna-cli ``` **Arch Linux** For Arch Linux, run the following commands in the terminal: ```shell yay -Syu --noconfirm tuna.am-bin ``` **Linux** To install on any Linux system (NixOS, Alpine Linux, and others), run the following commands in the terminal: **AMD64** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_amd64.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_amd64.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_amd64.tar.gz.sig tuna_linux_amd64.tar.gz tar -xzf tuna_linux_amd64.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **ARM64** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_arm64.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_arm64.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_arm64.tar.gz.sig tuna_linux_arm64.tar.gz tar -xzf tuna_linux_arm64.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **ARMv7** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_armv7.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_armv7.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_armv7.tar.gz.sig tuna_linux_armv7.tar.gz tar -xzf tuna_linux_armv7.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **ARMv6** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_armv6.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_armv6.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_armv6.tar.gz.sig tuna_linux_armv6.tar.gz tar -xzf tuna_linux_armv6.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **ARMv5** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_armv5.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_armv5.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_armv5.tar.gz.sig tuna_linux_armv5.tar.gz tar -xzf tuna_linux_armv5.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **MIPS** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_mips.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_mips.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_mips.tar.gz.sig tuna_linux_mips.tar.gz tar -xzf tuna_linux_mips.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **MIPS LE** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_mipsle.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_mipsle.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_mipsle.tar.gz.sig tuna_linux_mipsle.tar.gz tar -xzf tuna_linux_mipsle.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **RISC-V 64** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_linux_riscv64.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_linux_riscv64.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_linux_riscv64.tar.gz.sig tuna_linux_riscv64.tar.gz tar -xzf tuna_linux_riscv64.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` ##### Verify the installation To make sure the installation was successful, run the command: ```bash tuna help ``` **FreeBSD** ##### Installation using script Run the command in the terminal: **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` ##### Manual installation **AMD64** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_freebsd_amd64.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_freebsd_amd64.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_freebsd_amd64.tar.gz.sig tuna_freebsd_amd64.tar.gz tar -xzf tuna_freebsd_amd64.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **ARM64** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_freebsd_arm64.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_freebsd_arm64.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_freebsd_arm64.tar.gz.sig tuna_freebsd_arm64.tar.gz tar -xzf tuna_freebsd_arm64.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` **ARMv7** ```shell cd $(mktemp -d) curl -sfSL -O "https://releases.tuna.am/tuna/latest/tuna_freebsd_armv7.tar.gz" -O "https://releases.tuna.am/tuna/latest/tuna_freebsd_armv7.tar.gz.sig" curl -sfSL -O https://releases.tuna.am/tuna.asc gpg --import tuna.asc gpg --verify tuna_freebsd_armv7.tar.gz.sig tuna_freebsd_armv7.tar.gz tar -xzf tuna_freebsd_armv7.tar.gz chmod +x tuna sudo mv -f tuna /usr/local/bin/tuna ``` ##### Verify the installation To make sure the installation was successful, run the command: ```bash tuna help ``` **Docker** To get the latest version of the Docker image, run the command: ```shell docker pull yuccastream/tuna:latest ``` 🖌 Authenticity verification Starting from version **0.27.4**, all images are signed using [cosign](https://docs.sigstore.dev/). To verify the originality of the image, [install the cosign client](https://docs.sigstore.dev/cosign/system_config/installation/), download the public key, and perform the verification. ```shell curl -sSLfO https://releases.tuna.am/tuna-cosign.asc cosign verify --key tuna-cosign.asc docker.io/yuccastream/tuna:0.27.4 ``` ##### Docker Compose service example ```yaml --- services: whoami: image: traefik/whoami restart: always tuna: image: yuccastream/tuna:latest command: http whoami:80 --inspect=false restart: always depends_on: - whoami environment: - TUNA_TOKEN= ``` Example of usage in [Kubernetes](https://tuna.am/en/docs/tunnels/examples/kubernetes.md). *** #### Authorization To use the client, you need a Token. Register [via this link](https://my.tuna.am/?lang=en) and authorize the application. The token will be saved in the [config file](https://tuna.am/en/docs/guides/config-file.md), or you can copy it from a [separate page](https://my.tuna.am/token?lang=en) and specify it manually. ```bash tuna login ``` Specify the token manually ```bash tuna config save-token ``` #### Get started Everything is ready to start working. Run tuna with a port and share the link with your colleagues: ```bash tuna http 8080 ``` > **Permanent address.** Each time you start a tunnel it gets a new address. To keep the address, use your [free permanent subdomain](https://tuna.am/en/docs/tunnels/http.md#free-subdomain): `tuna http 8080 --subdomain=`. The subdomain is bound to your account and only works with your token from the [Authorization](#authorization) step. ##### What's next? * Explore [HTTP tunnel](https://tuna.am/en/docs/tunnels/http.md) configuration examples * Need access to a non-HTTP service? Learn more in the [TCP tunnel](https://tuna.am/en/docs/tunnels/tcp.md) section --- ### Tuna Desktop ![](/docs/img/desktop/main.png) ![](/docs/img/desktop/list.png) A detailed application overview is available in the [blog](https://tuna.am/en/blog/tuna-desktop-first.md). #### Installation **Windows** Download and install the full or portable version. [![Installer for Windows x64 (requires administrator privileges)](/docs/img/getting-started/badge-exe-installer.svg "Installer for Windows x64 (requires administrator privileges)")](https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_installer_latest.zip)[![Portable version for Windows x64](/docs/img/getting-started/badge-exe-portable.svg "Portable version for Windows x64")](https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_portable_latest.zip) 🖌 Authenticity verification Starting from version **v0.1.11**, all artifacts are signed using [cosign](https://docs.sigstore.dev/). To verify the originality of the archive, [install the cosign client](https://docs.sigstore.dev/cosign/system_config/installation/), download the public key, and perform the verification. ```shell winget install sigstore.cosign # installer Invoke-WebRequest -Uri "https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_installer_latest.zip" -OutFile "tuna-desktop_windows-amd64_installer_latest.zip" Invoke-WebRequest -Uri "https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_installer_latest.zip.bundle" -OutFile "tuna-desktop_windows-amd64_installer_latest.zip.bundle" cosign.exe verify-blob --bundle=tuna-desktop_windows-amd64_installer_latest.zip.bundle tuna-desktop_windows-amd64_installer_latest.zip # portable Invoke-WebRequest -Uri "https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_portable_latest.zip" -OutFile "tuna-desktop_windows-amd64_portable_latest.zip" Invoke-WebRequest -Uri "https://releases.tuna.am/desktop/latest/tuna-desktop_windows-amd64_portable_latest.zip.bundle" -OutFile "tuna-desktop_windows-amd64_portable_latest.zip.bundle" cosign.exe verify-blob --bundle=tuna-desktop_windows-amd64_portable_latest.zip.bundle tuna-desktop_windows-amd64_portable_latest.zip ``` **macOS** Download and install the macOS version. [![Download DMG for macOS](/docs/img/getting-started/badge-dmg.svg "Universal version for all architectures")](https://releases.tuna.am/desktop/latest/tuna-desktop_darwin-universal_latest.dmg) 🖌 Authenticity verification Starting from version **v0.1.11**, all artifacts are signed using [cosign](https://docs.sigstore.dev/). To verify the originality of the archive, [install the cosign client](https://docs.sigstore.dev/cosign/system_config/installation/), download the public key, and perform the verification. ```shell brew install cosign curl -sSLf -O https://releases.tuna.am/desktop/latest/tuna-desktop_darwin-universal_latest.dmg.bundle -O https://releases.tuna.am/desktop/latest/tuna-desktop_darwin-universal_latest.dmg cosign verify-blob --bundle=tuna-desktop_darwin-universal_latest.dmg.bundle tuna-desktop_darwin-universal_latest.dmg ``` **Linux** ##### Installation using script Run the command in the terminal: **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/desktop/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/desktop/get.sh)" ``` > **Tip.** The script will detect your distribution, add the repository, and install the package. ##### Install from the repository or download the package. **Debian/Ubuntu** For Debian and based distributions (Ubuntu, Mint, and others), run the following commands in the terminal: ```shell sudo apt update sudo apt install -y ca-certificates curl gnupg curl -fsSL https://code.tuna.am/api/packages/tuna/debian/repository.key | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/tuna-forgejo.gpg echo 'deb [signed-by=/usr/share/keyrings/tuna-forgejo.gpg] https://code.tuna.am/api/packages/tuna/debian stable main' | sudo tee /etc/apt/sources.list.d/tuna-forgejo.list sudo apt update sudo apt install -y tuna-desktop ``` > **Tip.** The repository contains builds for Debian 12+ / Ubuntu 22.04+ *** [![Download DEB x86_64 (for Debian 12+ / Ubuntu 22.04+)](/docs/img/getting-started/badge-deb-amd64.svg "Download DEB x86_64 (for Debian 12+ / Ubuntu 22.04+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop_latest-1_amd64.deb) [![Download DEB aarch64 (for Debian 12+ / Ubuntu 22.04+)](/docs/img/getting-started/badge-deb-arm64.svg "Download DEB aarch64 (for Debian 12+ / Ubuntu 22.04+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop_latest-1_arm64.deb) For older OS (Debian 11- / Ubuntu 20.04-) Older versions depend on `libwebkit2gtk-4.0-37`, while newer ones already use `libwebkit2gtk-4.1-0`. [![Download DEB x86_64 (for Debian 11- / Ubuntu 20.04-)](/docs/img/getting-started/badge-deb-amd64.svg "Download DEB x86_64 (for Debian 11- / Ubuntu 20.04-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop_latest-1_amd64.deb)[![Download DEB aarch64 (for Debian 12+ / Ubuntu 22.04+)](/docs/img/getting-started/badge-deb-arm64.svg "Download DEB aarch64 (for Debian 12+ / Ubuntu 22.04+)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop_latest-1_arm64.deb) **SUSE/OpenSUSE** For SUSE and OpenSUSE, run the following commands in the terminal: ```shell sudo zypper addrepo --repo https://code.tuna.am/api/packages/tuna/rpm/stable.repo sudo zypper install -y tuna-desktop ``` > **Tip.** The repository contains builds for SUSE/Leap 15.5+ and Tumbleweed *** [![Download RPM x86_64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+ )](/docs/img/getting-started/badge-rpm-amd64.svg "Download RPM x86_64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1.x86_64.rpm) [![Download RPM aarch64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+ )](/docs/img/getting-started/badge-rpm-arm64.svg "Download RPM aarch64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1.aarch64.rpm) For older OS (SUSE 15.4- / RHEL based 9- / ALT Linux p10- ) Older versions depend on `libwebkit2gtk-4.0-37`, while newer ones already use `libwebkit2gtk-4.1-0`. [![Download RPM x86_64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)](/docs/img/getting-started/badge-rpm-amd64.svg "Download RPM x86_64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1.x86_64.rpm)[![Download RPM aarch64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)](/docs/img/getting-started/badge-rpm-arm64.svg "Download RPM aarch64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1.aarch64.rpm) **RHEL/Fedora** For RHEL and based distributions (CentOS, Rocky Linux, AlmaLinux, as well as Fedora), run the following commands in the terminal: ```shell sudo curl -fsSL https://code.tuna.am/api/packages/tuna/rpm/stable.repo -o /etc/yum.repos.d/tuna-forgejo.repo sudo dnf install -y tuna-desktop ``` > **Tip.** The repository contains builds for RHEL/CentOS/AlmaLinux/Rocky Linux 10+ *** [![Download RPM x86_64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+ )](/docs/img/getting-started/badge-rpm-amd64.svg "Download RPM x86_64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1.x86_64.rpm) [![Download RPM aarch64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+ )](/docs/img/getting-started/badge-rpm-arm64.svg "Download RPM aarch64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1.aarch64.rpm) For older OS (SUSE 15.4- / RHEL based 9- / ALT Linux p10- ) Older versions depend on `libwebkit2gtk-4.0-37`, while newer ones already use `libwebkit2gtk-4.1-0`. [![Download RPM x86_64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)](/docs/img/getting-started/badge-rpm-amd64.svg "Download RPM x86_64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1.x86_64.rpm)[![Download RPM aarch64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)](/docs/img/getting-started/badge-rpm-arm64.svg "Download RPM aarch64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1.aarch64.rpm) **ALT Linux** For [ALT Linux](https://www.basealt.ru/alt-workstation-k?utm_source=tuna.am&utm_medium=organic&utm_campaign=docs), run the following commands in the terminal: ```shell sudo apt-get update sudo apt-get install -y curl apt-repo sudo rpm --import https://releases.tuna.am/tuna.asc sudo apt-repo add rpm https://code.tuna.am/api/packages/tuna/alt/stable.repo $(arch) classic sudo apt-get update sudo apt-get install tuna-desktop ``` > **Tip.** The repository contains builds for ALT Linux p11+ and Sisyphus Repository for ALT Linux p10- Older versions depend on `libwebkit2gtk-4.0-37`, while newer ones already use `libwebkit2gtk-4.1-0`. ```shell sudo apt-get update sudo apt-get install -y curl apt-repo sudo rpm --import https://releases.tuna.am/tuna.asc sudo apt-repo add rpm https://code.tuna.am/api/packages/tuna/alt/old.repo $(arch) classic sudo apt-get update sudo apt-get install tuna-desktop ``` *** [![Download RPM x86_64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+ )](/docs/img/getting-started/badge-rpm-amd64.svg "Download RPM x86_64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1.x86_64.rpm) [![Download RPM aarch64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+ )](/docs/img/getting-started/badge-rpm-arm64.svg "Download RPM aarch64 (for SUSE 15.5+ / RHEL based 10+ / Fedora / ALT Linux p11+)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1.aarch64.rpm) For older OS (SUSE 15.4- / RHEL based 9- / ALT Linux p10- ) Older versions depend on `libwebkit2gtk-4.0-37`, while newer ones already use `libwebkit2gtk-4.1-0`. [![Download RPM x86_64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)](/docs/img/getting-started/badge-rpm-amd64.svg "Download RPM x86_64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1.x86_64.rpm)[![Download RPM aarch64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)](/docs/img/getting-started/badge-rpm-arm64.svg "Download RPM aarch64 (for SUSE 15.4- / RHEL based 9- / ALT Linux p10-)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1.aarch64.rpm) **Arch Linux** For Arch Linux, run the following commands in the terminal: ```shell ( ARCH=$(uname -m) URL="https://releases.tuna.am" cd $(mktemp -d) curl -sSLfO "${URL}/tuna.asc" curl -sSLfO "${URL}/desktop/latest/tuna-desktop_latest_checksums_linux-pkgs.txt" curl -sSLfO "${URL}/desktop/latest/current/tuna-desktop-latest-1-${ARCH}.pkg.tar.zst" curl -sSLf "${URL}/desktop/latest/current/tuna-desktop-latest-1-${ARCH}.pkg.tar.zst.sig" -o tuna-desktop_latest.sig sha256sum -c --ignore-missing tuna-desktop_latest_checksums_linux-pkgs.txt gpg --batch --import tuna.asc gpg --batch --verify tuna-desktop_latest.sig tuna-desktop-latest-1-${ARCH}.pkg.tar.zst pacman -Sy pacman -U tuna-desktop-latest-1-${ARCH}.pkg.tar.zst ) ``` *** [![Download PKG x86_64 (for Arch Linux)](/docs/img/getting-started/badge-pkg-amd64.svg "Download pkg x86_64 (for Arch Linux)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1-x86_64.pkg.tar.zst) [![Download pkg aarch64 (for Arch Linux)](/docs/img/getting-started/badge-pkg-arm64.svg "Download pkg aarch64 (for Arch Linux)")](https://releases.tuna.am/desktop/latest/current/tuna-desktop-latest-1-aarch64.pkg.tar.zst) For older OS Older versions depend on `libwebkit2gtk-4.0-37`, while newer ones already use `libwebkit2gtk-4.1-0`. [![Download pkg x86_64 (for Arch Linux)](/docs/img/getting-started/badge-pkg-amd64.svg "Download pkg x86_64 (for Arch Linux)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1-x86_64.pkg.tar.zst)[![Download pkg aarch64 (for Arch Linux)](/docs/img/getting-started/badge-pkg-arm64.svg "Download pkg aarch64 (for Arch Linux)")](https://releases.tuna.am/desktop/latest/old/tuna-desktop-latest-1-aarch64.pkg.tar.zst) *** #### Update On Windows and macOS, download the latest version using the links above and install it. On Linux, update the `tuna-desktop` package with your package manager (`apt`, `zypper`, `dnf`, `apt-get`) or run the install script again. On Arch Linux, repeat the installation commands: they download the latest version. #### Authorization To use the client, you need a Token. Register [via this link](https://my.tuna.am/?lang=en) and authorize the application. The token will be saved in the [config file](https://tuna.am/en/docs/guides/config-file.md), or you can copy it from a [separate page](https://my.tuna.am/token?lang=en) and specify it manually. Click **Sign in to the app** ![Sign in to the app](/docs/img/getting-started/desktop-login.png) A link will open in the browser asking you to confirm the login, click **Sign in** ![Sign in to the tuna app](/docs/img/getting-started/desktop-login-browser.png) Specify the token manually Below the *Sign in to the app* button, click the **use token to sign in** link and enter the token from the [page](https://my.tuna.am/token?lang=en): ![Sign in to the tuna app](/docs/img/getting-started/desktop-set-token.png) #### Get started Everything is ready to start working. Create one or more tunnels and run them. ![Click - add tunnel](/docs/img/getting-started/desktop-use.png) #### What's next? * Explore [HTTP tunnel](https://tuna.am/en/docs/tunnels/http.md) configuration examples * Need access to a non-HTTP service? Learn more in the [TCP tunnel](https://tuna.am/en/docs/tunnels/tcp.md) section --- ### Removal **Windows** #### Removing a winget installation Open PowerShell and run the command: ```shell winget uninstall --id yuccastream.tuna ; powershell ``` #### Removing an MSI package installation Uninstall the program through the Programs control panel. #### Removing a manual installation Open PowerShell and run the command: ```powershell Remove-Item "$env:USERPROFILE\AppData\Local\Microsoft\WindowsApps\tuna.exe" -Force ``` **macOS** #### Removing a brew installation Run the command in the terminal: ```bash brew uninstall yuccastream/tap/tuna ``` #### Removing a manual or script installation Run the command in the terminal: ```bash sudo rm -f /usr/local/bin/tuna ``` **Linux** #### Removing a package manager installation **Ubuntu/Debian** For Debian and derivative distributions (Ubuntu, Mint and others), run the following commands in the terminal: ```shell sudo apt purge tuna.am tuna-cli tunad ``` **SUSE/OpenSUSE** For SUSE and OpenSUSE, run the following commands in the terminal: ```shell sudo zypper remove tuna.am tuna-cli tunad ``` **RHEL/Fedora** For RHEL and derivative distributions (CentOS, Rocky, AlmaLinux and others), as well as Fedora, run the following commands in the terminal: ```shell sudo dnf remove tuna.am tuna-cli tunad ``` **Arch Linux** For Arch Linux, run the following commands in the terminal: ```shell sudo pacman -R tuna.am-bin ``` #### Removing a manual installation Run the command in the terminal: ```bash sudo rm -f /usr/local/bin/tuna ``` **FreeBSD** Run the command in the terminal: ```shell sudo rm -f /usr/local/bin/tuna ``` --- ### SSH Keys On the [SSH keys page](https://my.tuna.am/public_keys?lang=en) you can add your public keys and use them for authentication in [SSH tunnels](https://tuna.am/en/docs/tunnels/ssh.md) or creating [HTTP tunnels with SSH client](https://tuna.am/en/docs/tunnels/guides/ssh-reverse.md). ![](/docs/img/ssh-keys.gif) To allow connections in SSH tunnels using your SSH keys, you need to enable the corresponding settings; in the case of a team, you can additionally allow team members to use your key. ![](/docs/img/ssh-keys.png) --- ### Terraform provider The `yuccastream/tuna` provider manages tuna resources as code: domains and domain zones, gateways, endpoints, IP policies, TLS certificates, SSH keys and temporal tokens. It works with Terraform and OpenTofu. > **The provider is in the Terraform Registry.** The reference for all resources and attributes is on the [yuccastream/tuna](https://registry.terraform.io/providers/yuccastream/tuna/latest/docs) page in the Terraform Registry. #### Installation Terraform 1.11 or newer is required: older versions do not support write-only attributes, which are used to pass the TLS certificate private key. ```hcl terraform { required_version = ">= 1.11" required_providers { tuna = { source = "yuccastream/tuna" } } } provider "tuna" {} ``` ```bash terraform init ``` The provider is not in the OpenTofu Registry yet. With OpenTofu, set the registry explicitly: `source = "registry.terraform.io/yuccastream/tuna"`. #### Authentication The provider needs an API key. Issue one in the [dashboard](https://my.tuna.am/api_keys?lang=en) with the `tunnels:write` scope: it covers every resource of the provider, full `api` access is not needed. A key with the default `read_api` scope is read-only, `apply` will fail with it. API keys cannot be issued through the API, so the provider does not manage them: create keys in the dashboard only. Pass the key via an environment variable (recommended) or in the `provider` block: ```bash export TUNA_API_KEY="" ``` ```hcl provider "tuna" { api_key = var.tuna_api_key } ``` | Argument | Environment variable | Description | | --------- | -------------------- | --------------------------------------------- | | `api_key` | `TUNA_API_KEY` | API key | | `api_url` | `TUNA_API_URL` | API address, `https://api.tuna.am` by default | #### Resources | Resource | Manages | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `tuna_domain` | [Reserved domain](https://tuna.am/en/docs/team/domains.md): a subdomain of a built-in zone, of your own zone, or a custom domain | | `tuna_domain_zone` | [Domain zone](https://tuna.am/en/docs/team/domain_zones.md) | | `tuna_tls_certificate` | [TLS certificate](https://tuna.am/en/docs/team/tls_certificates.md) for a domain zone | | `tuna_gateway` | [Gateway](https://tuna.am/en/docs/gateways.md) on a reserved domain | | `tuna_endpoint` | [TCP port](https://tuna.am/en/docs/team/tcp_ports.md) | | `tuna_ip_policy` | [IP policy](https://tuna.am/en/docs/team/ip_policies.md) | | `tuna_ip_policy_rule` | IP policy rule | | `tuna_public_key` | [Public SSH key](https://tuna.am/en/docs/team/ssh_public_keys.md) | | `tuna_temporal_token` | Temporal token for running tunnels | The `tuna_tunnels` data source returns the tunnels of the account. New domains and endpoints are created in a [cell](https://tuna.am/en/blog/high-availability.md), and location doesn't affect them, so starting with provider version 0.2.0, `tuna_domain` and `tuna_endpoint` no longer have a `location` argument. It remains only on `tuna_domain_zone`: it's optional, accepts `ru` or `nl`, and defaults to `ru`. If your configurations set `location` on domains or endpoints, remove it before upgrading the provider. The full attribute reference is in the [Terraform Registry](https://registry.terraform.io/providers/yuccastream/tuna/latest/docs). #### Scenario: temporal tunnels for CI Terraform reserves a domain and issues a limited temporal token, and a CI job starts a tunnel with that token. The main API key never reaches CI. ```hcl resource "tuna_domain" "preview" { subdomain = "myapp-preview" } resource "tuna_temporal_token" "ci" { description = "CI preview" duration = "24h" allow_http = true allow_tcp = false limit_by_active_tunnels = 5 limit_by_created_tunnels = 100 domains = [tuna_domain.preview.domain] } output "ci_tunnel_token" { value = tuna_temporal_token.ci.token sensitive = true } ``` The `domains` argument binds the token to reserved domains, and `endpoints` binds it to [TCP ports](https://tuna.am/en/docs/team/tcp_ports.md) by alias, up to 100 addresses each. With a bound token, a tunnel starts only on these addresses, and the server rejects a tunnel without an address or an [App](https://tuna.am/en/docs/apps.md). [`tuna ssh`](https://tuna.am/en/docs/tunnels/ssh.md) with such a token gets a one-time host key instead of the member's shared key. `domains` requires `allow_http = true`, and `endpoints` requires `allow_tcp = true`. If the CI token leaks, nobody can use it to bring up a tunnel on another team domain. The token is marked `sensitive`, so read it explicitly: ```bash terraform apply export TUNA_TOKEN="$(terraform output -raw ci_tunnel_token)" tuna http 8080 --subdomain=myapp-preview ``` Check that the tunnel is up with the data source: ```hcl data "tuna_tunnels" "all" {} output "active_tunnels" { value = [for t in data.tuna_tunnels.all.tunnels : t.public_url if t.active] } ``` #### Scenario: own zone with a certificate and IP access ```hcl resource "tuna_tls_certificate" "wildcard" { description = "wildcard for tunnels.example.com" certificate_pem = file("${path.module}/cert.pem") private_key_pem = file("${path.module}/key.pem") } resource "tuna_domain_zone" "example" { domain = "tunnels.example.com" location = "ru" tls_certificate_id = tuna_tls_certificate.wildcard.id } resource "tuna_domain" "app" { subdomain = "app" domain_zone_id = tuna_domain_zone.example.id } resource "tuna_ip_policy" "office" { name = "office-only" active = true } resource "tuna_ip_policy_rule" "office_allow" { ip_policy_id = tuna_ip_policy.office.id name = "office" cidr = "203.0.113.0/24" action = "allow" } ``` `private_key_pem` is a write-only attribute: the provider sends the key to the API but does not store it in state. #### Gateway ```hcl resource "tuna_domain" "api" { subdomain = "myapp-api" } resource "tuna_gateway" "api" { domain_id = tuna_domain.api.id policy = file("${path.module}/policy.yaml") } ``` `policy.yaml` holds a [traffic policy](https://tuna.am/en/docs/traffic-policy.md). A domain with a gateway is taken by the gateway: a tunnel on it will not start, the server responds with `Domain already reserved by gateway`. Reserve a separate domain for the tunnel. #### Changing resources The API cannot edit endpoints, domains, domain zones and temporal tokens, so any change of their attributes (including `alias` or `description`) recreates the resource. A temporal token also gets a new value: tunnels started with the old token have to be restarted. Gateways, IP policies and their rules are updated in place. A rule is recreated only when `ip_policy_id` changes. #### Import Existing resources are imported by their numeric ID from the dashboard or the API: ```bash terraform import tuna_domain.app 123 terraform import tuna_gateway.api 123 ``` An IP policy rule has a composite ID `/`: ```bash terraform import tuna_ip_policy_rule.office_allow 123/456 ``` The API does not return the certificate private key, so after importing `tuna_tls_certificate` set `private_key_pem` in the configuration. --- ### Troubleshooting Here we have tried to collect all possible issues that our users have encountered and describe their solutions. Most problems are related to network issues or user environment. > **Tip.** Many errors and problems may already be fixed in the latest version of the client, so before diagnosing or asking for help from [us](mailto:info@tuna.am) or the [community](https://telegram.me/+Kgbtt0_gJwI5ZmEy), make sure you have the latest version of the client installed. Update instructions are available [here](https://tuna.am/en/docs/guides/update.md). #### Self-diagnosis of network availability If you encounter errors when creating a tunnel, run the `tuna diagnose` command to get diagnostic information. The report will help you better understand where exactly the problem is. ```shell Please wait, testing connectivity... TLS connectivity: Google.com: ok [1.46189625s] Yandex.ru: ok [1.246520375s] tuna API: ok [1.484186875s] DNS: api.tuna.am: alt-lb.tuna.am. → eu-lb.tuna.am. [147.45.149.16, 194.31.173.118] a.2na.am: edge-eu-a.2na.am. [2.28.136.177, 89.19.217.251] TCP connectivity: tuna edge a 2.28.136.177:443: ok [41.53925ms] tuna edge a 89.19.217.251:443: ok [38.112708ms] tuna node DE node-1-de.2na.am:443: ok [52.81425ms] tuna node RU node-1-ru.2na.am:443: ok [34.079541ms] ``` As a rule, tuna uses port `443` to connect to nodes when creating a tunnel. If the `TCP connectivity` checks fail, check if there is a firewall that may be restricting connections. The addresses for firewall rules are listed in [Using tuna in a corporate network](https://tuna.am/en/docs/guides/corporate-network.md#firewall-addresses). #### Common errors ##### Tunnel already active ```shell ERRO[09:01:09] Failed to get connection settings: Tunnel already active Code: BadRequest Request ID: 2008cc71bdf725853748bb91d69c4669, retrying in 1s ERRO[09:01:10] Failed to get connection settings: Tunnel already active Code: BadRequest Request ID: 0118213a8f8d341f002ec30ee1499768, retrying in 2s ERRO[09:01:12] Failed to get connection settings: Tunnel already active Code: BadRequest Request ID: 608d043eb16376a619b671eb0d33dc0b, retrying in 4s ``` The error `Tunnel already active` means that the tunnel already exists, it may be stuck, or running somewhere in the background. The easiest way to solve the problem is to delete the stuck tunnel in the [tunnel management panel](https://my.tuna.am/tunnels?lang=en). The problem may occur when a connection was interrupted and our server could not correctly track it. We are working to prevent this behavior in the future. ##### Tunnel limit reached If you see the error `Tunnel limit reached`, although you know that this is not actually the case, it may also mean that there is a stuck active tunnel. As with the previous error, the way to solve the problem is to delete the stuck tunnel in the [tunnel management panel](https://my.tuna.am/tunnels?lang=en). ##### VPN A lot of strange behavior occurs when working through VPN or if you frequently connect/disconnect from it. Unfortunately, there is no universal solution here, as it all depends on the specific operating system or VPN client. We can recommend excluding routing to Tuna resources through VPN if your client allows it. Exclude requests to the IP `api.tuna.am` and [nodes](https://tuna.am/en/docs/tunnels/guides/locations.md). --- ### Update If you see the message `New version available` when starting tuna, it's time to update. Updating allows you to receive bug fixes and new features in a timely manner. The update procedure is no more complicated than installation: **Windows** Run the command in PowerShell or [update manually](https://tuna.am/en/docs/guides/install/install-cli.md?os=windows): ```bash winget upgrade --id yuccastream.tuna ; powershell ``` **macOS** Run the commands in the terminal or [update manually](https://tuna.am/en/docs/guides/install/install-cli.md?os=macos): ```bash brew update brew upgrade yuccastream/tap/tuna ``` **Linux** ##### Updating via the package manager **Ubuntu/Debian** For Debian and derivative distributions (Ubuntu, Mint and others), run the following commands in the terminal: ```shell sudo apt update sudo apt install tuna-cli ``` **SUSE/OpenSUSE** For SUSE and OpenSUSE, run the following commands in the terminal: If tuna was installed before version 0.35.0, switch the repository once: ```shell sudo zypper removerepo tuna sudo zypper addrepo --repo https://code.tuna.am/api/packages/tuna/rpm/stable.repo ``` Update the package: ```shell sudo zypper refresh sudo zypper install -y tuna-cli ``` **RHEL/Fedora** For RHEL and derivative distributions (CentOS, Rocky, AlmaLinux and others), as well as Fedora, run the following commands in the terminal: If tuna was installed before version 0.35.0, switch the repository once: ```shell sudo curl -fsSL https://code.tuna.am/api/packages/tuna/rpm/stable.repo -o /etc/yum.repos.d/tuna-forgejo.repo sudo rm -f /etc/yum.repos.d/tuna-rpm.repo /etc/yum.repos.d/tuna.repo ``` Update the package: ```shell sudo dnf install -y --refresh tuna-cli ``` **ALT Linux** For ALT Linux, run the command in the terminal: ```shell sudo apt-get update && sudo apt-get install tuna-cli ``` **Arch Linux** For Arch Linux, update the `tuna.am-bin` package from AUR: ```shell yay -S tuna.am-bin ``` Without yay, use the script below: it installs the package with yay or pacman. ##### Updating with the script The script works on any distribution: where a tuna package is available it updates the package, elsewhere (Alpine, NixOS and others) it replaces the binary. Run the command in the terminal or [update manually](https://tuna.am/en/docs/guides/install/install-cli.md?os=linux): **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` **FreeBSD** Run the command in the terminal or [update manually](https://tuna.am/en/docs/guides/install/install-cli.md?os=freebsd): **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` **Docker** Run the command: ```bash docker pull yuccastream/tuna:latest ``` To make sure the update was successful, run `tuna version`. #### Tuna update policy We strive to ensure that users always have access to the latest version of tuna. Therefore, we limit the use of outdated versions for users without a subscription. When a new version of the client is released, the previous one will continue to work for approximately 6 months. The exact end-of-support date is reflected in the table below. Users with a subscription can continue using any version without restrictions. | tuna Version | End of support date | | ----------------- | ------------------- | | older than 0.14.x | November 1, 2024 | | 0.14.x | December 1, 2024 | | 0.15.x | January 1, 2025 | | 0.16.x | February 1, 2025 | | 0.17.x | March 1, 2025 | | 0.18.x | April 1, 2025 | | 0.19.x | May 1, 2025 | | 0.20.x | June 1, 2025 | | 0.21.x | October 1, 2025 | | 0.22.x | October 1, 2025 | | 0.23.x | November 1, 2025 | | 0.24.x | November 1, 2025 | | 0.25.x | December 1, 2025 | | 0.26.x | February 1, 2026 | | 0.27.x | March 1, 2026 | | 0.28.x | May 1, 2026 | | 0.29.x | July 1, 2026 | | 0.30.x | August 1, 2026 | | 0.31.x | September 1, 2026 | | 0.32.x | October 1, 2026 | | 0.33.x | November 1, 2026 | | 0.34.x | December 1, 2026 | | 0.35.x | January 1, 2027 | | 0.36.x | February 1, 2027 | | 0.37.x | March 1, 2027 | --- ### Meets [Go to personal account](https://my.tuna.am/meet?lang=en) Video calls for your team: call, chat and solve problems quickly — all on our platform. ![Video meeting in Tuna](/docs/img/meet/main.png) *** #### Overview **Meets** are video calls right inside Tuna. A simple, familiar interface similar to Google Meet. > **What you can do in meetings** > > * Have calls in your team's permanent channels > * Create one-off meetings and share the link > * Configure microphone, camera and background before joining > * Share your screen > * Chat during the call > * Raise your hand and send reactions > * Join from your phone #### Service structure The **Meets** service consists of two types of rooms: * **Private channels** — permanent rooms of your team (for example, *General*, *Lounge*, *Incident*). Available only to members of the organization. * **One-off meetings** — created with the **New meeting** button and shared via link or code. ![Meets home screen with the New meeting button and a list of private channels](/docs/img/meet/start.png) #### Creating your first meeting 1. Go to the [Meets](https://my.tuna.am/meet?lang=en) section in your personal account. 2. Click **New meeting** to create a one-off room, or pick one of your team's private channels. 3. Copy the link or code and send it to your colleagues — for private channels they only need to open the same section. To join someone else's meeting, paste the link or code into the **Paste meeting link or code** field and click **Join**. *** #### Meeting features ##### Pre-call settings window Before joining, check your microphone and camera, pick the right device from those connected, or temporarily turn them off. That way you enter the room ready to go — without the awkward "can you hear me?". ![Microphone and camera setup window before joining](/docs/img/meet/precall.png) ##### Backgrounds and effects Replace your background with one of the presets (office, Miami beach, parking lot) or turn on blur — handy if there's a mess around you or you're calling from somewhere unusual. ##### Participants panel The list of active participants is shown next to the video. Anyone can **raise a hand** to take the floor — the host will immediately see who wants to speak. ![List of meeting participants](/docs/img/meet/participants.png) ##### Reactions Quick feedback without interrupting: send a like, heart, fire and other reactions — they appear for everyone in the meeting. ![Reactions panel in a meeting](/docs/img/meet/reactions.png) ##### Screen sharing Show your screen to colleagues without long setup — one click and the presentation is already running. Suitable for demos, debugging and reviewing mockups. ##### Chat You can message in the chat during a call — for example, drop a link without interrupting the speaker. The chat is kept for the duration of the meeting. ![Chat during a meeting](/docs/img/meet/chat.png) ##### Meets from your phone Join meetings from a mobile device — video, audio, chat and screen sharing work the same way as in the desktop version. ![Meeting on a mobile device](/docs/img/meet/mobile.png) *** #### Pricing Meets are included in the Tuna subscription and don't require a separate payment. The functionality is available on the **Developer** and **Legend** plans — see the [pricing](https://tuna.am/#pricing) page for details. --- ### Mesh > **Beta.** Tuna Mesh is in beta and is enabled for a team on request to [support](https://tuna.am/en/docs/support.md). After that, the [Mesh](https://my.tuna.am/mesh?lang=en) section appears in the dashboard. Tuna Mesh connects your team's laptops, servers, and virtual machines into a private network. Each device gets a permanent address and a DNS name like `laptop.tuna`, and devices reach each other directly over any protocol and port. You don't need a public IP address or open ports. A tunnel publishes one local service to the internet. Mesh is a closed network: only your team's devices can see each other in it, and nobody can reach them from outside. ![The My devices section: a list of devices with Mesh IP, OS, exit node, and last activity](/docs/img/mesh/devices.webp) What else mesh can do: * [exit nodes](https://tuna.am/en/docs/mesh/exit-nodes.md) — go out to the internet through another team device, for example a server in a specific country; * [subnets](https://tuna.am/en/docs/mesh/exit-nodes.md#subnets) — open access to an office network through one device without installing the client on every host; * [access rules](https://tuna.am/en/docs/mesh/acl.md) — define which devices can talk to each other and on which ports; * revoking devices from the dashboard: the session drops immediately. Traffic is encrypted with WireGuard. If the network blocks UDP, the client switches to a fallback transport over SSH on its own. #### Requirements * Linux, macOS, or Windows. Only Linux and macOS devices can share an [exit node and subnets](https://tuna.am/en/docs/mesh/exit-nodes.md), but devices on all three systems can use them. * The [tuna](https://tuna.am/en/docs/guides/install/install-cli.md) client. We recommend version 0.38.0 or later, and tunad 0.1.2 or later. * The `tunad` service. It configures a network interface, so it runs with root or administrator privileges. The `tuna mesh` commands themselves run as a regular user and don't need `sudo`. `tunad` doesn't run inside a Docker container; install it on the host. #### Installing tunad **Linux** The `tunad` package is in the same repository as tuna. Add the repository by following the [installation guide](https://tuna.am/en/docs/guides/install/install-cli.md#linux-manual) and install the package: ```shell # Debian, Ubuntu sudo apt install -y tunad # RHEL, Fedora sudo dnf install -y tunad # SUSE, openSUSE sudo zypper install -y tunad ``` The package enables and starts `tunad.service` itself. **macOS** ```shell brew install yuccastream/tap/tunad sudo tunad install ``` To manage the service, use `sudo tunad status`, `sudo tunad restart`, and `sudo tunad uninstall`. Logs are in `/var/log/tunad.out.log` and `/var/log/tunad.err.log`. **Windows** Download the [tunad_x64.msi](https://releases.tuna.am/tunad/latest/tunad_x64.msi) installer, or [tunad_arm64.msi](https://releases.tuna.am/tunad/latest/tunad_arm64.msi) for ARM, and run it. The installer registers the service itself. #### Connecting a device ##### With a browser On your work computer, run: ```shell tuna mesh login ``` The command opens a confirmation page in the browser. Check that the device name, OS, and IP address are yours, and click **Connect**. If the browser doesn't open, the CLI prints a link. You have 10 minutes to confirm. After you confirm, the browser returns the result to the CLI at `127.0.0.1`, so the browser must be open on the same computer. For a remote server you're connected to over SSH, use a [connection token](#enroll). ![The page for connecting a device to mesh: OS, runtime, environment, initiator IP, and the Connect and Decline buttons](/docs/img/mesh/authorize.webp) > **Warning.** The device joins the team that is currently selected in the dashboard. If you belong to several teams, switch to the right one in the sidebar before you confirm. Once confirmed, connect the device to the network: ```shell tuna mesh up ``` ##### A server without a browser For servers, virtual machines, and CI, use a connection token: 1. In the dashboard, open **Mesh → Connection tokens** and click **Create token**. 2. Choose a lifetime, from 1 hour to 1 day, and the number of activations, from 1 to 10. One token can connect several servers. 3. Copy the token: it is shown only once. ![The New connection token form: lifetime and number of activations](/docs/img/mesh/token-form.webp) On the server, run: ```shell tuna mesh enroll tuna mesh up ``` You can also pass the token in the `TUNA_MESH_ENROLL_TOKEN` environment variable. When the lifetime runs out, the token stops working even if activations are left. Devices connected with a token belong to the person who created it. ##### Environment label When you connect a device, you can give it an environment label, such as `production` or `staging`: ```shell tuna mesh enroll --env=production ``` Instead of the flag, you can use the `TUNA_MESH_ENV` variable; without it, the label is `default`. The label shows up in the device list, and a team admin can change it. For now it is only a label for convenience and doesn't affect access. #### Addresses and names Each device gets an IPv4 address from the `100.100.0.0/16` range and a paired IPv6 address from `fd74:756e:6100:100::/64`. The address is tied to the device and doesn't change when it reconnects. The range is the same for all teams, but team networks are isolated from each other. The device name is its hostname in the `.tuna` zone, for example `office-gw.tuna`. If the name is taken, the server adds a suffix: `laptop-1`, `laptop-2`. You can't rename a device from the dashboard: to change the name, change the hostname and connect the device again. Names work in any program: ```shell ssh ubuntu@office-gw.tuna psql -h prod-db-1.tuna -U app curl http://k8s-worker-3.tuna:8080/healthz ``` tunad configures DNS only for the `.tuna` zone; all other queries go through your usual DNS server. #### Status and diagnostics | Command | What it shows | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `tuna mesh status` | network state, device name and addresses, transport, exit node, shared and received subnets | | `tuna mesh ip` | device addresses; `-4` or `-6` shows only one | | `tuna mesh peers` | team devices and their state | | `tuna mesh trace ` | the path to a device and the latency | | `tuna mesh debug trace ` | whether [access rules](https://tuna.am/en/docs/mesh/acl.md#check) allow a connection, without sending real traffic | For scripts, `status`, `peers`, and `debug trace` have a `--json` flag. After a reboot, the tunad service brings the network back up with the same settings if it was enabled. #### Disconnecting `tuna mesh down` disconnects the device from the network but keeps it in the team: run `tuna mesh up` to connect again. `tuna mesh logout` disconnects the device, revokes it, and deletes the keys from the computer. After that, you can connect the device again, including as a different user. You can also revoke a device in the dashboard: **Mesh → My devices → Revoke**. The session drops immediately. A team admin can revoke any device in [Team → Devices](https://tuna.am/en/docs/mesh/team.md#devices). #### Plans | Plan | Devices per member | Exit nodes and subnets | Custom access rules | | ------- | ------------------ | ---------------------- | ------------------- | | Starter | 5 | use others' only | no | | Pro | 10 | share and use | no | | Legend | 100 | share and use | up to 100 rules | Revoked devices don't count toward the limit. Without custom access rules, devices of the same user see each other, but devices of different members don't. For colleagues to connect to each other's devices, you need the Legend plan and an [allow rule](https://tuna.am/en/docs/mesh/acl.md). #### Beta limitations * You choose the exit node manually; there is no automatic selection. * A device can't share an exit node and use another device as an exit node at the same time. * If you run `tuna mesh up` without the `--advertise-*` flags, the previously advertised exit node and subnets are withdrawn. Repeat the flags every time. * Only one OS user on a computer can use the tunad service — the one who connected the device first. Another user is refused until the first one runs `tuna mesh logout`. * If you clone a virtual machine, reinstall the OS, or replace the motherboard, you need to connect the device again. * A device certificate is valid for 7 days and renews on its own while the device is on. If the device was off for longer, connect it again with `tuna mesh login` or `tuna mesh enroll`. In this case, the `tuna mesh down && tuna mesh up` hint in the `tuna mesh status` output won't help. * The mesh addresses `100.100.0.0/16` are part of the range that Tailscale uses. If both run on a device, the routes may overlap. --- ### Access rules Access rules define which team devices can connect to each other and on which ports. You configure them in [Team → Access rules](https://my.tuna.am/team_mesh_acl?lang=en); the section is available to team admins and owners. #### Default behavior Without custom rules, the system rules apply: 1. devices of the same user see each other; 2. everything else is denied. So by default, devices of different members don't see each other. To open access, add an allow rule. Custom rules are available on the Legend plan, up to 100 of them. On other plans, the section shows only the system rules. ![The Mesh VPN access rules section: system security rules and five custom rules with priorities](/docs/img/mesh/acl.webp) #### How a rule works | Field | Values | | ----------- | ----------------------------------------------------------------------------------------------------------------- | | Source | all devices, a user's devices, a specific device, an IP address or subnet | | Destination | the same as for the source | | Protocol | any, TCP, UDP, ICMP, and other IP protocols | | Port | any, a single port `22`, a range `1000-2000`, or a list `80,443`. Only for TCP, UDP, SCTP, and the "any" protocol | | Action | allow or deny | Rules are checked in priority order, and the first matching rule wins. To change the order, drag the rules; system rules always come after yours. You can temporarily turn a rule off with the **Enabled** switch without deleting it. Rules work at the level of IP addresses and ports; they don't distinguish HTTP requests or TLS. #### Examples Give an admin access to all devices: | Source | Destination | Protocol | Port | Action | | -------------------- | ----------- | -------- | ---- | ------ | | User | All | any | any | Allow | Let everyone reach SSH on the office gateway, but close the database: | Source | Destination | Protocol | Port | Action | | ------ | ----------- | -------- | ---- | ------ | | All | office-gw | TCP | 22 | Allow | | All | prod-db-1 | any | any | Deny | Open the web interfaces in the office subnet shared by the [gateway](https://tuna.am/en/docs/mesh/exit-nodes.md#subnets) to everyone: | Source | Destination | Protocol | Port | Action | | ------ | ------------ | -------- | ---- | ------ | | All | 10.20.0.0/16 | TCP | 443 | Allow | #### Check access On the **Check access** tab, you can find out whether the rules allow a connection without sending any traffic. Select the source and destination, the protocol and port, and click **Check access**. The result shows whether access is allowed and which rule matched. ![The Check access tab: a connection from dev-laptop-boris to office-gw over TCP 22 is allowed by the rule with priority 1](/docs/img/mesh/acl-check.webp) The same from the command line, from the current device: ```shell tuna mesh debug trace self office-gw --proto=tcp --dport=22 ``` #### Changing the plan If the team moves from Legend to another plan, custom rules aren't deleted but stop working: only the system rules remain. After you return to Legend, the rules work again. --- ### Exit nodes and subnets A device in mesh can pass access further on, to the internet or to a local network. * An **exit node** routes the internet traffic of other devices through itself. With it, you can go online from the IP address of an office or a server in a specific country. * **Subnets** open access to the local network behind a device: office printers, NAS, and servers. You don't need to install the client on every host. Devices on Linux and macOS can share them on the Pro and Legend plans. Any device on any plan and OS can use exit nodes and subnets if the device owner has granted access. #### How to share an exit node On the device that will carry the traffic, run: ```shell tuna mesh up --advertise-exit-node ``` The exit node appears right away in **Mesh → Exit nodes** with the **Full** type. The tunad service enables packet forwarding and NAT itself. If Docker, firewalld, or ufw blocks forwarding, tunad tries to allow it, and if it can't, it warns you in the `tuna mesh status` output. #### How to share subnets List the subnets separated by commas: ```shell tuna mesh up --advertise-routes=192.168.10.0/24,10.20.0.0/16 ``` The subnets must not overlap with mesh addresses. You can't share the whole internet (`0.0.0.0/0`) this way; that's what `--advertise-exit-node` is for. Hosts on the local network see connections coming from the sharing device's address, so you don't need to add a return route on them. Other devices pick up the subnets automatically, no flags needed. Check the `Subnet routes` line in the `tuna mesh status` output, and then reach the hosts by their local addresses: ```shell ssh admin@192.168.10.15 ``` > **Warning.** Pass the `--advertise-*` flags every time you run `tuna mesh up`. Running it without them, just like `tuna mesh down`, withdraws the advertised exit node and subnets. #### Who can use them The device owner controls access to the exit node and subnets in **Mesh → Exit nodes → Permissions**. Your own devices can always use them. ![The Exit nodes section: office-gw shares two subnets, prod-db-1 is a full exit node, access is owner only](/docs/img/mesh/exit-nodes.webp) Others get access like this: * **Allow the whole team** — any team device can use it; * **Allow** for a specific device — an individual permission; * **Deny** for a specific device — the denial applies even when **Allow the whole team** is on. ![The Individual permissions dialog: the Allow the whole team switch and granting a permission or denial to a device](/docs/img/mesh/exit-node-permissions.webp) The **Withdraw** button removes the exit node or subnets from the device. A team admin can't grant permissions on the owner's behalf but can forcibly withdraw an exit node or subnet in [Team → Exit nodes](https://tuna.am/en/docs/mesh/team.md#routes). Other members' exit nodes that you have access to are collected on the **Others' exit nodes** tab. #### How to go out through an exit node Specify the device when you bring the network up: ```shell tuna mesh up --exit-node=prod-db-1 ``` You can use the device name, the name with `.tuna`, or its Mesh IP. If you don't have access, the CLI responds with `exit-node access not granted by owner`. The local network you're on still works directly. You don't have to route all traffic through the exit node: | Flag | What it does | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | `--exit-node-country-include=ru` | only traffic to one country goes through the exit node | | `--exit-node-country-exclude=ru` | traffic to one country goes directly, the rest goes through the exit node | | `--exit-node-routes-include=` | additional subnets through the exit node | | `--exit-node-routes-exclude=` | subnets that go directly | | `--fallback-to-direct` | if the exit node is unavailable for longer than the grace period, traffic goes directly and then switches back on its own | | `--fallback-to-direct-grace=60s` | the grace period before switching to a direct connection, from 10 seconds to 10 minutes | The country flags take a single country code and work only together with `--exit-node`. You can't set both of them at once. `tuna mesh countries` lists the codes. ```shell tuna mesh up --exit-node=prod-db-1 --exit-node-country-exclude=ru --fallback-to-direct ``` [Access rules](https://tuna.am/en/docs/mesh/acl.md) also apply to traffic through an exit node: a rule that denies a subnet closes it through the exit node too. --- ### Team management Team members manage their devices, tokens, and exit nodes in the **Mesh** section. Team admins and owners also have the **Team → Mesh VPN** section, which brings together the devices and settings of the whole team. > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. A personal team doesn't have this section: everything is configured on the **Mesh** pages. #### Devices The [section](https://my.tuna.am/team_mesh_devices?lang=en) lists all team devices: name, owner, environment label, OS, Mesh IP, transport, and last activity. ![The Mesh VPN devices section: counters of online devices and exit nodes, and a list of all members' devices](/docs/img/mesh/team-devices.webp) Here you can: * change a device's [environment label](https://tuna.am/en/docs/mesh.md#environment); * revoke any device: the session drops immediately, and the device can be connected again only with `tuna mesh login` or a new token. The **Connection tokens** tab collects the tokens of all members: who created each one, how many activations are used, and which devices were connected with it. You can revoke a token you no longer need. Members create tokens themselves in [Mesh → Connection tokens](https://tuna.am/en/docs/mesh.md#enroll). #### Exit nodes The [section](https://my.tuna.am/team_mesh_routes?lang=en) shows all exit nodes and subnets of the team: the type, who can use them, and how many devices are connected right now. The device owner controls access, and an admin can forcibly withdraw a route, for example if a device shares a subnet by mistake. For details, see [Exit nodes and subnets](https://tuna.am/en/docs/mesh/exit-nodes.md). #### Access rules [Access rules](https://tuna.am/en/docs/mesh/acl.md) define which devices can talk to each other and on which ports. #### Certificate authorities The [settings](https://my.tuna.am/team_mesh_settings?lang=en) show the fingerprints of two certificate authorities: * **Host CA (platform)** signs the Tuna nodes. The device uses it to verify that it connects to the real Tuna infrastructure; * **User CA (team)** signs the certificates of your team's devices. ![The Mesh VPN settings section: Host CA and User CA fingerprints and the Reissue User CA button](/docs/img/mesh/team-settings.webp) A device certificate is valid for 7 days and renews automatically while the device is on. A device that was off for longer needs to be [connected again](https://tuna.am/en/docs/mesh.md#connect). ##### Reissuing the User CA Reissue the User CA only if the keys of the team or its devices are compromised. To cut off a single device, just [revoke](#devices) it. After you click **Reissue User CA** and confirm, all team devices are disconnected: sessions drop, and the issued certificates and exit node assignments are revoked. You then need to connect each device again with `tuna mesh login` or `tuna mesh enroll`. --- ### Monitors [Open dashboard](https://my.tuna.am/monitors?lang=en) Functionality for monitoring CronJobs, Heartbeat, ICMP, DNS, HTTP and TCP applications with notification support through various communication channels. ![Monitors main page](/docs/img/monitors/main.png) *** #### Overview Monitors let you track the health of your services, jobs and infrastructure. When problems occur, you get notifications through the channels you have configured. *** #### Monitor types ![Monitor types](/docs/img/monitors/types.png) Tuna supports 6 monitor types, split into two categories: ##### User-driven monitors These monitors wait for signals from your applications and scripts: | Type | Description | Use case | | -------------- | -------------------------------------- | -------------------------------------------------------------- | | **Job** | Monitors execution of periodic jobs | Backup scripts, cron jobs, scheduled operations | | **Heartbeat** | Monitors regular signals from services | Services in closed networks, IoT devices, background processes | ##### External monitors These monitors check the availability of your resources from multiple regions on their own: | Type | Description | Use case | | --------- | ---------------------------------------- | ------------------------------------- | | **HTTP** | Checks availability of websites and APIs | Websites, REST APIs, webhooks | | **TCP** | Checks availability of TCP services | Databases, mail servers, game servers | | **DNS** | Checks DNS records | Domains, MX records, TXT records | | **ICMP** | Checks network reachability (ping) | Servers, network equipment | *** #### Monitor statuses Each monitor can be in one of the following states: | Status | Description | | -------------- | ---------------------------- | | ⚪ **New** | New monitor, not checked yet | | 🟢 **Up** | Working normally | | 🔵 **Running** | Running (for Job/Heartbeat) | | 🔴 **Down** | Failed/unavailable | ![screenshot with different monitor statuses](/docs/img/monitors/statuses.png) *** #### Creating a monitor 1. Go to the [Monitors](https://my.tuna.am/monitors?lang=en) page 2. Click **Create monitor** 3. Select the monitor type 4. Fill in the monitor parameters 5. Configure notifications (optional) 6. Save the monitor For all monitor types there are common settings: **Description** and **Notifications**. But **Details** and **Checks** differ depending on the monitor type. See the dedicated [page](https://tuna.am/en/docs/monitors/types.md) for more details on each type. *** #### Notifications Monitors are integrated with the notification system through [Contacts](https://tuna.am/en/docs/team/contacts.md) and [Integrations](https://tuna.am/en/docs/team/integrations.md). If no specific contact groups are specified, notifications are sent to the user's email. You can also disable notifications for a monitor. *** #### Viewing a monitor The monitor details page shows a lot of information. The data may differ depending on the monitor type. ![screenshot with the status and full information about the monitor](/docs/img/monitors/status.png) ##### Management * **Enable/Disable** notifications for the monitor * **Edit** — change monitor parameters * **Delete** — delete the monitor ![notifications disabled](/docs/img/monitors/alert_disable.png) *** #### Regions and check interval External monitors (HTTP, TCP, DNS, ICMP) can be checked from multiple geographic regions. When creating an external monitor you can choose one or several check regions. You also specify how often the resource availability check should run — the interval. ![screenshot with region selection](/docs/img/monitors/region.png) *** #### Working from the CLI For [Job](https://tuna.am/en/docs/monitors/types/job.md) and [Heartbeat](https://tuna.am/en/docs/monitors/types/heartbeat.md) monitors you can use the [tuna](https://tuna.am/en/docs/guides/install/install-cli.md) console client. See more: [Working with monitors in the CLI](https://tuna.am/en/docs/monitors/cli.md) *** #### [🗃Monitor types](https://tuna.am/en/docs/monitors/types.md) [6 items](https://tuna.am/en/docs/monitors/types.md) #### [📄️Status pages](https://tuna.am/en/docs/monitors/status_pages.md) [Public pages with the status of your services based on Tuna monitors](https://tuna.am/en/docs/monitors/status_pages.md) #### [📄️Working from the CLI](https://tuna.am/en/docs/monitors/cli.md) [Managing monitors through the tuna console client](https://tuna.am/en/docs/monitors/cli.md) #### [🗃Examples](https://tuna.am/en/docs/monitors/examples.md) [2 items](https://tuna.am/en/docs/monitors/examples.md) --- ### Working from the CLI #### Overview The [tuna](https://tuna.am/en/docs/guides/install/install-cli.md) console client lets you interact with [Job](https://tuna.am/en/docs/monitors/types/job.md) and [Heartbeat](https://tuna.am/en/docs/monitors/types/heartbeat.md) monitors directly from the terminal or scripts. Main commands: * `tuna monitor ping` — send a Heartbeat signal to a monitor * `tuna monitor exec` — run a command and track it as a Job monitor * `tuna monitor status` — view monitor statuses *** #### Authorization To view a monitor's status you need an [API key](https://tuna.am/en/docs/guides/api.md). It can be passed in several ways: **Environment variable** ```bash export TUNA_API_KEY=your-api-key tuna monitor ping YOUR_MONITOR_ID ``` **Flag** ```bash tuna monitor ping YOUR_MONITOR_ID --api-key=your-api-key ``` **Config file** ```bash # Save the key to the config tuna config save-api-key your-api-key # Now you can use it without explicitly passing the key tuna monitor ping YOUR_MONITOR_KEY ``` *** #### monitor ping Sends a signal to a Heartbeat monitor. ##### Syntax ```bash tuna monitor ping MONITOR_ID [flags] ``` ##### Parameters | Parameter | Description | Default | | ------------ | ---------------------------------------- | -------- | | `MONITOR_ID` | Monitor key (required) | — | | `--state` | State: `ping`, `start`, `finish`, `fail` | `ping` | | `--message` | Additional message | — | | `--host` | Sender host name | hostname | ##### Examples ###### Simple ping ```bash tuna monitor ping MONITOR_ID ``` ###### With explicit host ```bash tuna monitor ping MONITOR_ID --host=server-01 ``` ###### With a message ```bash tuna monitor ping MONITOR_ID --message="All systems operational" ``` ###### Different states ```bash # Start of work tuna monitor ping MONITOR_ID --state=start # Successful completion tuna monitor ping MONITOR_ID --state=finish # Error tuna monitor ping MONITOR_ID --state=fail ``` ##### Using in crontab ```bash # Heartbeat every 5 minutes */5 * * * * /usr/local/bin/tuna monitor ping MONITOR_ID --host=gitlab-runner ``` ##### Using in systemd ```ini # /etc/systemd/system/heartbeat.service [Unit] Description=Send heartbeat to Tuna [Service] Type=oneshot ExecStart=/usr/local/bin/tuna monitor ping MONITOR_ID ``` ```ini # /etc/systemd/system/heartbeat.timer [Unit] Description=Heartbeat timer [Timer] OnBootSec=1min OnUnitActiveSec=5min [Install] WantedBy=timers.target ``` *** #### monitor exec Runs a command and automatically tracks a Job monitor. On start it sends the `start` signal, on successful completion `finish`, and on error `fail`. ##### Syntax ```bash tuna monitor exec MONITOR_ID [flags] -- COMMAND [ARGS...] ``` ##### Parameters | Parameter | Description | Default | | ------------- | -------------------------------- | -------- | | `MONITOR_ID` | Monitor key (required) | — | | `--host` | Execution host name | hostname | | `--no-stdout` | Do not send output to the server | false | ##### Examples ###### Simple run ```bash tuna monitor exec MONITOR_ID -- ./backup.sh ``` ###### With explicit host ```bash tuna monitor exec MONITOR_ID --host=backup-server -- ./backup.sh ``` ###### Without sending logs ```bash tuna monitor exec MONITOR_ID --no-stdout -- ./backup.sh ``` ###### Command with arguments ```bash tuna monitor exec MONITOR_ID -- pg_dump -U postgres -d mydb -f /backups/dump.sql ``` ###### Command with a pipe ```bash tuna monitor exec MONITOR_ID -- bash -c 'pg_dump mydb | gzip > /backups/dump.sql.gz' ``` ##### Using in crontab ```bash # Backup every night at 3:00 0 3 * * * /usr/local/bin/tuna monitor exec MONITOR_ID -- /scripts/backup.sh # Log cleanup every hour 0 * * * * /usr/local/bin/tuna monitor exec MONITOR_ID -- /scripts/cleanup.sh ``` ##### Logging When `monitor exec` is used the system captures stdout and stderr of the running command. The logs can be viewed in the web UI in the monitor's event history. ![screenshot with the message field content](/docs/img/monitors/event.png) To disable sending logs use the `--no-stdout` flag: ```bash tuna monitor exec MONITOR_ID --no-stdout -- ./command.sh ``` ##### Signal handling `monitor exec` handles Unix signals correctly: * On SIGINT, SIGTERM, SIGHUP — the signal is forwarded to the child process * When the child process exits with a non-zero code — `fail` is sent * On clean exit (exit code 0) — `finish` is sent *** #### monitor status Shows the status of the team's monitors. To view the status you need an [API key](https://tuna.am/en/docs/guides/api.md). ##### Syntax ```bash tuna monitor status [MONITOR_ID] [flags] ``` ##### Parameters | Parameter | Description | Default | | ----------- | ------------------------------- | --------------- | | `KEY` | Specific monitor key (optional) | — | | `--api-key` | API key for authorization | `$TUNA_API_KEY` | ##### Examples ###### Status of all monitors ```bash tuna monitor status ``` ###### Status of a specific monitor ```bash tuna monitor status MONITOR_ID ``` *** #### Practical examples ##### PostgreSQL backup with monitoring ```bash #!/bin/bash # /scripts/backup-postgres.sh BACKUP_DIR="/backups/postgres" DATE=$(date +%Y%m%d_%H%M%S) DB_NAME="production" tuna monitor exec MONITOR_ID --host=pg-prod-replica-1 -- bash -c " pg_dump -U postgres $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz # Remove old backups (older than 7 days) find $BACKUP_DIR -name '*.sql.gz' -mtime +7 -delete " ``` ##### File synchronization ```bash #!/bin/bash # /scripts/sync-files.sh tuna monitor exec MONITOR_ID -- rsync -avz --delete /data/ backup-server:/backups/data/ ``` ##### Monitoring a service in Docker ```bash #!/bin/bash # healthcheck.sh inside the container # Check application health if curl -sf http://localhost:8080/health > /dev/null; then tuna monitor ping MONITOR_ID --message="healthy" else tuna monitor ping MONITOR_ID --state=fail --message="unhealthy" fi ``` ##### Monitoring GitLab Runner ```bash # In crontab on the GitLab Runner machine */5 * * * * /usr/local/bin/tuna monitor ping MONITOR_ID --host=raspberry-pi --message="Runner active" ``` --- ### Examples Examples and step-by-step guides for setting up monitors for various devices and scenarios. #### [📄️JetKVM](https://tuna.am/en/docs/monitors/examples/jetkvm.md) [Monitoring JetKVM availability with the Tuna Heartbeat monitor](https://tuna.am/en/docs/monitors/examples/jetkvm.md) #### [📄️NanoKVM (Sipeed)](https://tuna.am/en/docs/monitors/examples/nanokvm.md) [Monitoring NanoKVM availability with the Tuna Heartbeat monitor](https://tuna.am/en/docs/monitors/examples/nanokvm.md) --- ### Monitoring JetKVM availability [JetKVM](https://jetkvm.com/) is a compact IP-KVM device based on the Rockchip RV1106 (ARM Cortex-A7). For embedded devices like JetKVM, tracking their health is critical. Heartbeat monitoring lets you receive notifications if the device loses internet connectivity or stops responding. > **Info.** This guide explains how to set up JetKVM availability monitoring. If you also need remote access to the device over the internet, see the [tunnel setup guide](https://tuna.am/en/docs/tunnels/examples/jetkvm.md). *** #### Preparation ##### What you need * JetKVM with system version **v0.2.7 or newer** * **Developer Mode** enabled and SSH access configured (see the [tunnels guide](https://tuna.am/en/docs/tunnels/examples/jetkvm.md#connecting-via-ssh)) * An account at [tuna.am](https://my.tuna.am/?lang=en) ##### Installing tuna on JetKVM (optional) Heartbeat notifications can also be sent with wget — installing the tuna CLI is not mandatory, but if you also use tunnels for remote access, it makes sense to use the same client for Heartbeat notifications too. Connect to JetKVM over SSH: ```bash ssh root@ ``` Install tuna with the script, specifying `/userdata/tuna` as the install directory: ```bash mkdir -p /userdata/tuna INSECURE=true INSTALL_DIR=/userdata/tuna sh -c "$(wget -qO- http://releases.tuna.am/tuna/get.sh)" ``` > **Tip.** The `/userdata` partition is preserved across OTA updates, so tuna won't need to be reinstalled. *** #### Creating a Heartbeat monitor 1. Go to the [dashboard → Monitors](https://my.tuna.am/monitors?lang=en) 2. Click "Create monitor" 3. Choose the **Heartbeat** type 4. Set the parameters: * **Description**: JetKVM availability * **Interval**: 5 minutes (the minimum on the free plan is 30 minutes) * **Grace Period**: 1 minute (delay before the alert fires) 5. Configure notification channels 6. Save the monitor and copy the **MONITOR_ID** More on Heartbeat monitors: [Heartbeat documentation](https://tuna.am/en/docs/monitors/types/heartbeat.md) *** #### Creating a monitoring init script > **The tuna CLI is recommended.** Starting with version **0.31**, the tuna client ships with built-in CA certificates and does not depend on system ones. JetKVM has no root CA certificates, so wget can only work with `--no-check-certificate`, and the `monitor.tuna.am` API may be unreachable over HTTP. We recommend using the tuna CLI for monitoring — it is guaranteed to work over HTTPS. Create the init script `/userdata/init.d/S97tuna-heartbeat`: **tuna CLI (recommended)** ```bash cat << 'INITEOF' > /userdata/init.d/S97tuna-heartbeat #!/bin/sh PIDFILE=/tmp/tuna-heartbeat.pid wait_network() { i=0 while ! ip route | grep -q default; do sleep 1 i=$((i + 1)) if [ "$i" -ge 60 ]; then return 1 fi done return 0 } start() { ( wait_network || exit 1 while true; do /userdata/tuna/tuna monitor ping --host=jetkvm YOUR_MONITOR_ID # Interval between pings (30 minutes = 1800 seconds) sleep 1800 done ) & echo $! > "$PIDFILE" } stop() { if [ -f "$PIDFILE" ]; then kill "$(cat "$PIDFILE")" 2>/dev/null rm -f "$PIDFILE" fi } case "$1" in start) start ;; stop) stop ;; restart) stop; sleep 1; start ;; *) echo "Usage: $0 {start|stop|restart}" ;; esac INITEOF chmod +x /userdata/init.d/S97tuna-heartbeat ``` **wget** ```bash cat << 'INITEOF' > /userdata/init.d/S97tuna-heartbeat #!/bin/sh PIDFILE=/tmp/tuna-heartbeat.pid wait_network() { i=0 while ! ip route | grep -q default; do sleep 1 i=$((i + 1)) if [ "$i" -ge 60 ]; then return 1 fi done return 0 } start() { ( wait_network || exit 1 while true; do wget -q -T 5 --no-check-certificate -O /dev/null "https://monitor.tuna.am/YOUR_MONITOR_ID/jetkvm" # Interval between pings (30 minutes = 1800 seconds) sleep 1800 done ) & echo $! > "$PIDFILE" } stop() { if [ -f "$PIDFILE" ]; then kill "$(cat "$PIDFILE")" 2>/dev/null rm -f "$PIDFILE" fi } case "$1" in start) start ;; stop) stop ;; restart) stop; sleep 1; start ;; *) echo "Usage: $0 {start|stop|restart}" ;; esac INITEOF chmod +x /userdata/init.d/S97tuna-heartbeat ``` > **Important.** Replace `YOUR_MONITOR_ID` with the real monitor ID from your dashboard. **Parameter explanation:** | Parameter | Description | | ----------------- | ---------------------------------------------------------------------- | | `YOUR_MONITOR_ID` | Monitor ID from the dashboard (required) | | `--host=jetkvm` | Device identifier in monitoring logs (optional) | | `sleep 1800` | Interval between signals in seconds (must match the monitor's setting) | > **Init script format.** The script uses the same format as the tunnel scripts: `#!/bin/sh`, handling of `start`/`stop` arguments, background launch. For more on init script requirements see the [tunnels guide](https://tuna.am/en/docs/tunnels/examples/jetkvm.md#jetkvm-auto-start-system). *** #### Verifying it works Run the script manually: ```bash /userdata/init.d/S97tuna-heartbeat start ``` Make sure the process is running: ```bash ps | grep heartbeat ``` Check the monitor's status in the [dashboard](https://my.tuna.am/monitors?lang=en) — within 1-2 minutes the status should change from "New" to "Up". *** #### Managing monitoring **Stop monitoring:** ```bash /userdata/init.d/S97tuna-heartbeat stop ``` **Start monitoring:** ```bash /userdata/init.d/S97tuna-heartbeat start ``` **Fully disable autostart:** ```bash mv /userdata/init.d/S97tuna-heartbeat /userdata/init.d/disabled-tuna-heartbeat ``` **Re-enable:** ```bash mv /userdata/init.d/disabled-tuna-heartbeat /userdata/init.d/S97tuna-heartbeat ``` *** #### Recovery after an OTA update The script and binary are stored in `/userdata/`, which is preserved across OTA updates. After a firmware update monitoring will continue to work without any extra steps. After a full reflash (when `/userdata` was cleared) you will need to reinstall the binary and recreate the init script. *** #### Troubleshooting ##### The heartbeat process does not start Check that the process is in the list: ```bash ps | grep heartbeat ``` If the process is missing — check that the script exists and is executable: ```bash ls -la /userdata/init.d/S97*heartbeat* ``` Run the script manually for diagnostics: ```bash /userdata/init.d/S97tuna-heartbeat start ``` ##### The monitor shows "Down" **Reasons:** * Wrong MONITOR_ID * No internet connection * The interval in the script does not match the monitor's settings **Checks:** ```bash # Check that an IP address is assigned ip addr # Check the default route ip route # Check internet access ping -c 3 8.8.8.8 ``` ##### After a firmware update heartbeat stopped working Check that the script is still in place: ```bash ls -la /userdata/init.d/S97*heartbeat* ``` If the script is missing — it may have been removed by a full reflash. Recreate it using the instructions above. --- ### Monitoring NanoKVM availability [NanoKVM](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html) is a compact IP-KVM device from Sipeed based on RISC-V (Cube/PCIe), ARM64 (Pro) or ARMv7 (GxxKVM/JxxKVM). For embedded devices like NanoKVM, tracking their health is critical. Heartbeat monitoring lets you receive notifications if the device loses internet connectivity or stops responding. > **Info.** This guide explains how to set up NanoKVM availability monitoring. If you also need remote access to the device over the internet, see the [tunnel setup guide](https://tuna.am/en/docs/tunnels/examples/nanokvm.md). *** #### Preparation ##### What you need * NanoKVM connected to the local network and reachable over SSH or via the terminal in the web UI * An account at [tuna.am](https://my.tuna.am/?lang=en) ##### Installing tuna on NanoKVM (optional) Heartbeat notifications can also be sent with curl — installing the tuna CLI is not mandatory, but if you also use tunnels for remote access, it makes sense to use the same client for Heartbeat notifications too. Connect to NanoKVM over SSH: ```bash ssh root@ ``` Or open the terminal in the web UI: ![NanoKVM terminal](/docs/img/tunnels/guides/terminal.png) Install tuna with the script, specifying `/data/tuna` as the install directory: **curl** ```bash mkdir -p /data/tuna INSTALL_DIR=/data/tuna sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```bash mkdir -p /data/tuna INSTALL_DIR=/data/tuna sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` > **Tip.** The `/data` partition is mounted as exFAT with `fmask=0022`, files automatically get `0755` permissions — the binary can be run right after copying. In addition, `/data` is preserved across updates, so tuna won't need to be reinstalled. *** #### Creating a Heartbeat monitor 1. Go to the [dashboard → Monitors](https://my.tuna.am/monitors?lang=en) 2. Click "Create monitor" 3. Choose the **Heartbeat** type 4. Set the parameters: * **Description**: NanoKVM availability * **Interval**: 5 minutes (the minimum on the free plan is 30 minutes) * **Grace Period**: 1 minute (delay before the alert fires) 5. Configure notification channels 6. Save the monitor and copy the **MONITOR_ID** More on Heartbeat monitors: [Heartbeat documentation](https://tuna.am/en/docs/monitors/types/heartbeat.md) *** #### Creating a monitoring script Create the script `/data/tuna/tuna-heartbeat.sh`: ```bash cat << 'EOF' > /data/tuna/tuna-heartbeat.sh #!/bin/sh # Wait for the network while ! ip route | grep -q default; do sleep 1 done # Infinite heartbeat loop while true; do /data/tuna/tuna monitor ping --host=nanokvm YOUR_MONITOR_ID # Interval between pings (30 minutes = 1800 seconds) sleep 1800 done EOF chmod +x /data/tuna/tuna-heartbeat.sh ``` If you use curl, then instead of the line: `/data/tuna/tuna monitor ping --host=nanokvm YOUR_MONITOR_ID` put a curl call with the URL from the dashboard: `curl -sSLf -m 5 https://monitor.tuna.am/YOUR_MONITOR_ID/nanokvm` > **Important.** Replace `YOUR_MONITOR_ID` with the real monitor ID from your dashboard. **Parameter explanation:** | Parameter | Description | | ----------------- | ---------------------------------------------------------------------- | | `YOUR_MONITOR_ID` | Monitor ID from the dashboard (required) | | `--host=nanokvm` | Device identifier in monitoring logs (optional) | | `sleep 300` | Interval between signals in seconds (must match the monitor's setting) | *** #### Setting up autostart Add a line to `/etc/inittab` for automatic startup and restart of monitoring: ```bash cat << 'EOF' >> /etc/inittab # Tuna Heartbeat monitor (respawn = auto-restart) thb::respawn:/data/tuna/tuna-heartbeat.sh EOF ``` Apply without reboot: ```bash kill -HUP 1 ``` *** #### Verifying it works Make sure the monitoring process is running: ```bash ps -ef | grep heartbeat ``` The output should contain a line with `/data/tuna/tuna-heartbeat.sh`. If the process is missing — check `/etc/inittab`: ```bash grep heartbeat /etc/inittab ``` The line must start with `thb::respawn`, not `#`. Check the monitor's status in the [dashboard](https://my.tuna.am/monitors?lang=en) — within 1-2 minutes the status should change from "New" to "Up". *** #### Managing monitoring **Stop monitoring:** ```bash sed -i 's/^thb:/#thb:/' /etc/inittab && kill -HUP 1 ``` **Start monitoring:** ```bash sed -i 's/^#thb:/thb:/' /etc/inittab && kill -HUP 1 ``` *** #### Recovery after a reflash The binary and scripts in `/data/tuna/` are preserved when the application is updated through the web UI and during remote rsync. After a full reflash (Etcher) you will need to copy the binary again and recreate the scripts. For remote rsync you only need to restore the lines in `/etc/inittab`: ```bash cat << 'EOF' >> /etc/inittab # Tuna Heartbeat monitor (respawn = auto-restart) thb::respawn:/data/tuna/tuna-heartbeat.sh EOF kill -HUP 1 ``` *** #### Why not cron? NanoKVM has a [known cron issue](https://github.com/sipeed/NanoKVM/issues/211): * Crontabs do not exist by default * Crontabs that you create do not survive a reboot * There is no system-wide `/etc/crontab` That's why for periodic tasks on NanoKVM it is recommended to use `inittab` with an infinite loop — this approach is reliable and matches the architecture of the system. > **Alternative for other systems.** On regular Linux systems (Debian, Ubuntu) it is preferable to use cron or systemd timers for heartbeat monitoring. See [examples in the Heartbeat documentation](https://tuna.am/en/docs/monitors/types/heartbeat.md). *** #### Troubleshooting ##### The heartbeat process does not start Check that the process is in the list: ```bash ps -ef | grep heartbeat ``` If the output has no lines with `/data/tuna/tuna-heartbeat.sh` — make sure the entry in `/etc/inittab` is not commented out: ```bash grep heartbeat /etc/inittab ``` The line must start with `thb::respawn`, not `#`. If it is commented out — uncomment it and reload the configuration: ```bash kill -HUP 1 ``` ##### The monitor shows "Down" **Reasons:** * Wrong MONITOR_ID * No internet connection * The interval in the script does not match the monitor's settings **Checks:** ```bash # Check that an IP address is assigned ip addr # Check the default route ip route # Check internet access ping -c 3 8.8.8.8 ``` ##### After a firmware update heartbeat stopped working Check whether the firmware was updated — after remote rsync you need to restore the entries in `/etc/inittab` (see [Recovery after a reflash](#recovery-after-a-reflash)) *** --- ### Status pages [Open dashboard](https://my.tuna.am/monitors/status-pages?lang=en) Public pages for keeping users informed about the state of your services. ![Example status page with service statuses](/docs/img/monitors/status_pages/main.png) *** #### Overview A **status page** brings selected monitors together into a single public page with availability history and the current status of each service. It is convenient for showing customers and your team what is working, what is temporarily unavailable and what the uptime of your infrastructure is. > **Why status pages** > > * Inform customers about service availability > * Show availability (uptime) history with a clear scale > * Group monitors by logic (Dashboard, API, Databases, etc.) > * Protect the page with a password for internal use > * Brand the page for your project — logo, favicon, links > * Host it on your own subdomain `*.tuna-status.am` #### List of status pages All your pages are available in the [Status pages](https://my.tuna.am/monitors/status-pages?lang=en) section. Here you can see the name, alias (subdomain) and the number of attached monitors. ![List of status pages in the dashboard](/docs/img/monitors/status_pages/list.png) #### Creating a status page 1. Go to the [Status pages](https://my.tuna.am/monitors/status-pages?lang=en) section. 2. Click **+ Create status page**. 3. Fill in the basic settings and select monitors — see details below. 4. Save the page — it will be immediately available at `.tuna-status.am`. *** #### Settings ##### Basic * **Name** — shown in the page header and the browser tab title. * **Alias** — defines the page address in the format `.tuna-status.am`. * **Description** — short text under the title (optional). ![Basic status page settings: name, alias, description, access, links](/docs/img/monitors/status_pages/settings_1.png) ##### Access * **Set password** — protects the page with authorization. Useful for internal dashboards or private clients. * **Hide from search engines** — adds a block for search bots so the page is not indexed. ##### Links * **Website URL** — link to the main website of the service. Shown on the status page. * **Support URL** — link to the support form or portal where users can reach out during an incident. ##### Branding Tailor the look of the page to your corporate style: * **Logo** — image in the header (PNG, JPG or SVG). * **Logo link** — where the user goes when clicking the logo (usually the site home page). * **Favicon** — browser tab icon (PNG, ICO or SVG). ![Branding settings: logo, logo link, favicon, history period, color theme](/docs/img/monitors/status_pages/settings_2.png) ##### Display * **History period** — for what period to show availability history (for example, 7 days). * **Color theme** — `Auto` (follows the user's system), light or dark. ##### Monitors Add monitors and group them into sections — each group gets its own heading on the page. Inside a group, monitors can be renamed (what the user sees may differ from the internal name in Tuna). ![Monitor groups on the status page: Dashboard, Tunnels, Meets](/docs/img/monitors/status_pages/settings_3.png) > **Tip.** Groups and monitors inside them can be dragged — the order on the status page matches the order in the settings. *** #### How the page looks Here you can see an example of a branded status page in light and dark themes. ##### Light theme ![Branded status page in light theme](/docs/img/monitors/status_pages/branding_d.png) ##### Dark theme ![Branded status page in dark theme](/docs/img/monitors/status_pages/branding_n.png) In the lower right corner the user can manually switch the theme and the interface language. --- ### Monitor types Tuna supports 6 monitor types for different use cases. *** #### User-driven monitors These monitors wait for signals from your applications and scripts: * [**Job (CronJob)**](https://tuna.am/en/docs/monitors/types/job.md) — monitor execution of periodic jobs * [**Heartbeat**](https://tuna.am/en/docs/monitors/types/heartbeat.md) — monitor regular signals from services *** #### External monitors These monitors check the availability of your resources on their own: * [**HTTP**](https://tuna.am/en/docs/monitors/types/http.md) — check availability of websites and APIs * [**TCP**](https://tuna.am/en/docs/monitors/types/tcp.md) — check availability of TCP services * [**DNS**](https://tuna.am/en/docs/monitors/types/dns.md) — check DNS records * [**ICMP (Ping)**](https://tuna.am/en/docs/monitors/types/icmp.md) — check network reachability *** #### [📄️Job](https://tuna.am/en/docs/monitors/types/job.md) [Monitoring execution of periodic scheduled jobs](https://tuna.am/en/docs/monitors/types/job.md) #### [📄️Heartbeat](https://tuna.am/en/docs/monitors/types/heartbeat.md) [Monitoring regular signals from services and applications](https://tuna.am/en/docs/monitors/types/heartbeat.md) #### [📄️HTTP](https://tuna.am/en/docs/monitors/types/http.md) [Monitoring availability of websites and APIs](https://tuna.am/en/docs/monitors/types/http.md) #### [📄️TCP](https://tuna.am/en/docs/monitors/types/tcp.md) [Monitoring availability of TCP services and ports](https://tuna.am/en/docs/monitors/types/tcp.md) #### [📄️DNS](https://tuna.am/en/docs/monitors/types/dns.md) [Monitoring DNS records and domain resolution](https://tuna.am/en/docs/monitors/types/dns.md) #### [📄️ICMP (Ping)](https://tuna.am/en/docs/monitors/types/icmp.md) [Monitoring network reachability of hosts via ICMP](https://tuna.am/en/docs/monitors/types/icmp.md) --- ### DNS [Open dashboard](https://my.tuna.am/monitors?lang=en) The DNS monitor checks the correctness of DNS records and domain resolution. *** #### How it works 1. The system performs a DNS query for the specified domain 2. Checks the presence and value of DNS records 3. If the conditions are not met — a notification is sent *** #### Parameters ##### Description | Parameter | Description | Required | | --------------- | ----------------------------- | -------- | | **Name** | Unique monitor name | Yes | | **Description** | A note about the task purpose | No | ![screenshot with monitor description](/docs/img/monitors/description.png) ##### Details | Parameter | Description | Default | | ----------- | -------------------------- | ---------- | | **Domain** | IP address or domain | - | | **Timeout** | Maximum response wait time | 10 seconds | | **Retries** | Number of retries on error | 3 | ![screenshot with details of the DNS monitor](/docs/img/monitors/details_dns.png) ##### Checks The DNS monitor supports various response checks: ![screenshot with check parameters of the DNS monitor](/docs/img/monitors/check_dns.png) > **Tip.** For **MX** records the metric is specified through a colon, as in the screenshot above. ##### Regions Choose one or more regions for the check. The monitor will send requests from each selected region at the specified interval. | Parameter | Description | Default | | ------------ | --------------- | --------- | | **Interval** | Check frequency | 5 minutes | ![screenshot with monitor region](/docs/img/monitors/region.png) ##### Notifications Here you can specify contact groups for notifications. If left unspecified, the user's email is used. You can also set a waiting period — **Grace Period**. It is added to the expected time of the next signal. For example, if the interval is 5 minutes and the Grace Period is 2 minutes, the alert will be sent 7 minutes after the last signal. Repeated notifications remind you of the problem; you can set the interval and number of these reminders. ![screenshot with monitor job details](/docs/img/monitors/notify.png) *** #### DNS record types The DNS monitor supports checking the following record types: | Type | Description | Example value | | --------- | ------------------------------------- | ------------------------------------- | | **A** | IPv4 address | `93.184.216.34` | | **AAAA** | IPv6 address | `2606:2800:220:1:248:1893:25c8:1946` | | **CNAME** | Canonical name (alias) | `www.example.com` | | **MX** | Mail server | `mail.example.com.:10` | | **TXT** | Text record | `v=spf1 include:_spf.google.com ~all` | | **NS** | Name server | `ns1.example.com` | | **SOA** | Start of Authority | — | | **PTR** | Pointer (reverse DNS) | `example.com` | | **SRV** | Service record | `0 5 5269 xmpp.example.com` | | **CAA** | Certification Authority Authorization | `0 issue "letsencrypt.org"` | --- ### Heartbeat [Open dashboard](https://my.tuna.am/monitors?lang=en) The Heartbeat monitor is designed to track regular signals from services running in closed networks, IoT devices or background processes. *** #### How it works 1. You create a Heartbeat monitor and set the expected signal interval 2. Your application periodically sends a `ping` signal 3. If no signal arrives within the configured time — a notification is sent *** #### Parameters ##### Description | Parameter | Description | Required | | --------------- | ----------------------------- | -------- | | **Name** | Unique monitor name | Yes | | **Description** | A note about the task purpose | No | ![screenshot with monitor description](/docs/img/monitors/description.png) ##### Details Specify the expected interval between jobs and optionally the maximum execution time. ![screenshot with details of the heartbeat monitor](/docs/img/monitors/details_heartbeat.png) ![screenshot with details of the heartbeat monitor](/docs/img/monitors/cron_heartbeat.png) ###### Interval Two types of schedule are supported: **Interval** Expected interval between signals: | Value | Description | | ---------- | -------------------------------- | | 1 minute | Signal expected every minute | | 5 minutes | Signal expected every 5 minutes | | 15 minutes | Signal expected every 15 minutes | | 30 minutes | Signal expected every 30 minutes | | 60 minutes | Signal expected every hour | **Cron expression** For irregular schedules use a [Cron expression](https://en.wikipedia.org/wiki/Cron): ```shell ┌───────────── minute (0 - 59) │ ┌───────────── hour (0 - 23) │ │ ┌───────────── day of month (1 - 31) │ │ │ ┌───────────── month (1 - 12) │ │ │ │ ┌───────────── day of week (0 - 6) │ │ │ │ │ * * * * * ``` **Examples:** | Expression | Description | | ---------------- | ----------------------------------- | | `*/5 * * * *` | Every 5 minutes | | `0 * * * *` | Every hour | | `0 9-18 * * 1-5` | Every hour from 9 to 18 on weekdays | ##### Notifications The Heartbeat monitor sends notifications in the following cases: | Reason | Description | | --------------- | --------------------------------------------- | | **Not Running** | The job did not start within the Grace Period | | **Recovery** | The job recovered after an error | Here you can specify contact groups for notifications. If left unspecified, the user's email is used. You can also set a waiting period — **Grace Period**. It is added to the expected time of the next signal. For example, if the interval is 5 minutes and the Grace Period is 2 minutes, the alert will be sent 7 minutes after the last signal. Repeated notifications remind you of the problem; you can set the interval and number of these reminders. ![screenshot with monitor job details](/docs/img/monitors/notify.png) *** #### Usage ##### Through the CLI ```bash # Simple ping tuna monitor ping MONITOR_ID # With explicit host (useful for identifying the source) tuna monitor ping MONITOR_ID --host=server-01 # With a message tuna monitor ping MONITOR_ID --message="All systems operational" ``` ##### Via HTTP In addition to the tuna CLI, you can send signals directly via HTTP requests. This is useful when the CLI is unavailable or when you use other programming languages. **URL format:** ```shell https://monitor.tuna.am/{MONITOR_ID}/{slug-name} ``` > **Tip.** `{slug-name}` is optional and is mainly used for easy identification of the monitor in code. **curl** ```bash # Simple ping curl -s "https://monitor.tuna.am/MONITOR_ID/test" # With explicit status curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=ping" curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=start" curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=finish" curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=fail" # With a message (proper UTF-8 encoding) curl -G "https://monitor.tuna.am/MONITOR_ID/test" \ --data-urlencode "message=Backup completed successfully" # With host and message curl -G "https://monitor.tuna.am/MONITOR_ID/test?host=server-01" \ --data-urlencode "message=All systems operational" ``` **wget** ```bash # Simple ping wget -qO- "https://monitor.tuna.am/MONITOR_ID/test" # With parameters wget -qO- "https://monitor.tuna.am/MONITOR_ID/test?state=ping&host=server-01" ``` **Python** ```python import requests from urllib.parse import urlencode MONITOR_ID = "your_monitor_id" BASE_URL = f"https://monitor.tuna.am/{ENDPOINT_TOKEN}" # Simple ping requests.get(BASE_URL) # With parameters params = { "state": "ping", "host": "server-01", "message": "Task completed successfully" } requests.get(BASE_URL, params=params) ``` **PowerShell** ```powershell # Simple ping Invoke-WebRequest -Uri "https://monitor.tuna.am/MONITOR_ID/test" -Method GET # With parameters $params = @{ state = "ping" host = "server-01" message = "Backup completed" } $uri = "https://monitor.tuna.am/MONITOR_ID/test" Invoke-WebRequest -Uri $uri -Method GET -Body $params ``` ###### Request parameters | Parameter | Description | | --------- | -------------------------------------------------------- | | `state` | Signal status: `ping`, `start`, `finish`, `fail` | | `host` | Host identifier (useful when there are multiple sources) | | `message` | Arbitrary message | ##### In a systemd service ```ini # /etc/systemd/system/my-heartbeat.service [Unit] Description=Heartbeat ping to Tuna After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/tuna monitor ping MONITOR_ID [Install] WantedBy=multi-user.target ``` ```ini # /etc/systemd/system/my-heartbeat.timer [Unit] Description=Run heartbeat every 5 minutes [Timer] OnBootSec=1min OnUnitActiveSec=5min [Install] WantedBy=timers.target ``` ```bash systemctl enable --now my-heartbeat.timer ``` ##### In crontab ```bash # Heartbeat every 5 minutes */5 * * * * /usr/local/bin/tuna monitor ping MONITOR_ID ``` *** #### Statuses | Status | Description | | -------- | ------------------------------------------ | | **New** | Monitor created, awaiting the first signal | | **Up** | Signals are arriving regularly | | **Down** | The signal did not arrive in time | *** #### Examples ##### Monitoring an office GitLab Runner ```bash # On the GitLab Runner machine */5 * * * * /usr/local/bin/tuna monitor ping MONITOR_ID --host=gitLab-runner-0 ``` ##### Monitoring a VPN connection ```bash #!/bin/bash # Check the VPN channel and send a heartbeat only if the connection is up ping -c 1 192.168.14.88 && tuna monitor ping MONITOR_ID --message="VPN is up" ``` --- ### HTTP [Open dashboard](https://my.tuna.am/monitors?lang=en) The HTTP monitor checks availability of websites and APIs by sending HTTP requests from multiple geographic regions. *** #### How it works 1. The system sends an HTTP request to the specified URL from the selected regions 2. You can configure the method and check the response code, headers and body 3. If the conditions are not met — a notification is sent *** #### Parameters ##### Description | Parameter | Description | Required | | --------------- | ----------------------------- | -------- | | **Name** | Unique monitor name | Yes | | **Description** | A note about the task purpose | No | ![screenshot with monitor description](/docs/img/monitors/description.png) ##### Details | Parameter | Description | Default | | -------------------- | --------------------------------- | ---------- | | **Method** | HTTP request method | GET | | **URL** | URL of the resource being checked | - | | **Headers** | Additional HTTP headers | — | | **Timeout** | Maximum response wait time | 10 seconds | | **Retries** | Number of retries on error | 3 | | **Follow redirects** | Whether to follow redirects (3xx) | Yes | **Supported methods:** * GET * POST * PUT * PATCH * DELETE * HEAD * OPTIONS * TRACE ![screenshot with details of the HTTP monitor](/docs/img/monitors/details_http.png) ##### Checks The HTTP monitor supports various response checks: ![screenshot with check parameters of the HTTP monitor](/docs/img/monitors/check_http.png) ##### Regions Choose one or more regions for the check. The monitor will send requests from each selected region at the specified interval. | Parameter | Description | Default | | ------------ | --------------- | --------- | | **Interval** | Check frequency | 5 minutes | ![screenshot with monitor region](/docs/img/monitors/region.png) ##### Notifications Here you can specify contact groups for notifications. If left unspecified, the user's email is used. You can also set a waiting period — **Grace Period**. It is added to the expected time of the next signal. For example, if the interval is 5 minutes and the Grace Period is 2 minutes, the alert will be sent 7 minutes after the last signal. Repeated notifications remind you of the problem; you can set the interval and number of these reminders. ![screenshot with monitor job details](/docs/img/monitors/notify.png) *** #### SSL/TLS and Whois monitoring The HTTP monitor automatically checks SSL certificates for HTTPS URLs and shows information about them, as well as the domain registrar and the expiration date. ![screenshot with the SSL certificate of the HTTP monitor](/docs/img/monitors/tls.png) --- ### ICMP (Ping) [Open dashboard](https://my.tuna.am/monitors?lang=en) The ICMP monitor checks network reachability of hosts using ICMP Echo Request (ping). *** #### How it works 1. The system sends an ICMP Echo Request to the specified host 2. Waits for an ICMP Echo Reply 3. If there is no reply — a notification is sent *** #### Parameters ##### Description | Parameter | Description | Required | | --------------- | ----------------------------- | -------- | | **Name** | Unique monitor name | Yes | | **Description** | A note about the task purpose | No | ![screenshot with monitor description](/docs/img/monitors/description.png) ##### Details | Parameter | Description | Default | | ----------- | -------------------------- | ---------- | | **Host** | IP address or domain | - | | **Timeout** | Maximum response wait time | 10 seconds | | **Retries** | Number of retries on error | 3 | ![screenshot with details of the ICMP monitor](/docs/img/monitors/details_icmp.png) ##### Regions Choose one or more regions for the check. The monitor will send requests from each selected region at the specified interval. | Parameter | Description | Default | | ------------ | --------------- | --------- | | **Interval** | Check frequency | 5 minutes | ![screenshot with monitor region](/docs/img/monitors/region.png) ##### Notifications Here you can specify contact groups for notifications. If left unspecified, the user's email is used. You can also set a waiting period — **Grace Period**. It is added to the expected time of the next signal. For example, if the interval is 5 minutes and the Grace Period is 2 minutes, the alert will be sent 7 minutes after the last signal. Repeated notifications remind you of the problem; you can set the interval and number of these reminders. ![screenshot with monitor job details](/docs/img/monitors/notify.png) *** #### Limitations * Some hosts block ICMP traffic at the firewall level * ICMP does not check service health, only network reachability * To check specific services use [TCP](https://tuna.am/en/docs/monitors/types/tcp.md) or [HTTP](https://tuna.am/en/docs/monitors/types/http.md) monitors *** --- ### Job [Open dashboard](https://my.tuna.am/monitors?lang=en) The Job monitor is designed to track execution of periodic jobs: backup scripts, cron jobs and other scheduled operations. *** #### How it works 1. You create a Job monitor and define the execution schedule 2. When the job starts, the `start` signal is sent 3. On successful completion the `finish` signal is sent 4. On error the `fail` signal is sent 5. The system tracks both timely starts and execution time *** #### Parameters ##### Description | Parameter | Description | Required | | --------------- | ----------------------------- | -------- | | **Name** | Unique monitor name | Yes | | **Description** | A note about the task purpose | No | ![screenshot with monitor description](/docs/img/monitors/description.png) ##### Details Specify the expected interval between jobs and optionally the maximum execution time. ![screenshot with details of the job monitor](/docs/img/monitors/details_job.png) ![screenshot with details of the job monitor](/docs/img/monitors/cron_job.png) ###### Interval Two types of schedule are supported: **Interval** Expected interval between signals: | Value | Description | | ---------- | -------------------------------- | | 1 minute | Signal expected every minute | | 5 minutes | Signal expected every 5 minutes | | 15 minutes | Signal expected every 15 minutes | | 30 minutes | Signal expected every 30 minutes | | 60 minutes | Signal expected every hour | **Cron expression** For irregular schedules use a [Cron expression](https://en.wikipedia.org/wiki/Cron): ```shell ┌───────────── minute (0 - 59) │ ┌───────────── hour (0 - 23) │ │ ┌───────────── day of month (1 - 31) │ │ │ ┌───────────── month (1 - 12) │ │ │ │ ┌───────────── day of week (0 - 6) │ │ │ │ │ * * * * * ``` **Examples:** | Expression | Description | | ---------------- | ----------------------------------- | | `*/5 * * * *` | Every 5 minutes | | `0 * * * *` | Every hour | | `0 9-18 * * 1-5` | Every hour from 9 to 18 on weekdays | ###### Execution time The maximum job duration **Running Time** — if the job runs longer than the specified time, a notification is sent. ##### Notifications The Job monitor sends notifications in the following cases: | Reason | Description | | ---------------- | ---------------------------------------------------------------- | | **Fail** | The job finished with an error (`fail` signal or exit code != 0) | | **Not Running** | The job did not start within the Grace Period | | **Long Running** | The job runs longer than the Running Time | | **Recovery** | The job recovered after an error | Here you can specify contact groups for notifications. If left unspecified, the user's email is used. You can also set a waiting period — **Grace Period**. It is added to the expected time of the next signal. For example, if the interval is 5 minutes and the Grace Period is 2 minutes, the alert will be sent 7 minutes after the last signal. Repeated notifications remind you of the problem; you can set the interval and number of these reminders. ![screenshot with monitor job details](/docs/img/monitors/notify.png) *** #### Usage ##### Through the CLI (recommended) The `tuna monitor exec` command automatically sends `start`, `finish` or `fail` signals: ```bash tuna monitor exec MONITOR_ID -- ./backup.sh ``` If you want to control the signals manually: ```bash # Start of execution tuna monitor ping MONITOR_ID --state=start # Run your job ./your-task.sh # Successful completion tuna monitor ping MONITOR_ID --state=finish # Or error tuna monitor ping MONITOR_ID --state=fail ``` ##### Via HTTP In addition to the tuna CLI, you can send signals directly via HTTP requests. This is useful when the CLI is unavailable or when you use other programming languages. **URL format:** ```shell https://monitor.tuna.am/{MONITOR_ID}/{slug-name} ``` > **Tip.** `{slug-name}` is optional and is mainly used for easy identification of the monitor in code. **curl** ```bash # Simple ping curl -s "https://monitor.tuna.am/MONITOR_ID/test" # With explicit status curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=ping" curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=start" curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=finish" curl -s "https://monitor.tuna.am/MONITOR_ID/test?state=fail" # With a message (proper UTF-8 encoding) curl -G "https://monitor.tuna.am/MONITOR_ID/test" \ --data-urlencode "message=Backup completed successfully" # With host and message curl -G "https://monitor.tuna.am/MONITOR_ID/test?host=server-01" \ --data-urlencode "message=All systems operational" ``` **wget** ```bash # Simple ping wget -qO- "https://monitor.tuna.am/MONITOR_ID/test" # With parameters wget -qO- "https://monitor.tuna.am/MONITOR_ID/test?state=ping&host=server-01" ``` **Python** ```python import requests from urllib.parse import urlencode MONITOR_ID = "your_monitor_id" BASE_URL = f"https://monitor.tuna.am/{ENDPOINT_TOKEN}" # Simple ping requests.get(BASE_URL) # With parameters params = { "state": "ping", "host": "server-01", "message": "Task completed successfully" } requests.get(BASE_URL, params=params) ``` **PowerShell** ```powershell # Simple ping Invoke-WebRequest -Uri "https://monitor.tuna.am/MONITOR_ID/test" -Method GET # With parameters $params = @{ state = "ping" host = "server-01" message = "Backup completed" } $uri = "https://monitor.tuna.am/MONITOR_ID/test" Invoke-WebRequest -Uri $uri -Method GET -Body $params ``` ###### Request parameters | Parameter | Description | | --------- | -------------------------------------------------------- | | `state` | Signal status: `ping`, `start`, `finish`, `fail` | | `host` | Host identifier (useful when there are multiple sources) | | `message` | Arbitrary message | See more: [Working with monitors in the CLI](https://tuna.am/en/docs/monitors/cli.md) *** #### Statuses | Status | Description | | ----------- | ---------------------------------------------------------------- | | **New** | Monitor created, awaiting the first run | | **Running** | The job is running (the `start` signal was received) | | **Up** | The job finished successfully (the `finish` signal was received) | | **Down** | The job finished with an error or did not start in time | *** #### Logging When `tuna monitor exec` is used, the system automatically captures stdout and stderr of your job. The logs can be viewed in the UI in the monitor's event history. ![screenshot with the message field content](/docs/img/monitors/event.png) To disable sending logs use the `--no-stdout` flag: ```bash tuna monitor exec MONITOR_ID --no-stdout -- ./task.sh ``` *** #### Examples ##### PostgreSQL backup ```bash #!/bin/bash # /scripts/backup-postgres.sh tuna monitor exec MONITOR_ID -- pg_dump -U postgres mydb > /backups/mydb_$(date +%Y%m%d).sql ``` ##### Data synchronization ```bash #!/bin/bash # /scripts/sync-data.sh tuna monitor exec MONITOR_ID -- rsync -avz /data/ remote:/backup/data/ ``` ##### Crontab ```bash # Database backup every night at 3:00 0 3 * * * tuna monitor exec MONITOR_ID -- /scripts/backup-db.sh # Log cleanup every hour 0 * * * * tuna monitor exec MONITOR_ID -- /scripts/cleanup-logs.sh ``` --- ### TCP [Open dashboard](https://my.tuna.am/monitors?lang=en) The TCP monitor checks the availability of services over a TCP connection: databases, mail servers, game servers and other TCP applications. *** #### How it works 1. The system establishes a TCP connection to the specified host and port 2. If the connection succeeds — the service is considered available 3. On a connection error — a notification is sent *** #### Parameters ##### Description | Parameter | Description | Required | | --------------- | ----------------------------- | -------- | | **Name** | Unique monitor name | Yes | | **Description** | A note about the task purpose | No | ![screenshot with monitor description](/docs/img/monitors/description.png) ##### Details | Parameter | Description | Default | | ------------ | ---------------------------- | ---------- | | **Resource** | `host:port` | - | | **Timeout** | Maximum connection wait time | 10 seconds | | **Retries** | Number of retries on error | 3 | ![screenshot with details of the TCP monitor](/docs/img/monitors/details_tcp.png) ##### Regions Choose one or more regions for the check. The monitor will send requests from each selected region at the specified interval. | Parameter | Description | Default | | ------------ | --------------- | --------- | | **Interval** | Check frequency | 5 minutes | ![screenshot with monitor region](/docs/img/monitors/region.png) ##### Notifications Here you can specify contact groups for notifications. If left unspecified, the user's email is used. You can also set a waiting period — **Grace Period**. It is added to the expected time of the next signal. For example, if the interval is 5 minutes and the Grace Period is 2 minutes, the alert will be sent 7 minutes after the last signal. Repeated notifications remind you of the problem; you can set the interval and number of these reminders. ![screenshot with monitor job details](/docs/img/monitors/notify.png) *** --- ### Password Manager This section describes the features and functions of the [Password Manager](https://my.tuna.am/passwords?lang=en). ![passwords-main](/en/assets/images/main-dacca5895830a1cf52a5767b574b095a.png) #### Overview Tuna Password Manager is designed for secure storage and convenient sharing of passwords and other types of secrets for developers, administrators, their teams, and more. We do not store passwords in plain text. According to the [zero-knowledge](https://en.wikipedia.org/wiki/Zero-knowledge_proof) model, **only** the user possesses the keys that can decrypt the data. > **Info.** Currently, the [Password Manager](https://my.tuna.am/passwords?lang=en) is a free feature available to all users and teams without restrictions. You can store passwords, SSH keys, licenses, notes, database credentials, and other types of secrets. Create shared folders with secrets within your team and share them with anyone outside the Tuna platform. All features are described in detail in a [separate section](https://tuna.am/en/docs/passwords/usage.md). #### Motivation Why did we build the **Password Manager** into Tuna? We noticed that many development and administration teams lack a culture of secure password and secret storage. At the same time, everyone needs to store infrastructure passwords, SSH keys, office Wi-Fi credentials, and so on. But there are not always accessible tools for a secure and convenient way to share them with teams, clients, auditors, or just friends. Then people start sharing passwords in plain text in chats, emails, and notes. Our goal is to improve the culture of secret storage among our users and protect clients from leaks and unauthorized access to their infrastructure. That's why the **Password Manager** is a basic and free feature for everyone. #### Architecture When creating a vault, the browser generates **Private** and **Public** RSA keys, as well as a **Master Key** (Passphrase in OpenSSL terminology) that encrypts the **Private Key**. Only the user has the **Master Key**. We recommend storing it in a safe place (preferably written on paper) and never sharing it with anyone under any circumstances. If you lose your **Master Key**, it will be impossible to recover access to your passwords. The **Private** and **Public** keys are stored on the Tuna side in encrypted form. The passwords themselves are encrypted with the **Private Key**. ![schema](/en/assets/images/schema-464332b4967897a522780f7dd4631521.png) The algorithm for reading an encrypted record is as follows: 1. The user logs into their account at [my.tuna.am](https://my.tuna.am/?lang=en) 2. Goes to the [Password Manager](https://my.tuna.am/passwords?lang=en) tab 3. The user receives a PIN code request and their encrypted private key 4. The user enters the PIN code, after which the master key is decrypted and verified, then the private key is decrypted and verified 5. The user receives a list of folders and a list of records 6. The user opens a record, at which point the record is decrypted using the private key When editing or creating a new record, the data is first encrypted using the private key in the browser, then sent to the server - this guarantees that we cannot decrypt records stored on Tuna servers and will not be able to restore access to them if the master key is lost. > **Attention.** **ONLY** the owner has access to their password vault. If a user loses their **Master Key**, it is **impossible** to recover access to passwords! You see the **Master Key** only once when creating the **Master Key** and vault. We recommend storing the **Master Key** in a safe place (preferably written on paper) and never sharing it with anyone under any circumstances. #### Navigation #### [📄️Creating a Vault](https://tuna.am/en/docs/passwords/init.md) [Initializing the password vault and Master Key](https://tuna.am/en/docs/passwords/init.md) #### [📄️Features Overview](https://tuna.am/en/docs/passwords/usage.md) [Overview of features and functions](https://tuna.am/en/docs/passwords/usage.md) #### [📄️Access Recovery](https://tuna.am/en/docs/passwords/restore.md) [Recovering access when the Master Key is lost](https://tuna.am/en/docs/passwords/restore.md) --- ### Creating a Vault Click **Create Master Key**, then copy it and save it in a safe place (preferably write it on paper) and never share it with anyone under any circumstances. > **Danger.** If you lose your **Master Key**, it will be impossible to recover access to your passwords. ![init](/en/assets/images/init-a7974ec60b970a223370ec231a2ce0c9.png) For convenient work with the vault in the current browser session, enter a short PIN code - any string of 4 or more characters. The PIN code is unique only within the session; you can use different PIN codes in different browsers or on different devices. The Master Key will be encrypted with this PIN + a unique [salt](https://en.wikipedia.org/wiki/Salt_\(cryptography\)) of the current session. The Master Key encrypted in this way will be saved in the browser's [localStorage](https://www.w3schools.com/html/html5_webstorage.asp), and you won't need to enter the Master Key every time during the entire session lifetime. ![unlock](/en/assets/images/unlock-b9592161c0cbb8f02402245b60cff0ef.png) Access to passwords will be automatically locked after 5 seconds when navigating to any other section of the Tuna website, another browser tab, or program window. When returning to the Password Manager section, you will need to enter the previously entered PIN. If you forgot your PIN, you can reset it, re-enter the Master Key, and set a new PIN. When logging out of your account (closing the session), all data from localStorage is deleted. ![list](/en/assets/images/list-031871c5856889fe788459d8d9e183c8.png) After initialization, the **Master Key** will also be saved inside the vault as a password in the **My Passwords** folder. This folder differs from others in that it cannot be shared with other team members. --- ### Access Recovery > **Danger.** If you lose your **Master Key**, it will be impossible to recover access to your passwords. If a user has lost their **Master Key**: * If you had multiple sessions open in different browsers or devices and you remember the PIN from at least one session, you can open the **Password Manager** on the authorized device and view the **Master Key** saved in the **My Passwords** folder. You can view the list of active sessions in your user [profile](https://my.tuna.am/profile?lang=en). Click **Close all sessions** to see all active sessions. **Do not click** *Close sessions*, just look at what other authorized devices exist. ![session-close](/en/assets/images/session-close-98c7f1430587e089fdece50587990e80.png) ![session-show](/en/assets/images/session-show-e30f44a233001f6263c1d0d3f2506618.png) * In a [team](https://tuna.am/en/docs/team/passwords.md), you can delete the vault with all passwords yourself (this is available to **Founders** and **Owners**), and the user will be able to create it again. * For a personal account, you need to contact support at from the registered email address. Administrators will delete your vault with all passwords so you can create it again. --- ### Features Overview #### Secret Types You can create several different types of secrets. ![pwd-types](/en/assets/images/pwd-types-2d6a18327bbc102893246e45dd5e23fa.png) * Password * Note * Email * SSH Key * API Key * Database * License * WiFi Router #### Folders and Team Sharing You can create folders and group passwords by purpose or access level. For example, you can create a separate folder with Production and Testing infrastructure access containing SSH and API keys and database credentials. Or separate folders for DevOps, Backend, Frontend departments and add the corresponding people to the folders. Within a [team](https://tuna.am/en/docs/team.md), you can share these folders with other members. They will be able to view, edit, and add new passwords in this folder. ![dir-share](/en/assets/images/dir-share-5876f952a07bb0b4177f9ef7c4a87891.png) #### Share Externally To share a password with anyone outside the team, select the desired password and click the **share** icon at the bottom right. Then you will see a menu with settings for how long the link will work, and you can also limit it to a one-time password view. > **Danger.** Never publish a password with the **Master Key** ![](/docs/img/passwords/pwd-share-external-1.png) ![dir-share](/en/assets/images/pwd-share-external-2-8a82b021cd907354192a247d682d3030.png) ![dir-share](/en/assets/images/pwd-share-external-4-bb36935af49d4a82a33de92ac2dee3bf.png) --- ### Reports [Go to personal account](https://my.tuna.am/reports?lang=en) Functionality for collecting errors and creating comprehensive reports on your website. ![Image with report example](/docs/img/reports/main.png) *** #### Overview Using an embeddable script, users and QA engineers can easily show where the bug is hiding, and you can get debug information and immediately start fixing it. ![Setup](/docs/img/reports/setup.png) To do this, create a project and connect the script to your site. #### Service structure Inside the **Reports** service there are **Projects**, it is assumed that you create a separate project for each of your sites. Projects are separated by a unique `captureKey`. Inside projects, reports you create with screenshots and video recordings will appear. #### Installation Place the code on the HTML page before the closing `` tag: ```html ``` Replace **YOUR_CAPTURE_KEY** with yours from the desired project. #### Script options Options are set in the `window.reportOptions` object before the script is loaded. | Option | Type | Default | Description | | -------------------- | ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `captureKey` | `string` | — | Project key. Required. | | `video` | `boolean` | `true` | Video recording. See [below](#video). | | `sensitiveUrlParams` | `string[]` | `[]` | Additional URL parameters whose values are masked. See [Data privacy](https://tuna.am/en/docs/reports/privacyOptions.md#adding-your-own-parameters-sensitiveurlparams). | | `sensitiveHeaders` | `string[]` | `[]` | Additional HTTP headers whose values are masked. See [Data privacy](https://tuna.am/en/docs/reports/privacyOptions.md#adding-your-own-headers-sensitiveheaders). | ##### Video recording By default the user chooses what to attach to a report: a screenshot or a screen recording. The recording is kept in the page's memory, so on multi-page sites it is cut off when the user navigates to another page. For such sites video can be turned off: ```html ``` Only an explicit `video: false` turns video off. Without the option, or with any other value, recording stays enabled. With `video: false` the capture type dialog is not shown: a [button](#button-click), a [hotkey](#hotkeys) or a `showCaptureModal()` call from a click handler takes a screenshot right away. > **Exception: a link with ?report=true.** With the [URL argument](#url-argument) the dialog opens without a user action, and without one the browser will not start screen capture. So in this case a dialog with a single **Screenshot** card is shown. In your personal account, the project setup page has a **Video recording** checkbox — it changes the `video` value in the snippet you copy to your site. #### Calling the script to create a report There are several ways to create a report: * Hotkeys * Button (data attribute) * Call from code (JS) * URL argument ##### Hotkeys **Windows** > **Use the key combination.** `CTRL` + `ALT` + `S` **macOS** > **Use the key combination.** `⌘ command` + `⌥ option` + `S` **Linux** > **Use the key combination.** `CTRL` + `ALT` + `S` ##### Button click Call the report creation form by adding a button with a data attribute: ```html ``` ##### Call from code Call the report creation form from your code by adding the command: ```js if (typeof window.autoCapture !== 'undefined' && window.autoCapture.showCaptureModal) { window.autoCapture.showCaptureModal(); } ``` ##### URL argument Call the report creation form by adding the argument `report=true` to the URL in the address bar. ```url https://example.com/dashboard?report=true ``` #### User identification If the user is signed in on your site, pass their data to the script — the report will be linked to that user. The data is stored in the report as the reporter and is shown in the **Context** block on the report page. ```js if (window.autoCapture && window.autoCapture.capture) { window.autoCapture.capture.identify({ identifier: "user-42", name: "John Smith", email: "john@example.com", avatarUrl: "https://example.com/avatar.png" }); } ``` All fields are optional. | Field | Description | | ------------ | -------------------------------------------------------------------------------------------------------- | | `identifier` | User identifier in your system | | `name` | Name | | `email` | Email address. It is subscribed to notifications about the report, see [Reporter email](#reporter-email) | | `avatarUrl` | Avatar URL | To reset the data, for example on sign-out, pass `null`: ```js window.autoCapture.capture.identify(null); ``` > **Call it after the script is initialized.** The script is loaded with the `async` attribute, and `window.autoCapture.capture` is `null` until it is initialized. Check it before the call, as in the example above, otherwise you will get an error. #### Reporter email The report form has an optional email field. The address entered there: * receives a "Your report has been received" email with a link to the report; * is subscribed to new comments on the report, the email contains an unsubscribe link; * is granted access to the report. A public report opens from the link in the email right away. To open a private one, click **Request a link** — a one-time sign-in link will be sent to the address. The field always opens empty. If you pass an address through [`identify()`](#identify): * a filled-in field takes priority — its address is used; * with an empty field, the address from `identify()` is used. The reporter's email is visible only to members of the project team. It is not shown via a public link to the report. An invalid address passed through `identify()` does not block submission: the report is accepted, but no email is sent and no subscription is created. #### Request to create a report with screen recording If you want the user to record a report for you not from the site, but to demonstrate the problem on their workstation, then you can create a link with a request for screen recording. ![](/docs/img/reports/external_1.png) ![](/docs/img/reports/external_2.png) *** By following the link, the user will be able to record a video for you and tell you about the problem. ![](/docs/img/reports/external_3.png) #### What does a report look like? Inside the report you will find a screenshot or video and all the debugging information from the console, network operations, user actions and a snapshot of the page DOM tree. **Info** The first tab contains general summary information about the client, device and browser. ![](/docs/img/reports/info.png) **Inspector** A snapshot of the page DOM tree, with the ability to select an element. *** > **Note.** This tab is only available for reports with a screenshot. Reports with video cannot capture a DOM tree snapshot since it can change over time. **Console** A list of all events from the browser console. ![](/docs/img/reports/console.png) **Network** A list of all XHR requests with the option to look into the details. **Actions** User actions on the page. ![](/docs/img/reports/actions.png) **Comments** You can also leave comments and reactions to comments. ![](/docs/img/reports/message.png) #### Report visibility By default, a report is created with visibility - **For team members only**, but you can make it public. For example, to conduct a dialogue with a client who created a report. ![](/docs/img/reports/share.png) --- ### Data Privacy By default, Reports collects data about user interactions and network requests for debugging issues. Depending on your application, some of this data may contain confidential information that you do not want to log. Some types of data are never collected: * Authentication tokens and cookies in network requests * Information entered into forms Note that the information on this page applies only to captured events and logs. Visual information in screenshots and screen recordings is not removed automatically. *** #### Overview By default, user interactions, network requests, and page state are captured automatically. However, some data requires special protection: * Passwords and authentication data * Personal data (email, phone, tax ID) * Payment information (card numbers, CVV) * API keys and tokens * Other confidential information Tuna Reports provides several levels of data protection that you can combine depending on your application's security requirements. *** #### Automatic protection By default, the following data is masked automatically: ##### Input fields The following field values are masked automatically: * ``, ``, `` — by type * Fields whose `name` or `id` attribute contains keywords: * **Authentication:** `password`, `passwd`, `pwd`, `secret`, `pin`, `otp` * **Contact data:** `email`, `phone`, `tel`, `mobile` * **Identification:** `ssn`, `passport`, `license`, `national_id`, `tax_id`, `inn`, `snils` * **Payment data:** `card`, `credit`, `debit`, `payment`, `billing`, `visa`, `mastercard`, `amex`, `discover`, `maestro`, `mir` * **Card details:** `cvv`, `cvc`, `cvn`, `expir` * **Bank details:** `iban`, `swift`, `bic`, `routing`, `account` * **Tokens and keys:** `token`, `apikey`, `api_key`, `auth`, `private` * Fields with classes: `password`, `passwd`, `email`, `phone`, `ssn`, `credit`, `card`, `payment`, `sensitive`, `private`, `secret`, `masked`, `hidden`, `secure`, `confidential` > **Substring matching.** Keyword matching uses substrings. For example, `card` matches `cardnumber`, `card_number`, `creditcard`. And `expir` matches `expiry`, `expiration`. ```html ``` ##### URL parameters Query parameters in URLs containing the following are masked automatically: * **Authentication:** `password`, `passwd`, `pwd` * **Tokens and keys:** `token`, `key`, `secret`, `apikey`, `api_key` * **Sessions:** `auth`, `credential`, `session`, `access` * **OAuth/OIDC:** `code`, `nonce` ```text Before: https://example.com/api?token=abc123&user=john After: https://example.com/api?token=***&user=john ``` ###### Adding your own parameters (sensitiveUrlParams) You can extend the list of masked URL parameters via the `sensitiveUrlParams` option: ```javascript window.reportOptions = { captureKey: 'YOUR_CAPTURE_KEY', sensitiveUrlParams: ['my_secret_param', 'internal_id', 'ref_code'] }; ``` These parameters will be added to the standard list, not replace it. ##### HTTP headers The following HTTP headers in network requests are masked automatically: * **Standard:** `authorization`, `www-authenticate`, `cookie` * **Tokens and keys:** `token`, `secret`, `apikey`, `api-key`, `api_key` * **Sessions and signatures:** `signature`, `credential`, `session` * **Service:** `idempotency-key`, `idempotence-key` * **Cloud providers:** `x-amz-*` (AWS), `x-goog-*` (Google Cloud), `x-ms-*` (Azure), `cf-access-*` (Cloudflare), `ocp-apim-*` (Azure API Management) Headers with values starting with `Bearer `or `Basic `are also masked. ###### Adding your own headers (sensitiveHeaders) You can extend the list of masked HTTP headers via the `sensitiveHeaders` option: ```javascript window.reportOptions = { captureKey: 'YOUR_CAPTURE_KEY', sensitiveHeaders: ['x-my-custom-header', 'x-internal-token'] }; ``` These headers will be added to the standard list, not replace it. *** #### HTML attributes for masking You can explicitly mark elements that should be masked or hidden using data attributes. ##### data-capture-mask Masks the element's content, replacing the text with `***`. ```html Secret text

Confidential information

Contract number: 12345

``` > **Inheritance.** Instead of adding the attribute to each element, you can add it to a parent container: > > ```html > > > > > > >
> > > >
> > ``` ##### data-capture-hide Completely hides the element from the report. ```html

This information will not be captured

``` ##### Difference between mask and hide | Attribute | Result | When to use | | ------------------- | ----------------------------------------------- | --------------------------------------------- | | `data-capture-mask` | Element is visible, content replaced with `***` | When it is important to show that data exists | | `data-capture-hide` | Element is completely absent | When the element should not be visible at all | *** #### Usage examples ##### Example 1: Login form ```html
``` ##### Example 2: Payment form ```html

Payment data

Payment data

``` ##### Example 3: User profile ```html ``` *** #### Security recommendations 1. **Use the principle of least privilege** — mask anything that may be sensitive, even if you are not sure. 2. **Test in a dev environment** — before deploying, verify that all sensitive data is actually masked. 3. **Combine methods** — use both automatic protection and explicit data attributes. 4. **Review reports regularly** — ensure reports do not leak confidential data. 5. **Document for your team** — create internal guidelines for using masking data attributes. *** #### Compatibility Privacy features work in all supported browsers: * Chrome 80+ * Firefox 75+ * Safari 14+ * Edge 80+ *** --- ### Restrictions #### Tunnels We impose a limit of **100 RPS** (requests per second) per Host (domain) in [HTTP tunnels](https://tuna.am/en/docs/tunnels/http.md). There are currently no other restrictions, such as monthly traffic volume, but they may appear in the near future. We make restrictions democratic; they are designed to ensure comfortable work for all users on shared resources. --- ### Secrets This section describes the features and functions of [Secrets](https://my.tuna.am/secrets?lang=en). #### Overview **Secrets** is a Tuna platform component designed for secure storage of confidential and other data, convenient management of application configurations in any environment: locally, CI/CD, test and production environments. It is an excellent alternative to `.env` files in combination with [direnv](https://direnv.net/) or environment variables in [GitLab CI/CD](https://tuna.am/en/docs/secrets/examples.md#-gitlab-ci) / [GitHub Actions](https://tuna.am/en/docs/secrets/examples.md#-github-actions). **VK Video** **YouTube** #### Who is it for? * *Developers* — to simplify environment setup and reduce the number of "magic" values in code. * *DevOps engineers* — for centralization and automation of configurations. * *Teams* — for secure and controlled shared access to confidential data. #### Main features We adhere to common structure, so secret storage is divided by projects, within projects there are environments, and within environments there are main or child configurations. Within configurations there are many functions for working directly with secrets and access to them, auditing, and more. When working in a [Team](https://tuna.am/en/docs/team.md), the roles section provides the ability to granularly distribute team members' access to the necessary environments in projects. Access to secrets is performed through the web interface of the dashboard, using the console client [tuna](https://tuna.am/en/docs/guides/install/install-cli.md) or through the [API](https://tuna.am/en/docs/guides/api.md). All secrets are stored in our database in encrypted form, while the encryption key is not stored in the database itself, so in case of a database leak, your secrets will remain anonymous. However, **Secrets** is not [Passwords](https://tuna.am/en/docs/passwords.md), here we **do not** apply the [zero-knowledge](https://en.wikipedia.org/wiki/Zero-knowledge_proof) approach, in favor of convenience. Secrets allow you to conveniently manage sensitive application settings, like integration tokens with external services, database connection URLs and other settings. ##### Projects You can create multiple projects, where a project is understood as one application, for example an API or an authorization server. Each project has a Name and an Alias. Name is the name displayed in the interface convenient for perception, can have spaces, etc., and Alias is needed for referring to the project through tuna CLI or through the API. ![](/docs/img/secrets/projects.webp) The number of projects you can create is limited and depends on the plan. ##### Environments Each project contains environments, by default 3 (Development, Staging, Production), they can be renamed, reordered, and also added or removed according to your needs. ![](/docs/img/secrets/envs.webp) Like projects, environments have a Name and an Alias: ![](/docs/img/secrets/env_rename.webp) ##### Configurations Each environment has a main configuration that cannot be deleted. Relative to the main configuration in an environment, you can create its copies, i.e. child configurations. ![](/docs/img/secrets/configs.webp) For example, you have a Development environment - this is the `master` branch in git from which a stand is deployed. Relative to the main configuration, each developer can create their own unique configuration. ##### Secrets The secrets themselves represent a Key = Value pair. The Key is always a string in the [format](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html) of an environment variable name. The Value can be anything. For greater security, we add the ability to set a type for each variable, for example you know for sure that `API_ENABLED` is a **bool**, and you can define this, then you will definitely not make a mistake and write a string or number there. ![](/docs/img/secrets/secret_type.webp) By hovering over the value and clicking, you can display it, and additional value management buttons will appear at the top. ![](/docs/img/secrets/secret_value.webp) For example, this is what the secret generator looks like: ![](/docs/img/secrets/secret_generator.webp) ###### Comments You can leave a comment for each secret: ![](/docs/img/secrets/secret_comment.webp) 1. Display comment for a specific secret 2. Display comments for all secrets ###### Reminders You can also leave reminders for each secret, and you will receive email notifications: ![](/docs/img/secrets/secret_reminder_1.webp) ![](/docs/img/secrets/secret_reminder_2.webp) ##### Secret synchronization across environments When adding new variables to the main configuration of any environment, you will see an indicator on other environments that some variables are missing: ![](/docs/img/secrets/skiped_1.webp) ![](/docs/img/secrets/skiped_2.webp) You will be prompted to fill in the missing secrets or hide them: ![](/docs/img/secrets/skiped_3.webp) ##### Comparing secret values across environments You can always conduct an inventory and compare the value of a secret in all environments and make sure, for example, that the connection to the Production database is not mistakenly copied to Staging or Development. ![](/docs/img/secrets/secret_compare_1.webp) ![](/docs/img/secrets/secret_compare_2.webp) ##### Hidden secrets Of course, there may be variables that exist only in one environment, for example in Production, and to avoid constantly seeing a notification about missing secrets, you can hide them, but they will be displayed in the configuration settings. ![](/docs/img/secrets/secret_hide_1.webp) ![](/docs/img/secrets/secret_hide_2.webp) ##### Access > **Info.** Available only with a [subscription](https://tuna.am/#pricing). ###### Service keys You can issue a unique *Service key* for this configuration, this is analogous to a user [API key](https://tuna.am/en/docs/guides/api.md), but limited only to this configuration. A Service key can be restricted to either *read-only* or *read and write*. This is useful, for example, for CI/CD. ![](/docs/img/secrets/access_key.webp) A Service key can be identified by the prefix `tst_`, whereas an API key has the prefix `tak_`, and a [token](https://my.tuna.am/token?lang=en) for creating tunnels has the prefix `tt_`. ###### Allowed IP addresses and subnets Access can also be restricted by IP addresses and subnets in CIDR format. By default, everyone is allowed. ![](/docs/img/secrets/access_ip.webp) In combination, for example, you can issue a *Service key* for the prod environment and restrict receiving secrets to IP addresses from gitlab-runners and your Production servers. ##### Change requests A member without write access to an environment can propose a change to its secrets, and an environment editor reviews, approves, and applies it. Approval policies define how many approvals are needed and from whom. For details, see [Change requests](https://tuna.am/en/docs/secrets/change-requests.md). ##### Change history Full audit of all actions and changes within the configuration is also maintained. If someone changed the database connection password, you will be able to find out about it. ![](/docs/img/secrets/audit.webp) #### Navigation #### [📄️Change requests](https://tuna.am/en/docs/secrets/change-requests.md) [How to propose a change to secrets without write access, take it through review, and set up approval policies](https://tuna.am/en/docs/secrets/change-requests.md) #### [📄️Working in CLI](https://tuna.am/en/docs/secrets/cli.md) [Managing secrets, running applications and administering projects through the tuna console client](https://tuna.am/en/docs/secrets/cli.md) #### [📄️Examples](https://tuna.am/en/docs/secrets/examples.md) [Examples of working with tuna secrets](https://tuna.am/en/docs/secrets/examples.md) #### [🗃Guides](https://tuna.am/en/docs/secrets/guides.md) [1 item](https://tuna.am/en/docs/secrets/guides.md) --- ### Change requests A change request lets you change secrets in an environment you don't have write access to. A typical case: a developer with access to `dev` needs a new variable in `prod` for a release. They propose the change, and a `prod` editor reviews it, approves it, and applies it. > **Info.** Available only with a [subscription](https://tuna.am/#pricing). Requests work only in the [dashboard](https://my.tuna.am/secrets/requests?lang=en). You can't create or approve them through the API, CLI, or service keys: approval has to come from a person, not from a leaked key or a script. Environment editors can still edit secrets directly. Requests and policies don't forbid direct edits; they add a path for those who don't have write access. #### Who can do what | Action | Who can | | --------------------------- | ------------------------------------------------------------------------------------------ | | Create a request | any project member, even without access to the target environment | | See values in a request | a member with access to the environment, or the author of the value | | Approve and apply | an environment editor: a member with write access to it, as well as team admins and owners | | Configure approval policies | team admins and owners | Members' permissions for environments are set in [team roles](https://tuna.am/en/docs/team/roles.md). #### How to propose a change Open a configuration. If you don't have write access to it, you'll see **Propose change** instead of the **Save** button. ![The dev configuration without write access: the Propose change button in the top right corner](/docs/img/secrets/change-requests/config-viewer.webp) You put the request together in a form: 1. Enter a title and, optionally, a description of why the change is needed. 2. Select a project and a configuration. Environments you don't have access to are marked with a lock: you can change secrets in them, but you won't see the current values or secret names. 3. Add or change secrets. Each configuration is a separate block that is approved and applied separately. To change secrets in several environments, click **Add configuration**. 4. Click **Preview**: it shows what will change, and you can pick reviewers there. Without reviewers, the request waits for everyone who can approve it. 5. Click **Submit for review**. ![The new request form: title, description, project, and a prod configuration block with a new variable](/docs/img/secrets/change-requests/form-filled.webp) A request can't change secrets with `restricted` visibility or set that visibility. Reviewers get an email. It contains the request title, the project, the configurations, and the secret names, but no values. #### Review and apply Requests waiting for your review show up on the dashboard home page in the **Needs attention** block and in **Secrets → Change requests** with the **Awaiting my review** filter. ![The list of change requests with a Review button next to a request waiting for approval](/docs/img/secrets/change-requests/list-review.webp) On the request page, each block shows its diff and approvals. Values are hidden: click the eye icon to see them. An environment editor sees both the old values and the new ones. ![A prod block under review: new and changed secrets, the Approve button, and an inactive Apply button](/docs/img/secrets/change-requests/request-review.webp) 1. Click **Approve**. Changed your mind? Click **Withdraw approval**. 2. Once the block is approved, click **Apply** and confirm. The new values are written to the configuration right away, and an entry with a link to the request appears in the [change history](https://tuna.am/en/docs/secrets.md#history). ![An applied block: who applied the changes and when, and a link to the configuration history](/docs/img/secrets/change-requests/request-applied.webp) The author gets an email when the changes are applied or the request is closed. If you no longer need the request, click **Close request**. The author and reviewers can edit the request while it's open, but editing a block resets its approvals and the review starts over. ##### Statuses | Status | Meaning | | ----------------- | ------------------------------------------------------ | | Draft | the request is saved but not submitted for review | | In review | waiting for approval | | Approved | there are enough approvals, the changes can be applied | | Applied | the changes are written to the configuration | | Partially applied | some blocks are applied, the rest are canceled | | Canceled | the request was closed without being applied | ##### If a secret was changed directly Changes are applied strictly. If someone changed the same secret directly after the request was created, the block is marked **Outdated** and can't be applied: otherwise the request would silently overwrite someone else's edit. Click **refresh** to rebuild the block from the current values. This resets the approvals. ![A block marked Outdated: secrets changed after creation, with a refresh link](/docs/img/secrets/change-requests/request-outdated.webp) A configuration with an open request shows a banner with a link to it, so editors can see that changes are already waiting for review. #### Approval policies By default, a block can be applied after one approval from an environment editor. Approval policies set stricter rules, for example two approvals for `prod` and a ban on approving your own request. Team admins and owners manage policies in [Secrets → Approval policies](https://my.tuna.am/secrets/policies?lang=en). ![The form for a new Production policy: two approvals from any environment editor, and the author can't approve their own request](/docs/img/secrets/change-requests/policy-form.webp) A policy defines: * **Scope** — all team projects, including new ones, or selected projects. Within a project, you can limit the policy to environments (together with their child configurations) or to individual configurations. * **Rules** — how many approvals are needed, from 1 to 20, and whose approvals count: any environment editor or selected members. An approval counts only if the member has write access to the environment. All rules of the policy must be met. * **The request author can't approve it** — the author's approval doesn't count, and the **Approve** button is unavailable to them. * **Assign members from the rules as reviewers** — when a request is created, they become reviewers and get an email. If several policies apply to a configuration, all of them must be met. A block under a policy gets a progress badge, for example `0/2`, and the **Apply** button becomes available once all rules are met. ![A block under a policy: the 0/2 badge and a hint on the Apply button](/docs/img/secrets/change-requests/request-policy.webp) #### How long requests are kept Closed and applied requests are kept as long as the configuration [change history](https://tuna.am/en/docs/secrets.md#history) on your plan. Open requests aren't deleted. If you delete a configuration or an environment, its blocks disappear from requests. If you delete a project, its requests are deleted too. --- ### Working in CLI #### Overview Using the console client [tuna](https://tuna.am/en/docs/guides/install/install-cli.md), you can get secrets, automatically configure the current environment, or pass configuration to the [env](https://en.wikipedia.org/wiki/Environment_variable) of the required application. The console also lets you manage the structure itself — projects, environments, configs and service keys. | Commands | What for | | --------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | [`list`](#list), [`get`](#get), [`set`](#set), [`delete`](#delete), [`upload`](#upload) | Reading and changing secrets in a config | | [`run`](#running-applications) | Running an application with secrets in environment variables | | [`download`](#download), [`substitute`](#substitute) | Exporting secrets to a file or a template | | [`setup`](#setup), [`open`](#open) | Binding a directory to a config, opening it in a browser | | [`projects`](#projects), [`environments`](#environments), [`configs`](#configs) | Managing the structure and service keys | To get secrets through the tuna console application or [API](https://tuna.am/en/docs/guides/api.md), you need a user [API key](https://my.tuna.am/api_keys?lang=en) or [Configuration Service Key](https://tuna.am/en/docs/secrets.md#service-keys). For local development, it's more convenient to use global user API keys, but for CI/CD, Production, and other shared environments, we **strongly recommend** using Configuration Service Keys. #### Why is tuna secrets better than direnv for local development? [direnv](https://direnv.net) is a great utility for local configuration, but it still makes you monitor the local `.env` file, and if new variables appeared in Production, you may not know about it immediately and spend hours on pointless debugging. In tuna secrets, there is [automatic secret synchronization](https://tuna.am/en/docs/secrets.md#secret-synchronization-across-environments), which greatly simplifies this process. #### Why is tuna secrets better than Gitlab/Github/Bitbucket Variables? Actually, the reason here is the same as with direnv. For example, you have a special project designed for building and publishing an application using [goreleaser](https://goreleaser.com/) and it may require many keys and tokens for different external integrations, while for different environments, and using Gitlab/Github/Bitbucket Variables for this is inconvenient, and it's even more inconvenient to monitor this, plus the complexity in access control. In tuna secrets, access to environments is regulated by RBAC, and you can precisely distribute access only to the necessary people/robots/scripts. And by updating a secret or adding a new one, it will immediately start working in CI. #### Authorization To access secrets, you need a [Service Key](https://tuna.am/en/docs/secrets.md#service-keys) of a specific configuration or a user [API key](https://my.tuna.am/api_keys?lang=en). It can be passed through the `--api-key` flag, the `TUNA_API_KEY` environment variable, or saved to the config file with `tuna config save-api-key ` The keys differ not only in scope, but also in the set of available commands: | Key | What it can do | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Configuration service key (`tst_`) | Read secrets of its own config: `run`, `download`, `list`, `get`. With `write` access — also `set`, `delete` and `upload` into the same config | | User API key (`tak_`) | Everything above plus structure management: `projects`, `environments`, `configs` and issuing service keys | A service key used against another config returns `403`, and against structure management commands — `401`. So keep a configuration service key in CI/CD, and create projects and configs with a user key. #### Project and configuration Almost every command works in the context of a project and a configuration. There are three ways to set them, in order of priority: 1. the `--project` (`-p`) and `--config` (`-c`) flags; 2. the `TUNA_SECRETS_PROJECT` and `TUNA_SECRETS_CONFIG` environment variables; 3. a directory bound to a config with the [`setup`](#setup) command. The `--scope` flag (`.` by default) tells the client which directory's binding to use. See the [`setup`](#setup) section for details. ```shell tuna secrets list --project api-php --config stage tuna secrets list -p api-php -c stage tuna secrets list # project and config from the directory binding ``` #### Output format The client separates data from service messages: * data goes to `stdout`; * status messages, warnings and errors go to `stderr`. That is why the output of any command can be redirected to a file or piped without catching extra lines: ```shell tuna secrets list --only-names > names.txt tuna secrets list | grep API_ ``` ##### Tables For humans, commands print a borderless table: headers in caps, an empty cell is `-`, dates are RFC3339 in your local time zone. Such a table is easy to cut with `awk` and does not fall apart in a narrow terminal. ```shell $ tuna secrets list NAME VALUE TYPE NOTE UPDATED JKLNRFEDUILNNIF hmac:cea41705 string - 2026-09-17T11:56:51+04:00 ``` ##### Value fingerprints By default the `VALUE` column contains not the secret value but its fingerprint, like `hmac:cea41705` — an HMAC-SHA256 of the value keyed with the command's key. Equal values produce equal fingerprints, and the value cannot be restored from a fingerprint. So fingerprints can be compared across configs ("are the database passwords in stage and prod really different?") and printed into CI logs without disclosing the secret. Here is what a comparison of two environments looks like: `API_TOKEN` and `API_ENABLED` are the same in dev and prod, while `API_DB` and `LOG_LEVEL` differ — and none of the values had to be disclosed. *** Real values are shown by [`list --values`](#list) and [`get`](#get): ```shell $ tuna secrets list --values NAME VALUE TYPE NOTE UPDATED JKLNRFEDUILNNIF vcTpFuptQLtJ0beoTQA13rHNeAaGNtjs string - 2026-09-17T11:56:51+04:00 ``` ##### JSON and YAML The `-o` (`--output`) flag with `json` or `yaml` is available on every `tuna secrets` subcommand, including write commands: `set`, `delete`, `upload`, `create`, `rename`, `lock` and the rest print the result in machine-readable form. ```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" } } ``` In machine-readable output a secret comes with all its fields and **with the real value** — unlike the table, where a fingerprint is shown by default. Keep that in mind when redirecting such output to a file or a log. The exception is a secret with `restricted` visibility: the server returns neither the value nor the fingerprint, so both fields come back empty. An empty result is always a valid `[]` or `{}` rather than no output at all, so a script parser needs no special case for it. ##### Raw values for scripts The `--plain` flag of [`get`](#get) and [`configs tokens create`](#service-keys) prints only the value itself, **without a trailing newline**. This is handy for substitution into another command: ```shell $ docker login -u robot -p "$(tuna secrets get REGISTRY_PASSWORD --plain)" registry.example.com ``` ##### Errors Errors are printed to `stderr`, and the exit code is `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 ``` With `-o json` the error becomes JSON as well — it can be parsed and, for example, the `request_id` can be attached to a support request: ```json {"code":"NotFound","error":"Failed to list secrets in test/nonexistent: Config does not exist","request_id":"83d3961b1452ab3c7174965acbfbd711"} ``` #### Working with secrets ##### List Lists the secrets of a config. The alias is `ls`. | Flag | Description | | -------------- | ----------------------------------- | | `--values` | Show values instead of fingerprints | | `--only-names` | Names only, in a single column | ```shell $ tuna secrets list NAME VALUE TYPE NOTE UPDATED JKLNRFEDUILNNIF hmac:cea41705 string - 2026-09-17T11:56:51+04:00 ``` In the recording the `LOG_LEVEL` fingerprint changes together with the value, and `list --values` then shows the value itself: *** ##### Get Values of one or more secrets. Unlike `list`, `get` shows values right away. | Flag | Description | | --------- | ------------------------------------------------------------- | | `--plain` | Values only, without the table and without a trailing newline | ```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 ``` If a requested secret is missing from the config, the command fails and prints nothing — this way a typo in a name does not turn into an empty variable. ##### Set Writes one or more secrets. | Flag | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------------- | | `--type` | Value type: `string`, `json`, `boolean`, `integer`, `decimal`, `email`, `url`, `yaml`, `xml`, `date`, `datetime` | | `--note` | A note for the secret | | `--visibility` | Value visibility in the web interface: `masked` or `unmasked` | | `--no-interactive` | Fail instead of asking for the value interactively | The value can be passed in four ways: ```shell $ tuna secrets set API_TOKEN=PwZayIp9NpNntNYd2jkAAQgViM6dXdIm # inline $ tuna secrets set API_TOKEN PwZayIp9NpNntNYd2jkAAQgViM6dXdIm # as a separate argument $ openssl rand -hex 32 | tuna secrets set API_TOKEN # from stdin $ tuna secrets set API_TOKEN # interactively ``` Interactive input only kicks in for a single name without `=` when nothing arrived on stdin; with `--no-interactive` you get an error instead. The value may be multi-line, a private key for example: the input ends with an empty line followed by a line holding a single period. Several secrets can be written in one call; the `--type`, `--note` and `--visibility` flags then apply to all of them: ```shell $ tuna secrets set API_ENABLED=true API_DEBUG=false --type boolean ``` The command prints back the secrets it wrote: ```shell $ tuna secrets set FOO=bar NAME VALUE TYPE NOTE UPDATED FOO hmac:bd154f88 string - 2026-09-18T10:43:10+04:00 ``` > **Do not use the restricted visibility.** The `--visibility` flag also accepts `restricted`, but you should not use it for now: the server stops returning the value of such a secret to everyone — including `tuna secrets run` — and `tuna secrets download` starts answering `403` for the whole config. The value cannot be recovered afterwards. If somebody changed the same secret between the read and the write, the command reports it and fails without overwriting the other change. Repeating the call is enough in that case. ##### Delete Deletes one or more secrets. The alias is `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 ``` The command prints the config as it remains after the deletion. If at least one of the listed secrets is missing from the config, nothing is deleted. > **Delete confirmation.** Starting with version **0.36.0**, delete commands ask for confirmation: `delete`, `projects delete`, `environments delete`, `configs delete` and `configs tokens revoke`. The `--yes` (`-y`) flag skips the question. Without a terminal — in CI, cron or scripts — there is nobody to ask, so without `--yes` the command fails right away with `--yes is required when not running interactively`. ##### Upload Uploads secrets from a `.env` or `.json` file. ```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 ``` Like `delete`, the command prints the whole config — after the merge. The file **adds to** the config: secrets with matching names are overwritten, the rest stay in place. `upload` does not replace the config as a whole. #### Running applications The `run` command starts your application and passes secrets to it in environment variables. The secrets end up neither in a file nor in the shell history. ```shell tuna secrets run -- yarn start ``` *** ##### What the process receives | Flag | Description | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `--command` | Execute a string through the shell: `--command "echo $HOME && env"` | | `--only-secrets` | Pass only the secrets and a minimum of system variables (`PATH`, `HOME`, `PS1`, `USER`, `SHELL`, `TMPDIR`) | | `--preserve-env` | A comma-separated list of names for which an already existing environment value wins; without a value the flag means `true` — preserve all of them | | `--name-transformer` | Transform names: `upper-camel`, `camel`, `lower-snake`, `lower-kebab`, `tf-var`, `dotnet-env` | | `--forward-signals` | Forward signals to the child process; enabled by default when `stdout` is not a terminal | `tuna` returns the exit code of the child process, so the wrapper can be placed into a CI job without breaking its logic. ```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 ``` ##### Automatic restart when a secret changes If you pass the `--watch` flag, the client subscribes to config changes and restarts the process with the new values. ```shell tuna secrets run --watch -- yarn start ``` The restart does not happen instantly: the server checks the config once every 30 seconds and only then sends the event. The same delay can pass between a secret change and the application restart — so a couple of dozen quiet seconds mean `--watch` is working, not broken. The moment of the restart is visible on `stderr`: ```shell Secrets changed; restarting process ``` In the recording the application prints the value every five seconds: first the old one, then the restart line, then the new one. *** This video best demonstrates the work: **VK Video** **YouTube** In the video, we run this command: ```shell tuna secrets run --watch -- bash -c 'while true; do echo -n "$(date -Is) [pid: $$] ==> " ; echo ${API_TOKEN} ; sleep 1; done' ``` ##### Secrets as a file instead of environment variables Some applications read configuration only from a file. The `--mount` flag creates a temporary named pipe, puts the secrets into it and passes the path to the child process in the `TUNA_SECRETS_MOUNT_PATH` variable. In this mode the secrets do not go into the environment. The pipe is created at the given path with `0600` permissions, but the values live only in the client's memory: every open for reading gets a full snapshot and an EOF, and no file with secrets is ever written to disk. Once the process exits, the pipe is removed. ```shell $ tuna secrets run --mount ./secrets.json --mount-format json -- ./app --config "$TUNA_SECRETS_MOUNT_PATH" ``` | Flag | Description | | ------------------- | -------------------------------------------------------------------------- | | `--mount` | Path of the pipe to create | | `--mount-format` | Content format: `env` (default), `env-no-quotes`, `json`, `yaml`, `docker` | | `--mount-template` | A template file rendered into the pipe instead of a fixed format | | `--mount-max-reads` | How many times the file may be read, `0` for unlimited | *** The `--mount-max-reads N` flag removes the pipe after N reads — handy when exactly one process reads the secrets at startup. > **Info.** Named pipes are not supported on Windows, so `--mount` will not work there. > > If the client is killed with `SIGKILL`, the pipe stays on disk and the next run refuses to start with `mount path already exists`. Such a file has to be removed manually. ##### Offline mode `run` can keep the last successfully fetched secrets in an encrypted file and use them when the API is unavailable — the application will not stall because of network problems. | Flag | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `--fallback` | Path to the file; by default derived from the API address, the key, the project and the config | | `--fallback-only`, `--offline` | Read only the file, without contacting the API | | `--fallback-readonly` | Use the file but never update it | | `--no-fallback` | Neither read nor write the file | | `--fallback-passphrase` | The passphrase; by default derived from the key, the project and the config. Also read from `TUNA_SECRETS_FALLBACK_PASSPHRASE` | If the API answers that access is denied or the config is not found (`401`, `403`, `404`), the file is removed: a stale copy of secrets after access revocation is more dangerous than a stopped application. On network errors and `5xx`, on the contrary, the client reads the file and prints a warning. By default the passphrase is derived from the API key, the project and the config — which means that after a key rotation the old file can no longer be decrypted. If the file has to survive rotation, set the passphrase explicitly: the `--fallback-passphrase` flag takes precedence over the `TUNA_SECRETS_FALLBACK_PASSPHRASE` variable. Old files can be removed with `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 ``` By default files older than 14 days are removed (`--max-age 336h`). The first run goes to the API and writes the file along the way, the second one with `--offline` needs no network at all: *** #### Exporting secrets ##### Download Gets the secrets of a config into a file or to standard output. ```shell tuna secrets download ``` *** | Flag | Description | | -------------------- | ------------------------------------------------------------------------------------------------------- | | `--format` | `json` (default), `yaml`, `env`, `env-no-quotes`, `docker` | | `--no-file` | Print to standard output instead of writing a file | | `--name-transformer` | Transform names: `upper-camel`, `camel`, `lower-snake`, `lower-kebab`, `tf-var`, `dotnet`, `dotnet-env` | | `--scope` | The directory whose binding to use | The settings can also be given through [environment variables](https://tuna.am/en/docs/guides/environment-variables.md#secrets). ###### Passing project and configuration The `--project` and `--config` flags take the name (alias) of the project and configuration as input. By default, the configuration will be saved to the `tuna.json` file in the current directory. ```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" } ``` ###### Passing format You can specify the format you need for the file. **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" ``` An unknown format is an error rather than a silent export of "something". ###### The docker format The `docker` format prints `KEY=value` pairs without quotes — the same as `env-no-quotes`. Such a file suits `docker run --env-file`, where quotes would become part of the value: ```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 transformation The `--name-transformer` flag changes the naming style — useful when the consumer of the secrets does not expect `SCREAMING_SNAKE_CASE`: ```shell $ tuna secrets download --no-file --format json --name-transformer camel { "apiDb": "postgres://postgres:postgres@localhost:5432/postgres", "apiToken": "PwZayIp9NpNntNYd2jkAAQgViM6dXdIm" } ``` The `dotnet` transformer exists only in `download`: it produces names like `Section:Key`, and a colon is not allowed in an environment variable name. That is why `tuna secrets run` does not offer it — for .NET it has `dotnet-env` with a double underscore. ###### Custom file You can pass a path in the command to define your own file. ```shell $ tuna secrets download --project api-php --config stage foo-bar.json Downloaded secrets to foo-bar.json ``` ###### Output to stdout The `--no-file` flag disables writing to a file and prints the configuration to standard output. ```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 Renders a template file with the secret values. It fits configs that have their own format: `nginx.conf`, `application.yml`, `.ini` and the like. The template is a Go [text/template](https://pkg.go.dev/text/template), and the data is a set of name-value pairs: 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 - ``` *** Without `--output` the result goes to standard output. With `--output` the file is written atomically and with `0600` permissions. The `{{ index . "DB-NAME" }}` form is needed for names that are not Go identifiers. Besides the standard functions, `tojson` and `fromjson` are available. If the template mentions a secret that is missing from the config, the command does not substitute an empty value — it exits with code `1`. The message shows both the position in the template and the name itself: ```shell $ tuna secrets substitute app.conf.tpl Error: Failed to render template: template: secrets:2:11: executing "secrets" at <.API_TOKN>: map has no entry for key "API_TOKN" ``` For the same reason a `{{ if .DEBUG }}` condition on an optional secret is an error too. Write it through `index` — on a missing key it returns an empty string: ```gotemplate {{ if index . "DEBUG" }}debug{{ else }}quiet{{ end }} ``` The template in `tuna secrets run --mount-template` is rendered just as strictly — the engine is the same. #### Setting up the environment ##### Setup Binds a directory to a project and a config — after that `--project` and `--config` can be omitted. Bindings are stored in the client [config file](https://tuna.am/en/docs/guides/config-file.md). ```shell tuna secrets setup ``` Without flags the command offers a list of projects and configs to choose from. ###### Setting up without the menu As with `download`, in `setup` you can pass `--project` and `--config`: ```shell $ tuna secrets setup --project api-php --config stage SCOPE PROJECT CONFIG /home/user/src/api api-php stage ``` *** The `--no-interactive` flag (or the `TUNA_SECRETS_NO_INTERACTIVE` variable) forbids asking for missing values — the command fails instead. That is what you want in CI and in install scripts. ###### Setting up a directory other than the current one If you want to configure a non-current directory, pass the required path to the `--scope` flag: ```shell tuna secrets setup --project api-php --config stage --scope /home/user/src/api ``` ###### The tuna.yaml file in a repository So that a new developer only needs a single `tuna secrets setup`, put a `tuna.yaml` file into the repository root: tuna.yaml ```yaml setup: - project: api-php config: dev ``` For a monorepo you can describe several bindings, each entry with its own `path` relative to the root: tuna.yaml ```yaml setup: - project: api-php config: dev path: backend - project: ui config: dev path: frontend ``` The file is read only when the project and the config are not set by flags or environment variables, and only in the `--scope` directory — the client does not walk up the tree. ###### What a binding looks like The result of `setup` goes into the client config file: ```shell $ cat ~/.config/tuna/tuna.yml secrets: scoped: /home/user/src/api: project: api-php config: stage ``` While you are in `/home/user/src/api`, there is no need to pass the project and the config. From another directory either pass them explicitly or point `--scope` at the bound one: ```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 Opens the current config in the web interface: ```shell tuna secrets open ``` If the browser could not be opened, the command prints the link. With `-o json` it prints just the address. #### Managing the structure Projects, environments and configs can be created and changed right from the console — the same actions as in the [web interface](https://my.tuna.am/secrets?lang=en). ##### Projects ```shell $ tuna secrets projects SLUG NAME DESCRIPTION CONFIGS SECRETS CREATED test test - 5 12 2026-09-17T11:20:40+04:00 ``` | Command | Description | | --------------------------- | -------------------------------------------------------------- | | `projects` | List of projects | | `projects get [project]` | Information about a project | | `projects create [name]` | Create a project: `--slug`, `--description` | | `projects update [project]` | Change it: `--name`, `--slug`, `--description` | | `projects delete [project]` | Delete it; without an argument it offers a list to choose from | ```shell $ tuna secrets projects create "API PHP" --slug api-php --description "Main API" ``` ##### Environments ```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 ``` | Command | Description | | --------------------------------- | ----------------------------------------- | | `environments` | List of the project's environments | | `environments get ENVIRONMENT` | Information about an environment | | `environments create NAME` | Create it: `--slug`, `--personal-configs` | | `environments rename ENVIRONMENT` | Rename it: `--name`, `--slug` | | `environments delete ENVIRONMENT` | Delete an environment | The command aliases are `envs` and `environment`. ##### Configs ```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 ``` The `TYPE` column tells the environment's main config (`original`) from its copy (`duplicate`), and `LAST FETCH` shows when the secrets were fetched last — it is a good hint of whether anyone uses the config at all. | Command | Description | | ------------------------- | ---------------------------------------------------------- | | `configs` | List of the project's configs, filtered by `--environment` | | `configs get [config]` | Information about a config | | `configs create [name]` | Create it in an environment: `--environment`, `--locked` | | `configs clone [config]` | Copy it within its environment: `--name` | | `configs rename [config]` | Rename it: `--name` | | `configs lock [config]` | Forbid changes | | `configs unlock [config]` | Lift the lock | | `configs delete [config]` | Delete a config | | `configs logs` | Change history: `--page`, `--page-size` | ###### Change history ```shell $ tuna secrets configs logs TIME ACTOR ACTION SECRET 2026-09-17T16:20:37+04:00 Evgeniy secret_delete CLAUDE_ML ``` > **Info.** How long the config change history is kept depends on your [plan](https://tuna.am/#pricing): 3 days on the free plan, 90 days on Pro and 180 days on Legend. Older records are deleted automatically. ###### Service keys A [service key](https://tuna.am/en/docs/secrets.md#service-keys) is limited to a single config — this is what you should use in CI/CD instead of a user API key. | Command | Description | | -------------------------- | ---------------------------------------------------------------- | | `configs tokens` | List of the config's keys | | `configs tokens create` | Issue a key: `--description`, `--access`, `--max-age`, `--plain` | | `configs tokens revoke ID` | Revoke a key by its identifier | | `create` flag | Description | | --------------- | ------------------------------------------------------------------------------- | | `--access` | `read` (default) or `write` | | `--max-age` | Lifetime as a Go duration, for example `720h`; without it the key never expires | | `--description` | A description to recognize the key by in the list | | `--plain` | Print only the key itself, without the table and without a trailing newline | The key value is shown once — at creation: ```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 ``` The "Save this token" warning goes to `stderr`, so in a script the key can be captured straight into a variable: ```shell $ TUNA_API_KEY=$(tuna secrets configs tokens create --description "gitlab ci" --access read --max-age 720h --plain) ``` --- ### Examples > **Info.** All command names are just examples, you will most likely have your own variant. #### Local Development ##### 🍪 JavaScript (Vue, React, Angular, etc.) Practically any web project contains `package.json` with scripts where various commands are described for building, running, and debugging locally. It looks something like this: ```json ... "scripts": { "serve": "vite", ... ``` 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Set up the configuration for the project `tuna secrets setup`. 3. Edit `package.json` and add the `tuna secrets run` wrapper, it should look something like this: ```json ... "scripts": { "serve": "tuna secrets run --watch -- vite", ... ``` 4. Run the application as usual `yarn serve` or `npm run serve` *** ##### 🐹 Go (Golang) Go projects are usually run through `go run` or `make`: 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Run `tuna secrets setup`. 3. Run the application with the wrapper: ```shell tuna secrets run --watch -- go run ./cmd/server ``` Example Makefile: ```yaml .DEFAULT_GOAL := run .PHONY: run run: ### Run @tuna secrets run --watch -- go run ./cmd/server ``` ##### 🦀 Rust (Rocket, Actix, etc.) Rust projects most often use cargo: 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Run `tuna secrets setup`. 3. Run the application with the wrapper: ```shell tuna secrets run --watch -- cargo run ``` Example Makefile: ```yaml .DEFAULT_GOAL := run .PHONY: run run: ### Run @tuna secrets run --watch -- cargo run ``` *** ##### 🥠 Node.js server project (Express, Fastify, etc.) Node.js backend also usually uses `package.json`, for example: ```json ... "scripts": { "dev": "node server.js", ... ``` 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Set up the configuration for the project `tuna secrets setup`. 3. Edit `package.json`, adding the wrapper: ```json ... "scripts": { "dev": "tuna secrets run --watch -- node server.js", ... ``` 4. Run the application `yarn dev` or `npm run dev`. *** ##### 🐍 Python (Flask, Django, etc.) Python projects are often run through `python`, `manage.py`, or `Makefile`. 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Set up the configuration for the project by adding the wrapper `tuna secrets setup`. 3. Wrap the run command: ```bash tuna secrets run --watch -- python app.py ``` If you have Django: ```bash tuna secrets run --watch -- python manage.py runserver ``` If you have a Makefile: ```yaml .DEFAULT_GOAL := run .PHONY: run run: ### Run @tuna secrets run --watch -- python manage.py runserver ``` 4. Run the application with this command. *** ##### 🐘 PHP (Laravel, Symfony, etc.) Usually the built-in PHP web server is used: ```shell php -S localhost:8000 -t public ``` 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Set up the configuration for the project `tuna secrets setup`. 3. Run the server through the `tuna secrets run` wrapper: ```bash tuna secrets run --watch -- php -S localhost:8000 -t public ``` If you have Laravel: ```bash tuna secrets run --watch -- php artisan serve ``` Example Makefile: ```yaml .DEFAULT_GOAL := run .PHONY: run run: ### Run @tuna secrets run --watch -- php artisan serve ``` *** ##### ☕ Java/Kotlin (Spring Boot, Micronaut, etc.) Such projects often use Gradle or Maven: 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Set up the configuration for the project `tuna secrets setup`. 3. Wrap the run command: For Gradle: ```bash tuna secrets run --watch -- ./gradlew bootRun ``` For Maven: ```bash tuna secrets run --watch -- mvn spring-boot:run ``` Example Makefile: ```yaml .DEFAULT_GOAL := run .PHONY: run run: ### Run @tuna secrets run --watch -- mvn spring-boot:run ``` 4. Run the application as usual. *** ##### 🪟 C#/.NET (ASP.NET Core, etc.) .NET projects are run through `dotnet run`: 1. Navigate to the project directory `cd ~/path/to/your/project`. 2. Run `tuna secrets setup`. 3. Run the application with the wrapper: ```shell tuna secrets run --watch -- dotnet run ``` Example Makefile (on Linux/macOS): ```yaml .DEFAULT_GOAL := run .PHONY: run run: ### Run @tuna secrets run --watch -- dotnet run ``` *** #### CI/CD ##### 🦊 Gitlab CI Create [service keys](https://tuna.am/en/docs/secrets.md#service-keys) for the required configurations and save the key in [Gitlab ci variables](https://docs.gitlab.com/ci/variables/). Suppose you have a project `myproj` with an environment `prod`, create a service key and save it to the variable `TUNA_API_KEY_MYPROJ_PROD` in Gitlab variables. Below is an example of how it might look in a Job: **Environment variables** ```yaml production: extends: [.base] environment: name: prod variables: TUNA_API_KEY: $TUNA_API_KEY_MYPROJ_PROD TUNA_SECRETS_PROJECT: $CI_PROJECT_NAME TUNA_SECRETS_CONFIG: $CI_ENVIRONMENT_NAME TUNA_SECRETS_FORMAT: env TUNA_SECRETS_NO_FILE: true before_script: - source <(tuna secrets download) script: - goreleaser release ``` **Flags** ```yaml production: extends: [.base] environment: name: prod before_script: - source <(tuna --api-key=${TUNA_API_KEY_MYPROJ_PROD} secrets download --project=${CI_PROJECT_NAME} --config=${CI_ENVIRONMENT_NAME} --format=env --no-file) script: - goreleaser release ``` **The run wrapper** ```yaml production: extends: [.base] environment: name: prod variables: TUNA_API_KEY: $TUNA_API_KEY_MYPROJ_PROD TUNA_SECRETS_PROJECT: $CI_PROJECT_NAME TUNA_SECRETS_CONFIG: $CI_ENVIRONMENT_NAME script: - tuna secrets run -- goreleaser release ``` The secrets only reach the process environment: no file in the working directory and no `source` in the job history. The exit code of `goreleaser` becomes the exit code of the job. Note that **project** and **config** we get directly from the environment, this is possible if the project name in Gitlab and Tuna Secrets match, and also environment.name in Gitlab Job corresponds to the configuration alias. *** ##### 🐙 GitHub Actions Create [service keys](https://tuna.am/en/docs/secrets.md#service-keys) for the required configurations and save the key in [GitHub Secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions). Suppose you have a project `myproj` with an environment `prod`, create a service key and save it to the variable `TUNA_API_KEY_MYPROJ_PROD` in GitHub Secrets. Below is an example of how it might look in a Job: **Environment variables** ```yaml name: Release on: push: tags: - "v*" jobs: production: runs-on: ubuntu-latest env: TUNA_API_KEY: ${{ secrets.TUNA_API_KEY_MYPROJ_PROD }} TUNA_SECRETS_PROJECT: ${{ github.event.repository.name }} TUNA_SECRETS_CONFIG: prod TUNA_SECRETS_FORMAT: env TUNA_SECRETS_NO_FILE: true steps: - uses: actions/checkout@v4 - run: | source <(tuna secrets download) goreleaser release ``` **Flags** ```yaml name: Release on: push: tags: - "v*" jobs: production: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: | source <(tuna --api-key=${{ secrets.TUNA_API_KEY_MYPROJ_PROD }} secrets download --project=${{ github.event.repository.name }} --config=prod --format=env --no-file) goreleaser release ``` **The run wrapper** ```yaml name: Release on: push: tags: - "v*" jobs: production: runs-on: ubuntu-latest env: TUNA_API_KEY: ${{ secrets.TUNA_API_KEY_MYPROJ_PROD }} TUNA_SECRETS_PROJECT: ${{ github.event.repository.name }} TUNA_SECRETS_CONFIG: prod steps: - uses: actions/checkout@v4 - run: tuna secrets run -- goreleaser release ``` Note that `project = github.event.repository.name` we get directly from the environment, this is possible if the project name in GitHub and Tuna Secrets match, **config** is set explicitly. *** ##### 🧰 Individual secrets in scripts When a job needs a single secret, there is no point in exporting the whole config. The `--plain` flag prints the value without a trailing newline, so it is convenient to substitute right into a command: ```yaml publish: script: - docker login -u "$CI_REGISTRY_USER" -p "$(tuna secrets get REGISTRY_PASSWORD --plain)" "$CI_REGISTRY" ``` The `-o json` flag is available on every command, including writes — which is exactly what scripts need: ```yaml rotate-token: script: - openssl rand -hex 32 | tuna secrets set API_TOKEN - tuna secrets get API_TOKEN -o json | jq -r '.API_TOKEN.updated_at' ``` Rotation requires a service key with `write` access. And here is a check that stage and prod did not end up with the same passwords. It works with [fingerprints](https://tuna.am/en/docs/secrets/cli.md#value-fingerprints) rather than values, so its result is safe to leave in the job log: ```yaml check-secrets: script: - tuna secrets list -c stage -o json | jq -r 'to_entries[] | "\(.key) \(.value.fingerprint)"' > stage.txt - tuna secrets list -c prod -o json | jq -r 'to_entries[] | "\(.key) \(.value.fingerprint)"' > prod.txt - comm -12 stage.txt prod.txt ``` *** ##### 🧵 Bitbucket Pipelines Create [service keys](https://tuna.am/en/docs/secrets.md#service-keys) for the required configurations and save the key in [Bitbucket Pipelines variables](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/). Suppose you have a project `myproj` with an environment `prod`, create a service key and save it to the variable `TUNA_API_KEY_MYPROJ_PROD` in Bitbucket Pipelines variables. Below is an example of how it might look in a Job: **Environment variables** ```yaml pipelines: tags: "v*": - step: name: Production Build deployment: prod script: - export TUNA_API_KEY=$TUNA_API_KEY_MYPROJ_PROD - export TUNA_SECRETS_PROJECT=$BITBUCKET_REPO_SLUG - export TUNA_SECRETS_CONFIG=prod - export TUNA_SECRETS_FORMAT=env - export TUNA_SECRETS_NO_FILE=true - source <(tuna secrets download) - goreleaser release ``` **Flags** ```yaml pipelines: tags: "v*": - step: name: Release deployment: prod script: - source <(tuna --api-key=$TUNA_API_KEY_MYPROJ_PROD secrets download --project=$BITBUCKET_REPO_SLUG --config=prod) --format=env --no-file) - goreleaser release ``` Note that `project = $BITBUCKET_REPO_SLUG` we get directly from the environment, this is possible if the project name in `BITBUCKET_REPO_SLUG` and Tuna Secrets match, **config** is set explicitly. --- ### Guides #### [📄️Direnv](https://tuna.am/en/docs/secrets/guides/direnv.md) [Integration with direnv](https://tuna.am/en/docs/secrets/guides/direnv.md) --- ### Direnv Learn how to automatically load environment variables in your development environment. `direnv` is a tool that automatically loads environment variables when you `cd` into a directory and removes them when you leave that directory. This guide shows how to use it together with Tuna. > **Attention.** We do not recommend loading all secrets into the shell environment this way. However, some developers may need this functionality. This is purely a guide on using Tuna Secrets with `direnv` and is not a recommendation of this tool or workflow. #### Requirements * Install [direnv](https://direnv.net/) * A shell compatible with `direnv` #### Project Setup To do this, you first need to run the `tuna secrets setup` command in the project. Make sure you have completed the setup process and linked the directory to a project and configuration. #### Creating the `.envrc` File In the root of the project directory, create a `.envrc` file with the following content: > **Tip.** **Option 1** appeared later, as the `env-no-quotes` flag was added in a later version of the client. **Option 1** ```shell export <(tuna secrets download --no-file --format env-no-quotes) ``` **Option 2** ```shell set -a source <(tuna secrets download --no-file --format env) set +a ``` The `set -a` command enables automatic export of all subsequent variable assignments, and `set +a` disables this feature. *** After saving, you may receive a warning from `direnv`: ```shell direnv: error /path/to/project/.envrc is blocked. Run `direnv allow` to approve its content ``` By default, `direnv` will not load the contents of the `.envrc` file until you approve it. Since you just created it, allow execution with: ```shell direnv allow ``` After that, you will see a message about loading environment variables: ```shell direnv: loading /path/to/project/.envrc direnv: export +YOUR_VARIABLE ``` When you leave the directory, `direnv` will unload the variables: ```shell direnv: unloading ``` > **The above solution does not guarantee correct operation with all secret values. In particular, errors may occur with multiline secrets. Direnv may also incorrectly handle secrets containing characters `?`, `*` and \`\`\`. There may also be issues with other characters (e.g., `$` and `#`).** #### Overriding Individual Variables In some cases, you may need local overrides that are inconvenient to set through [configurations](https://tuna.am/en/docs/secrets.md#configurations). In this case, you can modify the `.envrc` file to look for another file and, if it exists, include it after loading secrets from Tuna. Let's call this file `.env.local`, and your `.envrc` will look like this: ```shell set -a source <(tuna secrets download --no-file --format env) test -f .env.local && source .env.local set +a ``` It will work the same as before, but if the `.env.local` file exists, the environment variables defined in it will take priority over those loaded from Tuna. --- ### Technical Support This page forms an integral part of the [Terms of Service](https://tuna.am/en/tos.md) (clause 17.3). #### Contact channels | Channel | What for | | ----------------------------------------------------------- | ------------------------------------------------------------------------------- | | | Main channel: questions about the services, subscription, payment and documents | | | Abuse reports, see [Abuse and Complaints](https://tuna.am/en/docs/abuse.md) | | [Telegram community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) | Talking to the team and other users, quick questions | Please send subscription-related requests from the email address specified in your personal account — that way we can identify the account without additional checks. #### Working hours Support works on business days from 10:00 to 17:00 Yerevan time (Asia/Yerevan, UTC+4). Requests received outside working hours are queued and handled on the next business day. Incidents affecting platform availability are handled as they are detected, including outside these hours. #### Response times | Plan | Response time for the first request | | ----------------- | ----------------------------------- | | Free, Novice, Pro | 3 business days | | Legend | 1 business day, priority queue | The response time is the time to the first substantive reply, not the time to resolve the issue. We do not guarantee that a malfunction will be resolved within a specific period (clause 17.4 of the [Terms of Service](https://tuna.am/en/tos.md)). Individual service terms, including guaranteed recovery times and higher availability requirements, are arranged under a separate agreement — write to . #### What support covers * Diagnosing problems with the platform services and the client software. * Help with configuring tunnels, gateways, traffic policies and other services within the scope of the documentation. * Questions about plans, subscriptions, payments and closing documents. * Accepting bug and vulnerability reports. #### What support does not cover * Configuration of the user's infrastructure, servers and network equipment. * Development, modification and debugging of the user's applications. * Integrations and work beyond the scope of the documentation — performed under a separate agreement (clause 17.5 of the [Terms of Service](https://tuna.am/en/tos.md)). #### How to speed things up Include in your request: * which component you are using and its version — the command-line client, the desktop application, the bastion or another platform component (for command-line components, the `version` command shows it); * your operating system and installation method; * the command or action you performed and the full error text; * the tunnel name, domain or resource identifier in the personal account; * the time the problem occurred, with the time zone. Before contacting us, take a look at [Troubleshooting](https://tuna.am/en/docs/troubleshooting.md) — common cases are covered there. Remove from logs and screenshots anything unrelated to the problem: we do not need passwords, tokens or request contents for diagnostics. --- ### Team This section of the documentation describes the features and functions when working with Tuna in a [team](https://my.tuna.am/team?lang=en). ![team-main](/en/assets/images/team-main-26fa049b61101e8df3969ff4ca542754.png) #### Overview Each Tuna platform user can be a member of multiple teams, as a creator or participant. Switching between personal and team accounts is done using the selector in the upper left corner of the screen, where there is also a button to create a new team. ![team-selector](/en/assets/images/team-selector-7ec3d42d4174364d633ed74639fa345d.png) #### Who is this for? We help companies and startups engaged in development, which often have distributed teams. With Tuna, you can significantly accelerate development and administration, simplifying interaction between participants without compromising security. * publishing local environments * transferring files directly to each other * remote access to workstations and servers on private networks * secure password transmission * publishing Demo environments to customers All this and many other use cases that you can come up with yourself will make your experience with Tuna exceptional. #### Navigation #### [📄️Members](https://tuna.am/en/docs/team/members.md) [Team member management, access roles](https://tuna.am/en/docs/team/members.md) #### [📄️Roles](https://tuna.am/en/docs/team/roles.md) [User access roles in the team](https://tuna.am/en/docs/team/roles.md) #### [📄️Integrations](https://tuna.am/en/docs/team/integrations.md) [Configuring integrations for sending notifications to third-party services](https://tuna.am/en/docs/team/integrations.md) #### [📄️Contacts](https://tuna.am/en/docs/team/contacts.md) [Contact groups for sending notifications from monitors](https://tuna.am/en/docs/team/contacts.md) #### [📄️Statistics](https://tuna.am/en/docs/team/statistics.md) [General quantitative indicators of resource usage in the team](https://tuna.am/en/docs/team/statistics.md) #### [📄️Tunnels](https://tuna.am/en/docs/team/tunnels.md) [List of active tunnels of all users in the team.](https://tuna.am/en/docs/team/tunnels.md) #### [📄️Domains](https://tuna.am/en/docs/team/domains.md) [List of reserved domains of all users in the team.](https://tuna.am/en/docs/team/domains.md) #### [📄️TCP ports](https://tuna.am/en/docs/team/tcp_ports.md) [List of reserved static TCP ports of all users in the team.](https://tuna.am/en/docs/team/tcp_ports.md) #### [📄️SSH keys](https://tuna.am/en/docs/team/ssh_public_keys.md) [List of public SSH keys of all users in the team.](https://tuna.am/en/docs/team/ssh_public_keys.md) #### [📄️Domain zones](https://tuna.am/en/docs/team/domain_zones.md) [Managing custom domain zones (wildcard)](https://tuna.am/en/docs/team/domain_zones.md) #### [📄️TLS certificates](https://tuna.am/en/docs/team/tls_certificates.md) [Managing TLS certificates in the team](https://tuna.am/en/docs/team/tls_certificates.md) #### [📄️IP policies](https://tuna.am/en/docs/team/ip_policies.md) [Team-wide access policies for tunnels by subnets](https://tuna.am/en/docs/team/ip_policies.md) #### [📄️Master keys](https://tuna.am/en/docs/team/passwords.md) [Managing members' password manager](https://tuna.am/en/docs/team/passwords.md) #### [📄️Audit logs](https://tuna.am/en/docs/team/audit_events.md) [Audit of team member actions](https://tuna.am/en/docs/team/audit_events.md) #### [📄️Organization](https://tuna.am/en/docs/team/organization.md) [🏢 Payment by invoice for companies and sole proprietors](https://tuna.am/en/docs/team/organization.md) #### [📄️Settings](https://tuna.am/en/docs/team/settings.md) [Managing basic team settings, security and more](https://tuna.am/en/docs/team/settings.md) --- ### Audit logs [This section](https://my.tuna.am/team_audit_events?lang=en) contains a list of all team member actions. Filtering by users, object types and actions is available. ![audit_events](/en/assets/images/audit_events-3fdc6bef8021cc66654d32fca66040a9.png) > **Tip.** This section is available to the **Founder** and **Owners**. #### Overview Here you can always track who, when, how interacted with resources, for example added/removed members, domains, ports and so on. > **Info.** By default, Audit logs are **disabled**, you can enable them in the team [settings](https://tuna.am/en/docs/team/settings.md), in the **Security** section. --- ### Contacts [Open in personal cabinet](https://my.tuna.am/team_contacts?lang=en) Contacts are groups of notification recipients. Each group can contain email addresses and [integrations](https://tuna.am/en/docs/team/integrations.md) (Telegram, Slack, etc.). ![Monitors home page](/docs/img/team/contacts.png) *** #### Concept The notification system is built on three levels: ```shell ┌────────────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │ Monitor or report │────▶│ Contact group │────▶│ Contact │ │ │ │ "DevOps" │ │ (email/integration) │ └────────────────────────┘ └──────────────────┘ └──────────────────────┘ ``` 1. **Monitor or report** — event source 2. **Contact group** — a set of recipients for a specific purpose 3. **Contact** — a specific recipient (email or integration) This structure allows you to: * Flexibly manage notification recipients * Use the same group for multiple monitors * Easily add/remove recipients *** #### Contact groups A contact group is a named set of notification recipients. ![Monitors home page](/docs/img/team/contacts_groups.png) ##### Usage examples | Group | Purpose | Contacts | | ----------------- | ----------------- | ----------------------------------- | | **DevOps** | Critical services | Team email, Telegram, Slack #alerts | | **First line** | All alerts | Support email | | **Backend** | Backend services | Developers' Telegram | | **Night on-call** | Night alerts | On-call engineer's Telegram | *** #### Contact types | Type | Icon | Description | | --------------- | ---- | ----------------------- | | **Email** | | Email address | | **Telegram** | | Telegram chat/group | | **Slack** | | Slack channel | | **Discord** | | Discord channel | | **VK Teams** | | VK Teams chat | | **Google Chat** | | Google Chat | | **Webhook** | | Arbitrary HTTP endpoint | *** #### Related sections * [Integrations](https://tuna.am/en/docs/team/integrations.md) — configuring notification channels * [Monitors](https://tuna.am/en/docs/monitors.md) — alerts on triggers * [Reports](https://tuna.am/en/docs/reports.md) — notifications about new reports --- ### Domain zones [In this section](https://my.tuna.am/team_domain_zones?lang=en) you can add your own domain zones (wildcard). ![domain_zones](/en/assets/images/domain_zones-561cf25dd9ba3a4321037fb19807bf2a.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. #### Overview Domain zone ownership is unique within the Tuna platform. If you have verified ownership of a zone, for example, `tuna-dev.ru`, then from that moment only members of your team can reserve domains matching the pattern `*.tuna-dev.ru`. You can reserve second-level and higher zones, for example, you can add separately `foo.tuna-dev.ru` and `bar.tuna-dev.ru`. > **Attention.** Connecting a zone is primarily needed for your security, you could have previously specified `*.your-domain.ru` in DNS settings pointing to the required node, but then any platform user could connect **Their own domain** in this zone and thereby compromise you. ![domain_zone-add](/en/assets/images/domain_zone-add-da33cf65180b722dc6bfbde25e44b791.png) When adding a zone, you will need to specify the region to which your zone will be linked, the zone itself and optionally a [TLS certificate](https://tuna.am/en/docs/team/tls_certificates.md). Then you will need to pass verification so we can ensure that you actually own the domain. ![domain_zones-verify](/en/assets/images/domain_zones-verify-8976edef4e6e4768f81af07827fa699c.png) #### Usage ##### Subdomains in the zone After successfully passing ownership verification, users will be able to start using it. On the [domain management](https://my.tuna.am/domains?lang=en) page, the user will see all available zones in the team linked to this region. ![domain-add](/en/assets/images/domain-add-0ed6d8a0d3b5fd9a224cc2d3175e6b6b.png) This greatly simplifies configuration, as the user only needs to specify the required subdomain. If you have not specified your own [TLS certificate](https://tuna.am/en/docs/team/tls_certificates.md) for the zone, a unique [Let's encrypt](https://letsencrypt.org/) certificate will be issued for this domain. ##### Custom wildcard TLS certificate If you have added and specified your own [TLS certificate](https://tuna.am/en/docs/team/tls_certificates.md), then your certificate will be used for all subdomains in this zone and Let's encrypt will not be issued. This significantly speeds up the addition of new subdomains. It also gives you the ability to use any purchased certificates, self-signed or for example from [Ministry of Digital Development](https://www.gosuslugi.ru/tls). ![domain_zones-tls](/en/assets/images/domain_zones-tls-8cfa29c8835ad88220c78546050962ea.png) There is also the ability to use your zone as the default zone in this region. This means that all dynamically created [HTTP tunnels](https://tuna.am/en/docs/tunnels/http.md) will have a domain in your zone. For example, when running `tuna http -f .` you will immediately get the address `4l7mqf-212-49-103-2.your-domain.ru` instead of the standard `4l7mqf-212-49-103-2.ru.tuna.am`, i.e. you don't need to specify the `--domain` flag and use some static domain, you get a branded (white label) domain *out of the box*! > **Note.** You can hide user IP addresses in dynamic domains, more details in the [domains](https://tuna.am/en/docs/team/domains.md) section. --- ### Domains [This section](https://my.tuna.am/team_domains?lang=en) displays reserved domains of all team members. Filtering by users is available. You can delete any user's domain except the [free permanent subdomain](https://tuna.am/en/docs/tunnels/http.md#free-subdomain), which cannot be deleted or transferred. ![domains](/en/assets/images/domains-ed8b4c8b83ad1c07d925597dc17be54e.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. #### Overview Also on the Legend plan, the ability to hide user IP addresses in dynamic domains becomes available. ![domains-dynamic-hide-ip-off](/en/assets/images/domains-dynamic-hide-ip-off-fd38f9f77e39b86a731201e0fadc8a50.png)
*Default behavior* ![domains-dynamic-hide-ip-on](/en/assets/images/domains-dynamic-hide-ip-on-ce47a0ee44dd8b0919c460910fa4e536.png)
*IP hiding enabled* > **Note.** How dynamic domain addresses are formed and why user IP addresses appear in them is described [here](https://tuna.am/en/docs/abuse.md#anonymity). You can enable this option in the team [settings](https://tuna.am/en/docs/team/settings.md), in the **Security** section. ![domains-dynamic-hide-ip](/en/assets/images/domains-dynamic-hide-ip-c22345a443508954db3f726418cad977.png) --- ### Integrations [Open in personal cabinet](https://my.tuna.am/team_integrations?lang=en) Integrations let you send notifications about monitor and report events to various messengers and alerting systems. ![Integrations home page](/docs/img/team/integrations.png) > **Info.** Available only with a [subscription](https://tuna.am/#pricing) on the **Legend** plan. *** #### Supported integrations | Integration | Description | | ---------------- | ---------------------------------------------- | | **Telegram** | Notifications to a Telegram chat or group | | **Slack** | Notifications to a Slack channel via Webhook | | **Discord** | Notifications to a Discord channel via Webhook | | **VK Teams** | Notifications to VK Teams (formerly MyTeam) | | **Google Chat** | Notifications to Google Chat via Webhook | | **Webhook** | Arbitrary HTTP Webhook | *** #### Telegram Send notifications to a Telegram chat or group. ##### Setup 1. Open Telegram and find the bot **@TunaAmBot** 2. Start a dialog with the bot using `/start` 3. For a group chat — add the bot to the group and run `/start@TunaAmBot` 4. The bot will send a confirmation link 5. Open the link — the integration will be created automatically ##### Parameters | Parameter | Description | | ----------- | ----------------------------------------- | | **Name** | Integration name for identification | | **Chat ID** | Chat identifier (filled in automatically) | *** #### Slack Send notifications to a Slack channel via Incoming Webhook. ##### Setup 1. Go to your Slack workspace settings 2. Create a new Incoming Webhook: [Slack documentation](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/) 3. Copy the webhook URL 4. In Tuna, create a Slack integration and paste the URL ##### Parameters | Parameter | Description | | --------------- | ----------------------------------- | | **Name** | Integration name for identification | | **Channel** | Channel name (e.g., #alerts) | | **Webhook URL** | Incoming Webhook URL | *** #### Discord Send notifications to a Discord channel via Webhook. ##### Setup 1. Open the Discord channel settings 2. Go to **Integrations** → **Webhooks** 3. Create a new Webhook: [Discord documentation](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) 4. Copy the webhook URL 5. In Tuna, create a Discord integration and paste the URL ##### Parameters | Parameter | Description | | --------------- | ----------------------------------- | | **Name** | Integration name for identification | | **Webhook URL** | Discord Webhook URL | *** #### VK Teams Send notifications to VK Teams (formerly MyTeam). ##### Setup 1. Open VK Teams and find the bot **@metabot** 2. Create a new bot and obtain a token 3. Get the Chat ID of the chat where notifications will be sent 4. In Tuna, create a VK Teams integration More info: [Getting a VK Teams token](https://teams.vk.com/profile/70001) ##### Parameters | Parameter | Description | | ----------- | ---------------------------------------------------------- | | **Name** | Integration name for identification | | **Chat ID** | Chat identifier | | **Token** | Bot token (format: `000.0000000000.0000000000:0000000000`) | *** #### Google Chat Send notifications to Google Chat via Webhook. ##### Setup 1. Open the desired chat in Google Chat 2. Go to chat settings → **Webhooks** 3. Create a new Webhook: [Google documentation](https://developers.google.com/workspace/chat/quickstart/webhooks#create-webhook) 4. Copy the webhook URL 5. In Tuna, create a Google Chat integration and paste the URL ##### Parameters | Parameter | Description | | --------------- | ----------------------------------- | | **Name** | Integration name for identification | | **Webhook URL** | Google Chat Webhook URL | *** #### Webhook (Generic) Send notifications to an arbitrary HTTP endpoint. ##### Setup 1. Create an endpoint on your server to receive notifications 2. In Tuna, create a Webhook integration and specify the URL ##### Parameters | Parameter | Description | | --------------- | ----------------------------------- | | **Name** | Integration name for identification | | **Webhook URL** | URL of your endpoint | ##### Request format When an event occurs, Tuna sends a POST request with a JSON body: ```json { "monitor_id": "2aB3cD4eF5gH6iJ7kL8m", "monitor_name": "My Service", "event_type": "fail", "message": "Monitor is down", "timestamp": "2024-01-15T10:30:00Z" } ``` *** #### Testing an integration Before saving an integration, you can send a test message to verify the configuration is correct. 1. Fill in the integration parameters 2. Click the **Test** button 3. Check that the message arrives in the target service 4. Save the integration *** #### Use in contacts Once an integration is created, it can be added to a [contact group](https://tuna.am/en/docs/team/contacts.md). When a monitor event occurs, notifications will be sent to all integrations added to the selected contact groups. --- ### IP policies [In this section](https://my.tuna.am/team_ip_policies?lang=en) you can configure access policies for tunnels from specific IP addresses and subnets. ![team_ip_policies_1](/en/assets/images/team_ip_policies_1-b6efb1916fbb84bbbcc4e979cba39ceb.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. #### Overview If you want to be sure that all tunnels and gateways are accessible only from specific subnets, you can create team-wide IP policies and block or allow certain subnets by CIDR. ![team_ip_policies_2](/en/assets/images/team_ip_policies_2-3032d9646251b7e280198f20bd0fc49d.png) --- ### Members [This section](https://my.tuna.am/team?lang=en) contains functions for managing team members. ![members](/en/assets/images/members-50b66a5143adc2e591a6ef8899d9ae7e.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. ##### Adding and removing Click **+Invite to team**, specify Email and Role, the user will receive an email with an invitation link. If the user does not have an account in the personal cabinet [my.tuna.am](https://my.tuna.am/?lang=en), they will need to register first and then follow the invitation link to join the team. ![members-invite](/en/assets/images/members-invite-a27aab53d80f7f297ea0728cb8a8f68e.png) ##### Team roles * **Founder** - team creator, can delete it. Has all privileges, can invite/remove team members, assign roles, view statistics, manage subscription, change team settings, view audit logs, view and manage all resources of all team members. There can be only 1 founder, their role cannot be changed and they cannot be removed. * **Owner** - all the same privileges as the founder, but cannot remove/change their role or delete the team. There can be multiple owners. * **Administrator** - can invite/remove team members, assign roles, view statistics, view and manage all resources of all team members. * **User** - can only use resources allocated to them in the team, create tunnels, reserve TCP ports and domains, access shared password folders. ![members-roles](/en/assets/images/members-roles-28cb3367274256c2b62f44bd931aa074.png) Administrators do not have access to all menu items: ![members-admin](/en/assets/images/members-admin-85e6856e6ad83447fd50ca756a165fb1.png) ##### Subscription usage You can specify whether a user will be counted in the subscription. You can add a user but disable them, i.e. not pay for them. For example, a manager creates a team and invites developers, buys a subscription for the number of developers, but does not pay for themselves, as they do not create tunnels, do not connect to bastion servers and do not use resources. At the same time, they have full control, access to audit, password manager, subscription management, etc. ![members-disable](/en/assets/images/members-disable-9f26005e6b4ea4001577dfa2d3593b25.png) --- ### Organization [In this section](https://my.tuna.am/team_organization?lang=en) you can add organization details for subsequent payment of services by bank transfer. ![organization](/en/assets/images/organization-34130844df77451a8c1307208d30a4fc.png) > **Tip.** This section is available to the **Founder** and **Owners**. > **Note.** Currently available only for organizations in Russia. ##### Instructions for switching to payment by invoice 1. Add the accountant's email to [team contacts](https://my.tuna.am/team_settings?lang=en) in the [Settings](https://tuna.am/en/docs/team/settings.md) section 2. Specify the [organization](https://my.tuna.am/team_organization?lang=en) details in the [Organization](https://tuna.am/en/docs/team/organization.md) section 3. On the [subscription](https://my.tuna.am/subscription?lang=en) management page, specify the payment method - **Bank transfer** 4. Click buy subscription (an invoice will be generated and sent to all contacts from step 1) 5. After the funds are received, the subscription will be automatically activated --- ### Master keys [In this section](https://my.tuna.am/team_passwords?lang=en) you can see a list of all members who use the password manager. ![passwords](/en/assets/images/passwords-8468be3f6ccf356e2779504ba7f4ddf1.png) > **Tip.** This section is available to the **Founder** and **Owners**. #### Overview The [Password manager](https://tuna.am/en/docs/passwords.md) in a team provides a secure (according to the [zero-knowledge](https://en.wikipedia.org/wiki/Zero-knowledge_proof) model) and simple way to share passwords, secrets, licenses and other important data for accessing team services or infrastructure, whether it's a support email box or credentials for connecting to a production database. More details about how the [Password manager](https://tuna.am/en/docs/passwords.md) works are described in a separate section. > **Attention.** **ONLY** the owner has access to their password vault. If a user has lost their Master key, it is impossible to restore access to the vault! You can only delete the vault with all passwords and the user will be able to create it again. --- ### Roles [In this section](https://my.tuna.am/team_roles?lang=en) you can create access roles to differentiate user permissions in the team. ![team_roles](/en/assets/images/team_roles-95216624e6e5412fae22205348d9dd29.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. ##### Overview Roles can restrict the use of certain types of tunnels, by default all types are available to all users, or allow access to bastion servers. After adding a role, it must be applied to a [user](https://tuna.am/en/docs/team/members.md). ![](/docs/img/team/team_role_add_1.png) ![](/docs/img/team/team_role_view_1.png) --- ### Settings [This section](https://my.tuna.am/team_settings?lang=en) contains management of basic team settings, security settings and team deletion. ![settings](/en/assets/images/settings-afd72dfd79d0093cb1cad8f680f9d132.png) > **Tip.** This section is available to the **Founder** and **Owners**. #### Overview ##### Team name All team members will see the team name in the upper left switcher, so it should be understandable to everyone. ![team-selector](/en/assets/images/team-selector-7ec3d42d4174364d633ed74639fa345d.png) ##### Contacts Here you can add email addresses that will be used for notifications about upcoming payments and invoicing, for example add your accountant who pays all invoices. More details about payment by invoice are described in the [Organization](https://tuna.am/en/docs/team/organization.md) section. ##### Security Enabling [Audit logs](https://tuna.am/en/docs/team/audit_events.md) and [hiding user IP addresses](https://tuna.am/en/docs/team/domains.md) in dynamic domains. ##### Deletion The [Founder](https://tuna.am/en/docs/team/members.md) can delete the team. > **Danger.** All reserved resources will be deleted without possibility of recovery. ![team-delete](/en/assets/images/team-delete-49b95bdd1c84090644b73260a24b8d3e.png) --- ### SSH keys [This section](https://my.tuna.am/team_public_keys?lang=en) displays public [SSH keys](https://tuna.am/en/docs/guides/ssh-keys.md) of all team members. Filtering by users is available. You can delete any user's key. ![ssh_public_keys](/en/assets/images/ssh_public_keys-8e45d783ce81fb3a8b499589231fbef9.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### Statistics [In this section](https://my.tuna.am/team_statistics?lang=en) you can see general quantitative indicators of resource usage in the team. ![statistics](/en/assets/images/statistics-9159cb3f9ab17705b25497fa4191cff2.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### TCP ports [This section](https://my.tuna.am/team_tcp_ports?lang=en) displays reserved static TCP ports of all team members. Filtering by users is available. You can delete any user's port. ![tcp_ports](/en/assets/images/tcp_ports-53ab105d8348d56367745346a15e302a.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### TLS certificates [In this section](https://my.tuna.am/team_tls_certificates?lang=en) you can manage your TLS certificates. Certificates can be used in [domain zones](https://tuna.am/en/docs/team/domain_zones.md). ![tls_certificates](/en/assets/images/tls_certificates-764491682239fe3cb267a38e3049cd4c.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. #### Overview You can upload any purchased certificates, [Let's encrypt](https://letsencrypt.org/), self-signed or for example from [Ministry of Digital Development](https://www.gosuslugi.ru/tls). A certificate cannot be modified, only added or deleted. --- ### Tunnels [This section](https://my.tuna.am/team_tunnels?lang=en) displays currently active tunnels of all team members. Filtering by users is available. You can terminate any tunnel by deleting it. ![tunnels](/en/assets/images/tunnels-ee5bbc47c6459a16beb824b77209bbf5.png) > **Tip.** This section is available to the **Founder**, **Owners** and **Administrators**. --- ### Traffic Policies #### Overview This feature allows you to describe rules and actions for processing traffic for incoming requests and more flexibly configure interaction with your application. > **Info.** Available only with a [subscription](https://tuna.am/#pricing) on the **Legend** plan. To understand why policies are needed and how they work, let's look at an example of enabling basic authorization only for the `/admin` prefix: ```yaml --- rules: - name: Authorization for admin panel expressions: - req.URL.Path contains "/admin" actions: - name: Enable basic auth type: basic_auth config: userList: admin: password ``` Here we have described 1 rule (rules), it has an expression (expressions), it contains a condition in which we check that the request is intended for the `/admin` prefix, if the condition is true, an action occurs (actions), in the example we apply basic authorization, set a list of `username`, `password` pairs. If the user passes authorization, then traffic is directed to your service. This rule will allow you to open access to the site under development for everyone, for example for customers or colleagues, but at the same time you can restrict access to private parts of the site. This is just one example, below you will find a complete description and more live examples. To make it easier for you to write the necessary rule, we added a [debugger](https://my.tuna.am/policy_debug?lang=en) right in the dashboard, where you can check if the rule works correctly, and several examples are also provided: [![policy_debug](/docs/img/policy_debug.png)](https://my.tuna.am/policy_debug?lang=en) #### Rules Rules consist of actions - `actions` (*required*), which must take effect and expressions - `expressions` (*optional*), filtering traffic to which they are applied. Rules are evaluated and executed sequentially in the order they are described, without interrupting their execution. That is, if a request falls under several rules at once, all actions will also be applied to this request. You can also specify a name `name`. Rules are written in **YAML**, see [below](#policy-source) for where they are stored. For example, there are 2 rules. In the first one we enable basic authorization for all requests to the `/admin` prefix. In the second one we check if there is an `Accept-Language` header and if it contains Russian language `ru-RU`. The client must first enter username and password, and then the ban by `Accept-Language` header will work if the header value contains `ru-RU`. ```yaml --- rules: - expressions: - req.URL.Path contains "/admin" actions: - type: basic_auth config: userList: admin: admin - expressions: - any(req.Headers["Accept-Language"], {# matches "ru-RU"}) actions: - type: deny ``` ##### Expressions > **Note.** Website with syntax description [expr-lang](https://expr-lang.org/) Expressions are based on [expr-lang](https://expr-lang.org/) syntax. This is a rich language with many functions, comparison operations, inclusion, string operations and much more. Listing expressions in a list is perceived as a logical **AND** (`and`). Example of blocking `POST` requests from bots: ```yaml --- rules: - expressions: - lower(req.UserAgent) matches "(chatgpt-user|gptbot)" - req.Method in ["POST"] actions: - type: deny ``` ###### Variables with request data To work with an incoming request, there are several variables for getting information or metadata: | Name | Description | | ----------------- | ---------------------------------------------------------------------------------------------------------------- | | req.Proto | Contains the HTTP protocol version that was used when sending the request (for example `HTTP/1.1` or `HTTP/2.0`) | | req.Host | Contains the URL host, including the port if specified (alias for req.URL.Host) | | req.Method | Contains the request method `GET` / `POST` and others | | req.URL | Full request URL (has its own methods, which are described in the table below) | | req.UserAgent | Content of the User-Agent header | | req.RemoteAddr | Client IP address from which the request came | | req.ContentLength | Returns the length of the request body in bytes, if known, taken from the `Content-Length` header. | | req.Headers | List of all headers | Variables inside **req.URL** allow you to get individual parts of the URL: | Name | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | req.URL.Scheme | Contains the URL scheme, which defines the protocol (for example, http, https, ftp, etc.). | | req.URL.Opaque | Contains opaque (encoded) URL data that cannot be directly parsed into parts such as scheme, host and path. | | req.URL.User | Contains user information, containing username and possibly password in the URL. | | req.URL.Host | Contains the URL host, including the port if specified (for example, `example.com:8080`). | | req.URL.Path | Contains the URL path, which points to a specific resource (for example, `/articles/2024/`). | | req.URL.RawPath | Contains the encoded version of the path (if it differs), may be useful for cases where the path contains special characters. | | req.URL.RawQuery | Contains encoded query parameters without the `?` character (for example, `name=John&age=30`). | | req.URL.Fragment | Contains the URL fragment, which points to a specific part of the document, without the `#` character (for example, `section2`). | | req.URL.RawFragment | Contains the encoded version of the fragment if it contains special characters (for example, spaces or Unicode characters). | Request metadata: | Name | Description | | -------------- | ---------------------------------------------------- | | meta.Time | Exact time when the request arrived, +- current time | | meta.RequestID | Unique request identifier | ###### Built-in functions In addition to the fairly rich [expr-lang](https://expr-lang.org/) syntax, built-in functions are also available for quick work with data. | Name | Description | Example | | ---------- | --------------------------------------------------------- | ---------------------------------------- | | IpInRange | Checks if an IP is in a subnet | IpInRange(req.RemoteAddr, "127.0.0.0/8") | | IsIPV6 | Checks if a string is an IPv6 address | IsIPV6(req.RemoteAddr) | | IsIPV4 | Checks if a string is an IPv4 address | IsIPV4(req.RemoteAddr) | | IsIP | Checks if a string is an IP address | IsIP(req.RemoteAddr) | | RandInt | Generate a random integer | RandInt(0, 10) | | RandDouble | Generate a random decimal number in the range from 0 to 1 | RandDouble() | ###### Expression examples Check that the request came via `https`: ```yaml - expressions: - req.URL.Scheme == "https" ``` Request contains the `/admin` prefix: ```yaml - expressions: - req.URL.Path contains "/admin" ``` Request is not from subnet `10.0.0.0/8`: ```yaml - expressions: - not IpInRange(req.RemoteAddr, "10.0.0.0/8") ``` ##### Actions Execution of actions depends on whether the condition in expressions is true, if the expression is missing, then the action is always executed. The action must contain a type `type` and configuration `config` (not always required). You can also specify a name `name`. ###### Action types Several types of actions are available, some modify the request, others are responsible for independent processing. You can specify multiple types, they will be processed in order. ###### add_headers Add headers. You can add headers both when passing them to the upstream application `context: request`, and when responding to the client `context: response`. The keyword `headers` defines a key=value map, where the key is the header name, and the value is the header content. ```yaml --- rules: - actions: - type: add_headers config: context: request headers: foo: bar qwe: rty - actions: - type: add_headers config: context: response headers: foo: bar qwe: rty ``` ###### remove_headers Remove headers. You can remove headers both when passing them to the upstream application `context: request`, and when responding to the client `context: response`. The keyword `headers` defines a list of headers. ```yaml --- rules: - actions: - type: remove_headers config: context: request headers: - foo - qwe - actions: - type: remove_headers config: context: response headers: ["foo", "qwe"] ``` ###### basic_auth Enable basic authorization. The keyword `userList` defines a key=value map, where the key is `username`, and the value is `password`. ```yaml --- rules: - actions: - type: basic_auth config: userList: admin: admin foma: kiniaev ащьф: лштшфум ``` ###### url_rewrite Rewrite URI. The keyword `from` defines the *path* from which the request to the upstream server should be rewritten, and `to` defines the *path* that will be passed to the upstream server. Regular expressions also work. ```yaml --- rules: - actions: - type: url_rewrite config: from: /foo to: /bar ``` ###### redirect URL redirect. The keyword `to` defines the *Location* to redirect to, you can also override `statusCode` (default 307). The keyword `headers` defines a key=value map, where the key is the header name, and the value is the header content. ```yaml --- rules: - actions: - type: redirect config: to: /foobar statusCode: 304 headers: foo: bar ``` ###### deny Prohibit access to the upstream application. You can override `statusCode` (default 403). ```yaml --- rules: - actions: - type: deny config: statusCode: 451 ``` ###### restrict_ips Restrict access by subnets. The keyword `allow` defines a list of subnets from which access is allowed. The keyword `deny` defines a list of subnets from which access is prohibited. ```yaml --- rules: - actions: - type: restrict_ips config: allow: - 127.0.0.1/32 deny: - 10.0.0.10/32 ``` ###### rate_limit Limit access by number of requests. The keyword `rate` defines the time interval, and `capacity` the number of requests that can be made. When this threshold is exceeded, the client will start receiving `429 Too Many Requests`. ```yaml --- rules: - actions: - type: rate_limit config: rate: "60s" capacity: 2 ``` ###### custom_response Custom response. The required parameter `statusCode` defines the HTTP status. You can also pass a body, the content is set in the `content` parameter. And headers, `headers` defines a key=value map, where the key is the header name, and the value is the header content. ```yaml --- rules: - actions: - type: custom_response config: statusCode: 200 content: | User-agent: * Disallow: / headers: content-type: "text/plain" ``` ###### reverse_proxy Reverse proxy to another service. With this action, you can direct individual requests to a third-party service - this can be useful, for example, when developing microservices. The required parameter `url` specifies an alternative point for the reverse proxy, the optional parameter `onError` defines the behavior when the request could not be passed to the third-party service: * `halt` (default) - request processing will be interrupted on error; * `continue` - the request will be processed further according to downstream rules. Standard behavior: ```yaml --- rules: - actions: - type: reverse_proxy config: url: "http://127.0.0.1:8000" ``` With the `onError` parameter set to `continue`: ```yaml --- rules: - actions: - type: reverse_proxy config: url: "http://127.0.0.1:8000" onError: continue - type: custom_response config: statusCode: 500 content: Unavailable ``` In this case, if the resource `http://127.0.0.1:8000` is unavailable, HTTP 500 and the text `Unavailable` will be returned in response to the request. ###### compress_response Compress the response. Compresses the response if the upstream application did not do it itself. The optional parameter `algorithms` defines the list of allowed algorithms: `gzip`, `deflate`, `br`, `zstd` (all four by default). Any other value fails policy validation. ```yaml --- rules: - actions: - type: compress_response config: algorithms: [br, gzip] - type: reverse_proxy config: url: "https://example.com" ``` The action does not respond to the client by itself. It compresses the response produced by the following actions and rules (`reverse_proxy`, `custom_response` and others), so it must be placed **before** the action that produces the response. If several `compress_response` actions match, the first one applies. The algorithm is chosen from the client's `Accept-Encoding` header, taking `q` weights and `*` into account. With equal weights, the algorithm listed earlier in the header wins. If the client does not ask for any of the allowed algorithms or rejects them with `q=0`, the response is sent uncompressed. When compressing, the `Content-Encoding` and `Vary: Accept-Encoding` headers are added to the response, and `Content-Length` and `Accept-Ranges` are removed. A strong `ETag` becomes weak (`W/"..."`). The response is compressed on the fly without buffering, so SSE and streaming responses keep working: events are not held back. The response is not compressed if: * it already has a `Content-Encoding` header; * it is a partial response (`Content-Range`); * the request method is `HEAD`; * the response status is 1xx, 204 or 304; * the connection switches to WebSocket or another protocol via `Upgrade`; * the content is already compressed by nature: `image/*` (except SVG), `video/*`, `audio/*`, `font/woff*`, archives (zip, gzip, 7z, rar, bzip2, xz, lzma, zstd, brotli). To check compression, send a GET request (`curl -I` sends `HEAD`, and such a response is not compressed): ```bash curl -s -o /dev/null -D - -H 'Accept-Encoding: br' https:/// ``` The response headers should contain `content-encoding: br` and `vary: Accept-Encoding`. Without the `Accept-Encoding` header the response comes uncompressed. #### Where rules are stored By default rules are read from the `.tuna.yml` file in the current directory. The path can be overridden with the `--policy-file` flag or the `TUNA_POLICY_FILE` environment variable. ##### Loading from a URL Starting with version **0.36.0**, rules for `tuna http` can be loaded from a URL with the `--policy-url` flag or the `TUNA_POLICY_URL` environment variable. This lets you keep one policy in a repository or storage and use it on different machines: ```shell tuna http 8080 --policy-url=https://gitlab.com/example/policies/-/raw/main/policy.yml ``` * The URL must be `https`. `http` is allowed only for `localhost`, `127.0.0.1` and `::1`. * Redirects are followed (up to 10), each one is checked by the same rule. * The format is YAML or JSON with the same structure as `.tuna.yml`. Only `rules` is taken from the response, other fields are ignored. * The response must be `200`, non-empty and no larger than 1 MB. The timeout is 15 seconds. * The policy is loaded once at startup. Restart the tunnel to apply changes. * With `--policy-url` the local `.tuna.yml` is not read for the policy. The flag cannot be combined with `--policy-file` or `TUNA_POLICY_FILE`. > **Warning.** A policy loaded from a URL can do everything a local one can, including proxying requests into your local network with `reverse_proxy`. Load it only from a source you trust. ##### Policy errors Starting with version **0.36.0**, the tunnel does not start if the policy fails to load: * the file set in `--policy-file` or `TUNA_POLICY_FILE` is not found; * the file, including the default `.tuna.yml`, cannot be parsed; * the URL response failed to load or cannot be parsed. If there is simply no `.tuna.yml` in the current directory, the tunnel starts without a policy. A tunnel with a policy starts only with a subscription on the **Legend** plan, otherwise tuna exits with `Traffic policy is available only in Team subscription`. The subscription is checked at startup: if it expires while the tunnel is running, the tunnel and its policy keep working until restart. Earlier versions of tuna printed a warning in both cases and started the tunnel anyway. #### Examples ##### Block requests by headers Block requests if there is no `X-Api-Version` header, and respond with HTTP 400 if below 3: ```yaml --- rules: - name: Block API v0 expressions: - len(req.Headers["X-Api-Version"]) == 0 actions: - name: Deny type: deny - name: 400 on API v1 and v2 expressions: - req.Headers["X-Api-Version"][0] in ["1", "2"] actions: - name: Respond with 400 JSON type: custom_response config: statusCode: 400 headers: content-type: "application/json" content: | {"msg":"You are using an old API version, minimum version is 3"} ``` ##### Reverse proxy to third-party services by prefix and headers Redirect requests to `/v1` to a third-party service on port **8000**, and requests with User-Agent containing *mobile* to the service on port **3000**. At the same time, the command `tuna http 8080` by default proxies all requests to **8080**. ```yaml --- rules: - expressions: - hasPrefix(req.URL.Path, "/v1") - lower(req.UserAgent) contains "mobile" actions: - type: reverse_proxy config: url: "http://127.0.0.1:3000" - expressions: - hasPrefix(req.URL.Path, "/v1") actions: - type: reverse_proxy config: url: "http://127.0.0.1:8000" ``` ##### Custom response on weekends ```yaml --- rules: - expressions: - now().Weekday().String() in ["Saturday", "Sunday"] actions: - type: custom_response config: statusCode: 200 content: Take a rest! ``` ##### Basic authentication ```yaml --- rules: - expressions: - req.URL.Path contains "/admin" actions: - type: basic_auth config: userList: admin: password ``` ##### Multiple actions without expressions Restrict requests from `127.0.0.1` and `10.0.0.10`, and for all others limit the number of requests per second to 2. ```yaml --- rules: - actions: - type: restrict_ips config: allow: - 127.0.0.1/32 deny: - 10.0.0.10/32 - actions: - type: rate_limit config: rate: "1s" capacity: 2 ``` ##### Rewrite with regular expression ```yaml --- rules: - expressions: - hasPrefix(req.URL.Path, "/blog") actions: - type: url_rewrite config: from: "/blog/([0-9]+)/([a-zA-Z]+)/" to: /index.php?p=$1&title=$2" ``` --- ### Troubleshooting Here we have tried to collect all possible problems that our users have encountered and describe their solutions. Most problems are related to network issues or the user environment. > **Tip.** Many errors and problems may already be solved in the latest version of the client, so before diagnosing or contacting [us](mailto:info@tuna.am) or the [community](https://telegram.me/+Kgbtt0_gJwI5ZmEy) for help, make sure you have the latest version of the client installed. Update instructions are available [here](https://tuna.am/en/docs/guides/update.md). #### Self-diagnosis of network availability If you get errors when creating a tunnel, run the `tuna diagnose` command to get diagnostic information; the report will help you better understand where exactly the problem is. ```shell Please wait, testing connectivity... TLS connectivity: Google.com: ok [1.46189625s] Yandex.ru: ok [1.246520375s] tuna API: ok [1.484186875s] DNS: api.tuna.am: alt-lb.tuna.am. → eu-lb.tuna.am. [147.45.149.16, 194.31.173.118] a.2na.am: edge-eu-a.2na.am. [2.28.136.177, 89.19.217.251] TCP connectivity: tuna edge a 2.28.136.177:443: ok [41.53925ms] tuna edge a 89.19.217.251:443: ok [38.112708ms] tuna node DE node-1-de.2na.am:443: ok [52.81425ms] tuna node RU node-1-ru.2na.am:443: ok [34.079541ms] ``` As a rule, tuna uses port `443` to connect to nodes when creating a tunnel. If the `TCP connectivity` checks fail, check whether there is a firewall that may be restricting connections. The addresses for firewall rules are listed in [Using tuna in a corporate network](https://tuna.am/en/docs/guides/corporate-network.md#firewall-addresses). #### Common errors ##### Subdomains unavailable for free plan This error appears when starting with `--subdomain` on the free plan. Without a subscription only your [free permanent subdomain](https://tuna.am/en/docs/tunnels/http.md#free-subdomain) works: other names, including ones reserved earlier with a subscription, are unavailable. Check the subdomain name in the [dashboard](https://my.tuna.am/domains?lang=en) and make sure the client is authorized with your token, not another account's. ##### Domain unavailable for free plan The same for `--domain`: without a subscription you can only specify your free subdomain in full, for example `--domain=brave-otter-4821.ru.tuna.am`. Custom domains and other users' subdomains are available only with a [subscription](https://tuna.am/#pricing). ##### Tunnel limit reached If you see the error `Tunnel limit reached`, although you know that this is not actually the case, it may also mean that there is a hung active tunnel. Delete the hung tunnel in the [tunnel management panel](https://my.tuna.am/tunnels?lang=en) and update the tuna client. ##### Tunnel already active ```shell ERRO[09:01:09] Failed to get connection settings: Tunnel already active Code: BadRequest Request ID: 2008cc71bdf725853748bb91d69c4669, retrying in 1s ERRO[09:01:10] Failed to get connection settings: Tunnel already active Code: BadRequest Request ID: 0118213a8f8d341f002ec30ee1499768, retrying in 2s ERRO[09:01:12] Failed to get connection settings: Tunnel already active Code: BadRequest Request ID: 608d043eb16376a619b671eb0d33dc0b, retrying in 4s ``` This `Tunnel already active` error means that the tunnel already exists, possibly hung, or running somewhere in the background. > **Tip.** The reconnect issue is fixed in the clients > > * tuna-cli v0.28.0 > * tuna-desktop v0.2.0 #### Testing tunnel speed If you need to estimate the throughput of a tunnel, the methods for testing HTTP and TCP tunnels are described below. > **Info.** We do not impose any restrictions on the data transfer rate through tunnels. The resulting speed depends on the bandwidth of your internet channel and network latencies on the route: from the end user to the [node](https://tuna.am/en/docs/tunnels/guides/locations.md) through which the tunnel is built, and from the node to the Tuna client. For testing, you will need a file of sufficient size. You can pre-download an ISO image from the Yandex mirror, for example [Ubuntu Server](https://mirror.yandex.ru/ubuntu-releases/24.04/ubuntu-24.04.5-live-server-amd64.iso): **Windows** ```powershell curl.exe -L -o ubuntu-server.iso https://mirror.yandex.ru/ubuntu-releases/24.04/ubuntu-24.04.5-live-server-amd64.iso ``` **Unix** ```shell curl -L -o ubuntu-server.iso https://mirror.yandex.ru/ubuntu-releases/24.04/ubuntu-24.04.5-live-server-amd64.iso ``` ##### HTTP tunnels ###### Download speed (download from client) Start the built-in file server, specifying the directory with the test file: **Windows** ```powershell tuna http -f C:\path\to\directory ``` **Unix** ```shell tuna http -f /path/to/directory ``` Where the path is the directory containing the previously downloaded file. After starting the tunnel, download the file from the resulting link using `curl`: **Windows** ```powershell curl.exe -o NUL https://your-tunnel-url.tuna.am/ubuntu-server.iso ``` **Unix** ```shell curl -o /dev/null https://your-tunnel-url.tuna.am/ubuntu-server.iso ``` `curl` will show the download speed — that is the data download speed through the tunnel. ###### Upload speed (upload to client) Start the built-in WebDAV server: ```shell tuna http --webdav ``` Then upload the file through the tunnel to the machine where the Tuna client is running: **Windows** ```powershell curl.exe -T ubuntu-server.iso https://your-tunnel-url.tuna.am/ubuntu-server.iso ``` **Unix** ```shell curl -T ubuntu-server.iso https://your-tunnel-url.tuna.am/ubuntu-server.iso ``` The upload speed shown by `curl` reflects the bandwidth of the tunnel in the direction of the client. ##### TCP tunnels To test TCP tunnels, use the [iperf3](https://iperf.fr/) utility. Start the iperf3 server on the machine with the Tuna client and forward the port through a TCP tunnel: ```shell iperf3 -s -p 5201 tuna tcp -p 5201 ``` From another machine, connect to the tunnel: ```shell iperf3 -c your-tunnel-host -p your-tunnel-port ``` Install iperf3 from your OS repository or download a prebuilt binary from the [official site](https://iperf.fr/iperf-download.php): ```shell # Windows (winget) winget install ar51an.iPerf3 # macOS brew install iperf3 # Debian / Ubuntu sudo apt install iperf3 # RHEL / CentOS sudo yum install iperf3 # Fedora sudo dnf install iperf3 # openSUSE sudo zypper install iperf3 # Arch Linux sudo pacman -S iperf3 # ALT Linux sudo apt-get install iperf3 # NixOS nix-env -iA nixpkgs.iperf3 # FreeBSD sudo pkg install iperf3 ``` --- ### Tunnels Functionality that allows you to create reverse proxy [HTTP](https://tuna.am/en/docs/tunnels/http.md), [TCP](https://tuna.am/en/docs/tunnels/tcp.md) and [SSHd](https://tuna.am/en/docs/tunnels/ssh.md) tunnels using the [tuna cli](https://tuna.am/en/docs/guides/install/install-cli.md) utility or the [Tuna Desktop](https://tuna.am/en/docs/guides/install/install-desktop.md) GUI application. You will be able to get direct access from the internet to locally running websites or applications. Just run a short command in the console, get a link in response, and your friend from anywhere in the world can follow it to access the website running on your PC in the local network, all without VPN, requirements for a "white" IP, without port forwarding on the router. *** #### [🗃HTTP](https://tuna.am/en/docs/tunnels/http.md) [1 item](https://tuna.am/en/docs/tunnels/http.md) #### [📄️TCP](https://tuna.am/en/docs/tunnels/tcp.md) [Provide access to database, SSH, RDP](https://tuna.am/en/docs/tunnels/tcp.md) #### [📄️SSHd](https://tuna.am/en/docs/tunnels/ssh.md) [Provide SSH access without an SSH server](https://tuna.am/en/docs/tunnels/ssh.md) #### [📄️Postgres](https://tuna.am/en/docs/tunnels/postgres.md) [Provide access to PostgreSQL database with a query inspector.](https://tuna.am/en/docs/tunnels/postgres.md) #### [📄️Redis](https://tuna.am/en/docs/tunnels/redis.md) [Provide access to Redis with a query inspector.](https://tuna.am/en/docs/tunnels/redis.md) #### [📄️Kubernetes](https://tuna.am/en/docs/tunnels/kubernetes.md) [Temporary access to the Kubernetes API without a VPN and without sharing your credentials.](https://tuna.am/en/docs/tunnels/kubernetes.md) #### [1C](https://tuna.am/en/docs/tunnels/1c.md) [Publish a 1C:Enterprise infobase to the internet without installing or configuring a web server.](https://tuna.am/en/docs/tunnels/1c.md) #### [📄️Trigger](https://tuna.am/en/docs/tunnels/trigger.md) [Execute actions on event occurrence](https://tuna.am/en/docs/tunnels/trigger.md) #### [📄️Ports](https://tuna.am/en/docs/tunnels/ports.md) [Reserving static ports for TCP, SSHd, Postgres, Redis and Trigger tunnels](https://tuna.am/en/docs/tunnels/ports.md) #### [📄️Terminal interface](https://tuna.am/en/docs/tunnels/tui.md) [Full-screen tuna CLI interface for HTTP, TCP, SSHd, Postgres, Redis and Kubernetes tunnels, hotkeys and how to turn it off](https://tuna.am/en/docs/tunnels/tui.md) #### [📄️Traffic Policies](https://tuna.am/en/docs/traffic-policy.md) [Traffic processing policies for incoming requests](https://tuna.am/en/docs/traffic-policy.md) #### [🗃Guides](https://tuna.am/en/docs/tunnels/guides.md) [7 items](https://tuna.am/en/docs/tunnels/guides.md) #### [🗃Examples](https://tuna.am/en/docs/tunnels/examples.md) [64 items](https://tuna.am/en/docs/tunnels/examples.md) --- ### Publishing a 1C:Enterprise infobase > **Info.** Available only with a [subscription](https://tuna.am/#pricing). #### Overview The `tuna 1c` command publishes a 1C:Enterprise infobase and makes it reachable from the internet: through the web client in a browser or the 1C thin client. You do not need to install and configure a web server, forward ports or get a public IP. 1C has no HTTP server of its own: the web client and the thin client over HTTP are served by the web server extension module, which is loaded into Apache or IIS. `tuna 1c` does all of that for you: 1. finds the installed 1C platform and its web server extension module; 2. on the first run downloads Apache httpd from `releases.tuna.am` into the cache (`%LocalAppData%\tuna`); 3. starts it on `127.0.0.1` and publishes the infobase in it; 4. opens an [HTTP tunnel](https://tuna.am/en/docs/tunnels/http.md) to that web server. The infobase is available at `/1c/`, and the root of the address redirects there. #### Requirements * **A tuna [subscription](https://tuna.am/#pricing).** On the free plan the command does not start: the server replies `1C unavailable for free plan`. * **Windows x64.** The command exists only in the tuna build for Windows x64. The 1C platform itself can be either 32-bit or 64-bit. The Linux, macOS and Windows ARM64 builds do not have this command. * **The "Web server extension modules" component** in the 1C installation. Without it tuna reports that the module was not found. [How to install it](#web-module). * **A 1C license that allows connections through a web server.** The [training edition](#training) does not work. On the first run tuna also installs Microsoft Visual C++ Redistributable if it is missing: Apache does not start without it. Windows may ask for confirmation (UAC). ##### Installing the web server extension modules The component is part of the platform distribution but may be unselected by default. To add it to an existing 1C installation: 1. Open "Settings" → "Apps" → "Installed apps", find "1C:Enterprise 8" and choose "Modify" in the "…" menu. On Windows 10 and older the same is done through "Control Panel" → "Programs and Features". ![1C:Enterprise 8 in the Windows installed apps list, the Modify item](/docs/img/tuna_1c_install_apps.png) 2. In the installer choose the "Modify" mode and click "Next". ![The Modify mode in the 1C installer](/docs/img/tuna_1c_install_modify.png) 3. In the component list click the icon next to "Web server extension modules" and choose "This feature will be installed on local hard drive". ![Enabling the Web server extension modules component in the 1C installer](/docs/img/tuna_1c_install_component.png) 4. Click "Next" and wait for the installation to finish. The screenshots show the Russian version of Windows and the 1C installer. You can check the result with `tuna 1c discover`: the path to `wsap24.dll` appears next to the installation. #### Quick start Pick the 1C installation and the infobase in the wizard once: ```shell tuna 1c setup ``` The wizard asks for the 1C installation, the infobase, the local port and where to save the settings. A step with a single option is skipped. Publish the infobase and protect it with a password right away: ```shell tuna 1c --basic-auth="login:password" ``` The header of the [terminal interface](https://tuna.am/en/docs/tunnels/tui.md) shows: | Line | What it contains | | ------------ | ------------------------------------------------------------------------------------ | | `Forwarding` | Public address of the tunnel | | `Local` | Local address of the publication, it opens the infobase from the same computer | | `1C client` | A ready `/WS"…"` parameter for starting the thin client | | `Platform` | Directory and bitness of the 1C platform in use | | `Warning` | Warnings: the infobase is published without protection, the training edition is used | Open the address from the `Forwarding` line in a browser or [connect the thin client](#thin-client). If the wizard did not find 1C or the infobase, see [Diagnostics](#diagnostics). #### Examples > **Note.** You can view all current flags, hints, and examples by calling help: > > ```shell > tuna 1c --help > > ``` > > Almost all flags have corresponding [environment variables](https://tuna.am/en/docs/guides/environment-variables.md). ##### Which infobase to publish There are three ways to specify the infobase: ```shell tuna 1c "My infobase" tuna 1c "C:\Bases\MyBase" tuna 1c "Srvr=localhost;Ref=MyBase" ``` 1. The infobase name from the 1C launcher list — the one you see in the 1C start window. 2. The directory of a file infobase. 3. A connection string of a client/server infobase. Without an argument tuna takes the infobase from the [saved settings](#setup). If there are none and the launcher list has a single infobase, tuna publishes it; if there are several, it asks you to choose. > **Caution.** Publishing client/server infobases (`Srvr=…;Ref=…`) is supported, but we have not yet verified it on a production 1C cluster. If you run into a problem, [contact us](https://t.me/tuna_support). ##### Saved settings `tuna 1c setup` writes your choice to the `1c` section of the [configuration file](https://tuna.am/en/docs/guides/config-file.md) — the global `tuna.yml` or `.tuna.yml` in the current directory: ```yaml 1c: home: 'C:\Program Files (x86)\1cv8' platform: 'x86' name: 'My infobase' infobase: 'File="C:\Bases\MyBase";' port: 8314 ``` The argument and command line flags take precedence over the settings from the file, and `.tuna.yml` in the current directory overrides the global `tuna.yml`. To change the settings, run the wizard again or edit the file by hand. ##### 1C installed in a non-standard directory tuna looks for the platform in `C:\Program Files\1cv8` and `C:\Program Files (x86)\1cv8`. If 1C is installed elsewhere or you need to pick one of several installations, pass the directory and the bitness: ```shell tuna 1c "C:\Bases\MyBase" --1c-home="D:\1cv8" --1c-platform=x86 ``` ##### Local port By default the web server takes a random free port on `127.0.0.1`. To pin the port: ```shell tuna 1c "C:\Bases\MyBase" --1c-port=8314 ``` ##### Permanent address The tunnel address is set the same way as for an [HTTP tunnel](https://tuna.am/en/docs/tunnels/http.md#address): with a subdomain or your own domain. With a permanent address users do not have to change the link and the `/WS` parameter after a restart. ```shell tuna 1c "My infobase" --subdomain=my-1c tuna 1c "My infobase" --domain=1c.example.com ``` ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna 1c "My infobase" --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna 1c "My infobase" --location=ru ``` #### Connecting clients ##### Web client Open the tunnel address in a browser, tuna redirects to `/1c/`: ```text https:///1c/ ``` ![1C web client opened at the tunnel address](/docs/img/tuna_1c_web_client.png) ##### Thin client Copy the parameter from the `1C client` line in the header and start the thin client: ```shell 1cv8c.exe ENTERPRISE /WS"https:///1c" ``` The same address can be added to the launcher list as an existing infobase located on a web server. #### Security > **Danger.** `tuna 1c` opens the infobase to the whole internet: anyone can guess or find the tunnel address. Only the 1C login window stands between a stranger and your data. If you run the command without `--basic-auth` and `--key-auth`, tuna shows a warning. What you should do: * **Protect the publication with a password** — tuna asks for it before the request reaches 1C: ```shell tuna 1c "My infobase" --basic-auth="login:password" ``` * **Set strong passwords for all infobase users.** A user without a password or with a weak one is an open door to the infobase. Check accounts with administrator rights first. * **Restrict access by IP** if the users' addresses are known: ```shell tuna 1c "My infobase" --cidr-allow="203.0.113.0/24" ``` The other [access protection](https://tuna.am/en/docs/tunnels/http.md#access) options of the HTTP tunnel work too: `--key-auth`, `--cidr-deny`, `--rate-limit`. Requests to the infobase are visible in the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md), you can turn it off with `--inspect=false`. #### 1C licenses tuna publishes the infobase but does not change the 1C licensing rules: how many users can work through the web server at the same time is defined by your license. ##### The training edition does not work The training edition of the platform (the `1cv8t` directory) allows a single connection to the infobase, and the publication itself takes it. You cannot log in with either the web client or the thin client. tuna warns about it in the header and in the `tuna 1c discover` output. ##### Developer license You do not have to buy a 1C license to try it: the free developer license issued at [developer.1c.ru](https://developer.1c.ru/) is enough. That is what we tested the command with. It is a 1C license and does not replace a tuna [subscription](https://tuna.am/#pricing). See the 1C website for the terms and limits of the license. In our test one web client worked at a time: until the tab with the infobase is closed or the session times out, you cannot log in from another browser. #### Publication files For each infobase tuna creates a publication directory in `%LocalAppData%\tuna\1c\`. It contains: | File | Purpose | | ------------------------- | ------------------------------------------------ | | `default.vrd` | 1C publication descriptor | | `httpd.conf` | Apache configuration, regenerated on every start | | `access.log`, `error.log` | Web server logs | tuna does not overwrite an existing `default.vrd`, so you can edit it by hand — for example, to enable OData or HTTP services. To restore the original file: ```shell tuna 1c "My infobase" --reset-vrd ``` Set your own publication directory with the `--1c-publish-dir` flag: ```shell tuna 1c "My infobase" --1c-publish-dir="C:\tuna-1c\my-base" ``` #### Diagnostics The `tuna 1c discover` command shows what tuna found on the computer: 1C installations, the path to the web server extension module in each of them and the infobases from the launcher list. ```shell tuna 1c discover ``` | What the output says | What to do | | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | | An installation is marked `web server extension module is not installed` | [Install the component](#web-module) | | The module is marked `training edition, single connection` | This is the [training edition](#training), you cannot log in to the infobase through it | | No installations | Pass the directory with the [`--1c-home`](#home) flag | | No infobases | Pass the [infobase directory or connection string](#infobase) as the argument | If the infobase is published but does not open, check `error.log` in the [publication directory](#publish-dir). #### Flag reference | Flag | Environment variable | Description | | ----------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ | | `--1c-home` | `TUNA_1C_HOME` | 1C:Enterprise installation directory | | `--1c-platform` | `TUNA_1C_PLATFORM` | Platform bitness: `x64` or `x86` | | `--1c-port` | `TUNA_1C_PORT` | Local port of the web server, a random free port by default | | `--1c-publish-dir` | `TUNA_1C_PUBLISH_DIR` | Directory with the publication files | | `--reset-vrd` | | Regenerate `default.vrd` even if it exists | | `--basic-auth` | `TUNA_BASIC_AUTH` | [Login and password](https://tuna.am/en/docs/tunnels/http.md#basic-auth) for access to the publication | | `--key-auth` | `TUNA_KEY_AUTH` | [Key](https://tuna.am/en/docs/tunnels/http.md#key-auth) in the `X-Token` header | | `--cidr-allow`, `--cidr-deny` | `TUNA_CIDR_ALLOW`, `TUNA_CIDR_DENY` | [Access by IP subnet](https://tuna.am/en/docs/tunnels/http.md#cidr) | | `--rate-limit` | `TUNA_RATE_LIMIT` | [Request rate limit](https://tuna.am/en/docs/tunnels/http.md#rate-limit) | | `--subdomain`, `-s` | `TUNA_SUBDOMAIN` | [Subdomain](https://tuna.am/en/docs/tunnels/http.md#address) | | `--domain`, `-d` | `TUNA_DOMAIN` | [Custom domain](https://tuna.am/en/docs/tunnels/http.md#custom-domain) | | `--location`, `-l` | `TUNA_LOCATION` | Connection [region](https://tuna.am/en/docs/tunnels/guides/locations.md) | | `--inspect` | `TUNA_INSPECT` | Enable or disable the [request inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) | | `--qr` | `TUNA_QR_CODE` | Show a [QR code](https://tuna.am/en/docs/tunnels/http.md#qr) with the address | | `--token` | `TUNA_TOKEN` | Token | #### Limitations The command is new. We tested it on Windows 11 with the 32-bit 1C 8.3.27 platform and a file infobase: logging in with the web client and the thin client through the public address, starting by infobase name and without arguments after `tuna 1c setup`, `--1c-port`, `--basic-auth` in a browser. Not verified yet: * the thin client together with `--basic-auth`; * the 64-bit 1C platform and 1C 8.5; * client/server infobases on a production 1C cluster; * infobase directories with Cyrillic characters and spaces in the path; * reports and print forms in the web client; * running as a [service](https://tuna.am/en/docs/tunnels/guides/service.md). If any of this does not work for you, [contact us](https://t.me/tuna_support) and we will help. --- ### Examples Examples and step-by-step guides for setting up tunnels for various devices and scenarios. #### [📄️AFFiNE](https://tuna.am/en/docs/tunnels/examples/affine.md) [Using AFFiNE with tuna](https://tuna.am/en/docs/tunnels/examples/affine.md) #### [📄️Airflow](https://tuna.am/en/docs/tunnels/examples/airflow.md) [Using Airflow with tuna](https://tuna.am/en/docs/tunnels/examples/airflow.md) #### [📄️amoCRM](https://tuna.am/en/docs/tunnels/examples/amocrm.md) [Developing amoCRM Webhooks with tuna](https://tuna.am/en/docs/tunnels/examples/amocrm.md) #### [📄️Apache](https://tuna.am/en/docs/tunnels/examples/apache.md) [Using Apache httpd with tuna](https://tuna.am/en/docs/tunnels/examples/apache.md) #### [📄️Black Candy](https://tuna.am/en/docs/tunnels/examples/black-candy.md) [Using Black Candy with tuna](https://tuna.am/en/docs/tunnels/examples/black-candy.md) #### [📄️Cassandra](https://tuna.am/en/docs/tunnels/examples/cassandra.md) [Using Cassandra with tuna](https://tuna.am/en/docs/tunnels/examples/cassandra.md) #### [📄️ClickHouse](https://tuna.am/en/docs/tunnels/examples/clickhouse.md) [Using ClickHouse with tuna](https://tuna.am/en/docs/tunnels/examples/clickhouse.md) #### [📄️CloudPayments](https://tuna.am/en/docs/tunnels/examples/cloudpayments.md) [Processing CloudPayments events with tuna](https://tuna.am/en/docs/tunnels/examples/cloudpayments.md) #### [📄️CockroachDB](https://tuna.am/en/docs/tunnels/examples/cockroachdb.md) [Using CockroachDB with tuna](https://tuna.am/en/docs/tunnels/examples/cockroachdb.md) #### [📄️Docker](https://tuna.am/en/docs/tunnels/examples/docker.md) [Using tuna with Docker](https://tuna.am/en/docs/tunnels/examples/docker.md) #### [📄️Docusaurus](https://tuna.am/en/docs/tunnels/examples/docusaurus.md) [Writing documentation with tuna](https://tuna.am/en/docs/tunnels/examples/docusaurus.md) #### [📄️Elasticsearch](https://tuna.am/en/docs/tunnels/examples/elasticsearch.md) [Using Elasticsearch with tuna](https://tuna.am/en/docs/tunnels/examples/elasticsearch.md) #### [📄️Emby Server](https://tuna.am/en/docs/tunnels/examples/emby-server.md) [Using Emby Server with tuna](https://tuna.am/en/docs/tunnels/examples/emby-server.md) #### [📄️Flask](https://tuna.am/en/docs/tunnels/examples/flask.md) [Developing with Flask and tuna](https://tuna.am/en/docs/tunnels/examples/flask.md) #### [📄️FTP](https://tuna.am/en/docs/tunnels/examples/ftp.md) [Using FTP with tuna](https://tuna.am/en/docs/tunnels/examples/ftp.md) #### [📄️Ghost](https://tuna.am/en/docs/tunnels/examples/ghost.md) [Using Ghost with tuna](https://tuna.am/en/docs/tunnels/examples/ghost.md) #### [📄️Gitea](https://tuna.am/en/docs/tunnels/examples/gitea.md) [Using Gitea with tuna](https://tuna.am/en/docs/tunnels/examples/gitea.md) #### [📄️GitLab](https://tuna.am/en/docs/tunnels/examples/gitlab.md) [Developing GitLab Webhook with tuna](https://tuna.am/en/docs/tunnels/examples/gitlab.md) #### [📄️GitVerse](https://tuna.am/en/docs/tunnels/examples/gitverse.md) [Использование GitVerse вместе с tuna](https://tuna.am/en/docs/tunnels/examples/gitverse.md) #### [📄️Gogs](https://tuna.am/en/docs/tunnels/examples/gogs.md) [Using Gogs with tuna](https://tuna.am/en/docs/tunnels/examples/gogs.md) #### [📄️Grafana](https://tuna.am/en/docs/tunnels/examples/grafana.md) [Using Grafana with tuna](https://tuna.am/en/docs/tunnels/examples/grafana.md) #### [📄️GraphQL](https://tuna.am/en/docs/tunnels/examples/graphql.md) [Using GraphQL with tuna](https://tuna.am/en/docs/tunnels/examples/graphql.md) #### [📄️gRPC](https://tuna.am/en/docs/tunnels/examples/grpc.md) [Using gRPC with tuna](https://tuna.am/en/docs/tunnels/examples/grpc.md) #### [📄️Herd](https://tuna.am/en/docs/tunnels/examples/herd.md) [Using Herd with tuna](https://tuna.am/en/docs/tunnels/examples/herd.md) #### [📄️Immich](https://tuna.am/en/docs/tunnels/examples/immich.md) [Using Immich with tuna](https://tuna.am/en/docs/tunnels/examples/immich.md) #### [📄️InfluxDB](https://tuna.am/en/docs/tunnels/examples/influxdb.md) [Using InfluxDB with tuna](https://tuna.am/en/docs/tunnels/examples/influxdb.md) #### [📄️JetKVM](https://tuna.am/en/docs/tunnels/examples/jetkvm.md) [Remote access to JetKVM over the internet using Tuna](https://tuna.am/en/docs/tunnels/examples/jetkvm.md) #### [📄️Kiwi TCMS](https://tuna.am/en/docs/tunnels/examples/kiwi-tcms.md) [Using Kiwi TCMS with tuna](https://tuna.am/en/docs/tunnels/examples/kiwi-tcms.md) #### [📄️Kubernetes](https://tuna.am/en/docs/tunnels/examples/kubernetes.md) [Using tuna with Kubernetes](https://tuna.am/en/docs/tunnels/examples/kubernetes.md) #### [📄️MAMP](https://tuna.am/en/docs/tunnels/examples/mamp.md) [Using MAMP with tuna](https://tuna.am/en/docs/tunnels/examples/mamp.md) #### [📄️MariaDB](https://tuna.am/en/docs/tunnels/examples/mariadb.md) [Using MariaDB with tuna](https://tuna.am/en/docs/tunnels/examples/mariadb.md) #### [📄️Mattermost](https://tuna.am/en/docs/tunnels/examples/mattermost.md) [Developing Mattermost Webhook with tuna](https://tuna.am/en/docs/tunnels/examples/mattermost.md) #### [📄️Minecraft](https://tuna.am/en/docs/tunnels/examples/minecraft.md) [Using Minecraft with tuna](https://tuna.am/en/docs/tunnels/examples/minecraft.md) #### [📄️Minio](https://tuna.am/en/docs/tunnels/examples/minio.md) [Using Minio with tuna](https://tuna.am/en/docs/tunnels/examples/minio.md) #### [📄️MongoDB](https://tuna.am/en/docs/tunnels/examples/mongodb.md) [Using MongoDB with tuna](https://tuna.am/en/docs/tunnels/examples/mongodb.md) #### [📄️MsSQL](https://tuna.am/en/docs/tunnels/examples/mssql.md) [Using MsSQL with tuna](https://tuna.am/en/docs/tunnels/examples/mssql.md) #### [📄️MySQL](https://tuna.am/en/docs/tunnels/examples/mysql.md) [Using MySQL with tuna](https://tuna.am/en/docs/tunnels/examples/mysql.md) #### [📄️NanoKVM (Sipeed)](https://tuna.am/en/docs/tunnels/examples/nanokvm.md) [Remote access to NanoKVM over the internet using Tuna](https://tuna.am/en/docs/tunnels/examples/nanokvm.md) #### [📄️Netdata](https://tuna.am/en/docs/tunnels/examples/netdata.md) [Using Netdata with tuna](https://tuna.am/en/docs/tunnels/examples/netdata.md) #### [📄️NGINX](https://tuna.am/en/docs/tunnels/examples/nginx.md) [Using NGINX with tuna](https://tuna.am/en/docs/tunnels/examples/nginx.md) #### [📄️Ollama](https://tuna.am/en/docs/tunnels/examples/ollama.md) [Using Ollama with tuna](https://tuna.am/en/docs/tunnels/examples/ollama.md) #### [📄️OSPanel](https://tuna.am/en/docs/tunnels/examples/ospanel.md) [Using OSPanel with tuna](https://tuna.am/en/docs/tunnels/examples/ospanel.md) #### [📄️Paddle](https://tuna.am/en/docs/tunnels/examples/paddle.md) [Processing Paddle events with tuna](https://tuna.am/en/docs/tunnels/examples/paddle.md) #### [📄️phpMyAdmin](https://tuna.am/en/docs/tunnels/examples/phpmyadmin.md) [Using phpMyAdmin with tuna](https://tuna.am/en/docs/tunnels/examples/phpmyadmin.md) #### [📄️PocketBase](https://tuna.am/en/docs/tunnels/examples/pocketbase.md) [Using PocketBase with tuna](https://tuna.am/en/docs/tunnels/examples/pocketbase.md) #### [📄️PostgreSQL](https://tuna.am/en/docs/tunnels/examples/postgresql.md) [Using PostgreSQL with tuna](https://tuna.am/en/docs/tunnels/examples/postgresql.md) #### [📄️Qdrant](https://tuna.am/en/docs/tunnels/examples/qdrant.md) [Using Qdrant with tuna](https://tuna.am/en/docs/tunnels/examples/qdrant.md) #### [📄️RabbitMQ](https://tuna.am/en/docs/tunnels/examples/rabbitmq.md) [Using RabbitMQ with tuna](https://tuna.am/en/docs/tunnels/examples/rabbitmq.md) #### [📄️Raspberry Pi](https://tuna.am/en/docs/tunnels/examples/raspberry-pi.md) [Remote access to Raspberry Pi](https://tuna.am/en/docs/tunnels/examples/raspberry-pi.md) #### [📄️RDP](https://tuna.am/en/docs/tunnels/examples/rdp.md) [Using RDP with tuna](https://tuna.am/en/docs/tunnels/examples/rdp.md) #### [📄️Redis](https://tuna.am/en/docs/tunnels/examples/redis.md) [Using Redis with tuna](https://tuna.am/en/docs/tunnels/examples/redis.md) #### [📄️Robokassa](https://tuna.am/en/docs/tunnels/examples/robokassa.md) [Processing Robokassa events with tuna](https://tuna.am/en/docs/tunnels/examples/robokassa.md) #### [📄️ScyllaDB](https://tuna.am/en/docs/tunnels/examples/scylladb.md) [Using ScyllaDB with tuna](https://tuna.am/en/docs/tunnels/examples/scylladb.md) #### [📄️SMTP](https://tuna.am/en/docs/tunnels/examples/smtp.md) [Using SMTP with tuna](https://tuna.am/en/docs/tunnels/examples/smtp.md) #### [📄️SSH](https://tuna.am/en/docs/tunnels/examples/ssh.md) [Using SSH with tuna](https://tuna.am/en/docs/tunnels/examples/ssh.md) #### [📄️Superset](https://tuna.am/en/docs/tunnels/examples/superset.md) [Using Superset with tuna](https://tuna.am/en/docs/tunnels/examples/superset.md) #### [📄️Temporal](https://tuna.am/en/docs/tunnels/examples/temporal.md) [Using Temporal with tuna](https://tuna.am/en/docs/tunnels/examples/temporal.md) #### [📄️TimescaleDB](https://tuna.am/en/docs/tunnels/examples/timescaledb.md) [Using TimescaleDB with tuna](https://tuna.am/en/docs/tunnels/examples/timescaledb.md) #### [📄️Transmission](https://tuna.am/en/docs/tunnels/examples/transmission.md) [Using transmission-daemon with tuna](https://tuna.am/en/docs/tunnels/examples/transmission.md) #### [📄️Vite](https://tuna.am/en/docs/tunnels/examples/vite.md) [Using Vite with tuna](https://tuna.am/en/docs/tunnels/examples/vite.md) #### [📄️WampServer](https://tuna.am/en/docs/tunnels/examples/wampserver.md) [Using WampServer with tuna](https://tuna.am/en/docs/tunnels/examples/wampserver.md) #### [📄️WebAsk](https://tuna.am/en/docs/tunnels/examples/webask.md) [Developing WebAsk Webhook with tuna](https://tuna.am/en/docs/tunnels/examples/webask.md) #### [📄️YooKassa](https://tuna.am/en/docs/tunnels/examples/yookassa.md) [Processing YooKassa events with tuna](https://tuna.am/en/docs/tunnels/examples/yookassa.md) #### [📄️Yucca](https://tuna.am/en/docs/tunnels/examples/yucca.md) [Camera access with tuna](https://tuna.am/en/docs/tunnels/examples/yucca.md) --- ### tuna and AFFiNE #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run AFFiNE server Installation instructions can be found in the [official documentation](https://docs.affine.pro/docs/self-host-affine). #### Step 3. Run tuna AFFiNE uses port `3010` by default: ```bash tuna http localhost:3010 ``` --- ### tuna and Airflow #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Airflow server Installation instructions can be found in the [official documentation](https://airflow.apache.org/docs/apache-airflow/stable/installation/index.html). #### Step 3. Run tuna Airflow uses port `8080` by default: ```bash tuna http 8080 ``` --- ### tuna and amoCRM #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run a server to process events from amoCRM Information about the structure of incoming requests can be found in the [official documentation](https://www.amocrm.ru/developers/content/crm_platform/webhooks-format). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=amocrm-webhook ``` #### Step 4. Create a new Webhook Check the [instructions for adding a Webhook](https://www.amocrm.ru/developers/content/crm_platform/webhooks-format), for the "Website URL" value, specify the address received from tuna (for example, `https://amocrm-webhook.ru.tuna.am`) and the path to your server's method for processing events (for example, `/event`). --- ### tuna and Apache #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Apache #### Step 3. Run tuna Special settings are not required, but for correct request routing, you may need to pass the `Host` header, for example: ```shell tuna http --request-header="host:tuna.am" 80 ``` --- ### tuna and Black Candy #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Black Candy server Installation instructions can be found in the [official documentation](https://github.com/blackcandy-org/blackcandy/blob/master/README.md#installation). #### Step 3. Run tuna Black Candy uses port `3000` by default: ```bash tuna http 3000 ``` --- ### tuna and Cassandra #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Cassandra server Installation instructions can be found in the [official documentation](https://cassandra.apache.org/doc/latest/cassandra/getting-started/cassandra-quickstart.html). #### Step 3. Run tuna Cassandra uses port `9042` by default: ```bash tuna tcp 9042 ``` --- ### tuna and ClickHouse #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run ClickHouse server Installation instructions can be found in the [official documentation](https://clickhouse.com/docs/en/getting-started/quick-start). #### Step 3. Run tuna ClickHouse uses many ports, described in the [official documentation](https://clickhouse.com/docs/en/guides/sre/network-ports). To provide access to the HTTP API, you can use the command: ```shell tuna http 8123 ``` For access via the native protocol: ```shell tuna tcp 9000 ``` --- ### tuna and CloudPayments #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run a server to process HTTP notifications Information about incoming CloudPayments notifications can be found in the [official documentation](https://developers.cloudpayments.ru/#uvedomleniya). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=billing ``` #### Step 4. Specify the address for incoming notifications From the [sites](https://merchant.cloudpayments.ru/next/sites) section, go to the site you need, then in the Notifications section, you need to set addresses for receiving notifications: ![cloudpayments](/en/assets/images/cloudpayments1-582a3da86daff4c1122f63df3017faf5.png) Each URL consists of the address received from tuna (for example, `https://billing.ru.tuna.am`) and the path to your server's method for processing events (for example, `/payments/pay`). After all settings, you can use [test payment methods](https://developers.cloudpayments.ru/#testirovanie) to debug the interaction. --- ### tuna and CockroachDB #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run CockroachDB server Installation instructions can be found in the [official documentation](https://www.cockroachlabs.com/docs/v24.3/install-cockroachdb-linux). #### Step 3. Run tuna CockroachDB uses port `26257` by default: ```bash tuna tcp 26257 ``` --- ### tuna and Docker Example of running tuna to provide access to a server launched with Docker and Docker Compose. #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Clone the repository with examples ```shell git clone https://gitlab.com/yuccastream/tuna-examples.git cd tuna-examples/docker-compose ``` > **Info.** If you want the tuna client to also run with docker-compose, then check the example from the `tuna-examples/docker-compose-v2` directory. #### Step 3. Run the server ```shell docker compose up -d ``` Make sure the container is running: ```shell docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS docker-compose-web-1 docker-compose-web "python3 app.py" web 9 minutes ago Up 2 seconds 0.0.0.0:8000->8000/tcp ``` #### Step 4. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8000 ``` To stop and remove all running containers, use the command `docker compose rm -sf`. --- ### tuna and Docusaurus #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Docusaurus server In the [official documentation](https://docusaurus.io/docs/installation) you can find instructions for creating a new project, then start the development server: ```shell yarn start ``` During startup, the hosting address will be displayed: ```shell [INFO] Starting the development server... [SUCCESS] Docusaurus website is running at: http://localhost:3000/ ``` #### Step 3. Run tuna ```bash tuna http localhost:3000 ``` --- ### tuna and Elasticsearch #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Elasticsearch server Installation instructions can be found in the [official documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html#getting-started). #### Step 3. Run tuna Elasticsearch uses port `9200` by default: ```bash tuna http 9200 ``` --- ### tuna and Emby Server #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Emby Server Installation instructions can be found in the [official documentation](https://emby.media/download.html). #### Step 3. Run tuna Emby Server uses port `8096` by default: ```bash tuna http 8096 ``` --- ### tuna and Flask #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run a server on the Flask framework Server code example (explanations can be found in the [official documentation](https://flask.palletsprojects.com/en/3.0.x/quickstart/)): ```python from flask import Flask app = Flask(__name__) @app.route("/") def hello_world(): return "

Hello, World!

" ``` #### Step 3. Run tuna Flask uses port 5000 by default: ```bash tuna http 5000 ``` --- ### tuna and FTP, SFTP FTP requires multiple ports to work, so tuna is not suitable for this task, but data can be transferred via SFTP – an extension for SSH. #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run SSH server #### Step 3. Run tuna SSH server uses port `22` by default: ```bash tuna tcp 22 ``` #### Alternatives 1. tuna has a built-in file server that provides an easy way to transfer files: ```bash tuna http --file-server ./ ``` 2. There is also a built-in [SSH server](https://tuna.am/en/docs/tunnels/ssh.md), which is also suitable for these purposes --- ### tuna and Ghost #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start Ghost server Installation instructions can be found in the [official documentation](https://ghost.org/docs/install/). #### Step 3. Run tuna Ghost uses port `2368` by default: ```bash tuna http 2368 ``` --- ### tuna and Gitea #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start Gitea server Installation instructions can be found in the [official documentation](https://docs.gitea.com/category/installation). #### Step 3. Run tuna Gitea uses port `3000` by default: ```bash tuna http 3000 ``` --- ### tuna and GitLab GitLab allows adding Webhooks in groups and projects - URLs to which messages about specific events will be sent, for example, tag creation, comment addition, issue editing. #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start a server to handle events from GitLab Information about the structure of incoming requests can be found in the [official documentation](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=gitlab-webhook ``` #### Step 4. Create a new Webhook Go to the Webhooks section in the project settings, specify the incoming message address and select the events you want to receive: ![gitlab](/en/assets/images/gitlab1-9f923b11f6e30445c68100087e26f6fe.png) The URL consists of the address received from tuna (for example, `https://gitlab-webhook.ru.tuna.am`) and the path to your server method for handling events (for example, `/event`). --- ### tuna и GitVerse GitVerse позволяет добавлять в проектах [Webhook](https://gitverse.ru/docs/developers/webhooks/) – ссылки на которые будут отправляться сообщения об определенных событиях, например, о добавлении комментария. #### Шаг 1. Скачайте клиент Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Шаг 2. Запустите сервер для обработки событий от GitVerse Пример входящего события Заголовки: ```js { "Content-Type": "application/json", "X-GitHub-Delivery": "0218f02f-cfb6-42d5-a08c-3c2709442b0a", "X-GitHub-Event": "push", "X-GitHub-Event-Type": "push", "X-Gitverse-Delivery": "0218f02f-cfb6-42d5-a08c-3c2709442b0a", "X-Gitverse-Event": "push", "X-Gitverse-Event-Type": "push", "X-Gitverse-Signature": "", "X-Gogs-Delivery": "0218f02f-cfb6-42d5-a08c-3c2709442b0a", "X-Gogs-Event": "push", "X-Gogs-Event-Type": "push", "X-Gogs-Signature": "", "X-Hub-Signature": "sha1=", "X-Hub-Signature-256": "sha256=" } ``` Тело запроса: ```js { "ref": "refs/heads/master", "before": "0000000000000000000000000000000000000000", "after": "990564d83c961519f581e8c539f30aa101ffbaa0", "compare_url": "https://gitverse.ru/", "commits": [ { "id": "990564d83c961519f581e8c539f30aa101ffbaa0", "message": "create: new_file\n", "url": "https://gitverse.ru/leoderoko/tuna-webhook-test/commit/990564d83c961519f581e8c539f30aa101ffbaa0", "author": { "name": "leoderoko", "email": "193755leoderoko@users.no-reply.gitverse.ru", "username": "" }, "committer": { "name": "GitVerse", "email": "noreply@gitverse.ru", "username": "" }, "verification": null, "timestamp": "2026-04-28T05:19:19Z", "added": [ "new_file" ], "removed": [], "modified": [] } ], "total_commits": 1, "head_commit": { "id": "990564d83c961519f581e8c539f30aa101ffbaa0", "message": "create: new_file", "url": "https://gitverse.ru/leoderoko/tuna-webhook-test/commit/990564d83c961519f581e8c539f30aa101ffbaa0", "author": { "name": "leoderoko", "email": "193755leoderoko@users.no-reply.gitverse.ru", "username": "" }, "committer": { "name": "GitVerse", "email": "noreply@gitverse.ru", "username": "" }, "verification": null, "timestamp": "2026-04-28T05:19:19Z", "added": [ "new_file" ], "removed": [], "modified": [] }, "repository": { "id": 250007, "owner": { "id": 193755, "login": "leoderoko", "login_name": "", "source_id": 0, "full_name": "", "email": "193755leoderoko@users.no-reply.gitverse.ru", "avatar_url": "https://gitverse.ru/avatars/9fc126f12ab01cadf5a370c07e92c23e", "html_url": "https://gitverse.ru/leoderoko", "language": "", "is_admin": false, "last_login": "0001-01-01T00:00:00Z", "created": "2026-02-07T17:44:11Z", "restricted": false, "active": false, "prohibit_login": false, "location": "", "website": "", "description": "", "visibility": "public", "followers_count": 0, "following_count": 0, "starred_repos_count": 0, "username": "leoderoko" }, "name": "tuna-webhook-test", "full_name": "leoderoko/tuna-webhook-test", "description": "", "empty": false, "private": true, "fork": false, "template": false, "parent": null, "mirror": false, "size": 22, "language": "", "languages_url": "http://gitverse-backend-http:3000/api/v1/repos/leoderoko/tuna-webhook-test/languages", "html_url": "http://gitverse-backend-http:3000/leoderoko/tuna-webhook-test", "url": "http://gitverse-backend-http:3000/api/v1/repos/leoderoko/tuna-webhook-test", "link": "", "ssh_url": "git@gitverse.ru:leoderoko/tuna-webhook-test.git", "clone_url": "https://gitverse.ru/leoderoko/tuna-webhook-test.git", "original_url": "", "website": "", "stars_count": 0, "forks_count": 0, "watchers_count": 0, "open_issues_count": 0, "open_pr_counter": 0, "release_counter": 0, "default_branch": "master", "archived": false, "created_at": "2026-04-28T05:09:11Z", "updated_at": "2026-04-28T05:19:21Z", "archived_at": "1970-01-01T00:00:00Z", "permissions": { "admin": true, "push": true, "pull": true }, "has_issues": false, "has_wiki": false, "has_pull_requests": true, "has_projects": false, "projects_mode": "all", "has_releases": true, "has_packages": false, "has_actions": true, "ignore_whitespace_conflicts": false, "allow_merge_commits": true, "allow_rebase": true, "allow_rebase_explicit": true, "allow_squash_merge": true, "allow_fast_forward_only_merge": true, "allow_rebase_update": true, "default_delete_branch_after_merge": false, "default_merge_style": "merge", "default_allow_maintainer_edit": false, "avatar_url": "", "internal": false, "mirror_interval": "", "object_format_name": "sha1", "mirror_updated": "0001-01-01T00:00:00Z", "repo_transfer": null, "topics": [], "licenses": null }, "pusher": { "id": 193755, "login": "leoderoko", "login_name": "", "source_id": 0, "full_name": "", "email": "193755leoderoko@users.no-reply.gitverse.ru", "avatar_url": "https://gitverse.ru/avatars/9fc126f12ab01cadf5a370c07e92c23e", "html_url": "https://gitverse.ru/leoderoko", "language": "", "is_admin": false, "last_login": "0001-01-01T00:00:00Z", "created": "2026-02-07T17:44:11Z", "restricted": false, "active": false, "prohibit_login": false, "location": "", "website": "", "description": "", "visibility": "public", "followers_count": 0, "following_count": 0, "starred_repos_count": 0, "username": "leoderoko" }, "sender": { "id": 193755, "login": "leoderoko", "login_name": "", "source_id": 0, "full_name": "", "email": "193755leoderoko@users.no-reply.gitverse.ru", "avatar_url": "https://gitverse.ru/avatars/9fc126f12ab01cadf5a370c07e92c23e", "html_url": "https://gitverse.ru/leoderoko", "language": "", "is_admin": false, "last_login": "0001-01-01T00:00:00Z", "created": "2026-02-07T17:44:11Z", "restricted": false, "active": false, "prohibit_login": false, "location": "", "website": "", "description": "", "visibility": "public", "followers_count": 0, "following_count": 0, "starred_repos_count": 0, "username": "leoderoko" } } ``` #### Шаг 3. Запустите tuna В аргументах запуска укажите порт вашего сервера: ```bash tuna http 8080 ``` Или с указанием статичного адреса: ```bash tuna http 8080 --subdomain=gitverse-webhook ``` #### Шаг 4. Создайте новый Webhook Перейдите в раздел Вебхуки настроек проекта, укажите адрес входящий сообщений и укажите события, которые хотиле получать: ![gitverse](/en/assets/images/gitverse1-bd5131d750b8a4e9b8072866fad04da3.png) URL состоит из адреса полученного от tuna (например, `https://gitverse-webhook.ru.tuna.am`) и пути к методу вашего сервера для обработки событий (например, `/event`). --- ### tuna and Gogs #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start Gogs server Installation instructions can be found in the [official documentation](https://gogs.io/getting-started/installation), for Docker deployment refer to the documentation in the [project repository](https://github.com/gogs/gogs/tree/main/docker). #### Step 3. Run tuna Gogs uses port `3000` by default: ```bash tuna http 3000 ``` --- ### tuna and Grafana #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start Grafana server Installation instructions can be found in the [official documentation](https://grafana.com/docs/grafana/latest/#installing-grafana). #### Step 3. Run tuna Grafana uses port `3000` by default: ```bash tuna http 3000 ``` --- ### tuna and GraphQL #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start GraphQL server #### Step 3. Run tuna Specify your server port in the startup arguments, for example: ```bash tuna http 4000 ``` --- ### tuna and gRPC #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start gRPC server #### Step 3. Run tuna To work with gRPC you need to use a TCP tunnel, for example: ```bash tuna tcp 8080 ``` --- ### tuna and Laravel Herd #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start Herd server Installation instructions can be found in the [official documentation](https://herd.laravel.com/docs/1/getting-started/installation). #### Step 3. Run tuna Herd uses port `80` by default to access your web project: ```bash tuna http 80 ``` For correct request routing, you may need to pass the Host header, for example: ```bash tuna http --request-header="host:tuna.am" 80 ``` --- ### tuna ❤️ Immich [Immich](https://immich.app/?utm_source=tuna.am) - a self-hosted photo storage service, an alternative to [Google Photos](https://www.google.com/photos/about/?utm_source=tuna.am). Tuna will help you provide access to your Immich server on your local network. We wrote an [article](https://vc.ru/dev/1107380-kak-hranit-fotoarhiv-u-sebya-a-ne-v-google-ili-yandekse) about using Immich on Raspberry PI 4. ![immich-login](/docs/img/examples/immich.png) #### Step 1. Install Immich Installation instructions can be found in the [official documentation](https://immich.app/docs/install/docker-compose). #### Step 2. Configure tuna launch 1. Add the token to the `.env` file: ```bash TUNA_TOKEN=tt_*** ``` 2. Add a service with tuna to the `docker-compose.yml` file and edit the arguments: ```yaml tuna: container_name: immich_tuna image: yuccastream/tuna:latest command: http immich-server:2283 --location=ru --subdomain= --inspect=false env_file: - .env restart: always depends_on: - immich-server ``` > **Note.** In immich-server version [v1.118.0](https://github.com/immich-app/immich/releases/tag/v1.118.0), the default connection port has changed. Previously it was **3001**, starting from version v1.118 - **2283**. If you are using a version before v1.118.0, specify `immich-server:3001` instead of `immich-server:2283`. #### Step 3. Run tuna Execute: ```bash docker compose pull && docker compose up -d ``` The service will be available in the browser and mobile application at the subdomain you specified. --- ### tuna and InfluxDB #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start InfluxDB server Installation instructions can be found in the [official documentation](https://docs.influxdata.com/influxdb/v2/install/). #### Step 3. Run tuna InfluxDB uses port `8086` by default: ```bash tuna http 8086 ``` --- ### tuna ❤️ JetKVM [JetKVM](https://jetkvm.com/) is a compact IP-KVM device based on the Rockchip RV1106 (ARM Cortex-A7). It allows you to remotely control a computer: see the screen, control the keyboard and mouse, mount disk images — all through a web interface in the browser. By default, JetKVM is only accessible on the local network. With Tuna, you can expose it to the internet — without a public IP, without a VPN, and without port forwarding on the router. ![Tuna + JetKVM](/docs/img/tunnels/guides/jetkvm_tuna.png) > **Info.** This guide is tested on JetKVM with system version **v0.2.7** or newer. Earlier versions do not support `/userdata/init.d/` — update JetKVM via the web interface (Settings → Device → Check for updates). *** #### Preparation In addition to installation, the setup guide is split into 2 types depending on whether you have a paid Tuna subscription. 1. [On the free plan](#type-1-free-plan--http-tunnel) 2. [With a paid subscription](#type-2-subscription--ssh-and-http-tunnels) ##### What you will need * JetKVM with system version **v0.2.7 or newer** * **Developer Mode** enabled in the JetKVM web interface (Settings → Advanced → Developer Mode) * An **SSH key** added via the JetKVM web interface * An account on [tuna.am](https://my.tuna.am/?lang=en) (free or with a subscription) * An auth token from the [token page](https://my.tuna.am/token?lang=en) ##### JetKVM system specifics JetKVM runs on a custom embedded Linux (Buildroot + Rockchip SDK). Important differences from a regular Linux system: | Feature | Value | | -------------------- | -------------------------------------------------- | | Shell | `/bin/sh` (BusyBox ash). **bash is not available** | | SSH server | Dropbear (no scp, sftp, rsync) | | Init system | BusyBox init + scripts in `/userdata/init.d/` | | Persistent partition | `/userdata/` — preserved across OTA updates | Storage layout: | Path | Purpose | Preserved across OTA | | ------------ | --------- | -------------------- | | `/` | Root FS | No | | `/userdata/` | User data | **Yes** | We will store tuna in `/userdata/tuna/` so that the binary and configuration are preserved across updates. ##### Connecting via SSH Enable Developer Mode in the JetKVM web interface and add a public RSA key (Settings → Advanced → Developer Mode). Then connect: ```bash ssh root@ ``` Or open the terminal in the web interface: ![JetKVM terminal](/docs/img/tunnels/guides/jetkvm_terminal.png) > **Tip.** You can find the IP address on the JetKVM built-in screen or in your router's DHCP table. ##### Installing tuna on JetKVM Connect to JetKVM via SSH and install the binary: ```bash mkdir -p /userdata/tuna INSECURE=true INSTALL_DIR=/userdata/tuna sh -c "$(wget -qO- http://releases.tuna.am/tuna/get.sh)" ``` > **Why INSECURE=true?** JetKVM does not have root CA certificates, so wget cannot verify the server's SSL certificate. The `INSECURE=true` flag switches the script to download over HTTP. This is safe because the tuna binary can be verified by checksum after download. > **Tip.** The `/userdata` partition is preserved across OTA updates, so you will not need to reinstall tuna after firmware updates. Save the auth [token](https://my.tuna.am/token?lang=en): ```bash /userdata/tuna/tuna --config=/userdata/tuna/.tuna.yml config save-token ``` > **Tip.** We recommend creating a dedicated token for the device. *** #### JetKVM auto-start system Unlike NanoKVM, where auto-start is configured via `/etc/inittab` with the `respawn` directive, JetKVM uses init scripts in the `/userdata/init.d/` directory, which needs to be created. ```bash mkdir -p /userdata/init.d ``` At boot, the JetKVM init system runs all scripts in `/userdata/init.d/` whose names match the pattern `S??*` (for example, `S99tuna-http`). Scripts are invoked with the `start` argument, and with the `stop` argument when stopping. > **Critical rules for init scripts.** Failing to follow these rules can cause the device to **fail to boot**: > > 1. **Use `#!/bin/sh`** — there is no `/bin/bash` on JetKVM. The `#!/bin/bash` shebang will cause an error and may block booting. > 2. **Do not use the `.sh` extension** — files with the `.sh` extension are included via `source` (not invoked as a separate process). An `exit` command in such a script will terminate the entire init process. > 3. **Do not block boot** — all long-running processes must be started **in the background** with `&`. If the script does not return control, boot will hang. > 4. **Handle the `start` argument** — the init system invokes the script as `S99tuna-http start`. Without `case "$1"`, the script may not run correctly. > **No automatic restart.** Unlike NanoKVM (where `inittab respawn` automatically restarts a crashed process), on JetKVM `/etc/inittab` is overwritten during OTA updates. That's why we use a `while true` loop inside the init script to auto-restart on failure. > **Test before rebooting.** Always run the script manually (`/userdata/init.d/S99tuna-http start`) and make sure the prompt returns. If the console "hangs" after running it — the script will block boot on reboot. *** #### Type 1. Free plan — HTTP tunnel The free plan provides an HTTP tunnel with a dynamic address. This is enough to expose the JetKVM web interface to the internet and control your computer through a browser. > **Free plan limitations** > > * The dynamic address changes every time the tunnel reconnects > * To keep the address, add your [free permanent subdomain](https://tuna.am/en/docs/tunnels/http.md#free-subdomain) to the launch command: `--subdomain=` > * The current address is always available in the [personal cabinet](https://my.tuna.am/tunnels?lang=en) ##### Creating the init script ```bash cat << 'INITEOF' > /userdata/init.d/S99tuna-http #!/bin/sh PIDFILE=/tmp/tuna-http.pid LOGFILE=/tmp/tuna-http.log wait_network() { i=0 while ! ip route | grep -q default; do sleep 1 i=$((i + 1)) if [ "$i" -ge 60 ]; then echo "$(date): network timeout" >> "$LOGFILE" return 1 fi done return 0 } start() { echo "$(date): starting tuna-http" >> "$LOGFILE" ( wait_network || exit 1 while true; do /userdata/tuna/tuna \ --config=/userdata/tuna/.tuna.yml \ --log="$LOGFILE" \ http \ --inspect=false \ --basic-auth=tuna:SecurePasswd \ --https-redirect \ 80 >> "$LOGFILE" 2>&1 echo "$(date): tuna-http exited, restarting in 5s" >> "$LOGFILE" sleep 5 done ) & echo $! > "$PIDFILE" } stop() { if [ -f "$PIDFILE" ]; then kill "$(cat "$PIDFILE")" 2>/dev/null pkill -f "/userdata/tuna/tuna.*http" 2>/dev/null rm -f "$PIDFILE" fi } case "$1" in start) start ;; stop) stop ;; restart) stop; sleep 1; start ;; *) echo "Usage: $0 {start|stop|restart}" ;; esac INITEOF chmod +x /userdata/init.d/S99tuna-http ``` ##### Init script explanation | Element | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------- | | `S99tuna-http` | Name **without** `.sh` — init invokes the script with the `start` argument rather than including it via `source` | | `wait_network()` | Waits for a default route to appear (up to 60 seconds) | | `( ... ) &` | Subshell runs **in the background** — does not block boot | | `while true` | Auto-restarts tuna on failure (5 second pause) | | `echo $! > "$PIDFILE"` | Saves the subshell PID for proper stopping | | `pkill -f` | On stop, kills both the wrapper and the child tuna process | ##### tuna flag explanation | Flag | Required | Description | | --------------------------------- | -------- | ---------------------------------------------------------------------- | | `--inspect=false` | No | Disables the traffic inspector (not needed for KVM) to save RAM | | `--basic-auth=admin:YourPassword` | No | Additional HTTP authentication ([details](#why-you-need---basic-auth)) | | `--https-redirect` | No | Redirects HTTP to HTTPS | | `80` | Yes | JetKVM web interface port | You can also [restrict access by IP subnets](#restricting-access-by-ip-subnets). ##### Verification Run the script manually: ```bash /userdata/init.d/S99tuna-http start ``` Make sure the prompt returns (the script did not block the console). Check that the process is running: ```bash ps | grep tuna ``` The output should contain a line with `/userdata/tuna/tuna ... http ...`. If there is no such process — see the [Troubleshooting](#troubleshooting) section. In the logs, you will see your tunnel URL: ```bash cat /tmp/tuna-http.log ``` The current address is also available in the [personal cabinet](https://my.tuna.am/?lang=en). *** #### Type 2. Subscription — SSH and HTTP tunnels With a [subscription](https://tuna.am/#pricing) you get: * **Fixed subdomain** for the HTTP tunnel — the address does not change * **SSH tunnel** — direct access to the JetKVM console from anywhere in the world ##### HTTP tunnel with a fixed address ```bash cat << 'INITEOF' > /userdata/init.d/S99tuna-http #!/bin/sh PIDFILE=/tmp/tuna-http.pid LOGFILE=/tmp/tuna-http.log wait_network() { i=0 while ! ip route | grep -q default; do sleep 1 i=$((i + 1)) if [ "$i" -ge 60 ]; then echo "$(date): network timeout" >> "$LOGFILE" return 1 fi done return 0 } start() { echo "$(date): starting tuna-http" >> "$LOGFILE" ( wait_network || exit 1 while true; do /userdata/tuna/tuna \ --config=/userdata/tuna/.tuna.yml \ --log="$LOGFILE" \ http \ --subdomain=jetkvm \ --inspect=false \ --basic-auth=admin:YourPassword \ --https-redirect \ 80 >> "$LOGFILE" 2>&1 echo "$(date): tuna-http exited, restarting in 5s" >> "$LOGFILE" sleep 5 done ) & echo $! > "$PIDFILE" } stop() { if [ -f "$PIDFILE" ]; then kill "$(cat "$PIDFILE")" 2>/dev/null pkill -f "/userdata/tuna/tuna.*http" 2>/dev/null rm -f "$PIDFILE" fi } case "$1" in start) start ;; stop) stop ;; restart) stop; sleep 1; start ;; *) echo "Usage: $0 {start|stop|restart}" ;; esac INITEOF chmod +x /userdata/init.d/S99tuna-http ``` The difference from the free version is the `--subdomain=jetkvm` flag, which reserves a permanent address for you. After restarting the tunnel, the URL will remain the same. > **Info.** The example subdomain may already be taken. Reserve your subdomain in advance in the [personal cabinet](https://my.tuna.am/domains?lang=en), then use it in the `--subdomain=your-subdomain` flag. ###### HTTP tunnel flag explanation | Flag | Required | Description | | --------------------------------- | -------- | ---------------------------------------------------------------------- | | `--subdomain=jetkvm` | No | Reserves a permanent tunnel address (subscription only) | | `--inspect=false` | No | Disables the traffic inspector (not needed for KVM) to save RAM | | `--basic-auth=admin:YourPassword` | No | Additional HTTP authentication ([details](#why-you-need---basic-auth)) | | `--https-redirect` | No | Redirects HTTP to HTTPS | | `80` | Yes | JetKVM web interface port | You can also [restrict access by IP subnets](#restricting-access-by-ip-subnets). ##### SSH tunnel The SSH tunnel allows you to connect to the JetKVM console over the internet. This is convenient for administration, firmware updates, and diagnostics. ```bash cat << 'INITEOF' > /userdata/init.d/S98tuna-ssh #!/bin/sh PIDFILE=/tmp/tuna-ssh.pid LOGFILE=/tmp/tuna-ssh.log wait_network() { i=0 while ! ip route | grep -q default; do sleep 1 i=$((i + 1)) if [ "$i" -ge 60 ]; then echo "$(date): network timeout" >> "$LOGFILE" return 1 fi done return 0 } start() { echo "$(date): starting tuna-ssh" >> "$LOGFILE" ( wait_network || exit 1 while true; do /userdata/tuna/tuna \ --config=/userdata/tuna/.tuna.yml \ --log="$LOGFILE" \ ssh \ --port=jetkvm-ssh \ --record-session=false \ --password-auth=false >> "$LOGFILE" 2>&1 echo "$(date): tuna-ssh exited, restarting in 5s" >> "$LOGFILE" sleep 5 done ) & echo $! > "$PIDFILE" } stop() { if [ -f "$PIDFILE" ]; then kill "$(cat "$PIDFILE")" 2>/dev/null pkill -f "/userdata/tuna/tuna.*ssh" 2>/dev/null rm -f "$PIDFILE" fi } case "$1" in start) start ;; stop) stop ;; restart) stop; sleep 1; start ;; *) echo "Usage: $0 {start|stop|restart}" ;; esac INITEOF chmod +x /userdata/init.d/S98tuna-ssh ``` ###### SSH tunnel flag explanation | Flag | Required | Description | | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--port=jetkvm-ssh` | No | Reserves the SSH tunnel port. Without this flag, the port is assigned randomly on each startup. The [port alias](https://my.tuna.am/tcp_ports?lang=en) must be reserved in advance before startup. | | `--record-session=false` | No | Disables recording SSH sessions to disk. Recommended on embedded devices with limited storage to save space. | | `--password-auth=false` | No | Disallows password authentication — access via SSH keys only ([details](#ssh-tunnel-authentication)) | You can also set [a static login and password](#ssh-tunnel-authentication) or [restrict access by IP subnets](#restricting-access-by-ip-subnets). ##### Verifying tunnels Run both scripts manually: ```bash /userdata/init.d/S99tuna-http start /userdata/init.d/S98tuna-ssh start ``` Make sure both processes are running: ```bash ps | grep tuna ``` The output should contain two lines — one with `http` and one with `ssh`. If a process is missing — see the [Troubleshooting](#troubleshooting) section. View the logs: ```bash cat /tmp/tuna-http.log cat /tmp/tuna-ssh.log ``` The HTTP tunnel logs will contain the URL, and the SSH tunnel logs will contain connection instructions with the address and port. *** #### Managing tunnels Auto-restart on failure is provided by the `while true` loop inside the init script. Use the following commands to manage the tunnels. ##### Check status ```bash ps | grep tuna ``` ##### Stop a tunnel ```bash # Stop the HTTP tunnel /userdata/init.d/S99tuna-http stop # Stop the SSH tunnel /userdata/init.d/S98tuna-ssh stop ``` ##### Start a tunnel ```bash # Start the HTTP tunnel /userdata/init.d/S99tuna-http start # Start the SSH tunnel /userdata/init.d/S98tuna-ssh start ``` ##### Restart a tunnel ```bash # Restart the HTTP tunnel /userdata/init.d/S99tuna-http restart # Restart the SSH tunnel /userdata/init.d/S98tuna-ssh restart ``` ##### Fully disabling auto-start To prevent a tunnel from starting at boot, rename the script removing the `S` prefix: ```bash # Disable HTTP tunnel auto-start mv /userdata/init.d/S99tuna-http /userdata/init.d/disabled-tuna-http # Re-enable it mv /userdata/init.d/disabled-tuna-http /userdata/init.d/S99tuna-http ``` *** #### Recovery after an OTA update The binary, configuration, and init scripts are stored in `/userdata/`, which is **preserved across OTA updates**. After a firmware update via the JetKVM web interface, the tunnels will keep working without any additional steps. > **Info.** Unlike NanoKVM, on JetKVM you do not need to restore `/etc/inittab` after an update — auto-start via `/userdata/init.d/` fully survives OTA. After a full re-flash (when `/userdata` was cleared), you will need to install the binary, save the token, and create the init scripts again. *** #### Additional security settings ##### Why you need `--basic-auth` JetKVM has its own authentication in the web interface. However, given the [history of vulnerabilities in IoT devices](https://owasp.org/www-project-internet-of-things/), we strongly recommend adding an extra layer of protection. When exposing access through the internet, it's better to be safe — the `--basic-auth` flag adds standard HTTP authentication (username and password in the browser) before the request even reaches JetKVM. ```bash --basic-auth=mylogin:MyStr0ngP@ss ``` > **Important.** Set **your own** login and password. Do not use the values from the examples. ##### SSH tunnel authentication **Option 1. SSH keys (recommended)** The `--password-auth=false` flag disables password login. To connect, you will need to add a public SSH key in advance in the [personal cabinet](https://my.tuna.am/public_keys?lang=en). Detailed guide: [SSH keys setup](https://tuna.am/en/docs/guides/ssh-keys.md) > **Warning.** Tunnel nodes have public addresses. Attackers can scan ports and start brute-forcing passwords. For long-lived SSH tunnels, we strongly recommend disabling password authentication. **Option 2. Static login and password** If SSH keys are not suitable, you can set fixed credentials: ```bash --auth=user:pass ``` Replace `user` and `pass` with your own values. ##### Restricting access by IP subnets For HTTP and SSH tunnels, you can restrict access by IP addresses. Allow access only from specific subnets: ```bash --cidr-allow="203.0.113.0/24" ``` Deny access from specific subnets: ```bash --cidr-deny="198.51.100.0/24" ``` You can combine — allow a subnet but exclude individual addresses: ```bash --cidr-allow="10.0.0.0/24" --cidr-deny="10.0.0.33/32" ``` > **Caution.** Use IP filtering carefully. If you make a mistake with the subnet or your IP address changes — you will lose remote access to JetKVM. Make sure you have an alternative way to manage the device (for example, local access). *** #### Troubleshooting ##### Device fails to boot after adding an init script > **Emergency recovery.** If JetKVM stopped booting after adding a script to `/userdata/init.d/`, the most common causes are: > > 1. **`#!/bin/bash` shebang** — bash is not available on JetKVM > 2. **Script blocks boot** — the process is not started in the background (`&`) > 3. **`.sh` extension** — the script was included via `source`, which broke the init process > 4. **Syntax error** — interrupts init execution > > **Recovery methods:** > > * **UART serial console** — if you have access to the JetKVM UART port, connect via serial console (115200 baud) and remove or rename the problematic script > * **Factory reset** — press and hold the reset button on JetKVM (note: this may clear `/userdata/`) ##### tuna process does not start Check that the process is in the list: ```bash ps | grep tuna ``` If the output has no lines with `/userdata/tuna/tuna`, check: 1. The script exists and is executable: ```bash ls -la /userdata/init.d/S9*tuna* ``` 2. The script runs without errors: ```bash /userdata/init.d/S99tuna-http start ``` 3. The tuna binary works: ```bash /userdata/tuna/tuna version ``` ##### Viewing logs Tunnel logs are written to `/tmp/` and are available until the device reboots: ```bash # HTTP tunnel logs cat /tmp/tuna-http.log # SSH tunnel logs cat /tmp/tuna-ssh.log ``` To watch logs in real time: ```bash tail -f /tmp/tuna-http.log ``` ##### Common errors in logs | Error | Cause | Solution | | ------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------- | | `token is invalid` | Invalid or expired token | Repeat `config save-token` with a fresh token from the [page](https://my.tuna.am/token?lang=en) | | `Domain already reserved` | Subdomain is taken by another user | Choose another name via `--subdomain=` | | `port alias not found` | Port alias is not reserved | Reserve the alias in the [personal cabinet](https://my.tuna.am/tcp_ports?lang=en) | | `connection refused` | No internet access | Check the network: `ping -c 3 8.8.8.8` | | `exec format error` | Wrong binary architecture | Make sure you are using `tuna_linux_arm` (ARMv7) | ##### No internet access Check that JetKVM has a network connection: ```bash # Check for an IP address ip addr # Check the default route ip route # Check internet access ping -c 3 8.8.8.8 ``` ##### Logs disappeared after a reboot Logs are stored in `/tmp/`, which is cleared on reboot. This is normal behavior. If you need to keep logs across reboots, change the path in the init scripts: ```bash LOGFILE=/userdata/tuna/tuna-http.log ``` > **Warning.** Frequent log writes may shorten the storage's lifespan. Use this only for temporary debugging. ##### Tunnel was working and stopped 1. Check the logs for errors 2. Make sure the token is still valid in the [personal cabinet](https://my.tuna.am/token?lang=en) 3. Check that the script in `/userdata/init.d/` is in place and executable: ```bash ls -la /userdata/init.d/S9*tuna* ``` *** #### Links * [JetKVM — official site](https://jetkvm.com/) * [JetKVM — GitHub](https://github.com/jetkvm/kvm) * [JetKVM — documentation](https://jetkvm.com/docs) * [HTTP tunnel — all flags](https://tuna.am/en/docs/tunnels/http.md) * [SSH tunnel — all flags](https://tuna.am/en/docs/tunnels/ssh.md) * [SSH keys setup](https://tuna.am/en/docs/guides/ssh-keys.md) * [Running tuna as a service](https://tuna.am/en/docs/tunnels/guides/service.md) *** #### Availability monitoring In addition to tunnels for remote access, you can set up availability monitoring for JetKVM. Heartbeat monitoring will send you notifications if the device loses internet connectivity or stops responding. **Full guide:** [JetKVM availability monitoring](https://tuna.am/en/docs/monitors/examples/jetkvm.md) --- ### tuna and Kiwi TCMS #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start Kiwi TCMS server Installation instructions can be found in the [official documentation](https://kiwitcms.readthedocs.io/en/latest/set_dev_env.html). #### Step 3. Run tuna Kiwi TCMS uses port `8000` by default: ```bash tuna http 8000 ``` --- ### tuna and Kubernetes An example of running tuna to provide access to a service in a running Kubernetes cluster. #### Step 1. Clone the examples repository ```shell git clone https://gitlab.com/yuccastream/tuna-examples.git cd tuna-examples/kubernetes ``` #### Step 2. Start the service Create a separate namespace for experiments and start a test service: ```shell kubectl create namespace tuna-example kubectl -n tuna-example apply -k podinfo ``` #### Step 3. Run tuna Make sure the correct token is specified in the `tuna/deployment.yaml` file, without it tuna will not work, then execute the command: ```shell kubectl -n tuna-example apply -k tuna ``` Wait for the test service and tuna to transition to `Running` status: ```shell kubectl -n tuna-example get pods NAME READY STATUS RESTARTS AGE podinfo-6779cc5586-t78q4 1/1 Running 0 2m14s tuna-64d9b5c698-7sr96 1/1 Running 0 25s ``` Then view the tuna event log to get the link: ```shell kubectl -n tuna-example logs -l app.kubernetes.io/name=tuna INFO[13:00:00] Welcome to Tuna INFO[13:00:00] Account: test (Paid till 05.04.2024) INFO[13:00:00] Web Interface: http://127.0.0.1:4040 INFO[13:00:00] Forwarding https://yae6hu-10-21-32-25.ru.tuna.am -> podinfo:9898 ``` To remove all created entities use the command `kubectl delete namespace tuna-example`. --- ### tuna and MAMP #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start MAMP or MAMP Pro #### Step 3. Run tuna No special settings are required, but for correct request routing you will need to pass the `Host` header, for example: ```shell tuna http --request-header="host:tuna.am" 80 ``` --- ### tuna and MariaDB #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start MariaDB server Installation instructions can be found in the [official documentation](https://mariadb.com/kb/en/installing-and-using-mariadb-via-docker/). #### Step 3. Run tuna MariaDB uses port `3306` by default: ```bash tuna tcp 3306 ``` As a result of running the command, the connection address will be displayed on the screen (for example, `ru.tuna.am:12345`), which can now be used to connect to MariaDB. --- ### tuna and Mattermost In addition to Mattermost App, it is possible to create Outgoing Webhooks - URLs to which messages will be sent if a keyword is mentioned in the specified channel. #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start a server to handle events from Mattermost Information about the structure of incoming requests can be found in the [official documentation](https://developers.mattermost.com/integrate/webhooks/outgoing/). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=mattermost-webhook ``` #### Step 4. Create a new Webhook Go to the Integrations section, then "Outgoing Webhooks" and click the "Add Outgoing Webhook" button. Fill out the form that appears, and for the "Callback URL" value, specify the address received from tuna (for example, `https://mattermost-webhook.ru.tuna.am`) and the path to your server method for handling events (for example, `/event`). --- ### tuna ❤️ Minecraft [Minecraft](https://www.minecraft.net/ru-ru?utm_source=tuna.am) - a popular indie sandbox computer game. Tuna will help you open access to Minecraft (java edition) on your local network. We wrote detailed instructions [on how to run a Minecraft server locally in Docker and open access to friends using Tuna](https://dtf.ru/howto/2821468-instrukciya-zapuskaem-minecraft-lokalno-v-docker-i-otkryvaem-dostup-druzyam-s-pomoshyu-tuna), on any operating system. *** > **Info.** To publish Minecraft, you need to create a [TCP tunnel](https://tuna.am/en/docs/tunnels/tcp.md), which is only available with a [subscription](https://tuna.am/#pricing). But you don't necessarily have to run your own Minecraft server to play with friends - you can simply open access to your game: #### Step 1. Launch Minecraft (java edition) * Create a *Single Player Game* * Press **esc** * Select *Open to LAN* * Set a port, for example `45678` * Click *Start LAN World* #### Step 2. Install the tuna client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 3. Run tuna Execute: ```bash tuna tcp 45678 ``` Share the connection link with your friends as in the example, **without** the `tcp://` scheme: ![minecraft_link](/docs/img/examples/minecraft_link.png) > **Tip.** If you need to know the real IP of clients, some builds, for example [Velocity](https://docs.papermc.io/velocity/configuration/#advanced-section), support Proxy Protocol. You need to enable it in the game config and in the `tuna` client. > > In the argument you need to pass version 1 or 2, which corresponds to the Proxy Protocol version: 1 is text, 2 is binary. > > ```shell > tuna tcp 45678 --proxy-protocol=1 > # or > tuna tcp 45678 --proxy-protocol=2 > > ``` --- ### tuna and Minio #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the Minio server To run with Docker, you can use the command: ```bash docker run -p 9000:9000 quay.io/minio/minio server /data ``` #### Step 3. Run tuna Minio uses port `9000` by default: ```bash tuna tcp 9000 ``` --- ### tuna and MongoDB #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the MongoDB server Installation instructions can be found in the [official documentation](https://www.mongodb.com/docs/manual/installation/). #### Step 3. Run tuna MongoDB uses port `27017` by default: ```bash tuna tcp 27017 ``` As a result of running the command, a connection address will be displayed on the screen (for example, `ru.tuna.am:12345`), which can now be used to connect to MongoDB. --- ### tuna and MsSQL #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the MsSQL server #### Step 3. Run tuna MsSQL uses port `1433` by default: ```bash tuna tcp 1433 ``` As a result of running the command, a connection address will be displayed on the screen (for example, `ru.tuna.am:12345`), which can now be used to connect to MsSQL. --- ### tuna and MySQL #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the MySQL server #### Step 3. Run tuna MySQL uses port `3306` by default: ```bash tuna tcp 3306 ``` As a result of running the command, a connection address will be displayed on the screen (for example, `ru.tuna.am:12345`), which can now be used to connect to MySQL. --- ### tuna ❤️ NanoKVM [NanoKVM](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html) is a compact IP-KVM device from Sipeed based on RISC-V (Cube/PCIe), ARM64 (Pro), or ARMv7 (GxxKVM/JxxKVM). It allows you to remotely control a computer: see the screen, control the keyboard and mouse, mount disk images — all through a web interface in the browser. By default, NanoKVM is only accessible on the local network. With Tuna, you can expose it to the internet — without a public IP, without a VPN, and without port forwarding on the router. ![Tuna + NanoKVM](/docs/img/tunnels/guides/nanokvm_tuna.png) > **Info.** We have only tested this guide on the [NanoKVM Cube](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html), but it most likely works on all devices in the NanoKVM lineup. *** #### Preparation In addition to installation, the setup guide is split into 2 types depending on whether you have a paid Tuna subscription. 1. [On the free plan](#type-1-free-plan--http-tunnel) 2. [With a paid subscription](#type-2-subscription--ssh-and-http-tunnels) ##### What you will need * NanoKVM, connected to the local network and accessible via SSH or the terminal in the web interface * An account on [tuna.am](https://my.tuna.am/?lang=en) (free or with a subscription) * An auth token from the [token page](https://my.tuna.am/token?lang=en) ##### NanoKVM storage layout NanoKVM has three partitions: | Partition | Mount point | Purpose | | ---------------- | ----------- | ------------------- | | `/dev/mmcblk0p1` | `/boot` | Bootloader, kernel | | `/dev/mmcblk0p2` | `/` | Root FS (Buildroot) | | `/dev/mmcblk0p3` | `/data` | Data, ISO images | The `/data` partition is preserved across application updates and remote rsync, so we will store tuna there. ##### Installing tuna on NanoKVM Connect to NanoKVM via SSH and install the binary: ```bash ssh root@ ``` Or open the terminal in the web interface: ![NanoKVM terminal](/docs/img/tunnels/guides/terminal.png) Install tuna using the script, specifying `/data/tuna` as the install directory: **curl** ```bash mkdir -p /data/tuna INSTALL_DIR=/data/tuna sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```bash mkdir -p /data/tuna INSTALL_DIR=/data/tuna sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` > **Tip.** The `/data` partition is mounted as exFAT with `fmask=0022`, files automatically get `0755` permissions — the binary can be run immediately after copying. Additionally, `/data` is preserved across updates, so tuna does not need to be reinstalled. Save the auth [token](https://my.tuna.am/token?lang=en): ```bash /data/tuna/tuna --config=/data/tuna/.tuna.yml config save-token ``` > **Tip.** We recommend creating a dedicated token for the device. *** #### Type 1. Free plan — HTTP tunnel The free plan provides an HTTP tunnel with a dynamic address. This is enough to expose the NanoKVM web interface to the internet and control your computer through a browser. > **Free plan limitations** > > * The dynamic address changes every time the tunnel reconnects > * To keep the address, add your [free permanent subdomain](https://tuna.am/en/docs/tunnels/http.md#free-subdomain) to the launch command: `--subdomain=` > * The current address is always available in the [personal cabinet](https://my.tuna.am/tunnels?lang=en) ##### Creating a startup script ```bash cat << 'EOF' > /data/tuna/tuna-http.sh #!/bin/sh # Wait for the network while ! ip route | grep -q default; do sleep 1 done exec /data/tuna/tuna \ --config=/data/tuna/.tuna.yml \ --log=/tmp/tuna-http.log \ http \ --inspect=false \ --basic-auth=tuna:SecurePasswd \ --https-redirect \ 80 EOF chmod +x /data/tuna/tuna-http.sh ``` ##### Flag explanation | Flag | Required | Description | | --------------------------------- | -------- | ---------------------------------------------------------------------- | | `--inspect=false` | No | Disables the traffic inspector (not needed for KVM) to save RAM | | `--basic-auth=admin:YourPassword` | No | Additional HTTP authentication ([details](#why-you-need---basic-auth)) | | `--https-redirect` | No | Redirects HTTP to HTTPS | | `80` | Yes | NanoKVM web interface port | You can also [restrict access by IP subnets](#restricting-access-by-ip-subnets). ##### Configuring auto-start Add a line to `/etc/inittab` so that init automatically starts and restarts the tunnel: ```bash cat << 'EOF' >> /etc/inittab # Tuna HTTP tunnel (respawn = auto-restart) tweb::respawn:/data/tuna/tuna-http.sh EOF ``` Apply without rebooting: ```bash kill -HUP 1 ``` ##### Verification Make sure the process is running: ```bash ps -ef | grep tuna ``` The output should contain a line with `/data/tuna/tuna ... http ...`. If there is no such process — see the [Troubleshooting](#troubleshooting) section. In the logs, you will see your tunnel URL: ```bash cat /tmp/tuna-http.log ``` The current address is also available in the [personal cabinet](https://my.tuna.am/?lang=en). *** #### Type 2. Subscription — SSH and HTTP tunnels With a [subscription](https://tuna.am/#pricing) you get: * **Fixed subdomain** for the HTTP tunnel — the address does not change * **SSH tunnel** — direct access to the NanoKVM console from anywhere in the world And in general there are essentially no limits — you can run a few more tunnels and expose access, for example, to the router web interface, SSH, or RDP of the controlled or any other computer on the local network. ##### HTTP tunnel with a fixed address ```bash cat << 'EOF' > /data/tuna/tuna-http.sh #!/bin/sh # Wait for the network while ! ip route | grep -q default; do sleep 1 done exec /data/tuna/tuna \ --config=/data/tuna/.tuna.yml \ --log=/tmp/tuna-http.log \ http \ --subdomain=nanokvm \ --inspect=false \ --basic-auth=admin:YourPassword \ --https-redirect \ 80 EOF chmod +x /data/tuna/tuna-http.sh ``` The difference from the free version is the `--subdomain=nanokvm` flag, which reserves a permanent address for you. After restarting the tunnel, the URL will remain the same. > **Info.** The example subdomain is already taken. Reserve your subdomain in advance in the [personal cabinet](https://my.tuna.am/domains?lang=en), then use it in the `--subdomain=your-subdomain` flag. ###### HTTP tunnel flag explanation | Flag | Required | Description | | --------------------------------- | -------- | ---------------------------------------------------------------------- | | `--subdomain=nanokvm` | No | Reserves a permanent tunnel address (subscription only) | | `--inspect=false` | No | Disables the traffic inspector (not needed for KVM) to save RAM | | `--basic-auth=admin:YourPassword` | No | Additional HTTP authentication ([details](#why-you-need---basic-auth)) | | `--https-redirect` | No | Redirects HTTP to HTTPS | | `80` | Yes | NanoKVM web interface port | You can also [restrict access by IP subnets](#restricting-access-by-ip-subnets). ##### SSH tunnel The SSH tunnel allows you to connect to the NanoKVM console over the internet. This is convenient for administration, firmware updates, and diagnostics. ```bash cat << 'EOF' > /data/tuna/tuna-ssh.sh #!/bin/sh # Wait for the network while ! ip route | grep -q default; do sleep 1 done exec /data/tuna/tuna \ --config=/data/tuna/.tuna.yml \ --log=/tmp/tuna-ssh.log \ ssh \ --port=nanokvm-ssh \ --record-session=false \ --password-auth=false EOF chmod +x /data/tuna/tuna-ssh.sh ``` ###### SSH tunnel flag explanation | Flag | Required | Description | | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--port=nanokvm-ssh` | No | Reserves the SSH tunnel port. Without this flag, the port is assigned randomly on each startup. The [port alias](https://my.tuna.am/tcp_ports?lang=en) must be reserved in advance before startup. | | `--record-session=false` | No | Disables recording SSH sessions to disk. Recommended on embedded devices with limited storage to save space. | | `--password-auth=false` | No | Disallows password authentication — access via SSH keys only ([details](#ssh-tunnel-authentication)) | You can also set [a static login and password](#ssh-tunnel-authentication) or [restrict access by IP subnets](#restricting-access-by-ip-subnets). ##### Configuring auto-start for both tunnels Add lines to `/etc/inittab`: ```bash cat << 'EOF' >> /etc/inittab # Tuna tunnels (respawn = auto-restart) tssh::respawn:/data/tuna/tuna-ssh.sh tweb::respawn:/data/tuna/tuna-http.sh EOF ``` Apply without rebooting: ```bash kill -HUP 1 ``` ##### Verifying tunnels Make sure both processes are running: ```bash ps -ef | grep tuna ``` The output should contain two lines — one with `http` and one with `ssh`. If a process is missing — see the [Troubleshooting](#troubleshooting) section. View the logs: ```bash cat /tmp/tuna-http.log cat /tmp/tuna-ssh.log ``` The HTTP tunnel logs will contain the URL, and the SSH tunnel logs will contain connection instructions with the address and port. *** #### Managing tunnels With `inittab respawn`, the tuna process is automatically restarted on failure. Use the following commands to manage it: ##### Check status ```bash ps -ef | grep tuna ``` ##### Stop a tunnel You cannot just kill the process — init will restart it immediately. First, comment out the line in `/etc/inittab`: ```bash # Stop the HTTP tunnel sed -i 's/^tweb:/#tweb:/' /etc/inittab && kill -HUP 1 # Stop the SSH tunnel sed -i 's/^tssh:/#tssh:/' /etc/inittab && kill -HUP 1 ``` ##### Start a tunnel Uncomment the line back: ```bash # Start the HTTP tunnel sed -i 's/^#tweb:/tweb:/' /etc/inittab && kill -HUP 1 # Start the SSH tunnel sed -i 's/^#tssh:/tssh:/' /etc/inittab && kill -HUP 1 ``` *** #### Recovery after re-flashing The binary and scripts in `/data/tuna/` are preserved during application updates via the web interface and during remote rsync. After a full re-flash (Etcher), you will need to copy the binary and create the scripts again. For remote rsync, you only need to restore the lines in `/etc/inittab`: ```bash cat << 'EOF' >> /etc/inittab # Tuna tunnels (respawn = auto-restart) tssh::respawn:/data/tuna/tuna-ssh.sh tweb::respawn:/data/tuna/tuna-http.sh EOF kill -HUP 1 ``` > **Info.** If you also use [availability monitoring](https://tuna.am/en/docs/monitors/examples/nanokvm.md), don't forget to restore the corresponding line in `/etc/inittab`. *** #### Additional security settings ##### Why you need `--basic-auth` NanoKVM has its own authentication in the web interface. However, given the [history of vulnerabilities in IoT devices](https://owasp.org/www-project-internet-of-things/), we strongly recommend adding an extra layer of protection. When exposing access through the internet, it's better to be safe — the `--basic-auth` flag adds standard HTTP authentication (username and password in the browser) before the request even reaches NanoKVM. ```bash --basic-auth=mylogin:MyStr0ngP@ss ``` > **Important.** Set **your own** login and password. Do not use the values from the examples. ##### SSH tunnel authentication **Option 1. SSH keys (recommended)** The `--password-auth=false` flag disables password login. To connect, you will need to add a public SSH key in advance in the [personal cabinet](https://my.tuna.am/public_keys?lang=en). Detailed guide: [SSH keys setup](https://tuna.am/en/docs/guides/ssh-keys.md) > **Warning.** Tunnel nodes have public addresses. Attackers can scan ports and start brute-forcing passwords. For long-lived SSH tunnels, we strongly recommend disabling password authentication. **Option 2. Static login and password** If SSH keys are not suitable, you can set fixed credentials: ```bash --auth=user:pass ``` Replace `user` and `pass` with your own values. ##### Restricting access by IP subnets For HTTP and SSH tunnels, you can restrict access by IP addresses. Allow access only from specific subnets: ```bash --cidr-allow="203.0.113.0/24" ``` Deny access from specific subnets: ```bash --cidr-deny="198.51.100.0/24" ``` You can combine — allow a subnet but exclude individual addresses: ```bash --cidr-allow="10.0.0.0/24" --cidr-deny="10.0.0.33/32" ``` > **Caution.** Use IP filtering carefully. If you make a mistake with the subnet or your IP address changes — you will lose remote access to NanoKVM. Make sure you have an alternative way to manage the device (for example, local access). *** #### Troubleshooting ##### tuna process does not start Check that the process is in the list: ```bash ps -ef | grep tuna ``` If the output has no lines with `/data/tuna/tuna`, make sure the entries in `/etc/inittab` are not commented out: ```bash grep tuna /etc/inittab ``` The lines should start with `tssh::` or `tweb::`, not `#`. If they are commented out, uncomment them and reload the configuration: ```bash kill -HUP 1 ``` ##### Viewing logs Tunnel logs are written to `/tmp/` and are available until the device reboots: ```bash # HTTP tunnel logs cat /tmp/tuna-http.log # SSH tunnel logs cat /tmp/tuna-ssh.log ``` To watch logs in real time: ```bash tail -f /tmp/tuna-http.log ``` ##### Common errors in logs | Error | Cause | Solution | | ------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------- | | `token is invalid` | Invalid or expired token | Repeat `config save-token` with a fresh token from the [page](https://my.tuna.am/token?lang=en) | | `Domain already reserved` | Subdomain is taken by another user | Choose another name via `--subdomain=` | | `port alias not found` | Port alias is not reserved | Reserve the alias in the [personal cabinet](https://my.tuna.am/tcp_ports?lang=en) | | `connection refused` | No internet access | Check the network: `ping -c 3 8.8.8.8` | ##### No internet access Check that NanoKVM has a network connection: ```bash # Check for an IP address ip addr # Check the default route ip route # Check internet access ping -c 3 8.8.8.8 ``` ##### Logs disappeared after a reboot Logs are stored in `/tmp/`, which is cleared on reboot. This is normal behavior. If you need to keep logs across reboots, change the path in the startup scripts to `/data/tuna/`: ```bash --log=/data/tuna/tuna-http.log ``` > **Warning.** On NanoKVM, the `/data` partition is exFAT, and frequent log writes may shorten the SD card's lifespan. Use this only for temporary debugging. ##### Tunnel was working and stopped 1. Check whether the firmware was updated — after remote rsync, you need to restore the entries in `/etc/inittab` (see [Recovery after re-flashing](#recovery-after-re-flashing)) 2. Check the logs for errors 3. Make sure the token is still valid in the [personal cabinet](https://my.tuna.am/token?lang=en) *** #### Links * [NanoKVM — official documentation](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html) * [NanoKVM — GitHub repository](https://github.com/sipeed/NanoKVM) * [Updating NanoKVM](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/system/updating.html) * [Re-flashing NanoKVM](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/system/flashing.html) * [HTTP tunnel — all flags](https://tuna.am/en/docs/tunnels/http.md) * [SSH tunnel — all flags](https://tuna.am/en/docs/tunnels/ssh.md) * [SSH keys setup](https://tuna.am/en/docs/guides/ssh-keys.md) * [Running tuna as a service](https://tuna.am/en/docs/tunnels/guides/service.md) *** #### Availability monitoring In addition to tunnels for remote access, you can set up availability monitoring for NanoKVM. Heartbeat monitoring will send you notifications if the device loses internet connectivity or stops responding. **Full guide:** [NanoKVM availability monitoring](https://tuna.am/en/docs/monitors/examples/nanokvm.md) --- ### tuna and Netdata #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the Netdata server Installation instructions can be found in the [official documentation](https://learn.netdata.cloud/docs/installing/). #### Step 3. Run tuna Netdata uses port `19999` by default: ```bash tuna http 19999 ``` --- ### tuna and NGINX #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start NGINX #### Step 3. Run tuna No special settings are required, but for correct request routing, passing the `Host` header may be necessary, for example: ```bash tuna http --request-header="host:tuna.am" 80 ``` Information on how NGINX processes requests can be found in the [official documentation](https://nginx.org/en/docs/http/request_processing.html). --- ### tuna and Ollama #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Ollama Installation instructions can be found in the [official documentation](https://github.com/ollama/ollama/tree/main/docs#getting-started). #### Step 3. Run tuna Ollama uses port `11434` by default: ```bash tuna http 11434 --request-header="host:localhost:11434" ``` Please note that the API will be accessible to everyone. If you need to restrict access, there are several ways: * [basic authentication protection](https://tuna.am/en/docs/tunnels/http.md#basic-auth) * [API token protection](https://tuna.am/en/docs/tunnels/http.md#key-auth) * [traffic policies](https://tuna.am/en/docs/traffic-policy.md#basic_auth) An example of basic authentication protection might look like this: ```bash tuna http 11434 --request-header="host:localhost:11434" --basic-auth="login:password" ``` All requests without correct credentials will receive a `401` error. --- ### tuna and Open Server Panel #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the OSPanel server Installation instructions can be found in the [official documentation](https://ospanel.io/docs/#ustanovka). #### Step 3. Run tuna OSPanel uses port `80` by default to access your web project: ```bash tuna http 80 ``` For correct request routing, passing the `Host` header may be necessary, for example: ```bash tuna http --request-header="host:tuna.am" 80 ``` > **Community comment from Telegram:** If using NGINX and HTTP to HTTPS redirect is configured, you need to specify the scheme, for example: > > ```bash > tuna http --request-header="host:tuna.am" https://127.0.0.1 > > ``` > > Aleksandr G. --- ### tuna and Paddle #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the server to process HTTP notifications Information about incoming Paddle notifications can be found in the [official documentation](https://developer.paddle.com/webhooks/overview). #### Step 3. Run tuna In the startup arguments, specify your server's port: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=paddle-webhook ``` #### Step 4. Specify the address for incoming notifications Detailed instructions for configuring event sending can be found in the [official documentation](https://developer.paddle.com/webhooks/notification-destinations). --- ### tuna and phpMyAdmin #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start phpMyAdmin Installation instructions can be found in the [official documentation](https://docs.phpmyadmin.net/en/latest/setup.html). #### Step 3. Run tuna phpMyAdmin uses port `8080` by default: ```bash tuna http 8080 ``` --- ### tuna and PocketBase #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the PocketBase server Installation instructions can be found in the [official documentation](https://pocketbase.io/docs/). #### Step 3. Run tuna PocketBase uses port `8090` by default: ```bash tuna http 8090 ``` --- ### tuna and PostgreSQL #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the PostgreSQL server To run with Docker, you can use the command: ```bash docker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:15 ``` #### Step 3. Run tuna PostgreSQL uses port `5432` by default: ```bash tuna postgres 5432 ``` As a result of running the command, a connection address will be displayed on the screen (for example, `ru.tuna.am:12345`), which can now be used to connect to PostgreSQL, for example: ```bash psql -h ru.tuna.am -p 12345 -U postgres -d postgres ``` When running the [Postgres tunnel](https://tuna.am/en/docs/tunnels/postgres.md), you will be provided with a link to the inspector for viewing sessions and executed queries. --- ### tuna and Qdrant #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the Qdrant server Installation instructions can be found in the [official documentation](https://qdrant.tech/documentation/quickstart/). #### Step 3. Run tuna Qdrant uses port `6333` by default for HTTP: ```bash tuna http 6333 ``` And port `6334` for gRPC: ```bash tuna tcp 6334 ``` --- ### tuna and RabbitMQ #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Start the RabbitMQ server Installation instructions can be found in the [official documentation](https://www.rabbitmq.com/docs/download). #### Step 3. Run tuna RabbitMQ uses port `5672` by default: ```bash tuna tcp 5672 ``` As a result of running the command, a connection address will be displayed on the screen (for example, `ru.tuna.am:12345`), which can now be used to connect to RabbitMQ. You can provide access to RabbitMQ Management, which uses port `15672` by default: ```bash tuna http 15672 ``` --- ### tuna and Raspberry Pi #### Step 1. Download the client Connect to Raspberry Pi via SSH: ```shell ssh root@raspberrypi.local ``` Install tuna with a single command: **curl** ```shell sh -c "$(curl -sSLf https://releases.tuna.am/tuna/get.sh)" ``` **wget** ```shell sh -c "$(wget -qO- https://releases.tuna.am/tuna/get.sh)" ``` Set the token for the client to work, it can be copied from a [dedicated page](https://my.tuna.am/token?lang=en): ```shell tuna config save-token ``` #### Step 2. Run tuna ```shell tuna tcp 22 ``` As a result of running the command, the connection address will be displayed (for example, `ru.tuna.am:12345`), which can now be used to connect to the SSH server on Raspberry Pi, for example: ```shell ssh -p 12345 user@ru.tuna.am ``` --- ### tuna and RDP #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run RDP server #### Step 3. Run tuna Typically, port `3389` is used for RDP: ```bash tuna tcp 3389 ``` Be careful, providing public access to such services can be insecure. --- ### tuna and Redis #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Redis server #### Step 3. Run tuna Redis uses port `6379` by default: ```bash tuna redis 6379 ``` As a result of running the command, the connection address will be displayed (for example, `ru.tuna.am:12345`), which can now be used to connect to Redis. When running a [Redis tunnel](https://tuna.am/en/docs/tunnels/redis.md), you will be provided with a link to an inspector to view sessions and executed commands. --- ### tuna and Robokassa #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run a server to handle HTTP notifications Information about incoming Robokassa notifications can be found in the [official documentation](https://docs.robokassa.ru/ru/notifications-and-redirects). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=billing ``` #### Step 4. Specify the address for incoming notifications From the [My Shops](https://partner.robokassa.ru/Shops) section, go to the shop you need, then to the Settings section on the Technical Settings tab, here in the Result URL field you need to set addresses for receiving notifications: ![robokassa](/en/assets/images/robokassa1-56fce8a6f231b668383b17687a6453f2.png) The URL consists of the address received from tuna (for example, `https://billing.ru.tuna.am`) and the path to your server's method for processing events (for example, `/payments/pay`). After all settings are complete, you can use [test payment methods](https://docs.robokassa.ru/ru/testing-mode) to debug the interaction. --- ### tuna and ScyllaDB #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run ScyllaDB server Installation instructions can be found in the [official documentation](https://opensource.docs.scylladb.com/stable/getting-started/install-scylla/index.html). #### Step 3. Run tuna ScyllaDB uses multiple ports, the description of which can be found in the [official documentation](https://opensource.docs.scylladb.com/stable/operating-scylla/admin.html#networking), to provide access to CQL you can use the command: ```shell tuna tcp 9042 ``` For REST API access: ```shell tuna tcp 10000 ``` --- ### tuna and SMTP #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run SMTP server #### Step 3. Run tuna SMTP server uses port `25` by default, or `465` for secure connection: ```bash tuna tcp 25 ``` As a result of running the command, the connection address will be displayed (for example, `ru.tuna.am:12345`), which can now be used to connect to the SMTP server. --- ### tuna and SSH #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run SSH server #### Step 3. Run tuna SSH server uses port `22` by default: ```bash tuna tcp 22 ``` As a result of running the command, the connection address will be displayed (for example, `ru.tuna.am:12345`), which can be used to connect to the SSH server, for example: ```bash ssh -p 12345 user@ru.tuna.am ``` #### Alternatives tuna has a built-in [SSH server](https://tuna.am/en/docs/tunnels/ssh.md), using the `tuna ssh` command you can quickly organize SSH access without installing and configuring a server. --- ### tuna and Superset #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Superset server Installation instructions can be found in the [official documentation](https://superset.apache.org/admin-docs/installation/installation-methods/). #### Step 3. Run tuna Superset uses port `8088` by default: ```bash tuna http 8088 ``` --- ### tuna and Temporal #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Temporal server Instructions for setting up the environment can be found in the [official documentation](https://learn.temporal.io/getting_started/). #### Step 3. Run tuna Temporal uses port `7233` by default for API: ```bash tuna http localhost:7233 ``` And port `8233` for Web interface: ```bash tuna http localhost:8233 ``` --- ### tuna and TimescaleDB #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run TimescaleDB server Installation instructions can be found in the [official documentation](https://docs.timescale.com/self-hosted/latest/install/). #### Step 3. Run tuna TimescaleDB uses port `5432` by default: ```bash tuna tcp 5432 ``` --- ### tuna and Transmission #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run transmission-daemon Installation instructions can be found in the [official documentation](https://transmissionbt.com/download). #### Step 3. Run tuna transmission-daemon uses port `9091` by default: ```bash tuna http 9091 ``` --- ### tuna and Vite #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Vite server Installation instructions can be found in the [official documentation](https://vitejs.dev/guide/). #### Step 3. Run tuna Vite server uses port 5173 by default: ```bash tuna http localhost:5173 ``` --- ### tuna and WampServer #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run WampServer #### Step 3. Run tuna No special settings are required, but for correct request routing, you will need to pass the `Host` header, for example: ```shell tuna http --request-header="host:tuna.am" 80 ``` --- ### tuna and WebAsk #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run a server to handle events from WebAsk Information about the structure of incoming requests can be found in the [official documentation](https://webask.io/help/webhooks). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=webask-webhook ``` #### Step 4. Create a new Webhook Study the [instructions for adding a Webhook](https://webask.io/help/webhooks), as the "Callback URL" value, specify the address received from tuna (for example, `https://webask-webhook.ru.tuna.am`) and the path to your server's method for processing events (for example, `/event`). --- ### tuna and YooKassa #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run a server to handle HTTP notifications Information about incoming YooKassa notifications can be found in the [official documentation](https://yookassa.ru/developers/using-api/webhooks), where you can also find instructions for creating a [test store](https://yookassa.ru/developers/payment-acceptance/testing-and-going-live/testing#test-store). #### Step 3. Run tuna Specify your server port in the startup arguments: ```bash tuna http 8080 ``` Or with a static address: ```bash tuna http 8080 --subdomain=billing ``` #### Step 4. Specify the address for incoming notifications On the [HTTP notifications](https://yookassa.ru/my/merchant/integration/http-notifications) page, in the Integration section, you need to specify the address for receiving notifications, such as payment status changes or refunds: ![yookassa](/en/assets/images/yookassa1-46e782a6d5ec4f70ea0c4bccb968e144.png) The URL consists of the address received from tuna (for example, `https://billing.ru.tuna.am`) and the path to your server's method for processing events (for example, `/payments/yookassa`). After all settings are complete, you can use [test payment methods](https://yookassa.ru/developers/payment-acceptance/testing-and-going-live/testing#test-bank-card) to debug the interaction. --- ### tuna and Yucca #### Step 1. Download the client Instructions are available on the [installation page](https://tuna.am/en/docs/guides/install.md). #### Step 2. Run Yucca server Installation instructions can be found in the [official documentation](https://docs.yucca.app/ru/install/). #### Step 3. Run tuna Yucca uses port `9910` by default: ```bash tuna http 9910 ``` --- ### Guides #### [📄️Connecting Your Own Domain](https://tuna.am/en/docs/tunnels/guides/connect-self-domain.md) [How to connect your own domain to tuna.am](https://tuna.am/en/docs/tunnels/guides/connect-self-domain.md) #### [📄️Port Detection](https://tuna.am/en/docs/tunnels/guides/detect.md) [Running your application wrapped in tuna with automatic port detection](https://tuna.am/en/docs/tunnels/guides/detect.md) #### [📄️Available Regions](https://tuna.am/en/docs/tunnels/guides/locations.md) [Available regions for connection](https://tuna.am/en/docs/tunnels/guides/locations.md) #### [📄️Multiple Tunnel Startup](https://tuna.am/en/docs/tunnels/guides/multi-tunnels.md) [Running multiple tunnels simultaneously](https://tuna.am/en/docs/tunnels/guides/multi-tunnels.md) #### [📄️Wrapped Execution](https://tuna.am/en/docs/tunnels/guides/run.md) [Running your application wrapped in tuna with automatic port detection and tunnel creation](https://tuna.am/en/docs/tunnels/guides/run.md) #### [📄️Running tuna as a Service](https://tuna.am/en/docs/tunnels/guides/service.md) [Running in background mode with Systemd](https://tuna.am/en/docs/tunnels/guides/service.md) #### [📄️HTTP Tunnels with SSH Client](https://tuna.am/en/docs/tunnels/guides/ssh-reverse.md) [Creating an HTTP tunnel using the built-in SSH client, without installing tuna](https://tuna.am/en/docs/tunnels/guides/ssh-reverse.md) --- ### Connecting Your Own Domain In this article, we will explain in detail how to connect your own domain to the Tuna platform and publish a local website to the internet without a public IP, complex network configurations like VPN, and without deploying code to any server or hosting. Publishing on your own domain can be interesting for Demo stands when you present a solution to a client. The client sees a finished solution while you haven't spent a minute on environment preparation, and you can also fix small things on the fly. However, there can be many more use cases. #### Connecting a Domain ##### Purchasing a Domain First, you'll need your own domain (if you don't have one). You can buy it, for example, from [Reg.ru](https://www.reg.ru/?utm_source=tuna.am) or [namecheap](https://www.namecheap.com/?utm_source=tuna.am) or any other domain registrar. For examples in this article, we use the domain `tuna-dev.ru`. ##### Connecting to Tuna and Getting Settings Go to the [domain management page](https://my.tuna.am/domains?lang=en) in your personal account and click **Add domain** in the top right corner. ![](/docs/img/connect-self-domain/add-domain.png) Select the **Own domain** type and enter your domain in the **Domain** field. In our case, it's `api.tuna-dev.ru`. If you have a Unicode domain, for example in one of these zones `.РФ, .ДЕТИ, .ҚАЗ`, you need to specify the converted [punycode](https://www.reg.ru/web-tools/punycode?utm_source=tuna.am) variant. ![](/docs/img/connect-self-domain/adding-in-my.png) After specifying your domain, you will see the required DNS settings. ##### DNS Configuration Go to the DNS records management panel and create the record shown in the dashboard. For a subdomain like `api.tuna-dev.ru`, this is a **CNAME**: it points to the Tuna infrastructure as a whole rather than to a specific server, so the domain works with [fault tolerance](https://tuna.am/en/blog/high-availability.md). An apex domain like `tuna-dev.ru` can't be connected with a CNAME; there is a [separate option](#apex) for it. If you have an **AAAA** record specified for the selected domain, it needs to be deleted, as IPv6 is not currently supported. ![](/docs/img/connect-self-domain/add-cname.png) ##### Waiting for Confirmation After creating the DNS record, you can return to your personal account and click **Add**. The domain will enter verification mode. ![](/docs/img/connect-self-domain/check-status.png) Validation usually takes from 15 minutes to 2 hours but can extend to a couple of days, depending on the DNS record TTL. The verification process is limited to **24 hours** and is divided into 2 main stages: * **DNS Check** - we verify that your domain is configured correctly * **TLS Check** - we issue a [Let's Encrypt](https://letsencrypt.org/?utm_source=tuna.am) certificate for your domain If the addition process failed with a DNS error, [check](https://www.reg.ru/nettools/dig?utm_source=tuna.am) that you correctly created the DNS record and it points to the correct server. If everything is correct, the DNS cache may not have updated yet and you need to wait, then simply resume the verification. If the addition process failed with a TLS error, check if there are obvious errors in the DNS configuration: AAAA records, recursively looped pointers. If you can't figure it out yourself, contact [support](mailto:info@tuna.am). ##### Usage After successful domain verification, you will receive a notification email. ![](/docs/img/connect-self-domain/domain-verified.png) Now you can start using it. To do this, add the `--domain` flag or its short variant `-d` or an [environment variable](https://tuna.am/en/docs/guides/environment-variables.md) and your domain to the `tuna http ` command, for example: ```shell tuna http 8080 --domain=api.tuna-dev.ru ``` ```shell tuna http 8080 -d api.tuna-dev.ru ``` ```shell export TUNA_DOMAIN=api.tuna-dev.ru tuna http 8080 ``` #### Apex Domain An apex domain is the domain itself without a subdomain: `example.com`, not `www.example.com`. DNS rules don't allow a CNAME on it, only A records pointing to specific addresses. Such a domain works, but without fault tolerance: if one of the addresses changes or becomes unavailable, some visitors won't be able to open the site. That's why, when you add an apex domain, the dashboard asks you to choose how to connect it. ##### Site on a Subdomain, with Fault Tolerance This is the option we recommend. The tunnel runs on a subdomain: `www.example.com` by default, but you can choose your own, for example `shop.example.com`. The subdomain is connected with a CNAME, the apex with A records. Once both domains pass the DNS check, Tuna creates a [gateway](https://tuna.am/en/docs/gateways.md) with a permanent redirect (code 308) from `example.com` to the subdomain. The page path is kept: `example.com/catalog` opens `www.example.com/catalog`. ![The form for adding an apex domain with the Site on a subdomain, with fault tolerance option selected](/docs/img/connect-self-domain/apex-create-site.webp) After you add the domain, the dashboard shows the DNS records for both domains and the command to start a tunnel on the subdomain: ![The New domains window: a CNAME record for www.example.com and A records for example.com](/docs/img/connect-self-domain/apex-created.webp) ```shell tuna http 8080 --domain=www.example.com ``` Once both domains pass the check, the apex in the list gets a redirect mark: ![The domain list: example.com redirects to www.example.com](/docs/img/connect-self-domain/apex-list.webp) The pair of domains takes two slots in your custom domain limit. If only one slot is free, the subdomain option is unavailable, and a link to upgrade your subscription appears next to it. What to know about the redirect: * The query string isn't kept on redirect: `example.com/catalog?utm_source=mail` opens `www.example.com/catalog`. Point links with UTM tags straight to the subdomain. * The redirect gateway is visible in the [Gateways](https://my.tuna.am/gateways?lang=en) section. If you delete it, the redirect turns off, and the apex domain card gets a button to turn it back on. * While the gateway runs on the apex, a tunnel on `example.com` won't start: the server responds with `Domain already reserved by gateway`. * If you delete the subdomain, the apex gateway stays and keeps redirecting to the deleted address. Delete it manually in the **Gateways** section. ##### Apex Domain Only The apex is connected with A records to every address of the [cell](https://tuna.am/en/docs/guides/corporate-network.md#firewall-addresses), and the tunnel runs on `example.com`. Such a domain has no fault tolerance, and the dashboard warns you about it. ![The form for adding an apex domain with the example.com only option selected and marked as having no fault tolerance](/docs/img/connect-self-domain/apex-only.webp) ##### How to Migrate an Already Connected Domain Apex domains connected earlier and subdomains on A records are marked with a warning icon in the list. ![The domain list: a warning icon next to the status of example.com](/docs/img/connect-self-domain/apex-warning.webp) For an apex domain, the card has a step-by-step migration: 1. **Add a subdomain for the site.** The dashboard shows the CNAME record to create at your registrar. 2. **Start the tunnel on the subdomain** instead of the apex. Stop the tunnel on the apex: while it runs, the redirect won't turn on. 3. **Turn on the redirect to the subdomain.** The button becomes available once the subdomain passes the DNS check. The redirect starts working in about a minute. ![The apex domain card: three steps to migrate to a subdomain](/docs/img/connect-self-domain/apex-migration.webp) Old links keep working: visitors coming to `example.com` land on the same pages of the subdomain. If a subdomain is connected with A records, you don't need to change the tunnel or settings. Replace the A records with a single CNAME record from the domain card. The warning disappears after the next DNS recheck, which runs once a day. ![A subdomain card on A records: a suggestion to replace them with a single CNAME record](/docs/img/connect-self-domain/subdomain-a-record.webp) --- ### Port Detection If you just want to detect which port your program started listening on, you can do this with the `tuna detect` command. A time is allocated for detection, by default 5 seconds, but this behavior can be defined using the `--seconds` flag or the `TUNA_DETECT_SECONDS` environment variable. After this period expires, tuna will terminate along with all child processes. Example run: ```shell tuna detect -- nc -l 23554 ``` Example output: ```shell tuna detect -- nc -l 23554 INFO[16:18:12] Waiting for available ports... INFO[16:18:12] 5 INFO[16:18:13] 4 INFO[16:18:13] Found port: 23554 INFO[16:18:14] 3 INFO[16:18:15] 2 INFO[16:18:16] 1 INFO[16:18:17] Here is your tunnel commands: INFO[16:18:17] 1. tuna tcp 23554 ``` ![tuna_detect](/en/assets/images/tuna_detect-35f718721bdbb52c4189c38be84b54a2.gif) --- ### Available Regions The region determines which tunnel server the connection will go through, currently 2 regions are available: | Region | Code | IP addresses | Used by default | | --------------- | ---- | -------------------------------------------------- | --------------- | | Russia (Moscow) | `ru` | `178.20.41.29` - main, `178.20.43.146` - service | YES | | Netherlands | `nl` | `89.19.217.251` - main, `147.45.238.190` - service | | You can override the region through: 1. [Configuration file](https://tuna.am/en/docs/guides/config-file.md): Edit the parameter in the file: ```shell location: nl ``` Or set it with the command: ```shell tuna config save-location nl ``` 2. [Environment variable](https://tuna.am/en/docs/guides/environment-variables.md): ```shell TUNA_LOCATION=ru ``` 3. Command line argument: ```shell tuna http 8080 --location=nl ``` --- ### Multiple Tunnel Startup In projects where you need to publish multiple services, you may need to run several tunnels at once. > **Note.** All current flags, hints, and examples can be viewed by calling help: > > ```shell > tuna start --help > > ``` #### Configuration You can describe all tunnels in the global [configuration file](https://tuna.am/en/docs/guides/config-file.md) or a local one (only for the **tunnels** section). If a local file named `.tuna.yml` is found in the current directory, it overrides the `tunnels` section from the global one. This way you can create a `.tuna.yml` in the root of the repository and it will immediately work for all developers on the team. The path to the local file can be overridden using the `--tunnels-file` flag or the `TUNA_START_TUNNELS_FILE` environment variable for the `tuna start` command. ```shell tuna start --tunnels-file=/your/path/.tuna.yml web ``` Example configuration describing the launch of four tunnels: ```yaml --- tunnels: web: commandLine: tuna http localhost:5173 --subdomain=web tags: [frontend, ui] api: commandLine: tuna http 8080 --subdomain=api tags: [backend, api] postgres: commandLine: tuna tcp 5432 --port=postgres tags: backend redis: commandLine: tuna tcp 6379 --port=redis tags: frontend ``` What each service consists of using the first as an example: ```yaml web: commandLine: tuna http localhost:5173 --subdomain=web tags: [frontend, ui] ``` 1. `web` - tunnel name, must be unique in the list of all tunnels 2. `commandLine` - command to create the tunnel 3. `tags` - optional field, can contain a string or a list of strings. Tags can be used to run a subset of tunnels. #### Running The `tuna start` command is responsible for running, you can separate the launch depending on your needs. ##### By service name Run tunnel only for the api service: ```shell tuna start api ``` Run tunnel only for api and postgres: ```shell tuna start api postgres ``` The name can also be passed in the `TUNA_START` environment variable: ```shell export TUNA_START="api" tuna start ``` ##### By tag Run all tunnels with the `frontend` tag: ```shell tuna start --tags=frontend ``` Run all tunnels with the `frontend` or `backend` tag: ```shell tuna start --tags=frontend,backend ``` Tags can also be passed in the `TUNA_START_TAGS` environment variable: ```shell export TUNA_START_TAGS="frontend" tuna start ``` ##### All at once Run all described tunnels: ```shell tuna start --all ``` Or with the `TUNA_START_ALL` environment variable: ```shell export TUNA_START_ALL=true tuna start ``` --- ### Wrapped Execution You can run your application wrapped in tuna, then the port your server is listening on will be automatically detected and a tunnel will be started for it. If tuna can get an HTTP response from the application during the check, an HTTP tunnel with a dynamic subdomain will start, otherwise TCP. #### Example An example of usage is described in our [blog](https://tuna.am/en/blog/invoices.md#tuna-run). ![tuna_run](/en/assets/images/tuna_run-6efa86afeb5224d166817a19bbc239f1.gif) #### Running Execution is done with the `tuna run` command. Example with a JavaScript application: ```shell tuna run -- yarn start ``` Example with Python: ```shell tuna run -- python3 -m http.server 8000 ``` --- ### Running a process in background mode, as a service Background running is useful if you want to establish permanent external access to services or control panels on a remote computer. #### Creating and Managing Services Creating and managing services is available on all supported platforms. To create and work with services, use the `tuna service` or `tuna svc` command. > **Note.** You can view all current flags, hints, and examples by calling help: > > ```shell > tuna service --help > tuna service install --help > tuna service start --help > tuna service restart --help > tuna service stop --help > tuna service uninstall --help > > ``` ##### Creating a Service > **Info.** In Windows, you need to run commands in PowerShell as administrator, in Linux and FreeBSD you will need sudo or root user. In macOS, the service is created under the current user and no privileges are needed. The `tuna service install` command indicates that a new service needs to be registered. You can create multiple services with different names by passing the name through the `--name` flag or the `TUNA_SERVICE_NAME` environment variable, default is **tuna**. After `--` the command that will be run in the service is described. ```shell tuna service install -- tuna --config=C:\Users\Administrator\AppData\Local\tuna\tuna.yml http 8080 --subdomain=web ``` In the example above, the full path to the [configuration file](https://tuna.am/en/docs/guides/config-file.md) is explicitly passed, this may be necessary because the service is created from the administrator account, while the file with the token is located in the current user's profile. This will most likely be required in Windows, Linux and FreeBSD and will not be required in macOS. You can also pass the token explicitly in the arguments: ```shell tuna service install --name=tuna-web -- tuna http 8080 --subdomain=web --token= ``` For greater security in Linux, FreeBSD and macOS, the token will be cut from the startup arguments and passed as an environment variable, so you won't see the token in the process list. > **Attention.** Since in Windows, Linux and FreeBSD the service is created from a privileged user, by default the process will also run from it, for security reasons it is not recommended to run tuna from a privileged user. Therefore, you can explicitly specify the user from whose name the process will run using the `--user` flag or the `TUNA_SERVICE_INSTALL_USER` environment variable. In Windows, you may also need to pass the user password using the `--password` flag or the `TUNA_SERVICE_INSTALL_PASSWORD` environment variable. > > ```shell > tuna service install --user=ubuntu -- tuna http 8080 --subdomain=web --token= > > ``` ##### Starting a Service Starting is done with the `tuna service start` command. Example with name passing: ```shell tuna service start --name=tuna-web ``` ##### Restarting a Service Restarting is done with the `tuna service restart` command. Example with name passing: ```shell tuna service restart --name=tuna-web ``` ##### Checking Service Status Status checking is done with the `tuna service status` command. Example with name passing: ```shell tuna service status --name=tuna-web ``` ##### Stopping a Service Stopping is done with the `tuna service stop` command. Example with name passing: ```shell tuna service stop --name=tuna-web ``` ##### Removing a Service Removal is done with the `tuna service uninstall` command. Example with name passing: ```shell tuna service uninstall --name=tuna-web ``` #### Debugging and Troubleshooting If the service is not working or is working incorrectly, first you need to check the service logs. Tuna provides only a high-level wrapper for creating and running a service, the debugging method will depend on your system. **Windows** To manage the tuna service in Windows, you can use the **Services** tool. In the service list, find `tuna` (or the name of your service). Right-click on the service and select the desired action: Start, Stop, or Restart. To view tuna service logs, you can use the **Event Viewer** tool or PowerShell. In **Event Viewer**, open **Windows Logs > Application or System** to find logs related to your service. Viewing logs via PowerShell: ```powershell Get-EventLog -LogName System -Source tuna ``` **macOS** > **Tip.** By default, macOS has power management policies that put the computer to sleep, which makes the service unavailable. This behavior can be configured if you want to use a macOS computer in server mode. You can view the current power plan with the `pmset -g` command. For continuous background operation, set the following values: > > ```shell > sudo pmset -a sleep 0 > sudo pmset -a autorestart 1 > sudo pmset -a networkoversleep 1 > sudo pmset -a tcpkeepalive 1 > sudo pmset -a powernap 0 > sudo pmset -a womp 1 > > ``` When creating a service, a `.plist` file will be created in the `~/Library/LaunchAgents` directory. To manage the service, use the `launchctl` command. To view logs, open the **Console application from Applications > Utilities**, go to the `system.log` section or use the search bar to search for keywords such as `tuna`. Viewing logs in terminal: ```shell log show --predicate 'process == "tuna"' --info ``` **Linux** > **Note.** Debugging is described only for **systemd**, if you are using a distribution with **sysvinit** or **openrc** we assume that you are an experienced Linux user and can debug the service yourself. When creating a service, a `.service` file will be created in the `/etc/systemd/system` directory. To manage the service, use the `systemctl` command. Viewing logs: ```shell sudo journalctl -xe -f -u ``` **FreeBSD** When creating a service, a `` file will be created in the `/usr/local/etc/rc.d/` directory. To manage the service, use the `service` command. By default, stdout and stderr should be collected in the system log. #### Running a Service in Docker Compose As an alternative, you can use running the service with Docker Compose Create a `compose.yml` file with the service description: ```yaml --- name: tuna services: web: image: yuccastream/tuna:latest command: http 3000 --subdomain=web restart: always network_mode: "host" environment: - TUNA_TOKEN= ``` > **Important.** The `network_mode: "host"` option, equivalent to `--net=host`, is only available by default in Linux. To make it work in Windows and macOS, you need to enable this beta feature in Docker Desktop. To enable this feature, go to the **Features in development** tab in **Settings**, then select **Enable host networking**. Starting the service: ```shell docker compose up -d ``` Stopping the service: ```shell docker compose down -v ``` Viewing logs: ```shell docker compose -f web ``` --- ### HTTP Tunnels with SSH Client Reverse SSH tunnel `ssh -R` is an alternative mechanism for creating a tunnel without installing and using the **tuna** client. A standard SSH client is used to create the tunnel, which is typically present in all Unix-like operating systems. > **Attention.** The feature is in BETA testing stage. Currently, only HTTP tunnels with dynamic subdomain creation are supported. #### Running First, add your public [SSH key](https://my.tuna.am/public_keys?lang=en), and immediately after that you can connect. For example, an HTTP tunnel for a service on port 8080: ```shell ssh -R 443:localhost:8080 ssh-ru.tuna.am http ``` ![](/docs/img/ssh-r.gif) --- ### HTTP tunnel Starting with version **0.36.0**, tuna shows a [full-screen interface](https://tuna.am/en/docs/tunnels/tui.md) in the terminal with a live request feed, request details and replay. An HTTP tunnel exposes a website, API, GraphQL or WebSocket server running locally to the internet. Tunnels work on all plans with no time limit. The free plan includes a dynamic address and one [free permanent subdomain](#free-subdomain). #### Quick start ```shell tuna http 8080 ``` The console shows a public HTTPS address like `https://4l7mqf-212-49-103-2.ru.tuna.am` — requests to it reach the application on `localhost:8080`. Next to it is a link to the [request inspector](https://tuna.am/en/docs/tunnels/http/inspect.md), where you can see every request and response. > **Note.** All flags with hints are in the built-in help: > > ```shell > tuna http --help > > ``` > > Every flag has an [environment variable](https://tuna.am/en/docs/guides/environment-variables.md) — see the [flag reference](#flags). #### Common recipes Flags can be freely combined. A few typical tasks: | Task | Command | | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | Show a website to a client behind a password at a permanent address | `tuna http 3000 --subdomain=brave-otter-4821 --basic-auth="client:s3cret"` | | Accept GitLab webhooks only with a valid signature | `tuna http 8080 --subdomain=brave-otter-4821 --verify-webhook=gitlab --verify-webhook-secret=s3cret` | | Expose an API to a frontend on another domain | `tuna http 8080 --cors --key-auth="dev-key"` | | Check a layout on a phone | `tuna http 5173 --qr` | | Share a folder with a colleague | `tuna http --file-server ./share --basic-auth="team:s3cret"` | | Expose a service that checks the `Host` header | `tuna http 11434 --request-header="host:localhost:11434"` | | Publish a built SPA (React, Vue) | `tuna http --file-server-spa ./dist` | In Docker, CI and systemd it is easier to pass the same settings as environment variables: ```shell TUNA_TOKEN=tt_*** TUNA_SUBDOMAIN=brave-otter-4821 TUNA_INSPECT=false tuna http 8080 ``` #### What you can publish The first argument of `tuna http` is the address of the application to expose: | Argument | Where the request goes | | --------------------------------- | ------------------------------------------------------------------ | | `8080` | `http://localhost:8080` | | `localhost:8080`, `10.0.0.1:8080` | the given host and port, including another machine on your network | | `https://localhost:443` | an application that serves HTTPS itself | | `unix:///run/gunicorn.sock` | an application on a unix socket | Instead of an address you can serve a directory with a built-in server: [`--file-server`](#file-server), [`--file-server-spa`](#spa) or [`--webdav`](#webdav). ##### HTTPS application ```shell tuna http https://localhost:443 ``` By default the application's certificate is not verified, so a self-signed one works too. To enable verification, use `--tls-skip-verify=false`. ##### Unix socket If the application accepts HTTP on a unix socket, like gunicorn does, pass the socket path with the `unix://` prefix: ```shell tuna http unix:///run/gunicorn.sock ``` The [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md), request replay and [traffic policies](https://tuna.am/en/docs/traffic-policy.md) work the same way as with a port. TLS to an application behind a socket is not supported, and `--tls-skip-verify` does not apply to it. If the application does not respond, the 502 error page shows the socket path (`unix:///run/gunicorn.sock`) instead of `localhost` with a port. Path requirements, permissions and the behavior when the socket is missing are the same as for a [TCP tunnel](https://tuna.am/en/docs/tunnels/tcp.md#unix-socket). ##### File server Serves files from a directory and shows a file listing: ```shell tuna http --file-server ./ ``` ##### Single page application (SPA) For built [single page applications](https://en.wikipedia.org/wiki/Single-page_application) (React, Vue, Angular): any non-existent path returns `index.html`, so client-side routing works. ```shell tuna http --file-server-spa ./dist ``` ##### WebDAV The directory can be mounted as a network drive over WebDAV, with read and write access: ```shell tuna http --webdav ./ ``` > **Warning.** The file server and WebDAV expose the directory to the whole internet. Protect it with a [password](#basic-auth) or a [key](#key-auth). #### Tunnel address | Address | How to get it | Plan | | --------------------------------------------- | ------------------------------ | ---------------------------------------- | | Dynamic, `4l7mqf-212-49-103-2.ru.tuna.am` | no flags | any | | Free permanent, `brave-otter-4821.ru.tuna.am` | `--subdomain=` | any, one per account | | Any free subdomain, `billing.ru.tuna.am` | `--subdomain=billing` | [subscription](https://tuna.am/#pricing) | | Custom domain, `api.example.com` | `--domain=api.example.com` | [subscription](https://tuna.am/#pricing) | > **Abuse protection.** On the free plan, a browser visitor sees a warning page on the first visit saying that the site is served through tuna. It does not affect API clients and webhooks, and it can be skipped with the `tuna-skip-browser-warning` header. Why this is needed and how we fight phishing and malicious content in general is described on the [Abuse and Complaints](https://tuna.am/en/docs/abuse.md) page. ##### Dynamic address Assigned automatically on every start and changes when the tunnel reconnects. The address follows the `-..tuna.am` scheme: the source IP address is visible in the link, so malicious content cannot be hosted anonymously — see [Anonymity](https://tuna.am/en/docs/abuse.md#anonymity). The permanent subdomain does not contain your IP address. Good for a one-off demo or debugging. If you need an address that does not change — for webhooks, payment callbacks, a browser bookmark — use a permanent subdomain. ##### Free permanent subdomain Every user gets one permanent subdomain — for free and on any plan. The name is assigned randomly in the `--<4 digits>` format, for example `brave-otter-4821`, and is the same in all locations: `brave-otter-4821.ru.tuna.am` and `brave-otter-4821.nl.tuna.am`. You can find your subdomain in the [dashboard](https://my.tuna.am/domains?lang=en): it is labeled "Free" and comes with a ready-to-use launch command. The client also suggests it when you start a tunnel without flags: ```shell Your permanent free domain: brave-otter-4821.ru.tuna.am. Use it with --subdomain=brave-otter-4821 ``` By default a tunnel still gets a dynamic address — the permanent subdomain has to be specified explicitly: ```shell tuna http 8080 --subdomain=brave-otter-4821 ``` In another location: ```shell tuna http 8080 --subdomain=brave-otter-4821 --location=nl ``` Or by the full name: ```shell tuna http 8080 --domain=brave-otter-4821.ru.tuna.am ``` > **Token required.** The subdomain is bound to your account, so the client must be authorized with your token. Save it once with `tuna login` or `tuna config save-token ` (the token is on a [separate page](https://my.tuna.am/token?lang=en)), or pass it via the `TUNA_TOKEN` environment variable — for example, in Docker and CI. Without a token, or with another account's token, the subdomain will not work. * You cannot choose or change the name — a subdomain with any name is available with a [subscription](https://tuna.am/#pricing). * The subdomain cannot be deleted or transferred to another user, `tuna domain clear` skips it. * The subdomain is kept when you change plans and does not count towards the subscription's domain limit. * If you registered earlier, the subdomain appears the first time you open the [Domains](https://my.tuna.am/domains?lang=en) section or start a tunnel without flags. ##### Reserved subdomain With a subscription you can take any free subdomain — the address stays the same after the tunnel restarts: ```shell tuna http 8080 --subdomain=billing ``` The same via an [environment variable](https://tuna.am/en/docs/guides/environment-variables.md): ```shell TUNA_SUBDOMAIN="billing" tuna http 8080 ``` The subdomain is reserved automatically on the first start with the flag. All your domains are in the [dashboard](https://my.tuna.am/domains?lang=en). > **Attention.** The `Domain already reserved` error means the subdomain in this location is already taken by another user — choose a different name. ##### Custom domain Add **your domain** following the instructions in the [dashboard](https://my.tuna.am/domains?lang=en), after DNS verification and Let's Encrypt certificate issuance you can use it: ```shell tuna http 8080 --domain=my-api-project.example.com ``` Step by step — in the article [Connecting your own domain](https://tuna.am/en/docs/tunnels/guides/connect-self-domain.md). For an apex domain like `example.com`, the dashboard offers to connect it [through a subdomain with a redirect](https://tuna.am/en/docs/tunnels/guides/connect-self-domain.md#apex): this way the site works with fault tolerance. ##### Location The tunnel is created in the `ru` [location](https://tuna.am/en/docs/tunnels/guides/locations.md) unless specified otherwise. Pick the one closest to your site's users: ```shell tuna http 8080 --location=nl ``` ##### QR code The `--qr` flag prints a QR code with the tunnel link in the console — handy for opening the site on a phone. ```shell tuna http 5173 --qr ``` ![](/docs/img/examples/qr.png) #### Access protection The tunnel is reachable from the whole internet. The checks below run in the tuna client — a request that fails them never reaches the application. | Flag | Who gets through | | ---------------------------------------- | ------------------------------------------- | | [`--basic-auth`](#basic-auth) | those who know the login and password | | [`--key-auth`](#key-auth) | requests with a key in the `X-Token` header | | [`--cidr-allow`, `--cidr-deny`](#cidr) | requests from allowed subnets | | [`--ua-allow`, `--ua-deny`](#user-agent) | requests with a matching `User-Agent` | | [`--verify-webhook`](#verify-webhook) | webhooks with a valid signature | | [`--rate-limit`](#rate-limit) | no more than N requests per second | | [`--https-redirect`](#https-redirect) | HTTPS only, HTTP is redirected | List flags (`--basic-auth`, `--key-auth`, `--cidr-*`, `--ua-*`, `--*-header`) can be repeated. In environment variables, separate values with commas. ##### Password (basic auth) The browser shows a login and password prompt: ```shell tuna http 8080 --basic-auth="login:password" ``` Several users — repeat the flag: ```shell tuna http 8080 --basic-auth="anna:pass1" --basic-auth="ivan:pass2" ``` From a script: ```shell curl -u login:password https://brave-otter-4821.ru.tuna.am/ ``` ##### Key in a header (API token) For APIs and services without a browser. A request gets through only if the `X-Token` header contains one of the keys: ```shell tuna http 8080 --key-auth="my-secret-key" ``` ```shell curl -H "X-Token: my-secret-key" https://brave-otter-4821.ru.tuna.am/ ``` ##### Access by IP subnet An allow list of [subnets](https://en.wikipedia.org/wiki/Subnet) in CIDR format — everyone else is denied: ```shell tuna http 8080 --cidr-allow="203.0.113.0/24" --cidr-allow="198.51.100.7/32" ``` Or, the other way around, deny specific subnets: ```shell tuna http 8080 --cidr-deny="10.0.0.1/32" ``` ##### Access by User-Agent Matching is by substring. Deny `curl` and allow everyone else: ```shell tuna http 8080 --ua-deny=curl ``` Allow only Chrome: ```shell tuna http 8080 --ua-allow=Chrome ``` > **Note.** `User-Agent` is easy to fake — use this filter against bots and random scanners, not as protection. ##### Webhook signature verification tuna lets through only webhooks signed with the secret you set at the provider. Other requests get `401 Unauthorized`. ```shell tuna http 8080 --verify-webhook=gitlab --verify-webhook-secret=1234 ``` Supported providers: `gitlab`, `github`, `sentry`, `linear`. For other services and more complex logic, use [traffic policies](https://tuna.am/en/docs/traffic-policy.md). ##### Request rate limit Protects the local application from overload — no more than N requests per second: ```shell tuna http 8080 --rate-limit=2 ``` When the limit is exceeded, the client gets `429 Too Many Requests`. ##### HTTPS only By default the tunnel answers over both HTTP and HTTPS. With `--https-redirect`, HTTP requests are redirected to HTTPS: ```shell tuna http 8080 --https-redirect ``` #### Headers and CORS ##### Request headers Add or replace a header before the request reaches the application. A common case is a service that only accepts requests with its "own" `Host`, like Ollama or dev servers: ```shell tuna http 11434 --request-header="host:localhost:11434" ``` ##### Response headers Add or replace a header in the response to the client: ```shell tuna http 8080 --response-header="env:test" --response-header="x-robots-tag:noindex" ``` ##### CORS The `--cors` flag or the `TUNA_CORS=true` variable adds CORS headers to all responses. By default the following headers are set: ```shell Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language, Origin Access-Control-Allow-Methods: GET, HEAD, POST Access-Control-Allow-Origin: * ``` They can be overridden with the [`--response-header`](#response-header) flag. **OPTIONS** requests are answered automatically with `200 OK` without reaching the proxied server. > **Note.** If you have trouble with CORS, we recommend reading [this article](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). We also recommend [CORS Tester](https://corsfix.com/tools/cors-tester) for checking your CORS headers. #### More features ##### Traffic policies When flags are not enough — path routing, conditions, custom responses — describe the rules in a [traffic policy](https://tuna.am/en/docs/traffic-policy.md) file. By default `.tuna.yml` from the current directory is used, another path can be set with `--policy-file`. The `--policy-url` flag loads the rules from a [URL](https://tuna.am/en/docs/traffic-policy.md#policy-url). A policy error stops the tunnel from starting, see [Policy errors](https://tuna.am/en/docs/traffic-policy.md#policy-errors). ##### Request inspector Enabled by default at `http://127.0.0.1:4040`. The inspector keeps requests in memory, so on servers and embedded devices it is better to turn it off: `--inspect=false`. More on the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) page. ##### Bug reports The `--capture-key` flag embeds the [reports](https://tuna.am/en/docs/reports.md) widget into HTML pages: a tester can send a bug with a screenshot without any changes to the site's code. ```shell tuna http 3000 --capture-key=YOUR_CAPTURE_KEY ``` ##### Token Usually the token is saved once with `tuna login`. You can specify another token with the `--token` flag or the `TUNA_TOKEN` variable, overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md): ```shell tuna http 8080 --token=tt_*** ``` #### Flag reference | Flag | Environment variable | Description | | ------------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | | `-s`, `--subdomain` | `TUNA_SUBDOMAIN` | [Subdomain](#address) in the tuna.am zone | | `-d`, `--domain` | `TUNA_DOMAIN` | [Full domain name](#custom-domain) | | `-l`, `--location` | `TUNA_LOCATION` | [Location](#location): `ru` (default), `nl` | | `-f`, `--file-server` | `TUNA_FILE_SERVER` | [Serve a directory](#file-server) | | `-F`, `--file-server-spa` | `TUNA_FILE_SERVER_SPA` | [Serve an SPA](#spa) | | `--webdav` | `TUNA_WEBDAV` | [WebDAV server](#webdav) for a directory | | `--basic-auth` | `TUNA_BASIC_AUTH` | [Login and password](#basic-auth), `login:password` | | `--key-auth` | `TUNA_KEY_AUTH` | [Key](#key-auth) in the `X-Token` header | | `--cidr-allow` | `TUNA_CIDR_ALLOW` | [Allowed subnets](#cidr) | | `--cidr-deny` | `TUNA_CIDR_DENY` | [Denied subnets](#cidr) | | `--ua-allow` | `TUNA_UA_ALLOW` | [Allowed User-Agents](#user-agent) | | `--ua-deny` | `TUNA_UA_DENY` | [Denied User-Agents](#user-agent) | | `--verify-webhook` | `TUNA_VERIFY_WEBHOOK` | [Webhook provider](#verify-webhook) | | `--verify-webhook-secret` | `TUNA_VERIFY_WEBHOOK_SECRET` | Webhook signing secret | | `--rate-limit` | `TUNA_RATE_LIMIT` | [Requests per second](#rate-limit) | | `--https-redirect` | `TUNA_HTTPS_REDIRECT` | [Redirect HTTP to HTTPS](#https-redirect) | | `--request-header` | `TUNA_REQUEST_HEADER` | [Request header](#request-header), `key:value` | | `--response-header` | `TUNA_RESPONSE_HEADER` | [Response header](#response-header), `key:value` | | `--cors` | `TUNA_CORS` | [CORS headers](#cors) and `OPTIONS` handling | | `--policy-file` | `TUNA_POLICY_FILE` | [Policy file](#traffic-policy), `.tuna.yml` by default | | `--policy-url` | `TUNA_POLICY_URL` | [Policy URL](#traffic-policy), https only | | `--inspect` | `TUNA_INSPECT` | [Inspector](#inspect), enabled by default | | `--tls-skip-verify` | `TUNA_TLS_SKIP_VERIFY` | [Skip certificate verification](#https-upstream) of the application, `true` by default | | `--qr` | `TUNA_QR_CODE` | [QR code](#qr) with the link | | `--capture-key` | `TUNA_CAPTURE_KEY` | [Reports widget](#capture-key) | | `--token` | `TUNA_TOKEN` | Authorization [token](#token) | #### Behavior ##### Hop-by-hop **NOT** supported are hop-by-hop headers except for the `Connection: upgrade` header, which is necessary for Websocket connections. ##### WebSocket Supports the protocol with bidirectional connection between client (e.g., browser) and server, no additional settings are required. The transmitted data will **NOT** be displayed in the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md). ##### Server-sent events Supports the technology of sending notifications from the server to the web browser, no additional settings are required. The transmitted data will **NOT** be displayed in the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md). > **Note.** With the inspector enabled, it's important that the client sends the `Accept: text/event-stream` header, otherwise the inspector will try to intercept the request and this will cause delays. Browsers send this header by default. ##### HTTP and HTTPS When starting the tunnel, an HTTPS link is displayed, but you can also access it via HTTP. You can prohibit HTTP access by adding the `--https-redirect` flag. ##### Flag and rule processing order Flags affecting traffic processing behavior are always executed in a specific sequence, regardless of the order they are passed in the arguments. 1. [https-redirect](#https-redirect) 2. [cors](#cors) 3. [cidr-allow](#cidr) 4. [cidr-deny](#cidr) 5. [ua-allow](#user-agent) 6. [ua-deny](#user-agent) 7. [rate-limit](#rate-limit) 8. [key-auth](#key-auth) 9. [basic-auth](#basic-auth) 10. [policy-file / policy-url](#traffic-policy) 11. [verify-webhook](#verify-webhook) 12. [request-header](#request-header) 13. [response-header](#response-header) ##### Managing CORS headers yourself If you want to manage CORS headers yourself on the downstream server and at the same time use authorization in tuna with `--basic-auth` or `--key-auth`. Then [**Preflight requests**](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) - **will not work**, because these *OPTIONS* requests do not contain `Authorization`, `X-Token` and any other specific headers. Solution to choose: 1. Do not use authorization in tuna. 2. Manage CORS headers in tuna, not yourself. 3. Handle `OPTIONS` and authorization separately using more flexible [traffic policies](https://tuna.am/en/docs/traffic-policy.md). ❓ How it looks on a diagram ###### Case 1 You manage CORS on the downstream Caddy server and enable authorization in tuna. ![Diagram depicting Case 1](/docs/img/tunnels/cors/case1.png) ###### Case 2 You manage CORS on the downstream Caddy server and **DO NOT** enable authorization in tuna. ![Diagram depicting Case 2](/docs/img/tunnels/cors/case2.png) ###### Case 3 You manage CORS and authorization in tuna. ![Diagram depicting Case 3](/docs/img/tunnels/cors/case3.png) --- ### Request Inspector #### Overview The request inspector allows you to debug incoming requests, view headers and other information that can facilitate application development work. It is available for the `tuna http`, `tuna postgres` and `tuna redis` commands. For `tuna http` it is controlled by the `--inspect` flag or the `TUNA_INSPECT` environment variable; for `tuna postgres` and `tuna redis` the inspector is always on. #### Management The request inspector is enabled by default and is available at `http://127.0.0.1:4040`. If port `4040` is busy, the next available port will be selected. You can disable the `tuna http` inspector with the `--inspect=false` flag or the `TUNA_INSPECT=false` variable. The address or port can be overridden using the `TUNA_INSPECT_ADDR` environment variable or the `inspectAddr` parameter in the [configuration file](https://tuna.am/en/docs/guides/config-file.md). * `TUNA_INSPECT_ADDR=0.0.0.0` - listen on all interfaces * `TUNA_INSPECT_ADDR=0.0.0.0:8080` - listen on all interfaces on port `8080`, if the port is busy, the next available port will be selected * `TUNA_INSPECT_ADDR=8080` - listen on address `127.0.0.1` on port `8080`, if the port is busy, the next available port will be selected > **RAM consumption.** The inspector stores requests in RAM. Disable the inspector (`--inspect=false`) in production and on embedded devices (IP-KVM, Raspberry Pi, etc.) to prevent memory leaks. The final address will be printed in the console at startup: ![inspector-link](/docs/img/examples/inspect1.png) #### Interface By following the link, you will see a minimalist WEB interface with a list of executed requests. You can click on any request to view more detailed information about headers and other details. ##### Request list ![list](/docs/img/inspect/list.png) You can also repeat requests, including with modifications: ![repeat](/docs/img/inspect/repeat.png) ##### Settings This displays general information and the current configuration of [traffic policies](https://tuna.am/en/docs/traffic-policy.md). ![settings](/docs/img/inspect/settings.png) ##### Tools For greater convenience, we have built developer tools right into the inspector. ![tools](/docs/img/inspect/tools.png) On a separate tab you can find: * CSV to JSON * Base64 Encode/Decode * JSON Formatter * YAML to JSON * URL Encode/Decode * Timestamp to Date * Query Parameters to JSON * JSON to CSV * JSON to YAML * Number System Converter * Regex Tester * JWT Parser * Hash Generator * UUID Generator * Lorem Ipsum Generator *If you didn't find the tool you need, [contact us](mailto:info@tuna.am) and we will consider the request.* ##### QR code The QR code contains a link to the tunnel, which is convenient for mobile devices. ![qr](/docs/img/inspect/qr.png) --- ### Kubernetes tunnel > **Info.** Available only with a [subscription](https://tuna.am/#pricing). #### Overview A Kubernetes tunnel gives a colleague or a contractor temporary access to your cluster with plain `kubectl`: no VPN, no exposing the API server, no sharing your credentials. The client reads your local kubeconfig, starts a reverse proxy to the API server and an HTTP tunnel to it. The guest gets a one-time link to a ready kubeconfig. It contains the tunnel address with a valid TLS certificate and a guest token. The proxy checks the token and replaces it with your real credentials, so they never leave your machine. #### Quick start ```shell tuna kubernetes --context=staging --service-account=shop/debug --ttl=4h ``` The client opens a terminal UI: the header shows the service account, namespace, restrictions and expiry, below is the feed of guest requests. Instructions for the guest: ```shell curl -sfO https:///tuna//kubeconfig.yaml KUBECONFIG=./kubeconfig.yaml kubectl get pods ``` `get`, `list`, `watch`, `logs`, `exec` (including stdin), `port-forward`, `pods/proxy` and writes such as `scale`, `create`, `delete` work through the tunnel. Guest requests are shown in the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) as regular HTTP requests. The command has a short alias `tuna k8s`. #### Examples > **Note.** All current flags, hints and examples are available in the help: > > ```shell > tuna kubernetes --help > > ``` > > Almost every flag has a matching [environment variable](https://tuna.am/en/docs/guides/environment-variables.md). ##### Kubeconfig and context The client uses a single kubeconfig file: from the `--kubeconfig` flag, otherwise from `$KUBECONFIG`, otherwise `~/.kube/config`. A `:`-separated list of files in `$KUBECONFIG` is not supported. The default context is `current-context`: ```shell tuna kubernetes --kubeconfig=./staging.yaml --context=staging ``` Client certificates, `token`, `tokenFile` and exec plugins that return a token are supported. Exec plugins that return a certificate are not supported. With `auth-provider` the client exits with an error. ##### Service account Without `--service-account` the guest acts with **your** cluster permissions, and the client warns about it. Create a service account with minimal RBAC permissions for the guest and pass it in the flag: ```shell tuna kubernetes --service-account=shop/debug ``` The format is `[namespace/]name`. Without a namespace the context namespace is used, or `default` if there is none. The client issues the service account token itself via the TokenRequest API and renews it every hour. ##### Access restrictions The restrictions below work on top of RBAC and only narrow permissions, never extend them. | Flag | What it does | | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--namespace=shop` | A single namespace only. Cluster-scoped resources (`nodes`, the namespace list), `-A` and other namespaces are denied. The namespace object itself is read-only | | `--read-only` | Reads only (`GET`, `HEAD`, `OPTIONS`). `exec`, `attach` and `port-forward` are denied; logs and `pods/proxy` are allowed | | `--no-delete` | Deleting is denied, other writes are allowed | | `--deny-resource=secrets,configmaps` | The listed resources are denied, names are case-insensitive | | `--deny-subresource=exec,attach` | Denied subresources: `exec`, `attach`, `portforward`, `log`, `proxy` | With `--namespace` and `--read-only`, `kubectl auth whoami` and `kubectl auth can-i` do not work: they are POST requests to cluster-scoped resources. The proxy strips `Impersonate-*` headers, so `kubectl --as` does not work through the tunnel. ##### Presets `--preset` enables a ready set of restrictions: | Preset | What is allowed | | ---------- | -------------------------------------------------------------------------------- | | `view` | Reads only, no `secrets`, no `exec`, `attach`, `portforward` or `proxy` | | `observer` | Reads of pods and their logs only, no `exec`, `attach`, `portforward` or `proxy` | | `debug` | Everything except deleting. `secrets` are not hidden: `exec` gets to them anyway | | `open` | No extra restrictions | ```shell tuna kubernetes --preset=debug --namespace=shop ``` A preset and flags add up and never weaken each other: `--preset=debug --read-only` gives reads only. The `observer` preset allows only `pods`, so `kubectl logs deploy/api` fails; point at a pod instead: `kubectl logs pod/api-7c9f8d6b5-x2k4q`. ##### Lifetime and download limit ```shell tuna kubernetes --ttl=4h --download-limit=1 ``` `--ttl` sets how long the access stays valid, `0` (the default) means no limit. After it expires `kubectl` gets `this access has expired, ask for a new link`. `--download-limit` limits kubeconfig downloads, `1` by default, `0` removes the limit. Beyond the limit the link responds with `410`, and you see a warning about the download attempt in the log. If the guest gets `410` on the very first download, someone else took the kubeconfig first: stop the tunnel and issue a new link. ##### Permanent address When the tunnel reconnects with a new address, the client issues a new token and a new link, and the lifetime and the download limit start over. To keep the address, use a reserved subdomain or your own domain: ```shell tuna kubernetes --subdomain=k8s-staging tuna kubernetes --domain=k8s.example.com ``` Guest tokens live only in the process memory: after the client restarts, old links and kubeconfigs stop working. ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna kubernetes --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna kubernetes --location=nl ``` ##### Running as a service The tunnel can run as a [service](https://tuna.am/en/docs/tunnels/guides/service.md) or with [multiple tunnel startup](https://tuna.am/en/docs/tunnels/guides/multi-tunnels.md): ```shell tuna service install --name=k8s-staging -- tuna kubernetes --context=staging --service-account=shop/debug --subdomain=k8s-staging ``` --- ### Ports Reserving static [ports](https://my.tuna.am/tcp_ports?lang=en). ![list](/en/assets/images/list-d0c77d23112dce9b180bc7555939041f.webp) #### Overview On the [ports page](https://my.tuna.am/tcp_ports?lang=en) you can reserve a static address in the desired region and use it in [TCP](https://tuna.am/en/docs/tunnels/tcp.md), [SSHd](https://tuna.am/en/docs/tunnels/ssh.md), [Postgres](https://tuna.am/en/docs/tunnels/postgres.md), [Redis](https://tuna.am/en/docs/tunnels/redis.md) and [Trigger](https://tuna.am/en/docs/tunnels/trigger.md) tunnels. > **Info.** Available only with a [subscription](https://tuna.am/#pricing). Click the Add button, select the desired region and enter an alias (name) by which you can later use the required port: ![add](/en/assets/images/add-114e3a733af3af0ee7b28f625486fca7.webp) After adding, you will see a notification with a usage example: ![notify](data:image/webp;base64,UklGRo4hAABXRUJQVlA4IIIhAABQfQCdASrkAdYAPjEYikQiIaER+ayEIAMEs7d+Lcy3Yr9uWd+x/4pJpv5TQ0Temn8PxbeQf+D9s3zr9Hv+V9QD9S/GA9+3mA/VX9jfem/4P+d9jn7I+wB/Qv6H6pHqm/2P/C+wB/Hv576aH/a/wnwof2T/Pftv8Bf65f+r2APQA6m/pR/J/xl73v5p+Sf9w9H/w34f+dfjl+9fNJiHfDfpf86/uH7O/2r9uPfb+o/kp5z+9T91/If4AvyD+J/zz8kP7Z+3HuO/uHbM5f/jfQC9Zflv95/tf7Z/17zZf0r+sfsx/bf/h8j/j/9S/rf5Yf0D7AP4h/I/7p/YP2u/w///+kv8j4MP03/Q/rp8Af8S/lX+b/sH+U/5/9m///2p/r/+t/t/+Q/9P+e9pX5t/XP9V/kf9F/6/9J///wF/kv8y/x/9k/yH/U/v//+/733W+vf9nv//7pP6v//UX3vGifteJljSt6CUzShhRfIATf34cFZGG9gEd4gXngvK7w/mNVVtMOD6QhAPeGYfAj8+c5yTnLdffESrJYjllQmCOFerrp7EqIapmlDCi+QOBUQ+a3hB40lUtO8HVyZ2DBNZj2fD5A4FRd40reglM0oKmZMm4z0AsIz+QGQ6cq/vqW0rGiJgAXG0N+viDmJyFXeTu+qWrEF9u08D1bk8PyR6i7X2c83N6yEZO+w8Vyla95ZULCUaILZHIs4hOey8CMzPjwMaVQA5t6aeL2R6f5o3uEF/YOFT0tFQvppeS4WiBnryFF8gb32JvWkSm0WAN4E4Lhyklq50mIQ2uMXo7aiK/6XlQvhGyS9jUZ9zzkgqIVGJJ5sU4r2w2jLKHI13KdPeg7fMZdwnuZWh04YcpAOXdgKlHxX3OzR+5sN6jgHb4RsCky36dGxu9MU9QpD2GweXUysEjgVF3hVCo3rLBLPH0tAStlpfIlkerjZCqOl2lJ43LIONvNoNbR6V06q876qCGFF8gcCoudYmNjhfLUdwPJFrleKPdHKz4lrTB/u7MX0hQRTAJ7eTcgFUpHHPedFEEBnRW5cMwe7WedbVr/w3qOkd280e8gSNFmSRg0sn1KXYAeAA1ErrLa1p6GELC1j6PN0V4gNdFSjrkRuQH4t8ddoV3XbDfEsF7G2RojL7IA1h5jebFegaix6iLTLfMzckJ8xbSYqmUybeHEJY9yNgBKNhNyNhNyNg7b76Mt3jSt6CUzShhRfIBRl/i3zDDTMr1mmaL5A4FRd40reglNFgqC3KIDk9USp2wGDgILH6nf74xzgopjbtNVGNDHsreglM0oYUXyAfDM3zUc+xvPLpCFb9LHFvKmaUMKL5A4FRd40reglKgAA/v5JhGOXHTb15lM0V4myZGiI3IEKxIwCG57/wo2u2dfW9tK9qtm1wiOeF2aMMBOTzuY7nSdRNIxk/j77qK/WvWDEHkEkGA2qnbaFm2ixknZPBUW8N9vm+Va8GUdByt4CxgHxNlJgaOpUdAv6IuC6O64tlJMwWmXfwOX5YjbFUJRCoCwbIH0lN+vml+Nz177VX9NGcxfDDDdjQX3nP7RjYE4LSmdwi/tcsdX/T84et8PwU53aYtTi/mgInIUwBpoDyf+5xwETshQqz+iH7PMS4SsyGk2IkVO2uumsjV2dYvBLtTJ5s1JjlHj5hFzf2Ti4E2vUjEr8Z/gqFWMmxiUbyvIV+iY/KP9S+xVgRX/6RsivlWKgONXMBF16NzILRh+6eeGjbsA9kpi522ovmUdyyn2bEQSJ3EI8qaj2ttnjhOrhpexg1WPKprE+qPJKSUK7U3aNXjurW4j/NqhDUrY8abqHUdKnTpFTdB58TyexBj0/AvkgaGP+/y9lPxvAkahKuiNkJQrlDjGip2vd7psM0tVPuRD6B0jiwXskeJbxtfR3RedKhiKvu/aMHkQKahz3lvHap8eHiLyB5CulOdRNICSLRYnGu+JT8uDB21wlH0sBDrsvsmd0IaQ/yKLREX3H/lnV0+1lUj7jV3rexeOhanhUO1pVFYOub9FcKZm9Mff2WhtIYZBhNJ+vCj76tJP0a7f5ChCFqz8qKvMP0/6oZsl84qIG7IhtZahJZB9LwjRN9Lb27O1E96ikriHUWwxwNORjFtCgYAPH1mfEGAxwE9rCeZOqPzgcjf9Frv5g1QTLEclZ53SBeCFACLICtdhGLVmrRCaZca6bdRqNljhY1zqW4hHml9WrwaGatLFR9oD+mpyDZ866v42JfLUEvNwExTm7h/NpuREor/IaCbnEWromUHqfiniuIjgApRfTv//krWif/OjJ6QRugr3Fa6x8KAbuHfEP2cYLhbR8PCnCVmNATV/98b9OVrEgoFtMiom0LWust3voqs6ZE78VZLwv/zj52eMgyMxqCxiuXvZrfAoFzzU7i43bNL5C+nPB5QKEQ8Dy12kZEQIFWR/7a17w3XJJhK4KcA4DQtdiZDlhb9eB6F2OQjbE2ddcWE85sK87cuUHwPj5DJrHwgKwvR38BUjRxGTYbn1kNvAGu+Kf0xVMHy2r7ECkRml3E3G8pnBaEEhid21rcW4hEMG10GG2LqniJ4lmZqAMgBWJHLRs0r2E1pV6448/EDi323o0MK9nzUxth3//Zr49Umz+5r+pJLb8y1X7EueupweC15kd1gRSrYDFcfGZZQWBwEGv5LafeXwPGxUT0i4LR27sP3d/ZKIWp6ZL8ryi1yxnQBZfgBonOk3ldzbmilqv4Mfq3WELYq7fNFJp9KhKUExwZiKJQmxoz15lLwchvF91+nQ2VynvVmM2fAKd1ziHS1Q6Pv87IVbqp5QAAAAAAAD3hRVfJkcE4mC9R14KKWXWHzvW79t6EnkU/+FL8P2ifkVY7FwfcPf0h6WREHTKmpH5UaLIO8LwKBPtKYN1mY7fzv2UaZ/pb/SE1dYJvsUoYe7cMHLnLd3ND665+Hnio/Q5lhDQDiibxasqMXNW/5TpgtxQ96t2nEoHp1UmJ/4OQsVs8tGkTs13iJENQJ6RJOhs6AY9GqHoALBvxsbC1EMSM8bc/soNVrmTvvMZ17tWID3EDFSiK9hnmVHbHYknNSbLb7dfYs8iloM6HyKVXZbOXrLrsve3vN5C71qY+O7QOCLBhZCRxctn0UqbqL0FEc45m9bb0QSjxqPyChLrF7MbNTeX3p2SYuaxiJm98wrPtjgbvz8quQXTcjMSUVA9hVxaShoAF0i5gL2kEikRMSEwsqAyD9i7KQQmWJ6Bigr/80X2M9WgrrH/V9L6qWKEnF1pUpKgf9mn30hmmH082EI36eRULQXc5AOZLbxG5nuuMj6jyXQk3PjGs82WDYwrj7zuDz+pimnqj9EL7gg32Ba/LE1cAmNoiQVP4fC8YRfZz25ZaBnbm5L76wV8HYD9+opP0Q3ozjWffgqcCytd8krM07ODELx1OECB8d3Ic9cYmmmGKCPtyKSCY/UhqtHvS+wl+ao5IA/mMlxdAmxIuliOn2i3kEQpFPgFOH0UiCtiuzL2SxhtryHULStPWJtkLE2xtco5MBCYMIFke1eG0fVnE+0xbm1bIvrZ8Oz4dR/BkSfCWF0Y9spTGS3PTCsNvu5pgyMHRQLwxJRCbnsWavyelL+YhDbX+AdttUmNZpMs3hIZLNIZDRWIlUpFNPzqILxHGjaCx023zGRNeC7Uw3HmVJ3iBC1YLKMQau55qMHluCYzOcIF+yhgR5yqXSoNWr7yDCKiPYLmlLjHFnI+10ozYFyXYel0140xeJv6PhaTEwsIRMu3uhKlm7VpeQOf/s6rBr0P1Hl5J+IWBQYaKeU9tr1TO3xNByzqZlb5RmhCivf8D1y27UppXWDj9ADiB9PWZ/1ylcYUKlbin/vlYobMkXSsxedfQahUIlnYWvVCh0qlEPor4CBtQJS4tUjwzY4Dqe6urHrnE+C/y/GVRYIOdnJ7HXXh+rdNKUyrnPJV1idzoK/C7l3uayJeFxaSG3d/ZPw3196hUgcmK2i1GZhTcCv0E0ny2uCstHLGxBnmxGYgbS/E+4xela5u28b4tbwaWY6Cz7FGmlciGLSfymFDRBV86Zr6sIAnKBonixDpSU9ee8KWyPppX4u+j5MVWNjMZPOE/YozgyEeyr+S/Z3hSXNPXimzWiGgksKvNiLwe2fCDM0SwipoEzenkN5/XztPr2kgiqBFWnoVdzM2wgrfeKw/Fqa2KmpOrTsZXYfp8I9EIA7SGAlonTBVDa+Df4XUnXwfGrE+RNzEyXMPF0QBKMktVZ+2d6vCGEHgMa7EQulMIfQu6qL2jemibOw/EzqGTL7litz+rys/r2wicZzEVuY/4Jewxuot72VgaoPUPZjhSRlr0/0spaXhEDa86pu6hKRNwsJxPNxFj7mZ+/pnNq/S35VfQj9NlJzwd6BEErl5sZbfKFoAmhjqtQNsJmc0BWmPTzCpBZScdM/eJi+mogIeq8h4SyDiCAIzH9JQ2tZb5wHIbHCDCiz8qd8erC9D2kevwU0JpPjoDpb7wt3ai7D2dAJ+vL+O0ExxVyjouyMJY00pWToGb/ywkqTavxY76ZOPhvWv/K47IAaEL0rdMsQ7ExU785Og/YhbQkoB7ZDYGPPFWpz1DB6bYiUpUfOkcTToCrTUS5P9PV0NVANDw71ILpUCLnDRjtIQDmJZBmDfaVs9rn7HJcTn3nmeLW021B2ttLIOoqvRKaPWjeRQLRdLn6ua35ihmH9nIDP8qKx2KXJdhqRijhcR1GEEBgphhJ81e76Xzgpf3u4W9X+l9PH5TJCMGRC6tyowyiWaNJIAlVQmIfCvb0Drqox0Mrtn85+psyx3inVRQqGxLqKpgehFYRJmkGBSzYFDtx2MqQAazw/JTEVPX5VV+NPkaTDDs/aQh7vgjdQhvDbsAaPbqGbUmocClCiz4DWgu13oJ3dtMC/FtKVdDGcYMxYJqZQ8z8ZoXLxaWc+Ery/NQd0eii71A+grQ7uZexmZib5talZbSBET7Hpp5BZ43+vd/FDXoYx8ROFAb/uG4yTP5AXDHUtUPsh2Qd+KpAC2gt6LDBcr2KB3ffmAMb3aYRCQ9bF+nmjcIkOXjOfQVKvEkzvyTivpTSwNqGcwv4TbBUbqzfz2eikxe1xGt1s224JvtViI9hcoA+dS+uhl2h7sudU3ty6JBm4qa/4asmjmU26es/NDNFXvjq5GBTDFv4JxwdaeXH53zOlPm5ByDNK104zN7kYO2X6bYXB2KPn/zUDOQPsgF+9W6osw7YcaCYe0S/O+ScD/6mfza3jDTbA2jYIXC06zuidXTv0MRZPmawrRoutjT9Jkm++1+4P6SfiNWpMFOBa9w+RPcWTwqtXtwM8o2QQA8ca4r0Ghr6g6UQTPZImyg0+VCxXcy/ZPVsDuz24BUANyhuokLAGqYi0jHOUgrR3LfrDCq+tA8MgdQsYpWPuGw7ZT4HfxvGmfdys3OoJN9PjQt2Hxz6Q9NvQV2SzqLXgKst7nKMq8I/9yFK1SaE0zNzNkEtTp+N1Gk42bXpYrc+TYljFhovvsBzjAVdMgGqWXGPSr2SIEpgc1t2Rfr1MxdUElvu4k6Jr0jDPNIT4AIP+sA3NBUf0oQeqPVU12bI0eSuZzu1XJCOOkTS+QH4CzphxFt7NhVhlANb/+/Ghm6+Y+F1E2ALb2OS6hZ6KoD08PtPzmt8syPOtw+u7DFKJkRLvwnl7bXdm8MsH49mClow/vf3mk0vd2YojjldcV3FTRtCEF8ppW1H/owlwALj/9+iUgVrhk6zNrBu4fHSPPLngUaFkG3NFFwkZ/wchamPGEXuMiwMFHArXM4Kcrr+3JCPl08ftZSPhtMvjOJRAHIKoEizXey/wah8USzlgjQa2OqsM1tUCqGaI5sVcmIqRwJ98D0mDuzoIRGu6GOyrqp8jvsXeyf9+tDnUuSxlwksEyMs2rYllgbTrTFWaLrCmUtA2fux6sMMqkmdIMcwvtNROJMOGPVQbDNx83om0mv2KKYvZVAHAroOX4VE+Kpzq8Fx7OYwzSR5Qm9TaI5V1oAXnfqDuj+3KgzssBZMyrZbMupz2QM2EWLVHAHblt5q5dQvFs98pWk7ZazFYLtkDeDClgWeQot5pAtqB++bnRHv8Ulm2Y0Lm8tbW/JblPP+/Zqbjvufu8tKHzZqnrTldguirjZSjhGxq1lvL9YMX7NTGWjTRtvaTaYCKLyE2QcZ9KIy/ha4WiEWk8y9fCa7ocNq5GsUzVXjpz1FdhSuo9MUpmhH7P3R2C6fqMKxSHJMOV5QfYxc/Ne+c/9CTukBLyDo6/ym5OiFrsbkHr/RearpRhK1wafttS5osNKoTefWarEwAcjKV6LxOCDUZJr68xI14jOw70yBOvNejmy5RwvJhSIHm46DU9B5yWGYDA7cnP1sezwOc1dxb+77cqv25BgtSR8dS9pBIpETFzarXC8P1g+IGCvP1EZq/sGppj8kPPykRpJ3bbYHDxeGN0w6+cdtk6UJvw/KY7RKTx+NAOerFFvhkcOczaOer2qfeBhQkY/TEY0ptIYlPYSzkZcAuIMLpfqsLybp8eXZQbEqnmXB6EMSqcgUyiNqHAxLP/obbd36Qg1b4Jk4T/INSVOOA3YanX/7Tm0pYDzQOuB+bwKCZRoTMm9waUmn9cY+L+97YDtBQCh/ST8Rq1J5KPs6SUDdW6EqSv4lEdZe4aUmy3qBlz7ujWU7qTmBDkiZqcjVGVHf4Q2OuizaAcK22A5uPPQOl+kGFfAKGYLW5r4rITn0Yv9N1stOTw9xSx79yydqWlSWF+cA4GHcwkQQh0gGnwabgLIrOIJ4oT5LgF73PSPnltd+m7Mr1DGroq0lcCPYTqCxLQhcUXtdYsQ7NazA92LNoZl2nTY5uaZsmrP0j0ssGfM/rtO2YZ5jkCez5JF1VzpIUdKx23aMWv24yHrwz2pSbr6T7XED4DbctvuaimPgl779sG8b+P+N70/Jc1dCsF+FT7ZciW/VjJiV39cITNizNYYV/wVwFdOpiEjG9VWdnLlKWPaetxzM5Bvqy3vYQdTUFyPnVeii4WEpSpydew7dFSXsY7qf92+m+DK/pSnvWemVslFeJHZ7FDwIIK37BkMTz3CHqIQ4q1v8oUAUG8EQLiy9ulvNrRwKHS0+ubS+gOc8V+WRts4p+f2nFobPRNYgaXkHgIoKhFvplbmvwo9b6obDjyojIoxc2UfJ6dUqm4wTArimlqBGtHMJuYaHNAjCagBrJtQ7YOa3doWkAtO4jGf8EIk7GPd8P/thXy+DEfUM+9VqcHOz7wFXnhlrUiFb2AO2+ywLPbLQWHJtgChCLuhAJ+Rehb0WadK2WO903+9EYcnPHlqnQvPmTYujLGu5b1mzHlXxRHYudgCImDjOpZgf/GlbHYY63tajnAZIG6O/hAWNKz2VIWu09AlujzjD5iX0u0JGABRXMmxQEHQdVRpjoGMYeuin9yjV30xX4vtTqLRjrWhAxyZOm8cTWL2rA3SmKHGqx6wbjvBtlXztcdg6msmqcQmwNrC8oHpw3MfCmmFTZZhPXr86+quLSSpLrYKW9yCoFVR6bBUoq5MFWCrSKCCvhXNTPaEjS2rQQquPboV4kupEq89fdaXTXnPyJ4rJIWYeQkitrH8WbQE2FKizapHs+lrNTEYJRs+ijjkygM5a+RhFXl+pt/nIfibxZC5PJwGDviBjZcHkwqPoJJkMH5fKmqIlreZdtyqn4vGubt/MQOrvUK0g1oo/HVO+lCmQwyJZBHqqfQptbbc/q7PR0r3e+P3Am7gS7eVZaDHyZEzN8D6SRWE3dYRuN/oXLVFtY0uRAOgvsGn0SRPz32oochC84JzyY9+7UcBfDi0Zledvqc6nVklns539s64XCPZwMS6ijxZX8LVicNU1gLFN+Lja1mer8q1V4x+Skdrc93DwdxaoGVsMhth4O/St4cyAEiidlfMMF7xHmn2A05FnBf1+rS8AgAXZv9WCKC1sAAAADeb2huAVOgrQQ6Hch10JYAAAR2ouK+cMyzeMFztGIVUi++AAB1TGqBvfAOgAAAGQJaJG+orjMuM8D5du2+ygaXy5LFIVd5JRvEjxRbRLzhfiWC4BlmJcV8DwtN6VPaPj0GgRUECzgO9CMYRkQ7TZNU6+TSJr/Ac1/U+r4qE+Q8JD+//3YVfCaEh4BvQLiGir7ju1aso91tRW8UoMdWt+LP/rMc8SkvNmTY6F3FfrW425pZDS+lo9wSrpcgPNOJ6FbhJ8fEA0bOx7ib+vfFCJqzrz0+f74h8hvKyaVCQpjcsVa4ECScDRy8FSh5oouj4lKAjegJDgCKUD8ev3O6x3yBcMM3htpfDPT2by/VrxuFSqrMaltjjdRIqIAou7QEtae84gnxVFbudvnrCvdl9RchWxVAHih8aCdOPK3g1Iq0+adeSS9zDNMF6+u8VTsExF8rnMxuK/M1dW7AjFueFQ+ISBrFxupmNSdJokR/PJ/ksfalqTEoetrHFDOsmxBQX6VQlcvrRcA0mho4wyVmycs0OuVjHpXyPsNbEHCVq053S88bx8KHHUFowsvUkFZ+Xgw66Ep6f2J3fX4pKHuMcRsiVPusDAjgJUJi4Z68l2Wyr9vRf9EDsgxiRRGPF+RxfM0LMu45JznGH8bWPsu+eDNQw40EY8/SW8YDCcCoZdFr9q+GqIy+0D/B/RWw8fr+st9gscuUNVn/sp7ijUWgGmqdDhd/PE2nixAl0GFturNpynDdvc2yibWftImi4RvCYjgHSNsOGgEoiIg40htxNMcDKwos68No19OU57L7kzcUhhaiaPn3mhykHByKGcaAeU7c52HKT1c9PesPVHYknU4SvOj6SXa/KB78vA9VaUjyDG1D/lPAIM+2BGh7/bb8nDcicUfeUvVjh/jjRBqaMfhGJR6FFsqwcX6qaLdXjYDRhljcRgLe/IMqqUO7pOrXM06m35W/XOLV6VA37jVaKEJV5tJNJuPcBeejBGctBCEfmmci7p1UYzLgRfuUQVPDVMiZKG4jd640HZCickralgjNkNxB6+vd/P62XaYl5acC2fuV40gMzaEaISz3UEj7xNKt9Pnm2198olc038QWnMYZJYWmCQQf7f13hsLLH/ObcBp4W8P7LGRnNT0nfvjqY2OIPnaOMle9l2UrdlkjYctfiKqjbvoW1RYVt+pF5IJ5peei9U8ZYT6tUWVXdlgXLgkW5nZzJ55I3lVzTI8VC/hg7y+/yHf/wmyVo/Lyywa1WGmluFOssh/Q+WlaprCs54aiEntx7nWtdNlcLFTW+XuepwoeY+4LgjAxAHpLCaRDjwoX54os+z1t3JfbkLbBl0PGCvZQkIKizxKxhHSXmqNrDwvdriZomlEuTfa4jIkPFq9Prdr++lTtgdy1hEUwKS07nO/FtuMn3bAKMk/tpbASVf3d9u/Yo/EiZODBO8FNgvU7cuc8+BNwxZ/y1ePIYAOJ46lQuqhXb+hIx3WCm70Ynl7a4AElf56Ffx21oKtWRj2ajoYOnpGGvCuvIya/L7WYOb1P+b793yJsKAiCg5Qk0QS8jl4NWApvycumYUESs/mTVLNdfC4WBYFW/eH9FCrh5jBdBLj8YxvpnWVWGsXTknKqlMKaxpxyfbUumz2JxH0htaDJJ8V9SdLBXvP9dHYhbwWab02vy/6CcBvYROC2LOT8Q8xN7FuB+Kc6Q5RhWXSYPLNxRUt11q/LRTqlcrthv7Ep7sZWIabkjxqNyyGTXC45jCCTgzSFQuZv7ORIMOb/UU+1Oa0UlY6f7Y5akxZDvf8qDhPIqThYW2n9MOhvWkORkUHSeb3AC765+XiNNq/ERWuR8KacwdYoSXhl8EbbhW93IJd0GpmU5Mh3HDMEq2z9y3p/T1y5dUXhHOHe/caPrP7SCL/DUw2FdReta4wPgWPYXqiKXPuLr25Ys6zzndPvpBMDJiJoty4Bwwa7/x0Q90Y2ejPPdKOAKz/t3SfgzKfrCLX7D/LKKTGnUUkEFf8beeBQ7uF0b+Q43EBmshMkZdgyt2gA4v1HCFAyvktqWEPkGPDLfVFniPh6/1sPoVqpthj4AXmbOwqnuRjEVXdjiTRpOdXZWS2V4CQdfkiZ0Zmaj0Of4XHS+/Z+DH3pcO36wSEybFLz2oE9Q8J9X5yriu3Ly/fWEZxQ8e/D9E3O1O9x1zJU0BiROST5PpLtmtM6nmMOfe6dB8xcKrHS1a7tD2nprAkNWBSHkaP85SaRnji2S9aeE2c1AI9BGAPGpbiiBa2AAF++GFgvQ/vmdT0Mc0UyCQj6/kRQ/Fg1JJKqUdBifp8jHqS5ja4/2W4mWYtRLNjI1pbPtXCEPq3T1xlugPqG2Jg4rPEODavSsKR7mww0vTlCJJIeTwAc6PTPBWp6u5VcHRn8/4AEdpSabH6UxbNzDY+J/TTlGkyaCkNEm/3mLrp/vLbVnt+NMfKkAWnWECHoXmKTeI04eQo4aQeRB615CRLTk07DYVClwj9kU0E6UinNArN9UwYQJeACj8a2CTMMrG7QGXgw9yXzoImydqtJfEUvBa4BATE2YNVcPZTJDfnsa/U7qqAP6S0R8AAOWsI2AAEkJyWJfe+IuMIlCdfBBVXjweSkQegoWkjScpOdyKNQw1cakEAApscJaQxi4ldLaLk5qKneNyBQmY6y6xniTyGvlfwFBLXFEHNp9DKLc3ynZRIIGrkeLB81y5X9eOCulmTcbgudcv2mhaSC0W5MO6gEJl3cqtxZLTFnfZ4k6CGLgTdKzgfKKxiP4+olKEoECv+UmQ+a/XnXUjvgF5Pc1vHsxQoDZ/4mvlSbEvmYqa9G8S1a9uI1Vmmm79tedP7eG5H5Xp0tI0RWNz0YZRukuQCxS2VOFXQdOzzC5QnCvPWXok3i4zPCDoJSplPoqhriEypcqaLI+ELitVfCn5z+RJkAMp7PGAWG9VcH1zbJXa37GkfI/ylsk4SZ9IqES0eGysWX4ZZbKO1FHRXKVrC6cFeqTMO4PB/LNi8EPTSqr2QWofeoDANTD9QnsBDSgaYmG7Lp0PUpwm7sVdi3Jj9EWuKt9+jPOf6POCrB3jCpkzLt4g55M9SquV42bWxiCfiNBKYDoCZ5VEYaSYWQWCl9yaSUmJcQUB0OE+ncYG2s80zbM7pm5afLG3v486k102GNvmc0lDiKvWSxSYbNCQOivpyC9BL3Xn6b6J9wh8XyQeGlToa8jypAqsUaLRGt9+YfI67p5LSe/x5FJu75EX6aGtIYNYcKZqVgB5uUlRNq59KtyMz2KVj6gbrflpJ131MD/tw4tN55pp3lij4O3jNkLbAxPDXWK5boGy48bnTE+2f347Ifd9StAiQWsjKLu+3p2DqQ4J2OCmHy6zfAfGYVU9B7SAJ+ZS6kfj2ZXOpK7/95mqRA5Q1NxknQrl9Lve0l8+J2eu2F95bIn1LI+jo7EC7+0SE2F4AAsNJgqZJWp8UAAAAA) > **Tip.** Port `5432` is given as an example, the address and port of your application may differ. You can view details later: ![preview](/docs/img/ports/preview.webp) #### Usage Examples > **Note.** You will find more examples in the [examples section](https://tuna.am/en/docs/tunnels/examples.md). ##### PostgreSQL 1. Reserve a port with the desired alias, for example `postgres`. 2. Start the tunnel: ```sh tuna postgres --port=postgres 5432 ``` > **Info.** For PostgreSQL we have implemented a special type of tunnels - [postgres](https://tuna.am/en/docs/tunnels/postgres.md) with the ability to inspect SQL queries, but you can also use a regular [TCP](https://tuna.am/en/docs/tunnels/tcp.md) tunnel for connection. ```sh tuna tcp --port=postgres 5432 ``` 3. Connect to the database through the tunnel: ```sh psql "postgres://:@ru.tuna.am:38400/?sslmode=disable" ``` or ```sh psql -h ru.tuna.am -p 38400 -U postgres postgres ``` > **Note.** When connecting through a [postgres](https://tuna.am/en/docs/tunnels/postgres.md) tunnel, SSL is not supported. ##### Redis 1. Reserve a port with the desired alias, for example `redis`. 2. Start the tunnel: ```sh tuna redis --port=redis 6379 ``` > **Info.** For Redis we have implemented a special type of tunnels - [redis](https://tuna.am/en/docs/tunnels/redis.md) with the ability to inspect queries, but you can also use a regular [TCP](https://tuna.am/en/docs/tunnels/tcp.md) tunnel for connection. ```sh tuna tcp --port=redis 6379 ``` 3. Connect through the tunnel: ```sh redis-cli -h ru.tuna.am -p 38400 -a ``` ##### SSH 1. Reserve a port with the desired alias, for example `ssh`. 2. Start the tunnel: ```sh tuna ssh --port=ssh ``` > **Info.** We have implemented a special type of tunnels with a built-in [SSH server](https://tuna.am/en/docs/tunnels/ssh.md), but you can also use a regular [TCP](https://tuna.am/en/docs/tunnels/tcp.md) tunnel to connect to a standard SSH server. ```sh tuna tcp --port=ssh 22 ``` 3. Connect through the tunnel: ```sh ssh -p 38400 @ru.tuna.am ``` ##### Trigger > **Info.** Read more about trigger tunnels in a separate [guide](https://tuna.am/en/docs/tunnels/trigger.md). 1. Reserve a port with the desired alias, for example `trigger-http`. 2. Start the tunnel: ```sh tuna trigger webhook --port=trigger-http /usr/local/bin/trigger.sh ``` 3. Connect through the tunnel: ```sh curl -sSLf http://ru.tuna.am:38400 ``` ##### MySQL 1. Reserve a port with the desired alias, for example `mysql`. 2. Start the tunnel: ```sh tuna tcp --port=mysql 6379 ``` 3. Connect through the tunnel: ```sh mysql --host=ru.tuna.am --port=38400 --user= --password --database= --ssl-mode=disabled ``` ##### RTSP / IP camera 1. Reserve a port with the desired alias, for example `rtsp`. 2. Start the tunnel: ```sh tuna tcp --port=rtsp 192.168.0.11:554 ``` 3. Connect through the tunnel: ```sh ffmpeg -rtsp_transport tcp -i rtsp://:@ru.tuna.am:38400/stream1 ``` ##### Minecraft > **Info.** For Minecraft we have a detailed [guide](https://tuna.am/en/docs/tunnels/examples/minecraft.md). 1. Reserve a port with the desired alias, for example `minecraft`. 2. Start the tunnel: ```sh tuna tcp --port=minecraft 25565 ``` 3. Connect in the launcher through the tunnel using the assigned address, **for example** - `ru.tuna.am:38400` --- ### Postgres tunnel > **Info.** Available only with a [subscription](https://tuna.am/#pricing). #### Overview Postgres tunnel allows you to provide access to a PostgreSQL database and track executed SQL queries in the inspector. > **Note.** Postgres tunnels are designed for debugging and working locally or in test environments, they do not support SSL connections, so you need to use `sslmode=disable`. We do not recommend using this type of tunnels in production environments. Starting with version **0.36.0**, tuna shows a [full-screen interface](https://tuna.am/en/docs/tunnels/tui.md) in the terminal with sessions and SQL queries. #### Examples > **Note.** You can view all current flags, hints, and examples by calling help: > > ```shell > tuna postgres --help > > ``` > > Almost all flags have corresponding [environment variables](https://tuna.am/en/docs/guides/environment-variables.md). ##### Basic example ```shell tuna postgres 5432 ``` ##### With IP address ```shell tuna postgres 10.0.0.1:5432 ``` ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna postgres 5432 --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna postgres 5432 --location=nl ``` ##### Static port The value of the `--port`/`-p` flag can be an alias or an assigned port. You can reserve ports in the [dashboard](https://my.tuna.am/tcp_ports?lang=en), and after restarting the tunnel the address will remain the same. For more details about ports, see the dedicated [guide](https://tuna.am/en/docs/tunnels/ports.md). ```shell tuna postgres 5432 --port=postgres tuna postgres 5432 --port=35000 ``` ##### IP subnet access restriction You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna postgres 5432 --cidr-allow="10.0.0.1/32" ``` Or prohibit access from specific subnets: ```shell tuna postgres 5432 --cidr-deny="10.0.0.1/32" ``` You can combine them, for example specify a wide network and subtract private IPs: ```shell tuna postgres 5432 --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists separated by commas: ```shell tuna postgres 5432 --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` #### Query inspector The request inspector lets you debug incoming requests, view headers and other information that can make application development easier. For `tuna postgres` and `tuna redis` it is always on. If you don't need the inspector, use a [TCP tunnel](https://tuna.am/en/docs/tunnels/tcp.md). ##### Configuration The request inspector is available at `http://127.0.0.1:4040`. If port `4040` is busy, the next free port will be selected. The address or port can be overridden using the `TUNA_INSPECT_ADDR` environment variable or the `inspectAddr` parameter in the [configuration file](https://tuna.am/en/docs/guides/config-file.md). * `TUNA_INSPECT_ADDR=0.0.0.0` — listen on all interfaces * `TUNA_INSPECT_ADDR=0.0.0.0:8080` — listen on all interfaces on port `8080`; if the port is busy, the next free one will be selected * `TUNA_INSPECT_ADDR=8080` — listen on `127.0.0.1` on port `8080`; if the port is busy, the next free one will be selected The resulting address will be printed to the console at startup: ![cli_postgres](/docs/img/inspect/cli_postgres.webp) ##### Interface On the left, a list of sessions is displayed, and on the right, the output of commands that were executed within the sessions. ![web_postgres](/docs/img/inspect/web_postgres.webp) --- ### Redis tunnel > **Info.** Available only with a [subscription](https://tuna.am/#pricing). #### Overview Redis tunnel allows you to provide access to a Redis database and track executed queries in the inspector. > **Note.** Redis tunnels are designed for debugging and working locally or in test environments, they do not support SSL connections. We do not recommend using this type of tunnels in production environments. Starting with version **0.36.0**, tuna shows a [full-screen interface](https://tuna.am/en/docs/tunnels/tui.md) in the terminal with sessions and Redis commands. #### Examples > **Note.** You can view all current flags, hints, and examples by calling help: > > ```shell > tuna redis --help > > ``` > > Almost all flags have corresponding [environment variables](https://tuna.am/en/docs/guides/environment-variables.md). ##### Basic example ```shell tuna redis 6379 ``` ##### With IP address ```shell tuna redis 10.0.0.1:6379 ``` ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna redis 6379 --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna redis 6379 --location=nl ``` ##### Static port The value of the `--port`/`-p` flag can be an alias or an assigned port. You can reserve ports in the [dashboard](https://my.tuna.am/tcp_ports?lang=en), and after restarting the tunnel the address will remain the same. For more details about ports, see the dedicated [guide](https://tuna.am/en/docs/tunnels/ports.md). ```shell tuna redis 6379 --port=redis tuna redis 6379 --port=35000 ``` ##### IP subnet access restriction You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna redis 6379 --cidr-allow="10.0.0.1/32" ``` Or prohibit access from specific subnets: ```shell tuna redis 6379 --cidr-deny="10.0.0.1/32" ``` You can combine them, for example specify a wide network and subtract private IPs: ```shell tuna redis 6379 --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists separated by commas: ```shell tuna redis 6379 --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` #### Query inspector The request inspector lets you debug incoming requests, view headers and other information that can make application development easier. For `tuna postgres` and `tuna redis` it is always on. If you don't need the inspector, use a [TCP tunnel](https://tuna.am/en/docs/tunnels/tcp.md). ##### Configuration The request inspector is available at `http://127.0.0.1:4040`. If port `4040` is busy, the next free port will be selected. The address or port can be overridden using the `TUNA_INSPECT_ADDR` environment variable or the `inspectAddr` parameter in the [configuration file](https://tuna.am/en/docs/guides/config-file.md). * `TUNA_INSPECT_ADDR=0.0.0.0` — listen on all interfaces * `TUNA_INSPECT_ADDR=0.0.0.0:8080` — listen on all interfaces on port `8080`; if the port is busy, the next free one will be selected * `TUNA_INSPECT_ADDR=8080` — listen on `127.0.0.1` on port `8080`; if the port is busy, the next free one will be selected The resulting address will be printed to the console at startup: ![cli_redis](/docs/img/inspect/cli_redis.webp) ##### Interface On the left, a list of sessions is displayed, and on the right, the output of commands that were executed within the sessions. ![web_redis](/docs/img/inspect/web_redis.webp) --- ### SSHd tunnel > **Info.** Available only with a [subscription](https://tuna.am/#pricing). #### Overview Tuna contains a built-in SSH server, this allows you to create a tunnel and provide remote SSH access without the need to run an SSH server and without any configuration on the user's side. In addition to the functionality of a regular SSH server, tuna allows you to record user sessions and view them. Starting with version **0.36.0**, tuna shows a [full-screen interface](https://tuna.am/en/docs/tunnels/tui.md) in the terminal: connection instructions, a session feed and live session viewing. *** > **Info.** Creating an SSHd tunnel is only available on Unix-like operating systems (macOS, Linux, FreeBSD), Windows is not supported. #### Examples > **Note.** You can view all current flags, hints, and examples by calling help: > > ```shell > tuna ssh --help > > ``` > > Almost all flags have corresponding [environment variables](https://tuna.am/en/docs/guides/environment-variables.md). ##### Basic example ```shell tuna ssh ``` After starting, connection instructions will be displayed: ```shell Connection instruction: 1. echo "[ru.tuna.am]:12524 ssh-ed25519 xxx" >> ~/.ssh/known_hosts 2. ssh -p 12524 ru.tuna.am 3. ru.tuna.am's password: fsk3xdpgqpje7m5l ``` By default, any login is accepted, and the password is generated automatically and displayed in the console. > **Warning.** The tunnel node address is public, attackers can scan ports, find running SSHd tunnels and attempt password brute-forcing. Therefore, we do not recommend using password authentication, especially for long-running SSHd tunnels [launched as a service](https://tuna.am/en/docs/tunnels/guides/service.md). Below are instructions on how to enable *SSH key authentication* and *disable password authentication* ##### Static password Login and password can be defined statically using the `--auth` flag or the `TUNA_SSH_AUTH` environment variable: ```shell tuna ssh --auth="*:pass" ``` Where `*` means that username validation will not be performed, to restrict by username use the following format: ```shell tuna ssh --auth="user:pass" ``` ##### SSH key authentication For key authentication, you need to first add your public key in the [SSH Keys](https://tuna.am/en/docs/guides/ssh-keys.md) section in your [personal account](https://my.tuna.am/public_keys?lang=en). Also, on the same page, in settings, enable - **Allow connections using your SSH keys**. In case of a team, you may have a common key, and you can - **Allow connections to team members** 1. On the [SSH Keys](https://my.tuna.am/public_keys?lang=en) page - add your public key. 2. Allow connections in SSH tunnels using your SSH keys: ![](/docs/img/ssh-keys.png) ##### Disable password authentication ```shell tuna ssh --password-auth=false ``` ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna ssh --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna ssh --location=nl ``` ##### Static port The value of the `--port`/`-p` flag can be an alias or an assigned port. You can reserve ports in the [dashboard](https://my.tuna.am/tcp_ports?lang=en), and after restarting the tunnel the address will remain the same. For more details about ports, see the dedicated [guide](https://tuna.am/en/docs/tunnels/ports.md). ```shell tuna ssh --port=ssh tuna ssh --port=35000 ``` ##### IP subnet access restriction You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna ssh --cidr-allow="10.0.0.1/32" ``` Or deny access from specific subnets: ```shell tuna ssh --cidr-deny="10.0.0.1/32" ``` You can combine them, for example specify a wide network and subtract private IPs: ```shell tuna ssh --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists through commas: ```shell tuna ssh --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` #### SSH session recording Session recording is enabled by default, managed by the `--record-session` flag or the `TUNA_RECORD_SESSION` environment variable: ```shell tuna ssh --record-session=false ``` #### Managing and viewing SSH sessions There is a separate command `tuna ssh-session` for viewing and managing records. ##### List sessions Run with `tuna ssh-session list`: ```shell tuna ssh-session list INFO[11:14:12] 2024-10-28 11:13:49. 2o3u9YtqncQ5JfCtrj73WjFHDqR (jidckii, active) ``` ##### View recording Run with `tuna ssh-session watch [session]`: ```shell tuna ssh-session watch 2o3u9YtqncQ5JfCtrj73WjFHDqR tuna:jidckii> ^C tuna:jidckii> exit ``` ##### Delete all recordings ```shell tuna ssh-session clear ``` #### Behavior ##### SFTP SFTP is supported, no additional configuration is required. --- ### TCP tunnel #### Overview TCP tunnel allows you to provide access to any service using TCP protocol, the most popular examples: * Databases ([mySQL](https://tuna.am/en/docs/tunnels/examples/mysql.md), [PostgreSQL](https://tuna.am/en/docs/tunnels/examples/postgresql.md), [MSSQL](https://tuna.am/en/docs/tunnels/examples/mssql.md)) * Remote access software ([SSH](https://tuna.am/en/docs/tunnels/examples/ssh.md), [RDP](https://tuna.am/en/docs/tunnels/examples/rdp.md)) * Game servers ([Minecraft](https://tuna.am/en/docs/tunnels/examples/minecraft.md)) Starting with version **0.36.0**, tuna shows a [full-screen interface](https://tuna.am/en/docs/tunnels/tui.md) in the terminal. > **Info.** Creating a TCP tunnel is only available with a [subscription](https://tuna.am/#pricing). #### Examples > **Note.** You can see all current flags, hints and examples by calling the help: > > ```shell > tuna tcp --help > > ``` > > Almost all flags have corresponding [environment variables](https://tuna.am/en/docs/guides/environment-variables.md). ##### Basic example ```shell tuna tcp 5432 ``` ##### With IP address ```shell tuna tcp 10.0.0.1:5432 ``` ##### Unix socket Instead of a port you can specify a unix socket, bytes from the tunnel are passed to it as is. The path is set with the `unix://` prefix and must be absolute: ```shell tuna tcp unix:///var/run/app.sock ``` * A path without the prefix (`/var/run/app.sock`), a relative path (`unix://tmp/app.sock`) or an empty one (`unix://`) is not accepted. * The `--cidr-allow`, `--cidr-deny` and `--proxy-protocol` flags work the same way as with a port. * If the socket does not exist at startup, tuna prints a warning and keeps running. It connects to the socket on every incoming connection, so the socket can appear later without restarting the tunnel. * tuna connects to the socket as the user who started it. That user needs write access to the socket, otherwise the connection fails with `permission denied`. * Unix sockets are supported only by `tuna tcp` and [`tuna http`](https://tuna.am/en/docs/tunnels/http.md#unix-socket), specialized commands such as `tuna ssh`, `tuna postgres` and `tuna redis` do not support them. * Windows is not guaranteed to work, named pipes (`npipe://` in Docker Desktop) are not supported. ###### Docker API access > **Danger.** Access to `docker.sock` is equivalent to root access to the host: through the Docker API anyone can start a container with the host root filesystem mounted. Do not publish the socket without `--cidr-allow`, restrict access to your own IP address. ```shell tuna tcp unix:///var/run/docker.sock --cidr-allow="203.0.113.10/32" ``` The user running tuna must be a member of the `docker` group, otherwise connecting to the socket fails with `permission denied`. On your machine, use the tunnel address from the tuna output: ```shell docker -H tcp://ru.tuna.am:12345 ps ``` ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna tcp 8080 --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna tcp 8080 --location=nl ``` ##### Generating a QR code with the link When the `--qr` flag is added, a QR code will be generated for the link and printed to the console. This can be convenient when you are testing a site on a mobile device. ![](/docs/img/examples/qr.png) ##### Static port The value of the `--port`/`-p` flag can be an alias or an assigned port. You can reserve ports in the [dashboard](https://my.tuna.am/tcp_ports?lang=en), and after restarting the tunnel the address will remain the same. For more details about ports, see the dedicated [guide](https://tuna.am/en/docs/tunnels/ports.md). ```shell tuna tcp 5432 --port=postgres tuna tcp 5432 --port=35000 ``` ##### IP subnet access restriction You can define a whitelist of [subnets](https://en.wikipedia.org/wiki/Subnetwork) in CIDR format: ```shell tuna tcp 3389 --cidr-allow="10.0.0.1/32" ``` Or deny access from specific subnets: ```shell tuna tcp 3389 --cidr-deny="10.0.0.1/32" ``` You can combine them, for example specify a wide network and subtract private IPs: ```shell tuna tcp 3389 --cidr-allow="10.0.0.1/24" --cidr-deny="10.0.0.33/32" ``` You can also pass lists through commas: ```shell tuna tcp 3389 --cidr-allow="10.0.0.1/24,192.168.0.1/24" --cidr-deny="10.0.0.33/32,192.168.0.33/32" ``` ##### Proxy Protocol Allows passing the original client IP and port if your application supports it. The argument expects version 1 or 2, which corresponds to the Proxy Protocol version: 1 - text, 2 - binary. Haproxy or Nginx support both and detect them automatically. ```shell tuna tcp 8443 --proxy-protocol=1 tuna tcp 8443 --proxy-protocol=2 ``` --- ### Trigger tunnel #### Overview Triggers are commands executed on a specific event. tuna supports two types of events: * HTTP request (Webhook) * Email (SMTP) Starting with version **0.36.0**, the command receives the event data: the request body or the email comes on stdin, and the method, path, headers and addresses come in environment variables. > **Info.** Creating a Trigger tunnel is only available with a [subscription](https://tuna.am/#pricing). ##### Example > **Note.** You can see all current flags, hints and examples by calling the help: > > ```shell > tuna trigger --help > tuna trigger webhook --help > tuna trigger smtp --help > > ``` > > Almost all flags have corresponding [environment variables](https://tuna.am/en/docs/guides/environment-variables.md). ##### Script to run For example, create a script `/tmp/trigger.sh` that will create a file and write the phrase `Hello tuna` to it: /tmp/trigger.sh ```shell #!/bin/sh set -xe touch /tmp/test_file.txt echo "Hello tuna" > /tmp/test_file.txt ``` Then make it executable with the command `chmod +x /tmp/trigger.sh`. The command runs in the background on every event. Its stdout goes to the tuna log at the `debug` level (`--log-level=debug`), stderr at the `error` level. #### HTTP request (Webhook) ```shell tuna trigger webhook /tmp/trigger.sh ``` tuna will generate a link: ```log INFO[13:00:00] Welcome to Tuna INFO[13:00:00] Forwarding tcp://ru.tuna.am:16502 -> 127.0.0.1:35839 INFO[13:00:00] Send an HTTP request to http://ru.tuna.am:16502 to execute the command "/tmp/trigger.sh" ``` Now you can send a request to the specified address, and tuna will execute the specified script, for example: ```shell curl -sSLf http://ru.tuna.am:16502 ``` tuna responds right away without waiting for the command to finish. ##### Request data The request body is passed to the command's stdin as is, binary included. The rest comes in environment variables: | Variable | Value | | ---------------------------- | ----------------------------------------------------- | | `TUNA_REQUEST_METHOD` | Request method, e.g. `POST` | | `TUNA_REQUEST_PATH` | URL-encoded path, e.g. `/hooks/a%20b` | | `TUNA_REQUEST_QUERY` | Query string without `?`, e.g. `id=1&debug=true` | | `TUNA_REQUEST_HEADER_` | Header value, e.g. `TUNA_REQUEST_HEADER_CONTENT_TYPE` | The header name is uppercased and every character other than `A-Z` and `0-9` is replaced with `_`: `Content-Type` → `TUNA_REQUEST_HEADER_CONTENT_TYPE`, `X-GitLab-Event` → `TUNA_REQUEST_HEADER_X_GITLAB_EVENT`. Values of repeated headers are joined with `, `. `Host` is passed too: `TUNA_REQUEST_HEADER_HOST`. A script that prints everything it receives: /tmp/trigger.sh ```shell #!/bin/sh echo "Method: $TUNA_REQUEST_METHOD" echo "Path: $TUNA_REQUEST_PATH" echo "Query: $TUNA_REQUEST_QUERY" echo "Headers:" env | grep '^TUNA_REQUEST_HEADER_' | sed 's/^TUNA_REQUEST_HEADER_/ /' | sort echo "Body:" cat ``` ```shell tuna trigger webhook --log-level=debug /tmp/trigger.sh curl -sSf "http://ru.tuna.am:16502/deploy?env=prod" -H "Content-Type: application/json" -d '{"ref":"main"}' ``` Reacting only to the right event — deploy on a push to `main` from GitLab: /tmp/deploy.sh ```shell #!/bin/sh set -e [ "$TUNA_REQUEST_HEADER_X_GITLAB_EVENT" = "Push Hook" ] || exit 0 jq -e '.ref == "refs/heads/main"' > /dev/null || exit 0 /opt/app/deploy.sh ``` ##### Secret check With the `--secret` flag, tuna accepts only requests with the same value in the `X-Webhook-Secret` header. Other requests get `400` and do not run the command. The header itself is not passed to the environment. ```shell tuna trigger webhook --secret=s3cr3t /tmp/trigger.sh curl -sSf http://ru.tuna.am:16502 -H "X-Webhook-Secret: s3cr3t" ``` ##### Limits The request body is limited to 10 MB. A larger request gets `413` and the command does not run. #### Email (SMTP) ```shell tuna trigger smtp /tmp/trigger.sh ``` tuna will generate a link: ```log INFO[13:00:00] Welcome to Tuna INFO[13:00:00] Forwarding tcp://ru.tuna.am:19284 -> 127.0.0.1:1025 INFO[13:00:00] Send a message to SMTP server ru.tuna.am:19284 to execute the command "/tmp/trigger.sh" ``` Now you can send an email to the specified address, and tuna will execute the specified script, for example: ```shell curl -s \ --url "smtp://ru.tuna.am:19284" \ --mail-from "tuna@example.com" \ --mail-rcpt "tuna@example.com" \ --upload-file /tmp/trigger.sh ``` The command runs on every accepted email. A connection without an email or without authentication does not run it. To accept emails only with a username and password, set the `--username` and `--password` flags: ```shell tuna trigger smtp --username=tuna --password=s3cr3t /tmp/trigger.sh curl -s --url "smtp://ru.tuna.am:19284" --user "tuna:s3cr3t" \ --mail-from "tuna@example.com" --mail-rcpt "tuna@example.com" --upload-file mail.eml ``` ##### Email data The whole email (headers and body in RFC 5322 format) is passed to the command's stdin. Addresses and headers come in environment variables: | Variable | Value | | ------------------------- | --------------------------------------------------- | | `TUNA_MAIL_FROM` | Envelope sender (`MAIL FROM`) | | `TUNA_MAIL_TO` | Envelope recipients (`RCPT TO`) joined with `,` | | `TUNA_MAIL_HEADER_` | Email header value, e.g. `TUNA_MAIL_HEADER_SUBJECT` | Header names are built the same way as [for webhook](#webhook-payload). Encoded values like `=?utf-8?b?...?=` are decoded when the charset is `utf-8`, `us-ascii` or `iso-8859-1`. Others come as is. /tmp/trigger.sh ```shell #!/bin/sh echo "From: $TUNA_MAIL_FROM" echo "To: $TUNA_MAIL_TO" echo "Subject: $TUNA_MAIL_HEADER_SUBJECT" echo "Message:" cat ``` ##### Local SMTP server address The trigger SMTP server listens on `127.0.0.1:1025`. If the port is taken by another application, such as Proton Mail Bridge, set a different address with the `--listen-address` flag or the `TUNA_TRIGGER_SMTP_LISTEN_ADDRESS` variable: ```shell tuna trigger smtp --listen-address=127.0.0.1:2525 /tmp/trigger.sh ``` ##### Limits An email is limited to 10 MB. A larger one gets code `552` and the command does not run. #### Command environment Event variables are added to the environment tuna runs with, so the command sees the other variables as well. Linux limits a single environment variable to 128 KB. If a header is longer, the command does not run and the tuna log shows `Failed to run command: ... argument list too long`. #### Common flags ##### Specifying a token You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna trigger webhook --token=tt_*** tuna trigger smtp --token=tt_*** ``` ##### Specifying a connection region You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy. ```shell tuna trigger webhook --location=nl tuna trigger smtp --location=nl ``` ##### Static port The value of the `--port`/`-p` flag can be an alias or an assigned port. You can reserve ports in the [dashboard](https://my.tuna.am/tcp_ports?lang=en), and after restarting the tunnel the address will remain the same. For more details about ports, see the dedicated [guide](https://tuna.am/en/docs/tunnels/ports.md). ```shell tuna trigger webhook --port=postgres tuna trigger webhook --port=35000 tuna trigger smtp --port=postgres tuna trigger smtp --port=35000 ``` --- ### Terminal interface Starting with version **0.36.0**, `tuna http`, `tuna tcp`, `tuna ssh`, `tuna postgres`, `tuna redis` and `tuna kubernetes` show a full-screen interface (TUI) instead of a text log. It turns on automatically when output goes to a terminal. #### What's on the screen The header always shows: * the account; * the tunnel address (Forwarding) and the local address it points to; * the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) address; * connection status: `online` or `reconnecting`; * messages from the server. If the tunnel runs with access restrictions or options, **Access** and **Options** lines appear below, for example `basic auth (1) · cidr deny 203.0.113.0/24` and `rate limit 20/s · cors`. Only the settings you set are shown. Passwords and keys are never displayed, only their count. The main area depends on the tunnel type: | Tunnel | What it shows | | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [HTTP](https://tuna.am/en/docs/tunnels/http.md) | A request feed (newest on top) and metrics: total requests, rpm, p50, p90, number of 4xx and 5xx responses. Enter opens request details: headers and a formatted JSON body | | [Postgres](https://tuna.am/en/docs/tunnels/postgres.md), [Redis](https://tuna.am/en/docs/tunnels/redis.md) | Sessions on the left: `●` — active, `○` — closed, `•` — has unseen events. Events of the selected session on the right, highlighted: queries, number of rows returned, errors | | [SSHd](https://tuna.am/en/docs/tunnels/ssh.md) | Connection instructions and a session feed, including denied attempts. Enter on an active session opens a live view, on a finished one — its recording | | [Kubernetes](https://tuna.am/en/docs/tunnels/kubernetes.md) | The header shows the service account, namespace, restrictions and access expiry, below is the feed of guest requests | | [TCP](https://tuna.am/en/docs/tunnels/tcp.md) | The tunnel log | #### Hotkeys Common to all tunnels: | Key | Action | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Ctrl+C` | Stop the tunnel and exit. This is the only way to exit, `q` only goes back. On the QR code screen and in the SSH session viewer `Ctrl+C` only closes them | | `c` | Copy the tunnel address | | `i` | Open the inspector in a browser | | `l` | Show or hide the log (except TCP, where the log is the main screen) | | `Q` | Show a QR code for the tunnel address, `Enter` or `Ctrl+C` goes back, the tunnel keeps running | | `↑` `↓` | Select an entry | | `g` / `G` | Jump to newest / oldest entries | | `Esc`, `q` | Back from details and the log | `g`, or `↑` on the first entry, turns on follow mode: the selection moves to new entries as they arrive. HTTP: | Key | Action | | ------- | --------------------------------------------------------- | | `Enter` | Request details | | `r` | Replay the request | | `/` | Filter by method, path or status, `Esc` clears the filter | | `o` | Open the tunnel address in a browser | Postgres and Redis: | Key | Action | | ----- | ------------------------------------------ | | `Tab` | Switch between the session list and events | | `y` | Copy the query | SSHd: | Key | Action | | ------- | ----------------------------------------------------------------------------------------------------------- | | `Enter` | Watch an active session or replay a finished one. `Ctrl+C` closes only the viewer, the tunnel keeps running | With the interface on, the `--qr` flag shows the QR code screen once after connecting, `Enter` or `Ctrl+C` returns to the main screen. With `--inspect=false` requests are not recorded, so the HTTP feed stays empty. Postgres and Redis always have the inspector on. #### Turning it off To get the text log back, start the tunnel with one of these flags: ```shell tuna http 8080 --log=stdout tuna http 8080 --log=stderr tuna http 8080 --log-format=json ``` Or set the environment variable `TUNA_TUI=0`. With `--log=/path/to/file.log` the interface stays on and the log is also written to the file. The interface does not turn on automatically when: * output is redirected to a file or a pipe, or the tunnel runs [as a service](https://tuna.am/en/docs/tunnels/guides/service.md) or under systemd; * `CI` or `NONINTERACTIVE` is set; * `TERM=dumb`; * tuna is run by an AI agent (`AI_AGENT`, `CLAUDECODE` or `CURSOR_AGENT` is set). In this case you can turn it on with `TUNA_TUI=1`. The `tuna start` command keeps its text output. --- ### Webhooks [Go to personal account](https://my.tuna.am/webhooks?lang=en) Functionality that allows you to easily find out what is in the request sent by a service. In particular, understand what information actually comes in the request body, headers, source IP, and so on. ![webhooks](/docs/img/webhooks/main.png) *** #### HTTP Webhooks Debugging and processing incoming HTTP requests. ##### Simple response You can also edit the response, in particular: code, `Content-Type` header and response body. The edit window can be called from the menu on the right, there are also other options, like a link, clearing history or deletion. ![webhook_edit](/docs/img/webhooks/edit.png) ![webhook_menu](/docs/img/webhooks/menu.png) Example call using `curl`: ![webhook_terminal](/docs/img/webhooks/terminal.png) ##### Traffic policy In addition to a simple response, you can describe complex webhook processing rules, simulating an API response, for example. There is a complete guide on a separate page about [Traffic Policies](https://tuna.am/en/docs/traffic-policy.md). ![Screenshot with traffic policy in webhook](/docs/img/webhooks/reply_tp.png) ##### Forwarding request to tunnel If you have running tunnels, you can forward the received request to it. ![Screenshot with forwarding request to tunnel](/docs/img/webhooks/resend_to_tunnel.png) ##### Other In the request overview area, there are hotlinks for checking the source IP in different services. ![webhook_hotlinks](/docs/img/webhooks/hotlinks.png) #### SMTP Webhooks SMTP webhooks allow you to simulate email sending and receive emails for debugging. Emails are not forwarded anywhere and you cannot use the service for real email distribution. ##### Receiving mail ![Screenshot SMTP Webhooks](/docs/img/webhooks/mail.png) You can send an email to the specified mailbox from any real email, on the page you will see the message body in text representation and all SMTP headers. Thus, you can also use SMTP webhooks as a private temporary mail, but keep in mind that messages in webhooks are automatically cleaned over time. Or for debugging how your mailings work in testing / dev environments. ##### Sending mail Even more often during development for debugging mailings you need to check whether messages are sent to everyone who needs them and whether they are sent to those who don't need them. In this case, you can use sending to the SMTP server `smtp.tuna.am:25` emails with any destination address, specifying the webhook mailbox address as the sender's mailbox. An example of sending an email using **curl** is below in the examples. #### Examples In GitLab and GitHub repositories, you can create webhooks for specific or all actions, for example opening a PR or push. We, for example, use this to notify our bot, which then writes to telegram about opening an MR or adds labels to a new Issue. You can use Tuna Webhooks to understand what exactly comes in the request body, what headers, and so on. ##### 🦊 GitLab Webhooks ![webhook_gitlab](/docs/img/webhooks/gitlab.png) ![webhook_gitlab_body](/docs/img/webhooks/gitlab_body.png) ##### 🐱 GitHub Webhooks ![webhook_github](/docs/img/webhooks/github.png) ![webhook_github_body](/docs/img/webhooks/github_body.png) ##### 🔵 YooKassa HTTP notifications ![webhook_yookassa](/docs/img/webhooks/yookassa.png) ![webhook_yookassa_body](/docs/img/webhooks/yookassa_body.png) ##### 📬 Sending e-mail using curl *replace `XXX` with your webhook address* Create an `email.txt` file with content: ```txt From: XXX@smtp.tuna.am To: John Doe Subject: Test Email This is the body. ``` Run the command to send: ```sh curl -s --url 'smtp://smtp.tuna.am:25' --mail-rcpt 'to@example.com' --mail-from 'XXX@smtp.tuna.am' --upload-file email.txt ``` --- ### What's next? We have a backlog where we write all our wishes and customer wishes, here are some of them: * \~~Many people are asking for static TCP ports and we will try to do this by the next major release~~ * \~~Custom domain, static subdomain is good, but sometimes your own domain can be an advantage~~ * \~~New website and personal account, the current one is made quickly and doesn't contain all the functionality, but we want it to be beautiful 🙂~~ * \~~Documentation. Currently there is none at all, but it could describe how to install the client from repositories that already exist, some recipes and so on...~~ * \~~New UI for HTTP request inspector~~ * \~~Reading request body for display in inspector~~ * \~~Recording SSH client session actions. Currently the client can connect, but what commands they enter is unknown~~ * \~~Team plan~~ * \~~Invoice payment for organizations~~ * \~~Multi-factor authentication for greater security~~ * \~~Social network login~~ * \~~Domain zones (wildcard)~~ * \~~Password manager~~ * \~~SSH Bastion - Zero Trust server access~~ * Single Sign-On (SSO) * International card payments * Running tuna-node tunnel node in your own environment (on-premise) * \~~Infrastructure secrets~~ * Combining agents into virtual networks * *Infinite* boards for collaboration, drawing, planning and prototyping * Simple database backups * ALPN Bastion - Zero Trust access to applications and databases * Kubernetes Bastion * API Gateway Bastion * Proxy Bastion - access to servers in DMZ and gray networks --- ## Dpa ### Customer Data Processing Terms **Revision of: 13 August 2026** #### 1. Subject Matter and Roles of the Parties 1.1. These Terms define how Individual Entrepreneur Lev Aleksandrovich Aminov (hereinafter — the "Provider", "Processor") processes personal data contained in Customer Data, and form an integral part of the [Terms of Service](https://tuna.am/en/tos.md) (clause 15.2). 1.2. With respect to personal data contained in Customer Data, the Customer acts as the operator (controller) and the Provider as the processor acting on the Customer's instructions. 1.3. With respect to account data, payment data, Operational Data (clause 2.13 of the Agreement) and website visitor data, the Provider acts as an independent operator. The procedure for such processing is described in the [Privacy Policy](https://tuna.am/en/policy.md) and is not governed by these Terms. 1.4. These Terms are drawn up taking into account the requirements of the Law of the Republic of Armenia "On Protection of Personal Data" and, for Customers subject to the General Data Protection Regulation of the European Union (GDPR, Regulation (EU) 2016/679), the requirements of Article 28 of that Regulation. 1.5. Terms not defined in these Terms are used in the meanings established by the [Terms of Service](https://tuna.am/en/tos.md). #### 2. Details of the Processing 2.1. **Subject matter of the processing** — provision of the Tuna platform Services under the Agreement. 2.2. **Duration of the processing** — the term of the Agreement, subject to the deletion periods established in Section 8. 2.3. **Nature and purpose of the processing** — storage, transmission, display and other technical processing of Customer Data necessary for the operation of the Services and for ensuring their operability and security. 2.4. **Categories of data subjects** are determined by the Customer and may include its employees, members of its Organization, its clients and other individuals whose data the Customer transfers to the Services. 2.5. **Categories of personal data** are determined by the Customer. Depending on the Services used, they may include: | Service | What is technically processed | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | Tunnels and gateways | Traffic transmitted in transit between the Customer and its users; technical connection data (IP addresses, time, volume, destination addresses) | | Webhooks, request inspector | Contents of incoming HTTP requests stored for debugging | | Applications (Zero Trust access) | Identifiers and attributes of authenticating users, access logs | | Bastion | User identifiers, logs and parameters of SSH sessions | | Secrets | Configuration data of the Customer's applications in encrypted form | | Passwords | Vault data in encrypted form; decryption keys are not transmitted to the Provider | | Reports | Screenshots, screen recordings, technical session details of the users of the Customer's website | | Meetings, boards | Audio and video streams, chat messages, collaboration materials | | Monitors, status pages | Addresses of monitored resources, check results, notification contacts | 2.6. The Customer independently determines what data it transfers to the Services and does not place in them data whose processing requires protection measures not provided for by the Agreement and the documentation (clause 13.5 of the Agreement). #### 3. Obligations of the Provider 3.1. The Provider processes Customer Data only on the Customer's documented instructions, which are constituted by these Terms, the Agreement and the Customer's actions when using the Services. 3.2. The Provider does not use Customer Data for its own purposes, including for training machine learning models or for advertising. This clause does not restrict the processing of Operational Data, in respect of which the Provider acts as an independent controller (clause 1.3 of these Terms, clause 13.8 of the Agreement). Operational Data does not include the contents of Customer Data, nor Application access logs and Bastion session logs, which a Service maintains for the Customer and in respect of which the Provider remains a processor. 3.3. The Provider ensures that persons with access to Customer Data are bound by a confidentiality obligation. 3.4. The Provider implements organizational and technical protection measures proportionate to the risks, including: * encryption of traffic in transit (TLS); * encryption of sensitive data at rest; * a zero-knowledge model for the password manager; * access segregation and minimization of the number of persons with access to the production environment; * access and security event logging; * infrastructure backups and regular component updates. 3.5. The Provider promptly notifies the Customer if, in its opinion, the Customer's instruction infringes applicable data protection law. 3.6. The Provider notifies the Customer of a security breach affecting Customer Data without undue delay after becoming aware of it and provides the information the Customer needs to fulfil its own notification obligations. 3.7. The Provider provides the Customer with reasonable assistance in responding to data subject requests and in carrying out data protection impact assessments, to the extent available to the Provider given the nature of the processing. 3.8. If the Provider receives a data subject request relating to Customer Data, it does not respond to it on the merits but forwards it to the Customer. #### 4. Obligations of the Customer 4.1. The Customer warrants that it has a legal basis for processing the personal data it transfers to the Services, including obtaining the necessary consents and notifying data subjects. 4.2. The Customer independently ensures the exercise of data subjects' rights, the fulfilment of controller obligations and compliance with the law applicable to its activities, including notifying the competent authorities where required. 4.3. The Customer independently configures the access rights of members of its Organization, promptly revokes access for departed members and determines which data is to be masked in those Services where such functionality is provided by the documentation. 4.4. The Customer independently determines and observes the retention periods for the data it places in the Services, within the technical capabilities provided by the documentation. #### 5. Engagement of Sub-processors 5.1. The Customer grants the Provider general authorization to engage sub-processors for the provision of the Services. 5.2. The Provider concludes an agreement with each sub-processor imposing data protection obligations no less strict than those set out in these Terms, and is responsible for the actions of sub-processors as for its own. 5.3. As of this revision, the following categories of sub-processors are engaged: | Category | Purpose | Location | | -------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Hosting and network infrastructure providers | Hosting of platform servers, tunnel and monitoring nodes, and databases | Russian Federation, Germany, the Netherlands and other countries listed in the [Regions](https://tuna.am/en/docs/tunnels/guides/locations.md) section | | Functional Software, Inc. (Sentry) | Application error monitoring | USA | 5.4. The current list of regions available for tunnel connections and monitoring checks is published in the [Regions](https://tuna.am/en/docs/tunnels/guides/locations.md) section and changes as the infrastructure evolves. The Customer selects the region itself when starting a tunnel or configuring a monitor and takes this into account when working with data containing personal data. 5.5. The introduction of a new region whose use the Customer selects itself does not constitute a change to the list of sub-processors and does not require notification under clause 5.6, since processing in such a region begins by the Customer's own decision. 5.6. The Provider notifies the Customer of changes to the list of sub-processors affecting the processing of Customer Data regardless of the Customer's choice (in particular, when platform servers and databases are relocated) through the Personal Account or by email at least 14 calendar days in advance. If the Customer reasonably objects to a new sub-processor, it may terminate the Agreement before the change takes effect; in this case the unused portion of the paid Subscription is refunded on a pro rata basis. #### 6. Cross-Border Transfers 6.1. Customer Data is processed on infrastructure located in the Russian Federation, Germany and the Netherlands, and, as regards error monitoring, in the United States. The list of countries changes as new regions are introduced; the current list is published in the [Regions](https://tuna.am/en/docs/tunnels/guides/locations.md) section. 6.2. For data subjects located in the European Economic Area, transfers to countries not recognized by the European Commission as providing an adequate level of protection are carried out on the basis of standard contractual clauses. 6.3. A Customer subject to data localization requirements independently determines whether the use of a particular region and Service is permissible. #### 7. Disclosure to Authorities 7.1. The Provider discloses Customer Data to authorities only on the basis of an official request made in accordance with the law, and only to the extent necessary to comply with legal requirements. 7.2. Unless prohibited by law, the Provider notifies the Customer of a received request before complying with it. #### 8. Return and Deletion of Data 8.1. During the term of the Agreement, the Customer independently manages its data and may delete and export it using the tools provided by the documentation. 8.2. After termination of the Agreement or deletion of the account, Customer Data is deleted within no more than 30 calendar days, except for data that must be retained under legal requirements. 8.3. Database backups are retained for no more than 30 days from creation and are deleted at the end of the rotation period. 8.4. Technical logs containing connection metadata are retained for no more than 90 days and are used for security, diagnostics and handling abuse reports. #### 9. Demonstrating Compliance 9.1. Upon the Customer's written request, the Provider provides the information necessary to demonstrate compliance with these Terms. 9.2. Such a review is carried out no more than once every 12 months, except where it is required by a competent authority or prompted by a confirmed security breach. The parties agree the format and timing of the review in advance; the review must not compromise the confidentiality of other clients' data or the operation of the platform. #### 10. Liability and Changes to the Terms 10.1. The parties' liability under these Terms is limited as set out in Section 19 of the [Terms of Service](https://tuna.am/en/tos.md). 10.2. The Provider may amend these Terms in accordance with Section 22 of the [Terms of Service](https://tuna.am/en/tos.md). The current revision is published at [/dpa/](https://tuna.am/en/dpa.md) with the date indicated. 10.3. For Customers that require a signed data processing agreement on paper or as an electronic document, such an agreement is concluded upon request to . --- ## Policy ### Privacy Policy **Revision of: 30 September 2026** #### 1. General Provisions 1.1. This Policy defines how personal data is processed by Individual Entrepreneur Lev Aleksandrovich Aminov, taxpayer identification number (ՀՎՀՀ/TIN): 71062923, Republic of Armenia (hereinafter — the "Operator", "we"). 1.2. The Policy is drawn up in accordance with the Law of the Republic of Armenia "On Protection of Personal Data" (ՀՕ-49-Ն of 18 May 2015). For individuals located in the European Economic Area, the provisions of the General Data Protection Regulation of the European Union (GDPR, Regulation (EU) 2016/679) additionally apply. In GDPR terms, the Operator acts as a data controller to the extent set out in Section 3. 1.3. The Policy covers the processing of data of: * visitors to the website , the documentation and its subdomains; * users of the personal account [https://my.tuna.am](https://my.tuna.am/?lang=en) and the API ; * users of the Tuna client software; * persons contacting technical support. 1.4. Use of the website, the personal account and the platform services means that you have reviewed this Policy. The terms of use of the services are defined by the [Terms of Service](https://tuna.am/en/tos.md). 1.5. The current revision of the Policy is published at [/policy/](https://tuna.am/en/policy.md). #### 2. Definitions 2.1. **Personal data** — any information relating directly or indirectly to an identified or identifiable individual. 2.2. **Processing** — any operation with personal data: collection, recording, storage, use, transfer, deletion and other operations. 2.3. **User** — a visitor to the website or a user of the personal account, the API or the client software. 2.4. **Customer** — a person who has concluded the [Terms of Service](https://tuna.am/en/tos.md) and uses the services of the Tuna platform. 2.5. **Customer Data** — data that the Customer and its users transmit, place or route through the platform services. #### 3. Allocation of Roles 3.1. With respect to account data, payment data, technical logs, support requests and website visitor data, the Operator acts as an independent operator (controller). The procedure for such processing is described in this Policy. 3.2. With respect to personal data contained in Customer Data, the Customer itself acts as the operator (controller): it determines the purposes and means of processing, while the Operator acts as a processor on its instructions. The terms of such processing are set out in the [Customer Data Processing Terms](https://tuna.am/en/dpa.md). 3.3. Traffic transmitted through tunnels and gateways is handled in transit. The Operator does not analyse its contents, except for technically necessary processing for routing, for applying traffic policies configured by the Customer, for security purposes and for handling abuse reports. 3.4. The password manager is built on a zero-knowledge model: decryption keys are generated on the user's side and are not transmitted to the Operator. The Operator has no technical access to the contents of the password vault. 3.5. Certain services by their nature involve storing data contents on the Operator's infrastructure (webhooks, reports, secrets, meeting and board materials). The composition of such data is determined by the Customer; the restrictions are set out in Sections 13 and 14 of the [Terms of Service](https://tuna.am/en/tos.md). #### 4. Categories of Data Processed ##### 4.1. Website and documentation visitors * IP address, browser and operating system type and version, language, screen resolution; * referral source, pages visited, time and duration of the visit; * search queries in the documentation; * cookie identifiers. ##### 4.2. Personal account users * email address; * name, if provided by the User; * password hash or the account identifier at the OpenID/SSO provider, if sign-in is performed through one; * WebAuthn credential data (public key and credential identifier), if the User has enabled this sign-in method; * data on purchased subscriptions, plans and additional licenses; * payment details: amount, currency, date, status, transaction identifier, masked payment method details (card type and last four digits), token of the saved payment method; * details required to issue invoices and closing documents, if payment is made by invoice; * IP address, date and time of sign-in, activity logs in the personal account and organization audit events. Full bank card numbers, CVV codes and other complete payment details are not transmitted to or stored by the Operator — they are processed by payment providers (clause 7.1). ##### 4.3. Users of the client software and the API When the client software connects and when requests are made to the API, the following data is processed: * account identifier and access token used; * connection IP address; * client software version, operating system type and version, architecture; * parameters of created resources: tunnel addresses and names, connection region, session start and end times, volume of transmitted traffic. Some of this data, primarily IP addresses, constitutes personal data, and this Policy therefore applies to it. In addresses of tunnels with dynamically assigned names, the source IP address is used as part of the domain name — this is an anti-fraud measure described in the [Abuse](https://tuna.am/en/docs/abuse.md) section; it can be hidden in the organization settings. ##### 4.4. Technical support requests and abuse reports * email address and the contents of the correspondence; * username and message contents when contacting the Telegram chat; * information voluntarily provided for diagnostics: client software logs, configurations, screenshots; * information contained in an abuse report, including the reporter's data. Users should remove from the materials they send any information not related to the issue being resolved. #### 5. Purposes and Legal Bases of Processing | Data | Purpose | Legal basis | | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | Personal account record | Registration, provision of access to the services, subscription management | Performance of the contract | | Payment data | Accepting payment, subscription renewal, refunds | Performance of the contract | | Details and payment documents | Accounting and tax records | Compliance with legal requirements | | Technical connection data (clause 4.3) | Provision of the services, billing and limit enforcement, diagnostics | Performance of the contract | | Access logs and security events | Protection against unauthorized access, countering abuse, handling reports | Legitimate interest of the Operator and third parties | | Depersonalized and aggregated derivatives of technical logs | Statistics, capacity planning, improvement and development of the services, training machine learning models for these purposes (clause 13.8 of the [Terms of Service](https://tuna.am/en/tos.md)) | Processing of depersonalized data; no personal data is used for these purposes | | Support requests | Provision of technical support | Performance of the contract; legitimate interest | | Web analytics data and cookies | Traffic analysis, website improvement | Legitimate interest of the Operator | | Session recording ("Webvisor") on the website and in the documentation | Diagnosing interface problems | User consent | | Email address for mailings | Information about products, updates and offers | User consent | | Service notifications (upcoming charges, changes to terms, incidents) | Performance of contractual obligations | Performance of the contract | 5.1. Service notifications are sent regardless of consent to marketing mailings, as they are necessary for the performance of the contract. It is not possible to opt out of them while continuing to use the services. 5.2. The Operator does not process special categories of personal data for its own purposes and does not take decisions based solely on automated processing that produce legal effects for the User. Automatic restrictions applied when limits are exceeded or abuse is detected may be appealed in accordance with clause 12.8 of the [Terms of Service](https://tuna.am/en/tos.md). #### 6. Cookies and Web Analytics 6.1. The website uses the following categories of [cookies](https://en.wikipedia.org/wiki/HTTP_cookie): * **Essential** — enable the website and the personal account to work: session, authentication, selected language and theme, storing the cookie choice. They are set without consent, since the service does not work without them. * **Analytics** — used by the Yandex.Metrica service to collect anonymized traffic statistics. They are set when the website is visited, on the basis of the Operator's legitimate interest in traffic analysis. You can opt out of them by blocking cookies in your browser settings. 6.2. On the first visit to the website or the documentation, the User is asked to allow or refuse the recording of on-page actions (clause 6.4). The choice is shared between the website and the documentation. No recording takes place without consent. You can change or withdraw your decision at any time here: Выбор ещё не сделан. Изменить настройки печенья 6.3. The Yandex.Metrica service is provided by Yandex LLC and involves the transfer of data to servers located in the Russian Federation. The terms of data processing by the service are available in the [Yandex privacy policy](https://yandex.ru/legal/confidential/). The same counter is shared by the website, the documentation and the personal account, which allows the Operator to see the User's path between them. 6.4. The website, the documentation and the personal account use the session recording feature ("Webvisor"). On the website and in the documentation it is enabled only after the User's consent (clause 6.2). Recording is needed to diagnose interface problems and to investigate support requests. Input fields and data classified as sensitive are masked and do not appear in the recording. On the website and in the documentation you can opt out of recording by not giving or by withdrawing consent (clause 6.2). If you object to your actions being recorded in the personal account, write to and we will exclude you from the collection. 6.5. The website embeds the script of the Operator's own [Reports](https://tuna.am/en/docs/reports.md) service. It collects technical session details, on-page actions and a screenshot, and transmits them to the Operator when an error report is created. Sensitive fields are masked as described in the [service documentation](https://tuna.am/en/docs/reports/privacyOptions.md). The data is processed by the Operator and is not transferred to third parties. 6.6. Documentation search is powered by the Algolia DocSearch service: search queries and technical request parameters are transmitted to the service to perform the search. Certain website resources (fonts, styles, terminal recording player scripts) are loaded from the public jsDelivr content delivery network, which learns the User's IP address when they are loaded. 6.7. Refusing the recording of on-page actions does not restrict access to the website, the documentation or the personal account. #### 7. Transfer of Data to Third Parties 7.1. To provide the services, the Operator engages the following categories of data recipients: | Category of recipient | Data transferred | Purpose | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PayPro Global (PayPro Global Inc. and its affiliates; Canada, USA, European Union) | Name, email address, payment details, payer's country, amount and payment parameters | Accepting payment in US dollars, issuing invoices, calculating and remitting taxes, processing refunds and automatic renewals. Acts as an authorized reseller (merchant of record) and is an independent controller of the data it collects | | YuMoney NBCO LLC (YooKassa service, Russian Federation) | Email address, payment details, amount and payment parameters | Accepting bank card payments in roubles | | Payment collection agent (clause 10.3 of the [Terms of Service](https://tuna.am/en/tos.md), Russian Federation) | Payer's name and details, email address, amount and payment parameters | Issuing invoices and accepting payments from legal entities and individual entrepreneurs in the Russian Federation | | Hosting and network infrastructure providers (Russian Federation, Germany, the Netherlands and other countries listed in the [Regions](https://tuna.am/en/docs/tunnels/guides/locations.md) section) | Data placed on the Operator's servers | Hosting and operation of the platform, tunnel and monitoring nodes, personal account and databases | | Functional Software, Inc. (Sentry service, USA) | Technical error details, IP address, user identifier, request context | Detecting and fixing malfunctions | | Email delivery provider | Email address, name, message contents and delivery status | Sending service notifications and mailings | | Yandex LLC (Yandex.Metrica, Russian Federation) | Data specified in clause 4.1 | Website traffic analysis; session recording only after consent | | Algolia (DocSearch service) | Search query, IP address, technical request parameters | Documentation search | | Telegram messenger | Data voluntarily sent by the User to the support chat | Provision of technical support | 7.2. All engaged recipients process data solely to the extent necessary for the stated purposes and are bound by confidentiality obligations. 7.3. The Operator does not sell personal data, does not transfer it for third-party advertising purposes and does not disclose it to anyone other than the recipients listed in this Section. 7.4. When handling an abuse report, the Operator may disclose to the affected party, an Internet service provider or another competent person the minimum necessary technical information (in particular, the IP address and session times) without disclosing the Customer's personal data, except in cases provided for by law. 7.5. Data is disclosed to authorities only on the basis of their official request made in accordance with the law, and only to the extent necessary to comply with legal requirements. 7.6. In the event of reorganization of the Operator's business or transfer of rights to the Tuna platform, data may be transferred to the successor while preserving the terms of this Policy; Users are notified of this in advance. #### 8. Storage Location and Cross-Border Transfer 8.1. Data is hosted on the servers of the Operator and of the infrastructure providers it engages, in the Russian Federation, Germany and the Netherlands. The list of countries changes as new regions are introduced; the current list of regions available for tunnels and monitoring checks is published in the [Regions](https://tuna.am/en/docs/tunnels/guides/locations.md) section. The User selects the region itself. 8.2. The use of the services listed in Section 7 entails the transfer of certain categories of data outside the Republic of Armenia, including: * to the Russian Federation — the infrastructure of the regions located in that country, the YooKassa service for rouble card payments, the payment collection agent for invoice payments, and the Yandex.Metrica web analytics service; * to Germany and the Netherlands — the infrastructure of the regions located in those countries; * to the United States, Canada and the European Union — PayPro Global for payments in US dollars; * to the United States — the Sentry error monitoring service and the Algolia documentation search service. 8.3. The legal bases for such transfers are: * the Operator's legitimate interest — for traffic statistics, and the User's consent — for session recording; * the necessity of performing a contract to which the User is a party — for payment data and data hosted on the servers of infrastructure providers. 8.4. For data subjects located in the European Economic Area, transfers to countries not recognized by the European Commission as providing an adequate level of protection are carried out on the basis of standard contractual clauses or the data subject's explicit consent. #### 9. Retention Periods | Data | Retention period | | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Personal account record | For the entire existence of the account; after its deletion — up to 30 days in backups | | Customer Data placed in the services | For the term of the contract; after its termination — up to 30 calendar days (Section 8 of the [Customer Data Processing Terms](https://tuna.am/en/dpa.md)) | | Payment data and closing documents | For the period established by the accounting and tax legislation of the Republic of Armenia | | Technical connection logs and security events | 90 days | | Depersonalized and aggregated derivatives of technical logs | Indefinitely; not personal data | | Organization audit events displayed in the personal account | For as long as the organization exists | | Technical support requests | 3 years from the date of the last message | | Abuse report materials | 3 years from the date of review | | Web analytics data | In accordance with the Yandex.Metrica service policy | | Email address in the mailing list | Until consent is withdrawn | 9.1. Upon expiry of the retention period, data is deleted or anonymized. If data is needed to protect the Operator's rights in a declared or potential dispute, it is retained until the expiry of the corresponding limitation period. #### 10. User Rights 10.1. The User has the right to: * obtain confirmation that their data is being processed and a copy of the data processed; * request clarification or correction of inaccurate or incomplete data; * request deletion of data if there are no lawful grounds for its further processing; * request restriction of processing; * object to processing carried out on the basis of legitimate interest, as well as to receiving marketing messages; * receive their data in a structured, machine-readable format and request its transfer to another operator (right to data portability); * withdraw previously given consent — withdrawal does not affect the lawfulness of processing carried out before it was received; * lodge a complaint against the Operator's actions with a supervisory authority or a court. 10.2. To exercise any of these rights, it is sufficient to send a message to from the email address specified in the personal account. No special subject line is required. The Operator responds within no more than 30 calendar days and may request additional information to verify the applicant's identity. 10.3. If a request concerns data processed by the Operator on a Customer's instructions (clause 3.2), the Operator forwards it to the relevant Customer, who acts as the operator of such data. 10.4. You can also unsubscribe from marketing mailings using the unsubscribe link in any email. 10.5. Deletion of an account terminates access to the services and results in the deletion of the data placed in them within the periods specified in Section 9. Data that must be retained under legal requirements is kept for the established periods. 10.6. The supervisory authority is the Personal Data Protection Agency of the Ministry of Justice of the Republic of Armenia. Users located in the European Economic Area may also contact the supervisory authority at their place of residence. #### 11. Security 11.1. The Operator implements organizational and technical data protection measures, including traffic encryption (TLS), storage of passwords solely as irreversible hashes, encryption of sensitive data at rest, the zero-knowledge model in the password manager, access segregation, access logging and backups. 11.2. Absolute security of data transmission and storage on the Internet cannot be guaranteed. The User must maintain the confidentiality of their password, password manager master key and access tokens. The allocation of responsibility in respect of sensitive data is set out in Section 14 of the [Terms of Service](https://tuna.am/en/tos.md). 11.3. Upon detecting a security breach that creates a high risk to the rights and freedoms of data subjects, the Operator notifies the affected Users and the competent authority within the periods established by applicable law. #### 12. Minors 12.1. The website, the personal account and the Tuna platform services are not intended for persons under 16 years of age. The Operator does not deliberately collect the data of such persons. If the registration of a person under 16 is discovered, the corresponding data is subject to deletion. #### 13. Changes to the Policy 13.1. The Operator may amend this Policy. The current revision, with the date indicated, is published at [/policy/](https://tuna.am/en/policy.md). 13.2. Registered Users are notified of material changes affecting the purposes of processing or the composition of data transferred to third parties by email or through the personal account at least 7 calendar days before the changes take effect. #### 14. Contacts For any questions related to the processing of personal data, and to exercise the rights provided for in Section 10: Individual Entrepreneur Lev Aleksandrovich Aminov
Address: 3901, Republic of Armenia, Tavush Province, Dilijan
Taxpayer identification number (ՀՎՀՀ/TIN): 71062923
Email: --- ## Tos ### Terms of Service **Agreement — Offer**
Revision of: 13 August 2026 #### 1. General Provisions 1.1. This document is a public offer of Individual Entrepreneur Lev Aleksandrovich Aminov, taxpayer identification number (ՀՎՀՀ/TIN): 71062923, Republic of Armenia (hereinafter — the "Provider"), addressed to any person wishing to use the Tuna platform (hereinafter — the "Customer"). 1.2. The Provider holds the exclusive rights to the Tuna platform, the software included in it, and the related trademarks and brands. 1.3. The current text of this Agreement is published at [https://tuna.am/tos/](https://tuna.am/en/tos.md). Upon Acceptance of the Offer, the Agreement is deemed concluded on the terms set out in the revision in force at the moment of acceptance. 1.4. The following documents form an integral part of this Agreement: * the pricing and limits page on the Website and in the Personal Account; * the [Acceptable Use Policy](https://tuna.am/en/aup.md); * the [Customer Data Processing Terms](https://tuna.am/en/dpa.md); * the [Technical Support Terms](https://tuna.am/en/docs/support.md); * the [Privacy Policy](https://tuna.am/en/policy.md); * the description of the functionality of the Services in the [documentation](https://tuna.am/en/docs.md) and in the Personal Account. 1.5. This Agreement is governed by the law of the Republic of Armenia (Section 23). #### 2. Terms and Definitions 2.1. **Tuna Platform (Platform)** — the aggregate of computer programs, services and infrastructure of the Provider, including the Personal Account, the API, the Provider's server infrastructure and the Client Software. 2.2. **Services** — the functional capabilities of the Platform provided to the Customer under this Agreement. The composition of the Services is determined in accordance with clause 6.1. 2.3. **Cloud Services** — Services operating on the Provider's infrastructure that do not require installation of software on the Customer's equipment. 2.4. **Client Software** — computer programs installed on the Customer's equipment: the `tuna` command-line client, the `tuna-desktop` application, server components distributed by the Provider for installation on the Customer's side, and similar components. 2.5. **Website** — the Provider's information and telecommunications resource at , including subdomains. 2.6. **Personal Account** — the set of pages at [https://my.tuna.am](https://my.tuna.am/?lang=en), accessed with the Customer's login and password or through a supported OpenID/SSO provider. 2.7. **Account** — the Customer's account in the Personal Account. 2.8. **Organization (Team)** — an association of several Accounts sharing resources, a plan and a role-based access model. The Customer in relation to an Organization is the person on whose behalf the Organization was created and in whose name the Subscription is issued. 2.9. **Plan** — a set of Services, features and limits provided for a fee or free of charge. Plans are published on the Website and in the Personal Account. 2.10. **Additional Licenses** — paid options that extend the limits of a Plan or add individual features. 2.11. **Subscription** — the right to use the Services within the scope of the selected Plan during the paid period. 2.12. **Customer Data** — any data that the Customer or its End Users transmit, place, store or route through the Services: tunnel and gateway traffic, request contents, files, secrets, passwords, meeting recordings, reports, configurations and other data. 2.13. **Operational Data** — data generated by the Provider's systems in connection with the operation of the Platform and the use of the Services: connection metadata (IP addresses, time, volume and duration, destination addresses, response codes, latency metrics), resource and limit consumption records, diagnostic and debugging records, security events, and infrastructure technical logs. Operational Data does not include the contents of Customer Data, nor logs and records that a Service maintains for the Customer as part of its functionality, in particular Application access logs and Bastion session logs. 2.14. **End User** — a member of the Customer's Organization, as well as any person to whom the Customer has granted access to the Services or to the results of their operation. 2.15. **Consumer** — an individual acquiring or using the Services for personal, family, household or other needs unrelated to business activity. 2.16. **Reseller** — the Provider's authorized reseller acting as the seller in relations with the Customer when payment is made through the corresponding channel (clause 10.2). 2.17. **Agent** — a person accepting payment in favour of the Provider under an agency agreement (clause 10.3). 2.18. **Offer** — this proposal of the Provider addressed to an indefinite range of persons. 2.19. **Acceptance of the Offer** — full and unconditional acceptance by the Customer of the terms of the Offer by performing the actions specified in Section 3. The terms and definitions are used in this Agreement solely in the meanings specified in this Section. #### 3. Conclusion of the Agreement (Acceptance of the Offer) 3.1. Acceptance of the Offer is deemed to occur when the Customer performs any of the following actions: * registering an Account in the Personal Account; * signing in to the Personal Account through an OpenID/SSO provider; * using any of the Services, including via the Client Software or the API; * paying for any paid Plan or Additional License. 3.2. By performing any of the actions specified in clause 3.1, the Customer confirms that it has reviewed the terms of this Agreement and the documents listed in clause 1.4, fully understands them and accepts them without reservation. 3.3. The Agreement is deemed concluded from the moment the first of these actions is performed and remains in force as provided in Section 22. 3.4. If the Customer does not agree with the terms of this Agreement, it must stop using the Platform and delete the Client Software from its equipment. 3.5. A Customer that is a legal entity or an individual entrepreneur confirms that the person performing the Acceptance holds the necessary authority. 3.6. Registration of an Account by persons under 16 years of age is not permitted. #### 4. Subject Matter of the Agreement 4.1. Under this Agreement, the Provider undertakes to grant the Customer access to the Services within the scope of the selected Plan, and the Customer undertakes to comply with the Agreement and to pay for the Subscription if the selected Plan is a paid one. 4.2. With respect to the Client Software, the Provider grants the Customer the right of use under a simple (non-exclusive) royalty-free license for the term of this Agreement, in the territory of all countries of the world, subject to the restrictions of Section 20. The remuneration for the use of the Client Software is included in the cost of the Subscription and is not charged separately. 4.3. The Client Software is distributed as executable (binary) files and in other formats published by the Provider. The source code is not transferred to the Customer unless expressly stated otherwise for a particular component. This Agreement is not an agreement on the alienation of an exclusive right. 4.4. Individual components of the Platform may be distributed under open source licenses. The terms of the respective licenses apply to such components, and the provisions of this Agreement do not limit the rights granted to the Customer by those licenses. 4.5. This Agreement is not a telecommunications services agreement and does not provide the Customer with access to the Internet. #### 5. Account and Access 5.1. The Customer must provide accurate information upon registration and keep it up to date, including the email address used for legally significant communications (clause 24.6). 5.2. The Customer is solely responsible for the confidentiality of its password, access keys, service tokens and other credentials. Actions performed using the Customer's credentials are deemed performed by the Customer. 5.3. The Customer must immediately notify the Provider at of any known instance of unauthorized access to the Account. 5.4. The Provider makes Account protection tools available, including multi-factor authentication. The Customer's decision not to use them falls within its own area of responsibility. 5.5. A Customer that has created an Organization independently determines its members and their roles, ensures their compliance with this Agreement and is responsible for their actions as for its own. 5.6. One Account is intended for use by one person. Sharing credentials with third parties is not permitted; an Organization is to be used for collaboration. #### 6. Composition of the Services and the Procedure for Their Provision 6.1. The current composition of the Services, their functional capabilities and limits are determined by the [documentation](https://tuna.am/en/docs.md) and the Personal Account at the moment of use. The list of Services is not exhaustive: the Provider may introduce new Services and features, and from the moment they are made available to the Customer they form part of the Platform and are governed by this Agreement without any amendment to it. 6.2. The Provider may establish special terms of use for individual Services, published in the documentation of the corresponding Service. Such special terms form part of this Agreement and, in the event of a conflict, prevail over the general provisions of the Agreement with respect to that Service. 6.3. The Cloud Services are provided on the Provider's infrastructure. The Provider independently determines the technical architecture, the contractors and infrastructure providers engaged, and the composition and location of data centres, unless a choice of region is provided for a particular Service. 6.4. The Client Software is installed and run by the Customer independently. The Customer provides, at its own expense, the equipment, communication channels, power supply, up-to-date versions of the Client Software and the information security of its own infrastructure. 6.5. The Provider may change the functionality of the Services, release new versions of the Client Software, and add or remove features. During an already paid Subscription period, the functionality declared for the corresponding Plan at the time of payment is not materially reduced, except where this is caused by legal requirements, security requirements, discontinuation of support by third parties, or other circumstances beyond the Provider's control. 6.6. The Provider notifies affected Customers by email or through the Personal Account at least 30 calendar days in advance of the discontinuation of a paid Service or a material reduction of its functionality. If such discontinuation falls within a paid period and the Customer does not wish to continue the Subscription, the Provider shall, at the Customer's request, refund the unused portion of the payment on a pro rata basis. 6.7. Features designated as experimental, beta or preview are provided "as is", without any guarantees of availability or data retention, and may be changed or discontinued at any time without prior notice and without compensation. 6.8. The Provider does not perform backups of Customer Data for the Customer's benefit unless such a feature is expressly declared for a particular Service. Backing up data critical to the Customer's operations falls within the Customer's area of responsibility. #### 7. Plans, Limits and Fair Use 7.1. Plans, the composition of included features and quantitative limits (including limits on the number of resources, speed, number of requests and volume of stored data) are published on the Website, in the Personal Account and in the [documentation](https://tuna.am/en/docs/restrictions.md). 7.2. The Provider may change the composition and value of limits. Changes that worsen the Customer's position within an already paid Subscription period apply to it no earlier than the end of that period, except in the cases provided for in clauses 6.5 and 7.4. 7.3. The Services are provided on a shared-resource and fair-use basis. The Provider may apply technical restrictions to the load generated by the Customer if it materially exceeds what is typical for the selected Plan and interferes with the normal operation of other users. 7.4. The Provider may temporarily restrict or suspend individual Services of the Customer without prior notice if this is necessary to eliminate a threat to the operability or security of the Platform. The Customer is notified of such a restriction within a reasonable time. 7.5. The free Plan is provided "as is", without any guarantees of availability or data retention. The Provider may change the terms of the free Plan, limit its functionality and discontinue it, notifying Customers by email or through the Personal Account at least 14 calendar days in advance. 7.6. The Provider may delete resources and data of Accounts on the free Plan that have not been used for more than 6 consecutive months, having notified the Customer by email at least 14 calendar days in advance. #### 8. Fees and Payment Procedure 8.1. The cost of the Subscription and Additional Licenses is determined by the prices in effect at the time of payment. Payment is made on a 100% prepayment basis. 8.2. The payment currency and the available payment methods are displayed in the Personal Account. The price of the same Plan in different currencies and for different regions is set by the Provider independently, may differ, and is not the result of conversion at an exchange rate. The region is determined from the Customer's data provided at registration and at payment, including the payer's country. The Customer pays the price displayed at checkout at the time of payment; any subsequent change in exchange rates, or in the prices set for other currencies and regions, does not give rise to a recalculation, and the Customer is not entitled to demand that a price established for another currency or region be applied to them. 8.3. The Customer's payment obligation is deemed fulfilled from the moment the funds are received in full by the Provider, its Reseller (clause 10.2) or its Agent (clause 10.3). In the event of incomplete payment, the Services under the paid Plan are not provided. 8.4. Fees and charges of payment systems, banks and other intermediaries are paid by the Customer independently, in addition to the cost of the Subscription. 8.5. When payment is made directly to the Provider or through the Agent, the price is not subject to value added tax in accordance with the tax regime of the Republic of Armenia applied by the Provider. 8.6. When payment is made through the Reseller (clause 10.2), the calculation, collection and remittance of applicable indirect taxes (value added tax, sales tax and similar) are performed by the Reseller in accordance with the laws of the Customer's country. The total amount payable displayed at checkout may exceed the base price of the Plan by the amount of such taxes. 8.7. Except in the cases provided for in clause 8.6, all taxes, levies and duties payable in the Customer's jurisdiction in connection with the purchase of a Subscription, including withholding taxes, are paid by the Customer independently and are not included in the cost of the Subscription. If the law of the Customer's jurisdiction requires the Customer to withhold any tax from the payment amount, the payment amount shall be increased so that the Provider receives an amount equal to the amount it would have received in the absence of such withholding. 8.8. Closing documents are provided at the Customer's request to the email address specified in Section 25. When payment is made through the Reseller, invoices and other payment documents are issued by the Reseller. 8.9. In the absence of reasoned written objections from the Customer within 5 (five) business days from the date of provision of access to the paid Plan, the services for the corresponding period are deemed rendered properly and accepted by the Customer in full. This does not deprive the Customer of the right to raise claims regarding the quality of the Services throughout the term of the Agreement in accordance with Section 23. 8.10. The Subscription fee is a retainer-type fee: it is paid for making the Services available within the selected Plan for the paid period and does not depend on the extent to which the Customer actually used the Services during that period. Non-use of the Services in whole or in part, including non-use of the limits and features available under the Plan, is not a ground for recalculation or refund of the fee; any unused volume is not carried over to the next period. #### 9. Subscription, Automatic Renewal and Price Changes 9.1. A Subscription is issued for a monthly or annual period in accordance with the selected Plan. 9.2. A **monthly Subscription** means the provision of the Services until the corresponding calendar date of the following month. If the corresponding date does not exist in the following month, the Subscription expires on the last day of that month. 9.3. An **annual Subscription** means the provision of the Services until the corresponding calendar date of the same month of the following year. If such a date does not exist, the Subscription expires on the last day of the corresponding month. 9.4. When a paid Subscription is purchased, the payment method used is saved and automatic renewal is enabled by default. 9.5. A charge under automatic renewal is made no earlier than 24 (twenty-four) hours before the end of the current paid period, at the price in effect at the time of the charge. 9.6. The Provider notifies the Customer by email of an upcoming charge no later than 3 (three) calendar days before the charge date. Upon receipt of the notice, the Customer may decline the renewal or keep the Subscription. 9.7. The Customer may cancel the Subscription and delete the saved payment method in the Personal Account at any time. Cancellation stops automatic renewal, while the current paid period remains in effect until its end. If payment was made through the Reseller (clause 10.2), the payment method is stored by the Reseller, and the Subscription may additionally be cancelled through its order management interface. 9.8. If there are no funds or insufficient funds on the saved payment method, or if the charge is impossible for other reasons, automatic renewal is not performed, and at the end of the paid period the Account is switched to the free Plan with limited functionality and limits. Resources and data exceeding the limits of the free Plan may become unavailable or be deleted in accordance with clause 13.6. 9.9. The Provider may change its prices. A price change does not affect the cost of periods already paid for. For Consumer Customers, a price change takes effect no earlier than 7 calendar days after notification by email or through the Personal Account; before the change takes effect, the Consumer Customer may cancel the Subscription in accordance with clause 9.7. 9.10. When upgrading to a more expensive Plan during a paid period, the cost of the new Plan is calculated taking into account the unused portion of the previously paid period, as displayed in the Personal Account. #### 10. Payment Channels 10.1. Payment may be made: * **directly to the Provider** — by the methods available in the Personal Account, including payment by bank card through the YooKassa payment service; * **through the Reseller** — in accordance with clause 10.2; * **through the Agent** — for invoice payments by legal entities and individual entrepreneurs located in the Russian Federation, in accordance with clause 10.3. Regardless of the channel chosen, the Services are provided by the Provider on the terms of this Agreement. 10.2. **Payment through the Reseller.** Payment may be accepted by PayPro Global (PayPro Global Inc. and its affiliates, hereinafter — the "Reseller"), acting as an authorized reseller (merchant of record). In this case the Reseller is the seller in relations with the Customer: it processes the order, issues the invoice, accepts the payment, calculates and remits applicable taxes (clause 8.6), handles automatic renewals and processes refunds. The Reseller's own terms and policies, available at , additionally apply to the order. 10.3. **Invoice payment through the Agent.** For legal entities and individual entrepreneurs located in the Russian Federation, invoice payments are accepted by the Provider's agent — Individual Entrepreneur Evgeniy Sergeevich Medvedev, TIN 661219749774 (hereinafter — the "Agent"). The Agent acts under an agency agreement with the Provider and accepts funds in favour of the Provider. The Customer's payment obligation is deemed fulfilled from the moment the funds are received by the Agent. No other payment channels are serviced by the Agent. 10.4. Neither the Reseller nor the Agent acquires any rights to the Platform. The party providing the Services in all cases remains the Provider. 10.5. All claims relating to the provision of the Services, their quality and technical support are submitted to the Provider in accordance with Section 23. Claims relating to payment processing, issued documents and assessed taxes are, in the case of payment through the Reseller, submitted to the Reseller. #### 11. Refunds 11.1. A Consumer Customer may request a refund for a first-time paid Subscription within 7 (seven) calendar days of payment if they are not satisfied with the functionality or quality of the Services. 11.2. Refunds are not made for Subscription renewals (automatic or manual), or for the purchase of a Subscription to a Plan similar to one previously purchased, except in the cases provided for in clauses 6.6 and 11.3. 11.3. For Consumer Customers, the right to a refund within 7 calendar days of payment for a new Subscription is retained regardless of previously purchased Subscriptions. 11.4. No refund of amounts paid is made to Customers who are not Consumers (legal entities, individual entrepreneurs, and individuals purchasing the Services for business purposes) on any of the grounds set out in this Section, except in the cases provided for in clauses 6.6 and 22.8, and in cases where the refund is made under the Reseller's rules (clause 11.8). Before purchasing a paid Subscription, such a Customer has the opportunity to evaluate the functionality and quality of the Services on the free Plan (clause 11.10). 11.5. No refund is made where the Agreement is terminated or the Account is blocked on the grounds provided for in Section 12 and Section 20. 11.6. Given the retainer nature of the fee (clause 8.10), if the Customer withdraws from the Agreement, cancels the Subscription, deletes the Account or ceases to use the Services before the end of the paid period, the amount paid for that period is neither refunded nor recalculated on a pro rata basis; access to the Services is retained until the end of the paid period. This clause does not apply to the cases provided for in clauses 6.6, 11.1, 11.3 and 22.4. 11.7. A refund request is sent to the email address specified in Section 25, indicating the payment identifier. The refund is made by the same method used for payment, within no more than 14 calendar days from confirmation of the request. Upon refund, the Account is switched to the free Plan. 11.8. When payment was made through the Reseller (clause 10.2), the refund is handled under the Reseller's rules, which provide their own request period and review procedure. The request is submitted through the Reseller's support form; the Provider is given an opportunity to resolve the matter itself, and if it is not resolved, the decision is made by the Reseller. The provisions of this Section apply to such payments to the extent they do not conflict with the Reseller's rules. 11.9. The provisions of this Section do not limit the rights of Consumers provided by mandatory rules of the law applicable to them (clause 23.4). 11.10. Before purchasing a paid Subscription, the Customer has the opportunity to evaluate the Platform free of charge on the free Plan. #### 12. Rules of Use, Abuse and Blocking 12.1. The Customer undertakes to use the Services in accordance with the [Acceptable Use Policy](https://tuna.am/en/aup.md), which forms an integral part of this Agreement. 12.2. The Customer undertakes: * to use the Services for their intended purpose and within the scope of the selected Plan; * not to use the Services for any unlawful activity, distribution of malware, phishing, spoofing, spam or other attacks; * not to place through the Services any content whose distribution infringes the rights of third parties, including copyright and related rights; * to refrain from actions that may interfere with the normal operation of the Platform, the Provider's infrastructure and other users; * not to circumvent technical restrictions, limits and billing mechanisms, including by registering multiple Accounts; * not to decompile, disassemble or modify the Client Software, except where expressly permitted by mandatory rules of applicable law; * to report discovered vulnerabilities to the Provider and not to exploit them; * not to conduct load testing, scanning or penetration testing against the Provider's infrastructure without its prior written consent. 12.3. The Customer is solely responsible for the lawfulness of Customer Data and for compliance with third-party rights when processing it through the Services, as well as for the actions of its End Users. 12.4. The Provider does not pre-screen Customer Data and is not obliged to monitor it. 12.5. The procedure for accepting and reviewing abuse reports is described in the [Abuse and Complaints](https://tuna.am/en/docs/abuse.md) section. Reports are sent to . 12.6. In the event of a breach by the Customer of this Section or of the Acceptable Use Policy, the Provider may, proportionately to the nature of the breach: * require the breach to be remedied within a set period; * restrict or suspend the operation of an individual resource, Service or Account; * remove content placed in breach of the rules; * terminate this Agreement unilaterally and out of court and block the Account. 12.7. Suspension or blocking without prior notice is permitted where the breach poses an immediate threat to the security of the Platform or to third parties, or creates a risk of liability for the Provider, in particular in cases of phishing, distribution of malware, spam and distribution of materials whose circulation is prohibited. In other cases, the Provider notifies the Customer in advance and allows a reasonable period to remedy the breach. 12.8. The Customer is notified of a restriction, suspension or blocking by email, stating the grounds, except where such notification is prohibited by law. The Customer may submit an objection to ; the Provider reviews it within 10 business days and restores access if the breach is not confirmed. 12.9. Where the Agreement is terminated on the grounds set out in this Section, amounts paid are not refundable and the Customer's losses are not compensated. #### 13. Customer Data 13.1. Customer Data belongs to the Customer. The Provider acquires no rights to it, except for the limited right specified in clause 13.2. 13.2. The Customer grants the Provider the right to process Customer Data (store, transmit, display, reproduce) solely to the extent necessary to provide the Services, ensure their operability and security, and comply with legal requirements. 13.3. The Provider does not use Customer Data to train machine learning models, for advertising, or for any other purpose unrelated to the provision of the Services. This clause does not apply to Operational Data, the use of which is governed by clause 13.8. 13.4. Traffic transmitted through tunnels and gateways is handled in transit. The Provider does not analyse the contents of such traffic, except for technically necessary processing for routing, for applying traffic policies configured by the Customer, for security purposes and for handling abuse reports. The restriction in this clause concerns the contents of traffic; connection metadata constitutes Operational Data (clause 2.13). Certain Services by their nature involve storing request contents, files and other data on the Provider's infrastructure; the procedure for such processing is described in the documentation of the corresponding Service and in the [Customer Data Processing Terms](https://tuna.am/en/dpa.md). 13.5. The Customer independently determines what data it transmits to and places in the Services and must not place in them data whose processing is prohibited by the law applicable to it or requires special protection measures not provided for by this Agreement and the documentation. 13.6. After termination of the Agreement or deletion of the Account, Customer Data is deleted within no more than 30 calendar days, except for data that must be retained under legal requirements. The Customer must export the data it needs before the Agreement ends; the export tools provided for in the documentation are available throughout the term of the Agreement. 13.7. When an Account is switched to the free Plan, resources and data exceeding the limits of the free Plan may become unavailable or be deleted after 30 calendar days from the date of the switch. Notification is sent by email at least 14 calendar days before deletion. 13.8. The Provider processes Operational Data as an independent controller (clause 15.1) solely for the following purposes: * operating the Platform, diagnosing faults and restoring the Services; * ensuring security, detecting and investigating attacks, abuse and vulnerabilities, and handling reports; * monitoring compliance with Plan limits, billing and capacity planning; * compiling statistics, improving existing Services and developing new ones, including training machine learning models for the purposes listed above. For purposes going beyond operation, security and the handling of reports — including statistics, Service development and model training — the Provider uses Operational Data solely in aggregated or depersonalized form that does not allow it to be related to a specific Customer or natural person. The results of such processing, including aggregated metrics and trained models, belong to the Provider and contain no Customer Data. #### 14. Passwords, Secrets and Other Sensitive Data 14.1. This Section applies to Services intended for storing the Customer's sensitive data, including the password manager and the secrets service, and prevails over the general provisions of the Agreement with respect to such Services. 14.2. The password manager is built on a zero-knowledge model: encryption keys are generated on the Customer's side, and the master key is neither transmitted to nor stored by the Provider. The Provider is technically unable to decrypt the contents of the password vault, to restore a lost master key, or to provide access to the data by any other means. 14.3. **Loss of the master key means irreversible loss of access to the corresponding data.** The Customer is solely responsible for securely storing the master key and its backup copy. The Provider is not liable for loss of access to data resulting from the Customer's loss of the master key. 14.4. Secrets are stored in encrypted form; the zero-knowledge model does not apply to them, which follows from their purpose and is described in the [documentation](https://tuna.am/en/docs/secrets.md). The Customer takes this into account when deciding what data to place there. 14.5. The Customer independently determines the composition of data placed in these Services and bears the risk of that decision. **The Customer should not place in the Services any data whose compromise or loss could cause it or third parties harm disproportionate to the cost of the Subscription**, including data for which the law applicable to the Customer establishes special storage requirements (information constituting banking, medical or other legally protected secrecy, biometric and special categories of personal data, and information classified as a state secret). 14.6. The Customer undertakes: * to maintain the confidentiality of credentials, the master key, service tokens and access keys; * to restrict the access of members of its Organization to sensitive data in accordance with the role model; * to promptly revoke access for departed members and rotate compromised secrets. 14.7. The Provider applies the protection measures described in clause 19.2 and in the [Privacy Policy](https://tuna.am/en/policy.md); however, it does not guarantee absolute data security: no data transmission or storage system can be considered absolutely secure. The Provider does not assume the Customer's risks associated with leakage, compromise or loss of data placed in the Services, except where such leakage is caused by the Provider's intent or gross negligence. 14.8. The Provider's liability relating to the loss, unavailability, corruption or unauthorized disclosure of Customer Data is in all cases limited as set out in Section 19 and does not include compensation for indirect losses or lost profits. 14.9. Upon detecting a security breach affecting Customer Data, the Provider notifies the affected Customers within a reasonable time and takes measures to remedy the breach and mitigate its consequences. #### 15. Personal Data and Allocation of Roles 15.1. With respect to Account data, payment data, technical logs and other data that the Provider processes for its own purposes, the Provider acts as the operator (controller) of personal data. The procedure for such processing is defined by the [Privacy Policy](https://tuna.am/en/policy.md). 15.2. With respect to personal data contained in Customer Data, the Customer acts as the operator (controller) and the Provider as the processor acting on the Customer's instructions. The terms of such processing are defined by the [Customer Data Processing Terms](https://tuna.am/en/dpa.md), which form an integral part of this Agreement. 15.3. The Customer warrants that it has a legal basis for processing the personal data it transfers to the Services, including obtaining the necessary consents of data subjects and notifying them, and independently ensures the exercise of data subjects' rights. 15.4. The Customer undertakes to compensate the Provider for documented losses, fines and reasonable expenses incurred as a result of claims by third parties or authorities arising from the Customer's breach of clauses 12.3, 13.5 or 15.3. 15.5. Information is disclosed to authorities only on the basis of an official request made in accordance with the law, and only to the extent necessary to comply with legal requirements. 15.6. When handling an abuse report, the Provider may disclose to the affected party, an Internet service provider or another competent person the minimum necessary technical information (in particular, the IP address and session times) without disclosing the Customer's personal data, except in cases provided for by law. #### 16. Confidentiality 16.1. The parties undertake not to disclose to third parties any information obtained in the course of performing this Agreement and designated as confidential by the disclosing party, except in cases provided for by law. 16.2. Confidentiality obligations remain in force for 3 (three) years after termination of the Agreement. #### 17. Availability of the Services and Technical Support 17.1. The Provider takes reasonable measures to ensure continuous operation of the Services but does not guarantee their uninterrupted and error-free operation unless a separate service level agreement has been concluded for a particular Plan. 17.2. Scheduled maintenance is carried out with notice through the Personal Account, the status site or by email, except for emergency work necessary to eliminate a threat to the operability or security of the Platform. 17.3. The terms and channels of technical support, working hours and response times by Plan are described on the [technical support page](https://tuna.am/en/docs/support.md), which forms an integral part of this Agreement. 17.4. Response times are not resolution times. The Provider does not guarantee that a malfunction will be resolved within a specific period. 17.5. Work beyond the scope of technical support (configuration of the Customer's infrastructure, development, integrations) is performed under a separate agreement between the parties. 17.6. Interruptions in the operation of the Services caused by circumstances beyond the Provider's control do not constitute a breach of the Agreement and do not entitle the Customer to a recalculation of the Subscription price, except as expressly provided by a separate service level agreement. Such circumstances include, in particular: * distributed denial-of-service (DDoS) attacks and other third-party attacks on the Provider's infrastructure; * outages, failures and maintenance at hosting providers, data centres, backbone and regional telecom operators, and power supply interruptions; * access restrictions, blocking, traffic filtering and other measures by government authorities, telecom operators and third parties, including regional restrictions and censorship; * failures of third-party networks, services and software accessed by the Customer or used to deliver traffic. 17.7. Upon the occurrence of the circumstances specified in clause 17.6, the Provider takes reasonable measures to restore the operation of the Services and informs Customers of the progress by the means available to it. #### 18. Intellectual Property 18.1. The exclusive rights to the Platform, the Client Software, the documentation, the design, the trademarks and other intellectual property belong to the Provider. This Agreement does not transfer them to the Customer. 18.2. The Customer may not use the Provider's trademarks and brand identity without its prior written consent, except for fair reference to the fact that the Services are used. 18.3. The Customer may send the Provider suggestions for improving the Services. The Provider may use such suggestions without restriction and without paying any remuneration. 18.4. Use of the Services to provide services to third parties is permitted within the scope provided by the selected Plan. Reselling access to the Services, as well as providing access to them to third parties for a fee outside the Customer's Organization, requires prior written approval from the Provider. #### 19. Warranties and Limitation of Liability 19.1. THE SERVICES AND THE CLIENT SOFTWARE ARE PROVIDED "AS IS". TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE PROVIDER MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, UNINTERRUPTED AND ERROR-FREE OPERATION, AND NON-INFRINGEMENT OF THIRD-PARTY RIGHTS. 19.2. The Provider applies organizational and technical data protection measures, including traffic encryption, storage of passwords as irreversible hashes, encryption of sensitive data at rest, access segregation and logging. 19.3. The Provider is not liable for: * the impossibility of using the Services for reasons beyond the Provider's control, including the circumstances listed in clause 17.6, failures of the Customer's equipment and communication channels, actions of providers and telecom operators, restrictions in third-party networks and decisions of authorities; * loss of or damage to Customer Data caused by the actions of the Customer, its End Users, or by the loss of credentials and keys; * the content of Customer Data and the lawfulness of its processing; * the consequences of settings made by the Customer, including traffic policies and access rules; * the operation of third-party software and services to which the Customer connects through the Platform. 19.4. The Provider's aggregate liability under this Agreement for any claims is limited to the amount actually paid by the Customer for the Subscription during the 12 (twelve) months preceding the event giving rise to the claim. With respect to the free Plan and the features referred to in clause 6.7, the Provider's liability is excluded to the maximum extent permitted by applicable law. 19.5. Under no circumstances is the Provider liable for lost profits, indirect, incidental or punitive damages, loss of business reputation, downtime or the cost of substitute solutions. 19.6. The limitations set out in this Section do not apply to liability for intentional harm and gross negligence, or in other cases where limitation of liability is not permitted by mandatory rules of applicable law. 19.7. The Services are not intended for use in systems whose failure may lead to loss of life, harm to health or environmental disaster, or as the sole means of ensuring the security of critical information infrastructure facilities. #### 20. Lawful Use, Sanctions and Export Restrictions 20.1. The Customer undertakes not to use the Services to violate human rights and freedoms or for purposes prohibited by applicable law. 20.2. The Customer confirms that it is not included in the sanctions lists of the Republic of Armenia, the European Union, the United States or the United Nations, and undertakes not to grant access to the Services to persons included in such lists and not to re-export the Client Software in violation of applicable export restrictions. 20.3. In the event of a breach of this Section, the Provider may immediately block the Account and terminate the Agreement without compensation for losses and without refunding amounts paid. #### 21. Force Majeure 21.1. The parties are released from liability for failure to perform or improper performance of obligations if it is caused by force majeure circumstances as determined under the law of the Republic of Armenia. 21.2. The party affected by such circumstances must notify the other party within a reasonable time. Once such circumstances cease, the Agreement continues to operate as usual. #### 22. Term, Amendment and Termination of the Agreement 22.1. The Agreement enters into force upon Acceptance of the Offer and remains in effect until the Customer's Account is deleted or until termination on the grounds provided for in this Agreement. 22.2. The Provider may unilaterally amend this Agreement and the documents listed in clause 1.4. The new revision is published on the Website with the revision date indicated. 22.3. For Customers that are legal entities and individual entrepreneurs, amendments take effect upon publication of the new revision on the Website. 22.4. For Consumer Customers, amendments take effect no earlier than 7 calendar days after notification by email or through the Personal Account. If the Consumer does not agree with the amendments, it may withdraw from the Agreement; in this case the Agreement terminates at the end of the paid period, and the unused portion of the paid Subscription is refunded on a pro rata basis. 22.5. Amendments to the Agreement do not apply to Subscriptions already paid for and in effect, in terms of their price and the scope of functionality declared at the time of payment. 22.6. The Customer may withdraw from the Agreement at any time by ceasing to use the Services and deleting the Account. Amounts paid are not refundable, except in the cases provided for in Section 11. 22.7. The Provider may withdraw from the Agreement in respect of a Customer using only the free Plan, having notified it by email at least 14 calendar days in advance. 22.8. The Provider may withdraw from the Agreement in respect of a Customer who is not a Consumer, without stating reasons, by giving that Customer no less than 30 calendar days' notice by email. The Agreement terminates upon expiry of the notice period, but no earlier than the end of the paid Subscription period; automatic renewal does not take place from the date of the notice, and no refund of amounts paid is required. The Provider may cease providing the Services before the end of the paid period, refunding to the Customer the unused portion of the payment pro rata to the remaining period; no other losses of the Customer are compensated. 22.9. Termination of the Agreement does not affect the provisions of Sections 13, 14, 15, 16, 18, 19 and 23, which survive its termination. #### 23. Governing Law and Dispute Resolution 23.1. This Agreement, its conclusion, interpretation, performance and termination are governed by the law of the Republic of Armenia, without regard to its conflict of laws rules. 23.2. The parties resolve all disputes and disagreements through negotiations. A pre-trial claim procedure is mandatory: a claim is sent in writing to the email address specified in Section 25 and is reviewed within 30 calendar days of receipt. 23.3. If no agreement is reached, the dispute is subject to consideration by a court of the Republic of Armenia at the Provider's location. 23.4. Clauses 23.1 and 23.3 do not deprive a Consumer Customer of the protection afforded by mandatory rules of the law of its country of residence. A Consumer Customer may bring proceedings before the court of its place of residence where this is provided by mandatory rules of the law applicable to it. 23.5. This Section governs disputes between the Customer and the Provider. Disputes arising from the Customer's relations with the Reseller (clause 10.2) regarding order processing, payment and taxes are governed by the Reseller's terms and by the law applicable to its relations with the purchaser. 23.6. The United Nations Convention on Contracts for the International Sale of Goods (Vienna, 1980) does not apply to this Agreement. #### 24. Miscellaneous 24.1. This Agreement is published in several languages. In the event of discrepancies between versions, the Russian version prevails. 24.2. The invalidity or unenforceability of any provision of this Agreement does not entail the invalidity of the remaining provisions. 24.3. The Customer may not assign its rights or transfer its obligations under this Agreement without the Provider's prior written consent. The Provider may assign its rights and obligations under this Agreement to a successor in the event of reorganization or transfer of rights to the Platform, having notified the Customer. 24.4. This Agreement, together with the documents listed in clause 1.4, constitutes the entire agreement of the parties in respect of its subject matter and supersedes all prior arrangements. 24.5. The Provider's failure to demand remedy of a breach does not constitute a waiver of the right to demand its remedy in the future. 24.6. Legally significant communications are sent by the parties to the following email addresses: to the Provider — to the address specified in Section 25; to the Customer — to the address specified by it in the Personal Account. The Customer must keep its email address up to date and is deemed to have received a communication on the next business day after it is sent. 24.7. In all matters not regulated by this Agreement, the parties are governed by the law of the Republic of Armenia. #### 25. Provider's Details Individual Entrepreneur Lev Aleksandrovich Aminov
Address: 3901, Republic of Armenia, Tavush Province, Dilijan
Taxpayer identification number (ՀՎՀՀ/TIN): 71062923
Email:
Abuse reports: **Authorized reseller** (clause 10.2): PayPro Global Inc. and its affiliates. **Agent for accepting invoice payments in the Russian Federation** (clause 10.3): Individual Entrepreneur Evgeniy Sergeevich Medvedev
TIN: 661219749774 ---