diff --git a/doc/progress.md b/doc/progress.md
index 7a25a53..f3ec9b9 100644
--- a/doc/progress.md
+++ b/doc/progress.md
@@ -258,6 +258,30 @@
#### Правила репозитория
Создан `.github/copilot-instructions.md` с правилами:
+
+### Обновление этапа (UI invoke stability: JS)
+
+- Проведена диагностика JS invoke через реальный UI-путь (`/console/api/functions/{name}/invoke`) и роутер/экзекьютор логи.
+- Найдена корневая причина JS-таймаутов:
+ - для `node-env` используется `v2/specialize`
+ - при `functionName=main` runtime пытался загрузить `/userfunc/deployarchive/main`
+ - при plain literal `deployarchive` является файлом, не директорией
+ - после specialize без корректного контракта запросы зависали и упирались в router roundtripper timeout.
+- Для `nodejs-acc` зафиксирован runtime image: `ghcr.io/fission/node-env:1.32.5`.
+- Для JS-функций выровнен контракт runtime:
+ - `spec.package.functionName` выставлен в пустой entrypoint (`""`) для default export
+ - код приведен к формату ответа Node runtime: `return { status: 200, body: "..." }`
+ - для основных JS-маршрутов включены методы `GET` + `POST`.
+- Подтверждена работоспособность через console invoke:
+ - `fn-js-acc` -> `status=200`, `response_raw=hello-js-ok`
+ - `fn-js-direct` -> `status=200`, `response_raw=hello-js-ok`
+ - тестовая матрица `jsm1..jsm4` -> `status=200`.
+
+### Статус после фикса
+
+- Python invoke: работает.
+- JS invoke: работает по UI-пути и по прямому GET роутов.
+- Go invoke (`fn-go-acc`): остается отдельной runtime-проблемой (вне JS-фикса).
- Маппинг путей (локально ~/remote_dev/ = ВМ ~/terra/)
- Редактирование файлов — разрешено локально
- Команды — ИСКЛЮЧИТЕЛЬНО через SSH (VPN-конфликты)
@@ -326,3 +350,119 @@
### Статус `fn-go-acc`
- `fn-go-acc` продолжает падать не из-за UI, а из-за runtime specialization на стороне Fission.
- Подтверждено логами `router/executor`: `GetServiceForFunction ... context canceled` и постоянными readiness-fail у poolmgr pod-ов `go-acc`.
+
+## 2026-04-15 (дополнение) — Финальный фикс `fn-go-acc`
+
+### Симптом и root cause
+- `fn-go-acc` стабильно timeout'ился на invoke.
+- Изначальный пакет содержал `main.go` как `deployment.literal`, из-за чего go-runtime пытался грузить текст как plugin:
+ - `plugin.Open("/userfunc/deployarchive/main.go"): invalid ELF header`.
+
+### Что сделано
+- Пересобран deploy-артефакт как Go plugin (`main.so`) и упакован в `deploy.zip`.
+- Важно: сборка выполнена в том же образе, что у Fission environment builder:
+ - `ghcr.io/fission/go-builder` (Go 1.25.6), чтобы избежать несовместимости plugin ABI.
+- Функция обновлена через Fission CLI:
+ - `fission function update -n default --name fn-go-acc --env go-acc --entrypoint Handler --deployarchive /tmp/fn-go-acc-fix2/deploy.zip -f`.
+
+### Результат проверки
+- `/go-acc` через router с JWT: `hello-go-ok`, `HTTP 200`.
+- Executor логи: specialization проходит успешно (`specialized pod`, `added function service`), без `invalid ELF`.
+
+### Контрольный smoke после фикса
+- `/auto/ok` -> `HTTP 200`
+- `/js-acc` -> `HTTP 200`
+- `/js-direct` -> `HTTP 200`
+- `/go-acc` -> `HTTP 200`
+
+## 2026-04-15 (дополнение) — Edit Code для `fn-go-acc` снова показывает исходник
+
+### Проблема
+- В `Edit Code` для `fn-go-acc` поле `code` было пустым.
+- Причина: после `--deployarchive` пакет `pkg-go-acc` перешел на `spec.deployment.type=url`, а в console backend чтение кода шло только из `spec.deployment.literal`.
+
+### Исправление
+- В `console/main.go` добавлен fallback-поиск исходника:
+ - `spec.source.literal`
+ - `spec.deployment.literal`
+ - `spec.source.url`
+ - `spec.deployment.url`
+- Добавлена загрузка архива по URL и извлечение исходника из zip (если есть текстовые файлы).
+- Добавлен unit-тест `TestGetFunctionUsesSourceLiteralWhenDeploymentLiteralMissing`.
+- Собран и выкачен образ `naeel/fission-console:v0.2.6`, деплой обновлен.
+
+### Дополнительно по данным в кластере
+- Для `fn-go-acc` обновлен `sourcearchive` (`main.go`), чтобы `pkg-go-acc.spec.source.literal` содержал исходник и был доступен в Edit Code.
+
+### Проверка
+- `GET /console/api/functions/fn-go-acc` возвращает непустой `code` (Go source).
+- `/go-acc` продолжает отвечать `hello-go-ok`, `HTTP 200`.
+
+## 2026-04-15 (дополнение) — TLS в console, бренд NUBES, проверка `tf-neg-syntax-fn`
+
+### TLS: фактический статус
+- `http://fission.kube5s.ru/console/` -> `308 Permanent Redirect` на `https://...`
+- `https://fission.kube5s.ru/console/` -> `200`
+- Ingress `fission-console` настроен с:
+ - `nginx.ingress.kubernetes.io/force-ssl-redirect: "true"`
+ - `tls.secretName: fission-tls`
+ - `cert-manager.io/cluster-issuer: letsencrypt-prod`
+
+### UI оформление (NUBES)
+- В `console/ui/index.html` добавлены:
+ - favicon (inline SVG)
+ - брендинг в navbar: mark + wordmark `NUBES` / `FISSION CONSOLE`
+ - обновлен `
` на `NUBES Fission Console`
+- Выкат: `naeel/fission-console:v0.2.9`.
+
+### Invoke `tf-neg-syntax-fn` — проверка
+- Подтверждено, что у функции синтаксически невалидный код (`def main(: ...`).
+- Console invoke теперь возвращает fail-fast ошибку (не «молчание»):
+ - `{"error":"invoke \"tf-neg-syntax-fn\" timeout after 20s: function specialization likely failed (for example, syntax error)"}`
+- Причина на кластере: specialization падает в runtime с `500`, executor делает ретраи.
+
+## 2026-04-15 (дополнение) — Лечение "красного" HTTPS
+
+### Root cause
+- На одном host `fission.kube5s.ru` было два ingress:
+ - `fission-console` с TLS
+ - `fission-router` без TLS
+- Из-за смешанной host-конфигурации TLS мог работать нестабильно/давать красный индикатор в браузере.
+
+### Fix
+- Пропатчен `fission/fission-router`:
+ - добавлен `spec.tls` с `secretName: fission-tls`
+ - добавлена аннотация `nginx.ingress.kubernetes.io/force-ssl-redirect: "true"`
+
+### Проверка
+- `http://fission.kube5s.ru/neg/syntax` -> `308` redirect на HTTPS
+- Сертификат endpoint: Let's Encrypt R13, CN/SAN `fission.kube5s.ru`, валиден
+- `https://fission.kube5s.ru/console/` -> `HTTP 200`
+
+## 2026-04-15 (дополнение) — Строгая очистка: оставлены только рабочие и с видимым кодом
+
+### Требование
+- Убрать всё лишнее и оставить только функции, которые одновременно:
+ - имеют непустой `code` в `GET /console/api/functions/{name}`
+ - успешно проходят `POST /console/api/functions/{name}/invoke` со `status=200`
+
+### Что сделано
+- Выполнена финальная очистка функций по whitelist.
+- После очистки дополнительно проверены все оставшиеся функции через Console API (code + invoke).
+- Исправлена ошибка в служебном cleanup-скрипте (битый jsonpath с `{n}`), из-за которой падала пост-обработка orphan-ресурсов.
+
+### Итоговый набор
+- `auth-test2`
+- `fn-js-direct`
+- `fn-js-acc`
+- `hello`
+- `tf-auto-ok-fn`
+- `tf-stress-fast-fn`
+
+### Финальное состояние кластера
+- `functions=6`
+- `httptriggers=6`
+- `packages=6`
+
+### Финальная валидация
+- Для каждой из 6 функций: `code=OK`, `invoke=OK`.
diff --git a/doc/thinking/2026-04-15.md b/doc/thinking/2026-04-15.md
index c4daa5b..0b9cfc3 100644
--- a/doc/thinking/2026-04-15.md
+++ b/doc/thinking/2026-04-15.md
@@ -147,3 +147,224 @@ User → POST /console/api/functions/NAME/invoke
Причина: VPN может быть активен локально, вызывая сбои при прямом выполнении команд.
Монтирование sshfs делает локальное редактирование файлов эквивалентным редактированию на ВМ.
+
+---
+
+## Сессия: JS invoke timeouts в UI (root cause + fix)
+
+### Симптом
+
+- Все JS invoke через UI (`/console/api/functions/{name}/invoke`) падали по timeout.
+- В router логах: `roundtripper timeout (60s)`/`error sending request to function`.
+- В executor логах specialization для node окружения могла проходить, но ответ функции не возвращался.
+
+### Ключевые находки
+
+1. Node runtime работал через `POST /v2/specialize`.
+2. При `functionName=main` runtime строил путь загрузки как:
+ - `/userfunc/deployarchive/main`
+3. В реальном поде `/userfunc/deployarchive` был **файлом** (literal payload), а не директорией.
+4. После перевода на пустой entrypoint (`functionName=""`) specialization стала успешной (`202`, `user code loaded`),
+ но запросы всё равно зависали.
+5. Прямая проверка контейнера показала второй контрактный нюанс node runtime:
+ - пользовательская функция должна возвращать объект формата
+ `{ status, body, headers? }`
+ - возврат простой строки приводил к зависанию ответа (callback не отправлял HTTP response).
+
+### Принятые изменения
+
+- `nodejs-acc` закреплен на `ghcr.io/fission/node-env:1.32.5`.
+- Для JS функций (`fn-js-acc`, `fn-js-direct`, тестовые `jsm1..jsm4`):
+ - выставлен пустой entrypoint (`functionName=""`)
+ - код приведен к контракту Node runtime:
+ - `module.exports = async function(context) { return { status: 200, body: "..." }; }`
+- Для основных JS trigger включены методы `GET` и `POST`.
+
+### Проверка
+
+- UI invoke:
+ - `fn-js-acc` -> `status=200`, `response_raw=hello-js-ok`
+ - `fn-js-direct` -> `status=200`, `response_raw=hello-js-ok`
+- Прямые GET через роутер:
+ - `/js-acc` -> 200
+ - `/js-direct` -> 200
+- Тестовые `jsm1..jsm4` после унификации кода -> 200 через UI invoke.
+
+### Вывод
+
+Проблема была не в UI-рендеринге и не в одном конкретном trigger, а в несовпадении контракта Node runtime:
+- entrypoint/path specialization
+- shape возвращаемого значения функции.
+
+---
+
+## Сессия: Финальный fix `fn-go-acc` (Go runtime specialization)
+
+### Симптом
+
+- `fn-go-acc` выдавал timeout на invoke через console/router.
+
+### Диагностика
+
+1. На исходной конфигурации в executor логах:
+ - `plugin.Open("/userfunc/deployarchive/main.go"): invalid ELF header`.
+2. Это означало, что runtime ожидал plugin-артефакт, но в package deployment лежал исходник `main.go`.
+3. После первой попытки собрать plugin локальным `go1.26.1` ошибка `invalid ELF` ушла, но появился новый фейл specialization:
+ - `Post "http://127.0.0.1:8888/v2/specialize": EOF`.
+4. Вывод: plugin собран несовместимой версией Go относительно runtime.
+
+### Решение
+
+- Сборка plugin выполнена в `ghcr.io/fission/go-builder` (Go 1.25.6), то есть тем же toolchain, что у Fission environment builder:
+ - `GO111MODULE=off /usr/local/go/bin/go build -buildmode=plugin -o main.so ./main.go`
+ - упаковка `main.so` в `deploy.zip`.
+- Обновление функции через CLI:
+ - `fission function update -n default --name fn-go-acc --env go-acc --entrypoint Handler --deployarchive /tmp/fn-go-acc-fix2/deploy.zip -f`
+
+### Проверка
+
+- `/go-acc` через router с JWT: `hello-go-ok`, `HTTP 200`.
+- В executor логах specialization успешен:
+ - `specialized pod`
+ - `added function service`
+ - без `invalid ELF` и без `EOF`.
+
+### Контрольная матрица маршрутов
+
+- `/auto/ok` -> 200
+- `/js-acc` -> 200
+- `/js-direct` -> 200
+- `/go-acc` -> 200
+
+### Практический вывод
+
+Для go-env в этом кластере критично соблюдать контракт runtime:
+1. deploy-пакет должен содержать именно plugin (`.so`), а не исходник.
+2. plugin должен быть собран совместимой версией Go (через `ghcr.io/fission/go-builder`).
+
+---
+
+## Сессия: `Edit Code` для `fn-go-acc` пустой
+
+### Симптом
+
+- В UI (`GET /console/api/functions/fn-go-acc`) поле `code` было пустым.
+
+### Почему так произошло
+
+- После фикса runtime функция была переведена на deploy-архив plugin (`main.so`).
+- Package имел вид:
+ - `spec.deployment.type=url`
+ - `spec.deployment.url=...`
+ - без `spec.deployment.literal`
+- Backend в `handleGetFunction` извлекал код только из `spec.deployment.literal`, поэтому для URL-пакета отдавал пустую строку.
+
+### Что изменено
+
+- В backend добавлен `extractPackageSourceCode(...)` с fallback-приоритетом:
+ 1. `spec.source.literal`
+ 2. `spec.deployment.literal`
+ 3. `spec.source.url`
+ 4. `spec.deployment.url`
+- Добавлена загрузка URL-архива через `fetchPackageArchive(...)` и декодирование через `decodeArchiveBytesToSource(...)`.
+- Обновлен unit-тестами сценарий, где `deployment.literal` отсутствует, но есть `source.literal`.
+
+### Операционные шаги
+
+- Собран и задеплоен `naeel/fission-console:v0.2.6`.
+- Для текущей `fn-go-acc` дополнительно обновлен `sourcearchive` (`main.go`), чтобы исходник гарантированно отображался в Edit Code.
+
+### Результат
+
+- `GET /console/api/functions/fn-go-acc` теперь возвращает непустой Go source в `code`.
+- Вызов `/go-acc` остается рабочим (`hello-go-ok`, HTTP 200).
+
+---
+
+## Сессия: Проверка TLS + branding NUBES + верификация `tf-neg-syntax-fn`
+
+### TLS
+
+- Проверен доступ к console:
+ - `http://.../console/` -> `308` redirect на HTTPS
+ - `https://.../console/` -> `200`
+- Ingress конфиг содержит корректный TLS блок и принудительный SSL redirect.
+- Вывод: endpoint защищен TLS, «незащищенного HTTP-доступа» нет.
+
+### Брендинг NUBES
+
+- В `console/ui/index.html` добавлены:
+ - `NUBES Fission Console`
+ - favicon (inline SVG)
+ - navbar brand block с `NUBES` / `FISSION CONSOLE`.
+- Обновлен образ до `naeel/fission-console:v0.2.9`.
+
+### `tf-neg-syntax-fn` — почему timeout вместо прямого SyntaxError
+
+- Функция содержит заведомо битый Python (`def main(:`), confirmed через `GET /console/api/functions/tf-neg-syntax-fn`.
+- На runtime это роняет specialization (`500`), после чего executor ретраит.
+- Поэтому router/console не получает «чистый traceback» сразу из runtime API и видит timeout/ошибку specialization.
+- Для UX введен fail-fast и явная ошибка в console invoke:
+ - `timeout after 20s: function specialization likely failed (for example, syntax error)`.
+
+---
+
+## Сессия: "Не защищено красным" — экстренное лечение
+
+### Наблюдение
+
+- Сертификат endpoint сам по себе валидный (Let's Encrypt, CN/SAN = `fission.kube5s.ru`).
+- Но на том же host были два ingress с разной TLS-конфигурацией:
+ - `fission-console` с TLS
+ - `fission-router` без TLS
+
+### Действие
+
+- Патч `fission-router`:
+ - добавлен `spec.tls` с `secretName: fission-tls`
+ - добавлен `force-ssl-redirect=true`
+
+### Результат
+
+- HTTP на роутер-пути дает 308 -> HTTPS
+- HTTPS на console стабильно 200
+- Конфигурация host выровнена: теперь и console, и router на одном сертификате.
+
+---
+
+## Сессия: Строгая чистка до "только точно рабочие и видимые"
+
+### Запрос
+
+- Оставить только функции, которые в UI/Console:
+ - показывают исходник (`code` не пустой)
+ - реально вызываются (`invoke.status == 200`)
+
+### Что произошло
+
+- Выполнен массовый cleanup с keep-list.
+- По логу cleanup выявилась ошибка jsonpath в пост-обработке orphan-пакетов:
+ - использовалось `{n}` вместо `{"\\n"}`
+ - это ломало шаг удаления orphan-объектов после основного удаления.
+- Основное удаление функций при этом сработало корректно.
+
+### Проверка после cleanup
+
+- Текущее состояние:
+ - functions: 6
+ - httptriggers: 6
+ - packages: 6
+- Остались только:
+ - `auth-test2`
+ - `fn-js-direct`
+ - `fn-js-acc`
+ - `hello`
+ - `tf-auto-ok-fn`
+ - `tf-stress-fast-fn`
+- Для каждой функции через Console API подтверждено:
+ - `code=OK`
+ - `invoke=OK`
+
+### Вывод
+
+- Финальный набор теперь соответствует строгому критерию "однозначно работает и показывается".