# Changelog This file is the canonical Hermes Linux Stack release history from v0.5.2 onward. Older component-specific history remains in the component release-note files and Git history. The current runtime release is **v0.5.9**. ### Features — content production platform: agents, timeline renderer, operations console (2026-09-22) - The Smart Router gains the four content production agents (Storyboard, Video Director, Media Planning, NotebookLM Recovery) as registered agents with deterministic normalizers, exposed on `GET /v1/content/agents` and `POST /v1/content/{storyboard,timeline,video-plan,media-plan,recover}`, so planning rides on the existing registry, orchestrator, approvals and audit trail. Release: Smart Router `0.6.2`. - Media Studio gains the `timeline-video` driver: a validated timeline document renders to one MP4 with ffmpeg (per-scene fragments, transitions, narration track, brand chip), and `POST /timeline/validate` checks a document with the renderer's own normalizer without rendering. The renderer never receives a raw script. Release: Media Studio `0.5.1` (timeline driver in `0.5.0`). - The operator panel becomes the Content Operations Center: Video Studio plans, validates, renders and tracks jobs (status, log tail, cancel, retry, download, preview), Hermes shows the control-plane agents, routing profiles and measured telemetry, Orchestration lists runs with steps, approvals and reviewer verdicts, and Knowledge lists bases and runs retrieval tests. Release: operator panel `0.5.0`. - NotebookLM keeps its place as the research connector; its failures now feed the Recovery Agent instead of blind retries. - Operator panel `0.5.1`: Video Studio reports the drivers the running worker loaded instead of trusting `.env`, and the exposure warnings only fire for services that are actually published (with a bind/state/port matrix in the Overview). - Operator panel `0.5.2`: the n8n exposure note states that Hermes reaches n8n over the internal Docker network, so the host publish only adds LAN access and binding the port to loopback does not affect the agent. - Smart Router `0.6.4` adds `POST /v1/content/scene`: the Storyboard Agent rewrites one scene of an existing storyboard (index, instruction, draft context) and the answer is repaired with the storyboard scene bounds, so the operator's scene editor regenerates a single scene instead of a whole plan. - Operator panel `0.6.0` adds the Storyboard view: drafts planned by the agents are stored under `data/panel/storyboards/` and edited scene by scene (edit, reorder, add, remove, per-scene regenerate), validated against the renderer, approved or rejected with a note, and rendered through the `timeline-video` driver with the job tracked on the draft. - Smart Router `0.6.4` also makes virtual API keys fully removable: `DELETE /control/api/keys/{id}?purge=true` deletes the key instead of the reversible revoke, answers `409 key_in_use` while ACL rules or budgets still reference it, and `cascade=true` removes those references in the same transaction. Access -> Users & Keys gains the Delete button and an Active column next to the existing Revoke action. - Both consoles now document themselves. The Smart Router Docs page covers every navigation area, the complete HTTP API (OpenAI-compatible, Anthropic-compatible and content production surfaces), the virtual key lifecycle and ready-to-copy Codex and Claude client configurations. The operator panel gains a Docs view with one section per panel view plus the full `/api/` reference and curl automation examples. ### Features — NotebookLM Video Overviews for the Content Bot (2026-09-15) - A new optional worker (`notebooklm-worker`, compose profile `notebooklm`) produces narrated overview videos with Google NotebookLM instead of a paid video API. It drives the NotebookLM web app in a real browser session (cdp against the operator's Chrome by default, or a container profile), adds the draft title and body plus the original link as sources, renders the Video Overview, and serves the mp4 as a job artifact. - The media question gains a fifth choice, **NotebookLM video**, with three profiles (`technical_fa`, `educational_fa`, `news_fa`) and a documented length-bucket API (`short`, `standard`, `deep`). The ask message is edited as the job advances (`⏳ 📚 📤 🎬 ⬇️`), the finished video arrives as the usual preview, and a failed job reports the worker error with a retry button that reuses the stored profile. - NotebookLM is an independent provider next to Media Studio: the button appears only when `CONTENT_NOTEBOOKLM_URL` is set, the bot keeps a shared bearer token with the worker, and Media Studio keeps generating images and recorded clips. `./manage.sh notebooklm-enable|status|login|disable` manages the profile, the token, and the sign-in, and `docs/NOTEBOOKLM-STUDIO.md` covers sessions, the API, profiles, selector calibration, and failures. - The worker publishes as `afsharidevops/notebooklm-worker` through `.github/workflows/publish-notebooklm-worker.yml`; `data/notebooklm-worker` is a scoped backup section, and selector overrides load from `NOTEBOOKLM_SELECTORS_FILE` when the NotebookLM UI changes. - Releases: Content Bot `0.4.3` (image and workflow tag, release notes in `content-bot/V0.4.3-RELEASE-NOTES.md`) and NotebookLM Worker `0.1.0`. ### Features — Content Bot scheduled proposals: media question and daily toggle (2026-09-14) - The Content Bot media question now reaches scheduled proposals, not only operator-sent links. When Media Studio is configured (`CONTENT_MEDIA_STUDIO_URL`), every daily proposal is followed by the same four choices as an on-demand draft (text only, AI image, send your own image, or a video prompt), and scheduled routines can select `media: ask` in `editorial-policy.yaml`. `media: auto` (AI image) and `media: none` keep working unchanged. - More than one proposal per day needs no code change: add one routine per slot, each with its own `id`, `time`, `count`, and `media` mode. Run markers are per routine, so a second daily slot is independent of the first. - The built-in daily pass can be turned off with `pipeline.daily_enabled: false` in `editorial-policy.yaml` when the scheduled routines own the cadence. The default stays enabled, so existing deployments are unchanged, and the routines keep running either way. ### Features — Aparat video publishing for the Content Bot (2026-09-14) - Aparat is an automatic channel now. Once a browser session is stored (`CONTENT_APARAT_TOKEN`, the `jwt` value of a signed-in aparat.com tab, or `CONTENT_APARAT_COOKIE`), a draft that carries a video publishes to Aparat the moment the platform is picked: the bot reserves an upload slot, sends the file in chunks, and submits the title, description, tags, and category. The publication row keeps the watch URL when Aparat returns one. - Aparat is video-only and on demand. Text and photo drafts keep the copy-ready package, the chooser marks the entry `Aparat (needs a video)` until a video is attached, **All targets** skips it without a video, and picking it without one asks for the file and uploads it as soon as it arrives. The daily proposals and the scheduled routines never publish to Aparat on their own. - The adapter reads the same per-destination adapted text the other channels use, so the title, description, and hashtags of the Aparat upload follow the draft language and the platform profile. `docs/APARAT-SETUP.md` covers the session copy step, the panel card, the category ids, and the failure modes; the panel gained an Aparat card with a session test, and `./manage.sh content-aparat-check` probes the stored session without uploading anything. - The content-bot container now receives every `CONTENT_APARAT_*` key from the stack `.env` file, so a session stored by the panel (or by hand) reaches the bot after the container is recreated; a panel regression test keeps every platform-card key wired to its service. ### Fixes — Aparat uploader matches the web uploader and retries failed chunks (2026-09-14) - The metadata call that finishes an Aparat upload now sends the body and the headers the Aparat web uploader sends: `video_pass` as a number, `watermark` with `watermark_bool`, `subtitle` as a list, `publish_date` as null, optional `duration` and `thumbnail`, and the `isNext`/`jsonType`/`domain`/`currentUrl`/ `isRedesign` headers. The uploader form also adds `upload_base_url`, `uploadId`, and `video` right before it submits, and the endpoint answers 400 (`upload_base_url: ...`) without them, so the client carries them too. - The upload server is read from the nested `data.attributes` object Aparat returns; the flat `data.server` key the code read before does not exist, so an upload stopped right after `upload_config`. A 3 s private upload now runs end to end against the live service with the stored session. - `request_multipart` and `request_multipart_many` return the `(status, body)` pair their callers unpack again; they leaked the internal `(status, body, headers)` triple, so every Aparat chunk and every Telegram file send (photo, video, document) failed with "too many values to unpack". - A refused chunk is uploaded again up to three times with a 1 s, 2 s, 4 s backoff, the `chunksdone` close of the upload is retried the same way, and a `4xx` rejection fails on the first answer. `AparatClient.publish` takes a duration and a thumbnail (image bytes or a `data:image/...` URL), the Aparat channel forwards both from the draft `meta`, and the uploader logs its steps (file, size, chunk, retry, metadata answer) for debugging. Content Bot 0.4.2 carries the change; no new environment key is required. ### Fixes — Buffered chat clients and streaming upstreams (2026-09-14) - A Chat Completions request that omits `stream` is now forwarded with an explicit `stream: false` instead of relying on the upstream default, and a `text/event-stream` body that an upstream returns for a buffered request is collapsed into one `chat.completion` JSON body. Internal callers that post to the router's own `/v1/chat/completions` (the Orchestrator planner, executor, reviewer, and team synthesis) no longer fail with "agent returned no completion" when the gateway streams by default. Streaming clients are unchanged. - Smart Router release: `afsharidevops/hermes-smart-router:0.6.1`. ### Features — Multi-agent Orchestrator and the LocalLab router console (2026-09-14) - The Operations Center gained an **Orchestrator** page and the matching `/api/orchestrations` API. A planner turns one operator task into a validated machine-readable plan, the supervisor executes the steps through the registered agents, a sensitive step pauses the run in `awaiting_approval` until an operator approves or rejects it, and a reviewer pass records a verdict with an optional rollback suggestion. The planner reuses the recent completed runs as history, so repeating a task stays consistent with what was done before. Runs and steps persist in the existing Operations DB (`v60_agent_runs`, `v60_agent_run_steps`); the compatibility filename `control-v0.5.2.sqlite3` is unchanged and the in-place schema marker moves to `0.6.0`. - Approval gating is deterministic: the planner flag, a dangerous-operation pattern (`kubectl delete`/`drain`, `terraform destroy`/`apply`, `iptables`, `DROP TABLE`/`DATABASE`, `TRUNCATE`, `docker rm`/`rmi`/`volume rm`/`system prune`, `systemctl stop`/`disable`/`mask`, `mkfs`, `dd if=`, `chmod 777`, `fdisk`, `userdel`, `kill -9`, `git push --force`, `DELETE FROM`, `shutdown`/`reboot`), or a step tool that is registered as high risk or missing from the plugin registry. Tool names stay declarative: the run record and the audit log show the intent, and execution remains with the Execution Broker. - `SMART_ROUTER_ORCHESTRATOR_APPROVAL_MODE` (`auto`), `SMART_ROUTER_ORCHESTRATOR_PLANNER_TIER` (`standard`), and `SMART_ROUTER_ORCHESTRATOR_REVIEWER_TIER` (`strong`) control the flow in `.env` and in `deploy/helm/hermes-linux-stack`. See [docs/ORCHESTRATION.md](docs/ORCHESTRATION.md). - The Smart Router Operations Center now shares the Content Console design language: the LocalLab gradient palette, glass surfaces, gradient accents, and the LocalLab mark on the login and sidebar brand. - Smart Router release: `afsharidevops/hermes-smart-router:0.6.0`. ### Features — Public service links in the operator console (2026-09-13) - The console **Overview -> Endpoints** card now links every publishable service through the public origin recorded in `.env` (`PANEL_PUBLIC_URL`, `SMART_ROUTER_PUBLIC_URL`, `MEDIA_STUDIO_PUBLIC_URL`, `N8N_PUBLIC_URL`, `INSTAGRAM_MEDIA_PUBLIC_BASE_URL`, `S3_PUBLIC_BASE_URL`, `S3_PUBLIC_CONSOLE_URL`) and falls back to the LAN bind address when a service has no public URL yet, so one console works from the LAN and from outside it. Loopback placeholders such as `NINEROUTER_PUBLIC_BASE_URL= http://localhost:20128` count as "not published" and keep the bind fallback. The object-storage view resolves `S3_PUBLIC_BASE_URL` and `S3_PUBLIC_CONSOLE_URL` the same way, the row for the active router backend follows `COMPOSE_PROFILES`, and the Environment editor gained **Add key** so a key that is not in `.env` yet can be created from the browser. - `./manage.sh domains` (also in the panel menu) prints one block per service: the recorded or suggested host name derived from `STACK_BASE_DOMAIN`, the `.env` key and value to record, the LAN `bind:port` target, a warning when the bind is still loopback, and the ready-to-paste Caddy block - including the GET-only root redirect the RustFS console needs. - `install.sh` records `PANEL_PUBLIC_URL` when the operator publishes the panel, points at `SMART_ROUTER_PUBLIC_URL` and `MEDIA_STUDIO_PUBLIC_URL` when a base domain is known, and prints the `./manage.sh domains` checklist in the final summary. Panel tests grew from 93 to 96. ### Features — Platforms view in the operator console (2026-09-13) - The console gained a **Platforms** section: one card per publishing platform (Telegram, Bale, Eitaa, Instagram, the AI writer, Media Studio, the platform chooser, and the package-only YouTube/Aparat/LinkedIn entries). Each card lists the bot token, the channel or business id, and the API **base URL** that client dials, marks the required fields, and reports the platform state (`ready`, `incomplete`, `not configured`, `package handover`, or `on`/`off` for the chooser switch) without ever returning a stored secret to the browser. - **Test connection** asks the provider with the values currently in the form, so a token can be validated before it is saved: `getMe` for Telegram, Bale, and Eitaa, the Graph node lookup for Instagram, `/v1/models` with a `/health` fallback for the writer, and `/healthz` for Media Studio. **Save** writes only the edited `.env` lines through the line-preserving editor, and **Apply changes** recreates the containers so `content-bot` reads them. - `panel/platforms.py` holds the registry, the validation, and the tests; `EnvStore` gained `value()`/`is_secret()` for single-key reads. Panel tests grew from 73 to 93. ### Features — manage.sh channel setup for Bale and Eitaa (2026-09-13) - `./manage.sh content-connect-bale` and `./manage.sh content-connect-eitaa` store the token, the destination, and the API base in `.env`, optionally check the token with `getMe` (`--verify`), publish one test message through the running adapter (`--test`), and recreate `content-bot` so the draft chooser flips from `(no token)` to `(auto)` in one command. Running either command without flags prompts for the values. - `./manage.sh content-channels` reports the state of both channels without printing a token, and `content-status` prints the same lines. `manage.sh content` gained the matching menu entries. ### Docs — Bale and Eitaa setup guide (2026-09-13) - `docs/BALE-EITAA-SETUP.md` walks through creating the bot with Bale's BotFather, preparing the channel, verifying the token with `getMe` and `sendMessage` before touching the stack, the EitaaYar equivalent, the `.env` keys, and the failure modes worth knowing (wrong token, wrong channel id, a bot that is not an administrator, oversized media). ### Features — Bale and Eitaa publish automatically (2026-09-13) - The per-draft platform chooser is no longer package-only. A platform whose profile runs in `auto` mode is published the moment the operator picks it, caption and media included: `Bale` uses the Telegram-shaped Bot API (`https://tapi.bale.ai`) and `Eitaa` uses the EitaaYar gateway (`https://eitaayar.ir/api`), each configured with a token plus a destination chat id. Instagram keeps the manual package, and the package platforms (YouTube, Aparat, LinkedIn) are unchanged. - Channels that are still missing a token are marked `(no token)` in the chooser and fall back to the package, and every automatic publish records its target on the draft so a post can never be sent to the same channel twice. ### Features — Per-post upload packages for LinkedIn, YouTube, and Aparat (2026-09-13) - Every enabled manual platform is offered per draft under **More platforms...**, so a post that suits LinkedIn or Shorts can be handed over without touching the others. LinkedIn joins the built-in profiles with its own title and description limits, and all of them stay overridable in the `platforms:` section of `editorial-policy.yaml`. - Published drafts are archived in a small bounded copy, and the post-publish message keeps the **More platforms...** button, so a package can still be requested after the post went out instead of only before the approval. ### Fixes — Instagram manual mode hands over the post package (2026-09-13) - `INSTAGRAM_AUTO_PUBLISH=false` used to switch Instagram off instead of switching it to manual: the Telegram buttons disappeared, and approving a draft with Instagram as a target was refused outright, so the operator had no way to collect a copy-ready package from the chat. The approval path now treats the Instagram half as a hand-off: **Approve to Instagram** sends the package, **Approve to Telegram + Instagram** publishes to the channel and sends the package, and approving a media draft to Telegram alone sends the package with the same step. Drafts carry an **Instagram package** button whenever automatic publishing is off, and the console actions behave the same way instead of reporting "skipped". ### Fixes — Smart Router image publishes on its own again (2026-09-12) - `.github/workflows/publish-smart-router.yml` only ran on `workflow_dispatch`, so Smart Router changes merged to `main` never reached Docker Hub and deployments kept pulling a stale image. The workflow now also triggers on pushes that touch `smart-router/**` or itself, and it derives the image version from `smart-router/pyproject.toml` when no manual version is given, so the published tags can no longer drift from the package version. A manual run still accepts an explicit version and rejects a value that does not match the package version. ### Features — Codex and Claude Code client protocols (2026-09-12) - `POST /v1/responses` now speaks the Responses API properly instead of flattening it to plain chat: instructions become the system message, `function_call` / `function_call_output` history round-trips as assistant tool calls and tool results, tool definitions keep their JSON schema, and the stream emits `response.created` / `output_item.added` / `output_text.delta` / `function_call_arguments.delta` / `output_item.done` / `response.completed` with `sequence_number` and Responses-shaped usage. A broken upstream stream ends with `response.failed` rather than a truncated `response.completed`. Codex connects with `wire_api = "responses"` and runs its tool loop through the router. - New `POST /v1/messages` implements the Anthropic Messages API for Claude Code: system blocks, text/image/tool_use/tool_result content, tools and `tool_choice`, streaming `message_start` … `message_stop` events with `text_delta` and `input_json_delta` blocks, and Anthropic usage reporting. `POST /v1/messages/count_tokens` returns a local estimate, and `GET /v1/models` answers in the Anthropic shape for clients that ask with `anthropic-version` or `x-api-key`. - `examples/clients/` ships a ready-to-copy Codex `config.toml`, a Claude Codex environment file, and a verification checklist. - `/v1/chat/completions`, `/v1/responses`, and `/v1/messages` share one routing path, so tiering, budget enforcement, sticky sessions, and telemetry apply to every protocol. `docs/SMART-ROUTER-CLIENT-API.md` documents both client configurations end to end. ### Fixes — panel config edits no longer lock the bot out (2026-09-12) - `ConfigStore.write` staged edits in a `0600` temporary file and kept that mode after `os.replace`, so editing `editorial-policy.yaml` in the panel made it unreadable for the bot container, which runs under a different uid and crashed every maintenance cycle with `PermissionError`. Configuration files are now written world readable (`0644`, existing bits preserved), matching the shipped defaults and the read-only mounts of the bot, router, and n8n containers. ### Features — Instagram manual mode and the post package (2026-09-12) - `INSTAGRAM_AUTO_PUBLISH` (default `true`) decides whether Instagram publishes through the Graph API. With `false` the two Instagram approval buttons disappear from the Telegram keyboards and the operator console, and the queued `publish_ig` / `publish_both` actions answer with a skip message that names the switch. Credentials, the media host, and the token refresh are untouched, so setting it back to `true` restores the API path. - The operator console gains a **Post package** action for every publishable draft. The bot re-sends the stored media as an untouched Telegram document and follows it with the exact Instagram caption inside a `
` block, which Telegram renders with a copy button, so a manual post needs no text selection. Text-only drafts receive the caption package alone. - `/api/instagram` reports the new switch as `auto_publish`, and the Overview card shows `auto publish on` or `manual package` next to the token state. - `.env.example` and `docs/INSTAGRAM-SETUP.md` document `INSTAGRAM_AUTO_PUBLISH` and `INSTAGRAM_DISABLE_REFRESH`, including the manual checklist for posting from the Instagram app. - `content-bot` adds `TelegramApi.send_document` for full-quality file hand-offs, and `/status` prints the Instagram automatic-publishing state. ### Fixes — RustFS console route behind a reverse proxy (2026-09-11) - The console signs in with a signed `POST /` (`Action=AssumeRole`) against the host that serves it. A router route that redirects `/` to `/rustfs/console/` for every method answers that POST with a 302 and login fails without a useful message. The installer wizard and `./manage.sh s3-guide` now print a route that keeps the redirect for `GET` only, and `docs/S3-STORAGE.md` explains the requirement plus the `Host` header rule behind signed requests. - The installer asks directly whether the console should be published, derives `S3_PUBLIC_CONSOLE_URL` from the host name it receives, and prints the matching Caddy block, so the console URL recorded in `.env` always matches the published route. - `install.sh` asks once for the zone the published services live in and stores it as `STACK_BASE_DOMAIN` (for example `stack.example.com`). The panel, the Instagram media host, the S3 API and the console are then suggested as `. `, and `./manage.sh s3-guide` prints the same names, preferring the public origins already recorded in `.env`. - Examples, installer defaults, fixtures, and the Firefox extension id use the `example.com` placeholder zone instead of the operator's own domain, so a public checkout never names the real zone. ### Fixes — Content Bot startup resilience (2026-09-11) - The first Telegram call of the Content Bot runs while the container starts, which can race a resolver or an outbound route that is not ready yet. A single `Temporary failure in name resolution` there used to end the process, leaving the recovery to the container restart policy and printing a full traceback. Startup now retries five times with a growing delay and logs one warning per attempt, while a rejected bot token still fails immediately. ```text WARNING content_bot: Telegram unreachable at startup (attempt 1/5): [Errno -3] Temporary failure in name resolution; retrying in 3s ``` - `docs/CONTENT-PRODUCTION-GUIDE.md` documents both the startup retries and the `Telegram connection problem, retrying` warning, including the host resolver check to run when the message repeats. - All Telegram API methods now convert transient connection errors to ``TelegramNetworkError`` (a subclass of ``TelegramError``), so existing ``except TelegramError`` guards in ``_startup`` (``delete_webhook``, ``set_my_commands``) no longer crash when the outbound route stalls between container startup and the first API call. The startup retry logic in ``_startup_identity`` distinguishes ``TelegramNetworkError`` from a rejected token and retries the former, while a bad token still fails immediately. ## Content Manager — fork of Hermes Linux Stack v0.5.9 (2026-09-07) This repository is **Content Manager**: a fork of the Hermes Linux Stack v0.5.9 platform (upstream unchanged) extended with a deterministic daily content-production layer. The upstream changelog below documents the inherited platform; this section tracks the fork additions. ### Fork release — Content Manager v0.4.1 (2026-09-14) - Added Aparat as an automatic video channel (`content_bot/aparat.py`, `AparatChannel`): the bot reserves an upload slot, sends the clip in chunks, and submits the title, description, tags, and category through the Aparat upload API of a stored browser session (`CONTENT_APARAT_TOKEN` or `CONTENT_APARAT_COOKIE`). Aparat is on demand and video-only: the daily proposals and the scheduled routines never publish to it, the chooser marks the entry `Aparat (needs a video)` until a clip is attached, **All targets** skips it without one, and picking it without a video asks for the file and uploads it as soon as it arrives. Text and photo drafts keep the copy-ready package. `docs/APARAT-SETUP.md` covers the session copy step, the category ids, and the failure modes; the panel gained an Aparat card with a session test, and `./manage.sh content-aparat-check` probes the stored session without uploading anything. - Scheduled proposals of the Content Bot now carry the same media question as an on-demand draft, each routine selects its own `media` mode (`none`, `auto`, or `ask`), and the built-in daily pass can be switched off with `pipeline.daily_enabled: false` when the routines own the cadence. The default stays enabled, so existing deployments are unchanged. - Added `docs/SMART-ROUTER-USER-GUIDE.md`, the complete operator guide for the Smart Router console, the OpenAI-compatible API, model and alias selection, routing profiles, the Orchestrator, and the client integrations. - A stored Aparat session that is really the text `undefined` or `null` - what the browser console hands back when `copy(...)` is used without reading the value - or that still carries its printed quotes, is discarded instead of failing with a puzzling `401` later. `docs/APARAT-SETUP.md` explains the console answers, and the card test names the placeholder it found. - An image job that fails because the endpoint has no images API now says so: the Media Studio driver names `MEDIA_STUDIO_WRITER_BASE_URL`, the panel's Media Studio test probes the image endpoint before an operator presses **AI image**, `./manage.sh media-status` warns when the endpoint is the stack chat gateway, and `docs/MEDIA-STUDIO.md` gained a troubleshooting table (the Smart Router serves chat completions only, so an image model and an image-capable provider are required). - Added the Media Studio `api-video` driver: it submits `POST /videos/generations`, polls `GET /videos/ `, downloads the finished clip, and bakes in the brand chip, so a deployment without a Google session can answer the video button of the Content Bot through a video-capable provider of the gateway (`xai/grok-imagine-video`, `openrouter/google/veo-3.1`, `vertex/veo-3.1-fast-generate-preview`). A chat combo such as `ai` or `ai-strong` is rejected by the gateway with "Combos are not supported for video generation", and both the driver and the panel test say so instead of failing silently. New keys: `MEDIA_STUDIO_VIDEO_BASE_URL`, `MEDIA_STUDIO_VIDEO_API_KEY`, `MEDIA_STUDIO_VIDEO_MODEL`, `MEDIA_STUDIO_VIDEO_PROVIDER`, `MEDIA_STUDIO_VIDEO_DURATION`, `MEDIA_STUDIO_VIDEO_ASPECT_RATIO`, `MEDIA_STUDIO_VIDEO_RESOLUTION`, `MEDIA_STUDIO_VIDEO_POLL_SECONDS`, `MEDIA_STUDIO_VIDEO_TIMEOUT_SECONDS`; the video endpoint and key fall back to the writer values, and `MEDIA_STUDIO_DRIVERS` now ships `api-image,api-video,flow-video,video-edit`. - Content Bot 0.4.1, operator panel 0.4.1, and Media Studio 0.4.0. The Helm chart moves to 0.6.4 with the same image tags. ### Fork release — Content Manager v0.4.0 (2026-09-14) - Added LinkedIn as an automatic channel (`content_bot/linkedin.py`): posts are created through `POST /rest/posts`, an attached image goes through `initializeUpload` plus a binary `PUT`, transient failures (`429`/`5xx`) are retried with backoff, and the created post URN is kept as the publication's `remote_id`. Each configured account - a personal profile or a company page - is its own channel on the draft. - Social accounts now live in `social-accounts.yaml` inside the policy directory (`CONTENT_SOCIAL_ACCOUNTS_FILE`), with the `CONTENT_LINKEDIN_*` environment block as the single-account fallback; tokens are never hardcoded, and an account without one is skipped with a log line. - Added the per-destination content adapter (`content_bot/adapt.py`): the draft body is rewritten for each target according to a tone profile (`linkedin_personal`, `linkedin_company`, `telegram`), profiles are overridable in the `tones:` section of `editorial-policy.yaml`, the rewrite prompt lives in `prompt-templates/ .md`, and any failure - or `CONTENT_ADAPT_ENABLED=false` - publishes the untouched draft text. Only the body is adapted, so title, source link, and captions keep the existing behavior. - Added multi-target publishing with a publication ledger: drafts may carry `targets:`, `publishing.targets` supplies defaults, **All targets** reaches every automatic channel, and every attempt is a row (`id`, `content_id`, `platform`, `account`, `status`, `remote_id`, `published_at`, `error`) in `data/content-bot/state.json`. One destination failing never blocks the others, and the adapted text is cached so a retry never re-runs the writer. - The operator console gained a LinkedIn card (token, type, author URN or person/organization id, API base and version) whose **Test connection** reserves one image upload slot to validate the token and the author without publishing. `./manage.sh content-connect-linkedin` stores the same values, and `content-channels`/`content-status` report the account state. - Added `docs/LINKEDIN-SETUP.md` (app, scopes, three-legged OAuth, author id, account file, tones, targets, publish flow, token rotation, troubleshooting) and extended the production guide and panel documentation. Content Bot 0.4.0 and operator panel 0.4.0; Media Studio stays 0.3.0. ### Fork release — Content Manager v0.3.0 (2026-09-11) - Added `panel/`: an optional operator console (Compose profile `panel`) for stack status, validated YAML/JSON editing with backups, `.env` edits, service logs, and a fixed whitelist of compose actions. It stays bound to loopback, authenticates with a token stored outside the repository, and never returns a secret to the browser. - The panel lists the live drafts and applies the same decisions as the Telegram keyboard (text only, AI image, publish, discard). The bot keeps the authoritative `state.json`, so the panel appends validated requests to `data/content-bot/panel-actions.requests.jsonl` and the bot drains them from its main loop, reporting each outcome in the console and in Telegram. - Added the shared tool registry (`content/config/tools.json`) and scheduled per-platform routines (research, draft, media, queue) driven by the policy cadence, plus the "edit or publish as-is" choice for operator-recorded clips. - The Instagram long-lived token now refreshes itself: the bot extends it weekly, stores the result in `data/content-bot/instagram-token.json`, keeps publishing with the previous token when the Graph API refuses, and notifies the operator once instead of failing quietly. `/instagram` and the console card show the remaining days and can force a refresh. - The brand mark is now the LocalLab gradient pill (cyan to blue to violet to magenta over navy) with a halo, highlight sweep, hairline border, and hexagon mark, rendered with Pillow only. `MEDIA_STUDIO_BRAND_STYLE=chip` restores the previous flat chip, and the `video-edit` driver bakes the same mark into prepared clips through ffmpeg. - Added the optional `ig-media` compose profile: nginx serves `data/content-bot/media` read-only on loopback while a cloudflared sidecar publishes it, so the stack provides its own public media host instead of depending on a hand-started process. `./manage.sh instagram-media-enable`, `-status`, and `-disable` manage it, and the bot resolves the public base URL from a pinned `media-base-url.txt`, a non-quick-tunnel `INSTAGRAM_MEDIA_PUBLIC_BASE_URL`, or the live tunnel log, rebuilding the Graph client when the hostname changes. Setting `IG_MEDIA_TUNNEL_TOKEN` switches the same profile set to a named Cloudflare tunnel, which keeps one permanent hostname on a domain you own instead of a random quick tunnel, and the quick tunnel now lives in its own `ig-media-quick` profile so nginx can stay up behind a reverse proxy (`./manage.sh instagram-media-tunnel-off`). Installing the content stack on a server can publish the media directory on a Caddy domain straight from the wizard, which needs no tunnel at all. - Documented quick-tunnel versus stable media URLs, since a tunnel hostname that changes on restart breaks Instagram publishing with a confusing media-processing error. - Section backups: `./manage.sh backup --only SECTION[,...]` archives only the named part of the stack (`env`, `secrets`, `hermes`, `router`, `content`, `media`, `panel`, `n8n`, `openwebui`, `s3`, `caddy`, `execution`, `state`, `compose`). Archives carry a manifest and a `.meta.json` sidecar with the section list, so `backup-list` shows what each archive contains and `restore` knows it must copy those paths without replacing the rest of the stack. `./manage.sh backup-sections` lists the names, and the maintenance menu gained a "create a section backup" entry. - Restores are portable across servers: when an archive records a different checkout root, absolute `*_HOST_PATH`/`*_STACK_PATH` values in the restored `.env` are rewritten to the current checkout (`--no-relocate` disables it). Full archives still replace `.env` and `data/`; partial archives merge, keep a pre-restore safety backup, and put the previous state back when validation or readiness fails. - `install.sh` now asks whether a reverse proxy on another host publishes the operator panel and suggests the detected LAN IPv4 address for the bind, which is exactly what a router-side reverse proxy reaches; the final summary prints the resulting URL and the proxy target. The same wizard already suggested the LAN address for the Instagram media host, and the Helm chart documentation repeats the pattern for cluster ingress. - Added `tests/test-stack-ops.sh` section coverage (partial backup, partial restore, path relocation, `backup-list` metadata) and wired the file into the Validate workflow, which had not been running it. - Helm chart review: the chart did not render at all (the escaped quotes in the router PostgreSQL and Redis URLs were an invalid template), fixed now. The chart gained an optional in-cluster 9router/OmniRoute upstream, optional Content Bot, operator panel, Media Studio, and media file server components, a generic ingress (with `ingress.tls: false` for an external reverse proxy), image pull secrets, and `helm lint`/render coverage in `tests/test-helm-chart.sh`. `examples/helm/content-stack-values.yaml` enables the whole stack as an overlay, and `docs/HELM.md` documents the values, component secrets, publishing the chart (GHCR OCI, GitHub Pages, ChartMuseum, other OCI registries, Artifact Hub), and the free registry options. `.github/workflows/publish-helm-chart.yml` and `scripts/helm-publish.sh` publish the packaged chart on release tags. - Added `docs/BACKUP-RESTORE.md`: sections, full versus partial restores, the cross-server procedure, what to re-check after a restore, and why the single-host Compose stack does not need PostgreSQL (PostgreSQL and Redis exist for the multi-replica Kubernetes deployment). - Object storage: the stack now has one shared S3 block (`S3_STORAGE_BACKEND=rustfs|external|off` plus the `S3_*` endpoint, bucket, region, and credential variables) that every consumer reads. The optional `rustfs` Compose profile runs RustFS - an Apache-2.0 S3-compatible server - in-stack with its S3 API on 9000, its console on 9001 under `/rustfs/console`, data under `data/rustfs/`, and an unprivileged identity pinned to the operator (root-managed installs keep 10001:10001). Point any other provider (AWS S3, Cloudflare R2, Backblaze B2, MinIO, Arvan S3, ...) at the same block with `./manage.sh s3-enable --external`. Open WebUI consumes it natively (`STORAGE_PROVIDER=s3` with path-style addressing); the Content Bot, Media Studio, n8n free tier, and Hermes Agent keep their local storage and the supported matrix is documented rather than implied. `./manage.sh s3-status`, `s3-enable [--rustfs|--external] [--bind-ip IP]`, `s3-disable`, `s3-verify [--create-bucket]` (signed requests, no cloud CLI), `s3-keys [--show-secrets|--rotate]`, `s3-guide`, a storage menu group, `logs rustfs`, and the `s3` backup section cover day-two operations. `install.sh` asks for the backend, generates or preserves the credentials, suggests the LAN address when a reverse proxy on another host publishes the API or console, and records the public origins. The Helm chart gained an optional `components.rustfs` (PVC, Deployment, Services for the API and console) and the ingress can publish it. `docs/S3-STORAGE.md` documents the consumer matrix, the configuration reference, the ArvanCloud-to-Caddy route, the security notes, and troubleshooting; `data/rustfs` is gitignored and `tests/test-s3-storage.sh` covers the configuration, the Compose rendering, and the manage.sh commands. - Install-time public-route checklists: every wizard step that needs a domain now prints the two manual steps together where the decision is made (DNS A record to the router public IP plus the Caddy site block with the exact proxy target), the operator panel stores `PANEL_COOKIE_SECURE=true` when a public hostname is given, the Instagram media host records `INSTAGRAM_MEDIA_PUBLIC_BASE_URL` from the answer, re-runs that keep an existing bind address repeat the route reminder, and the final summary lists the DNS records to create for every selected hostname. - The operator panel gained **Object storage** and **Backups** views, so the two features that only existed on the command line are now visible where the rest of the stack is managed. Object storage reports the shared `S3_*` block, the per-service consumer matrix, and the bundled RustFS state, with **Run status** and **Verify endpoint** buttons for `s3-status` and `s3-verify` (the verify runs against the in-network endpoint the containers use, not the host loopback value). Backups lists the archives with their `.meta.json` metadata - creation time, full or partial, section list, stack version - and **Create backup** runs `manage.sh backup --label panel --no-pause` after a confirmation, and a **Partial backup** picker whose section names come from `./manage.sh backup-sections`, so `backup --only SECTION[,...]` is available from the console as well (the action takes the section list as a validated parameter, and the panel builds a command only for names the CLI publishes). Because the panel runs as the operator uid and cannot read every data directory, that action starts a throwaway container from the panel image with the Docker socket, checkout, and backup directory mounted, runs the same `manage.sh backup` inside it, and hands the `hermes-stack-*` files back to the backup directory owner (stack-ops writes them as `0600` because they carry `.env` secrets). `panel-enable` now also creates the ` -backups` directory and mounts it, the panel image installs GNU `tar` for the archive step, `manage.sh s3-verify` accepts an endpoint override so a caller inside the stack network can test the in-network endpoint, and `docs/PANEL.md` documents both views. - The panel's **Media jobs** card now dials the Compose service address (`media-studio: `) instead of the published host bind, which a process inside the stack network cannot reach; `MEDIA_STUDIO_INTERNAL_URL` overrides the derived address. - The README opens with five operator-console screens captured from the panel image against a seeded demo stack (`docs-site/assets/content-console-*-v0.3.0.png`): stack status, pipeline state, object storage, backups, and the action whitelist. - Release pipeline fix: the Helm publish workflow built its GHCR destination from `github.repository_owner` (`Afsharidevops`), and the OCI distribution spec requires a lowercase repository path, so the release tag failed with `invalid_reference: invalid repository`. The workflow now lowercases the owner before `helm push` and refuses an uppercase reference outright, and `scripts/helm-publish.sh` lowercases `--registry` the same way. `tests/test-helm-chart.sh` covers both paths. The workflow also attaches the packaged chart to the GitHub release for the tag, which is what `docs/HELM.md` already described, and skips the attachment when the tag has no release. ### Fork release — Content Manager v0.2.0 (2026-09-09) - Added `media-studio/`: a single-worker media job service with API image generation through the selected writer gateway and optional Google Flow/Gemini drivers driven over CDP. The `locallab-flow-unlock` extension keeps `flow.google.com` usable on a laptop without stack components, and `MEDIA_STUDIO_BLOCK_GEO_REDIRECT` / `MEDIA_STUDIO_FREEZE_ON_READY` keep a container profile usable where Flow enforces the unsupported-country redirect. Flow credit prompts are auto-approved and the download quality selector is calibrated automatically. - The Content Bot accepts topics as well as links: a topic is searched and drafted by the writer, and proposals can attach AI-generated or operator-supplied images plus a video prompt flow through Media Studio. - Unified media publishing posts the draft and its media together with caption continuation when the post exceeds one Telegram message, keeps the title bold and RTL-safe, appends the source link once at the end, and repairs mojibake writer replies. - A configurable corner brand chip (`MEDIA_STUDIO_BRAND_TEXT`) is stamped on AI-generated and operator-uploaded images before approval. - `main` now supports both router backends (`9router` and `omniroute`) as Compose profiles with install-time selection and backend switching; the `hermes-omniroute-linux-stack` branch is obsolete. Installer options 5/6/7 keep the standalone and combined content pipeline router-free. - Component images advance to `0.2.0`: `afsharidevops/content-bot:0.2.0` and `afsharidevops/media-studio:0.2.0` (plus `:latest`). ### Features — scheduled per-platform routines (2026-09-10) - `editorial-policy.yaml` gains an optional `routines:` list. Each enabled routine runs one research -> draft -> media -> queue pass per cadence period (daily at `time`, or weekly on or after `weekday`), drafts up to `count` of the ranked candidates, and drops them into the Telegram approval queue as "Scheduled proposal" drafts. - `media: auto` submits one AI image per queued draft through Media Studio and attaches it when the job finishes; `media: none` queues text only. Drafts are still queued when Media Studio is not configured, and a failed image job is reported to the operator (scheduled drafts have no media question). - Run markers live in `data/content-bot/state.json` under `routine_last_run`, so a restart never repeats a period; `/status` and `/help` report the active routines. Routines share the discovery feeds, dedupe/filter/scoring rules, and the category-mix rule with the daily proposal; the shipped default is `routines: []` and the daily flow is unchanged. ### Features — operator panel for the stack (2026-09-10) - Added `panel/`: an optional operator console served by the new `content-panel` container (Compose profile `panel`). It reads stack status, health, image tags, published host ports, `data/` disk usage, pipeline counters, drafts, and Media Studio jobs, and warns when an internal endpoint such as n8n/MCP is published beyond loopback. - The console edits the four managed configuration files through validated editors that keep a timestamped backup before every atomic write and can restore a previous version. `.env` keys are editable in place with secret values masked, and state-changing requests require a CSRF header on top of the HMAC-derived HttpOnly session cookie. - Actions are a fixed whitelist (apply changes, restart Content Bot / Media Studio / Smart Router, pull published images, status summaries, doctor) and never accept a free-form command; `PANEL_ACTIONS_ENABLED=false` serves read-only views. - The panel is a separate container on purpose: the Content Bot image keeps its read-only root filesystem and unprivileged uid, while the panel mounts the Docker socket and writes repository files as the stack owner. It is bound to loopback by default, is enabled or disabled with `./manage.sh panel-enable` / `panel-disable`, and stores its operator token in `data/panel/token`. - `.env.example`, `docker-compose.yml`, `install.sh`, and `manage.sh` carry the new `PANEL_*` settings, the panel menu, and the `panel-*` commands; `.github/workflows/publish-panel.yml` publishes the image and `docs/PANEL.md` documents the security model, remote-access guidance, and the configuration reference. ### Features — shared tool registry for the bot, router, and n8n (2026-09-10) - `content/config/tools.json` is the single source of truth for the remote tools the content stack calls (MCP servers, OpenAPI services, plain HTTP endpoints). Each entry lists the kind, address, capabilities, allowed consumers, and the *name* of the environment variable that holds the credential; secrets are never stored in the file, and `${ENV_NAME}` placeholders are substituted by each runtime, so one registry works for a combined server and for a split deployment. - The Content Bot loads the registry from its policy directory and exposes `/tools` (entries for the bot with credential state and warnings) plus a one-line summary in `/status`. A missing or invalid file never stops the bot. - The Smart Router serves `GET /v1/tools` behind the same client auth as `/v1/models`, read from `SMART_ROUTER_TOOLS_REGISTRY`; a broken file answers with `tools_registry_invalid` and HTTP 500. - The n8n bootstrap reads the same file (mounted at `/tools` by `./manage.sh bootstrap-n8n` or the `N8N_TOOLS_REGISTRY` override) and adds a tool summary with the count of missing credential variables to its reconcile result. - Media Studio publishes `GET /openapi.json` (no token, no secrets) so the registry entry can point at a live spec, including the `video-edit` driver and `POST /uploads`. - `docs/TOOL-REGISTRY.md` documents the schema, the per-consumer wiring, and how to add a tool; `.env.example`, `docker-compose.yml`, `install.sh`, and the CI validation workflow ship the new file and settings. ### Features — edit or publish an operator-sent video (2026-09-10) - When the operator sends a recorded clip to the bot, the media question now asks **Edit it** or **Publish as-is** instead of attaching the file straight away. **Publish as-is** keeps the recording untouched; **Edit it** uploads the clip to Media Studio and runs the new offline `video-edit` driver, which re-encodes it to an MP4 with H.264/AAC and faststart, caps the long side at 1920 px with the aspect ratio preserved, fixes rotation, strips container metadata, and can trim with `MEDIA_STUDIO_VIDEO_EDIT_MAX_SECONDS`. - Media Studio gained `POST /uploads` (raw video bytes in, upload id out), the `video-edit` driver and registry entry, and ffmpeg resolution that prefers `MEDIA_STUDIO_FFMPEG`, then a system binary, then the `imageio-ffmpeg` wheel, so the image needs no extra system package. Stored uploads are pruned after `MEDIA_STUDIO_UPLOAD_TTL_SECONDS`. - A failed edit job never loses the recording: the original file is restored as the draft media, the bot explains the failure, and **Approve** publishes the clip unchanged. Clips above the 20 MB Bot API limit stay on Telegram and skip the question because they cannot be copied into Media Studio. - `CONTENT_MEDIA_VIDEO_EDIT_DRIVER`, the `MEDIA_STUDIO_VIDEO_EDIT_*` set, `MEDIA_STUDIO_UPLOAD_TTL_SECONDS`, and `MEDIA_STUDIO_FFMPEG` are documented in `.env.example`, `docker-compose.yml`, and `docs/MEDIA-STUDIO.md`. ### Changes — manual platform packages are opt-in (2026-09-10) - The copy-ready upload packages (YouTube, Aparat) and their **More platforms...** button are hidden unless `CONTENT_PLATFORMS_ENABLED=true`, so the live bot stays on Telegram and Instagram only. The packages, the `platforms:` policy section, and their callbacks are unchanged behind the switch; a callback that arrives while it is off answers with the setting name instead of sending a package. - `.env.example`, `docker-compose.yml`, and `docs/CONTENT-PRODUCTION-GUIDE.md` document the switch; the shipped default is off. ### Features — Firefox build and ZIP packages for Flow Unlock (2026-09-10) - `extensions/locallab-flow-unlock-firefox/` is the Flow unlock for Firefox 140+ as an independent package: the same page-world response patch and freeze guard as the Chrome extension, with a Firefox MV3 manifest (event page instead of a service worker) and a blocking `webRequest` listener that sends a navigation on the unsupported-country route back to the app root of the same account and cancels other resources on that route, replacing the `declarativeNetRequest` rule set. The package lints clean for AMO. - `extensions/package-flow-unlock.sh` builds `locallab-flow-unlock-chrome- .zip` and `locallab-flow-unlock-firefox- .zip` with a per-browser README, so a machine can install the unlock without the repository layout. Builds use fixed timestamps and are byte-identical for unchanged sources. - `tests/test-flow-unlock-rules.sh` now validates both manifests and keeps the shared scripts byte-identical across the two packages; `tests/test-flow-unlock-package.sh` builds the ZIPs on every CI run and checks their layout. ### Fixes — oversized video uploads stay on Telegram (2026-09-10) - A video above the 20 MB Bot API download limit no longer fails with `Could not download the file: Telegram getFile HTTP 400`. The bot keeps the Telegram `file_id` instead of the bytes, re-sends the clip as the media preview, and publishes it to the channel by id, so a reel created in Flow or on a phone can be attached without re-encoding it. - Files kept on Telegram live there only: Instagram approvals and the platform upload packages need the file itself and now say so, asking for a copy under 20 MB instead of failing silently or shipping a package without media. - Bot API failures include Telegram's own description (for example the `getFile` limit message) instead of only the HTTP status. ### Improvements — friendlier copy and RTL-safe captions (2026-09-10) - The writer prompt now asks for a warm, conversational, second-person voice with short everyday sentences and explicitly rules out formal, official, and news-desk phrasing, so drafts read like a note from a friend. - Instagram captions start with an invisible right-to-left mark and mark every Latin-led paragraph. Instagram renders the account name inline before the caption, and without the mark a Latin account name flips the first paragraph to LTR and scrambles the Persian title. Telegram and Instagram now share the same bidi helpers in `content_bot/rtl.py`. - Writer titles keep the English product name inside the sentence; the prompt already required a Persian opening word and now enforces it together with the friendlier voice. ### Improvements — segmented video prompt package and character choice (2026-09-10) - "My video (get a prompt)" now builds a reel package instead of one prompt: a scene setting and the exact Persian narration line per ten-second segment, ready for the Google Flow Extend flow. Short packages arrive in one message and longer ones as one copy-ready message per segment. - The flow asks whether the reel should use the saved Flow character (`CONTENT_VIDEO_CHARACTER`, for example `@mohammad`) or run as a pure-AI promo with no character. Character reels repeat the character block on every segment, AI promo reels describe the shot only and list the Persian line as an optional voiceover. - `CONTENT_VIDEO_CHARACTER_PROMPT` overrides the built-in character description, `CONTENT_VIDEO_ASPECT` sets the reel frame (default `9:16 vertical`), and `CONTENT_VIDEO_SEGMENT_SECONDS` sets the clip length Flow generates per segment (default 10). The writer drafts the narration lines and falls back to the post text when the writer backend is down. - Video script requests use a larger token budget (at least 2600) with low reasoning effort; reasoning-heavy routers otherwise spend the whole budget on hidden reasoning and return empty content, which silently pushed every reel package back to the post-text fallback. ### Fixes — Flow unlock for multi-account URLs and long-poll timeouts (2026-09-10) - The `locallab-flow-unlock` rules only matched the bare `/unsupported-country` path. With several Google accounts signed in the block page is reached through `/u/ /unsupported-country`, which the rules missed; the rule set now covers the account-scoped paths, `flow.google-*` hosts, and the `labs.google` tool path, and the companion filter file was extended the same way. The content script also watches the address bar and restarts the app root (at most twice per tab) when the web app swaps the route client-side, where no request exists for the rule set to block. - The recovery navigation is no longer cancelled by the freeze that targets the block page: scheduled `window.stop()` calls are cleared before bouncing, which previously left the tab sitting on the block view. - New `background.js` service worker: when a blocked navigation still strands a tab on Chrome's `ERR_BLOCKED_BY_CLIENT` page, the tab is returned to the app root up to twice a minute. The block view itself is server-decided for a signed-in session, so the extension can only avoid the dead end, not bypass the verdict. - Recovery navigations keep the `/u/ /` account prefix. Restarting the app on the bare root silently fell back to the first signed-in Google account, which looked like the browser switching accounts on its own. - The create-button matcher used by the freeze step is back to its original form: the wider selectors added earlier could fire the freeze during the app bootstrap and abort data the dashboard needs. - The Content Bot polling loop survives transient network failures: Telegram long polls that time out through a slow route are logged as a warning and retried after a short backoff instead of dumping a full traceback into the container log. The long-poll HTTP read timeout is now derived from the poll timeout instead of a fixed 35 seconds. ### Fixes — Flow app-config response patch (2026-09-10) - The `locallab-flow-unlock` extension no longer depends on winning a race against the region check. Flow decides the unsupported-country verdict inside the page, from two batchexecute answers, and both are now rewritten in flight: `VideoFxService.GetFlowAppConfig` (`cPZSdc`) carries the country flag in field 31 and the age flag in field 32, `AiSandbox.CheckToolAvailability` (`KV2T2d`) carries the per-tool status in field 1. Only those fields change and the rest of the answer passes through untouched. - The patch runs in the page world (`unlock.js`, `world: MAIN`, loaded at `document_start`) and handles both encodings the backend uses, the JSON protocol-buffer form and the base64 one, so the dashboard renders for an account the Flow backend marks as an unsupported country. - `freeze.js` stands down as soon as the config answer is known to be patched (the page carries a `data-locallab-flow-config="patched"` marker), which keeps the freeze a fallback for tabs where the patch did not apply. - The declarative rules are anchored to the `/unsupported-country` path (`regexFilter`) instead of matching the string anywhere in the URL. The old wildcard filters also blocked the app's own `batchexecute` calls, because their `source-path` parameter contains the blocked route. - Rebuilt answers keep the batchexecute framing valid: each frame's length token counts the JSON plus the newlines around it in UTF-16 units (not bytes) and moves with the frame, and the closing `e` frame's value is re-computed to the byte size of the rewritten answer, so the page accepts a patched response instead of staying on the block route. - `tests/test-flow-unlock-patch.mjs` covers both payload encodings, the omitted-field case, the length-token and closing-size framing, and the pass-through of unrelated RPCs; the rules test now also guards the second content script and the anchored filters. ### Fixes — Instagram API version default (2026-09-10) - The default Graph API version moves from `v23.0` to `v26.0`, matching the version the Meta dashboard now shows. Live checks against `graph.instagram.com` confirm `v26.0` is the newest accepted version (`v27.0` is rejected as unknown) and that account lookup, media listing, and publishing all keep working on it. ### Fixes — Instagram approval flow and Media Studio reachability (2026-09-10) - Media previews now carry the Instagram approval buttons: photo and video previews show them inline, and albums (user photo collections) send a follow-up message with the same buttons, because Telegram albums cannot hold an inline keyboard. - The photo-collection preview offers the **Done** button that its own ask message describes; every upload replaces the previous preview and both preview messages are removed when a draft is replaced, rejected, or published. - `Approve to Instagram` publishes to Instagram only. Previously it also posted to the Telegram channel, which made it identical to the combined button, and `content_bot/bot.py` never imported the Instagram module, so every Instagram approval failed with a `NameError`. - The callback query is acknowledged before the slow Instagram publish instead of after it, so Graph API processing can no longer fail the update with a stale callback id. - Media Studio listens on every container interface so `content-bot` can reach `http://media-studio:8850`; the published host port still follows `MEDIA_STUDIO_BIND_IP`. ### Improvements — platform upload packages (2026-09-10) - Draft and media previews gained a **More platforms...** button that opens a chooser for platforms that need a human upload step. Picking a platform sends a copy-ready package: the title cut to the platform limit, the description with the source link and hashtags, the direct upload URL, and the stored media file re-sent for a quick download. - Packages are driven by the `platforms:` section of `editorial-policy.yaml`: YouTube and Aparat ship as defaults, any key can be overridden, added, or set to `null` to hide its button. Telegram and Instagram keep publishing through their APIs; a package never consumes the draft or counts toward the daily limit. ### Fork additions — content layer v0.1.0 - Added the `content/` Python layer for deterministic discovery normalization, deduplication, filtering, and candidate scoring; see `content/README.md`. - Added owner-editable policy under `content/config/` (`editorial-policy.yaml`, `categories.yaml`). Persian copy is produced at runtime, never stored. - `install.sh` now provisions the Content Manager workspace: it seeds a gitignored working copy of the policy under `data/content-manager/config`, defaults the n8n timezone to `Asia/Tehran`, and reports the content layer at the end of install. - Quickstart and installer identity now point at this repository. ### Fork additions — Media Studio worker v0.1.0 (2026-09-09) - Added `media-studio/`: a single-worker job service with a JSON API on `127.0.0.1:8850`, JSON-backed job state, artifact storage, and pluggable drivers. - Added the `api-image` driver (OpenAI-compatible `/images/generations`) so media generation works with the existing router or hosted writer API, without any Google subscription. - Added Google drivers (`flow-video`, `gemini-image`) that drive a signed-in Chrome over CDP (operator desktop) or a persistent container profile; both support the Flow unsupported-country region workaround (`MEDIA_STUDIO_BLOCK_GEO_REDIRECT`, `MEDIA_STUDIO_FREEZE_ON_READY`). - Added `extensions/locallab-flow-unlock/`: a standalone Chrome extension and uBlock filter that keep `flow.google.com` usable on a laptop without any stack component (`docs/FLOW-UNLOCK-STANDALONE.md`). - `install.sh` gained a Media Studio wizard and `--media-reconfigure`; `manage.sh` gained the `media` menu group and `media-status`, `media-guide`, and `media-configure` commands; the compose profile is `media` with image `afsharidevops/media-studio:0.1.0`. - Documentation: `docs/MEDIA-STUDIO.md` (install, sessions, API, selector calibration) and `docs/publishing/MEDIA-STUDIO-DOCKERHUB.md`. ### Fork additions — selectable router backend: 9router or OmniRoute (2026-09-09) - `install.sh` now asks on fresh installs whether to use the `9router` or the `omniroute` backend profile and can switch an existing install between them without losing data; the `hermes-omniroute-linux-stack` branch is obsolete. - Added the OmniRoute Compose service (dashboard on 20128, OpenAI-compatible API on 20129, profile `omniroute`) and a backend-agnostic `router-upstream-probe` one-shot that waits for the selected backend before the Smart Router starts. - OmniRoute installs default the Smart Router route profiles, observe model, n8n hosted-chat model, Hermes provider, and Open WebUI connection to the `auto/best-*` aliases (`http://omniroute:20129/v1`); 9router installs keep the `combo-*` defaults. Switching backends resets Smart Router upstream URLs, route profiles, and stale upstream keys. - `manage.sh` n8n provisioning now creates, stores, and validates a dedicated OmniRoute API key for the hosted-chat router credential; `set-router-mode`, `set-backend-api-key`, `verify-n8n`, `reconcile-n8n`, and the n8n MCP commands all work with either backend. - The standalone pipeline options (5/6/7) and the combined Content Bot + Media Studio wiring described below are unchanged and remain router-free when no backend is installed. ### Fork additions — combined standalone content pipeline install (2026-09-09) - `install.sh` fresh-install option 7 installs the Content Bot and Media Studio together as one content pipeline with no router, Hermes, n8n, or Open WebUI. Options 5 and 6 ask whether the other pipeline service should be added too, then walk through both configuration wizards in one run. - A combined install writes the Content Bot -> Media Studio link into `.env`: `CONTENT_MEDIA_STUDIO_URL=http://media-studio:8850`, the Media Studio API token mirrored into `CONTENT_MEDIA_STUDIO_TOKEN`, and image/video driver names that match the enabled Media Studio drivers. Reconfiguring either service keeps the link in sync, so a rotated Media Studio API token is propagated to the Content Bot. - `manage.sh` gained `pipeline-status`: a combined status view of the Content Bot, Media Studio, and their API link (URL, token sync, driver coverage). - Documentation: `docs/CONTENT-PRODUCTION-GUIDE.md`, `docs/MEDIA-STUDIO.md`, and the README describe the one-server content pipeline install. ### Fork additions — Content Bot Telegram MVP (2026-09-07) - Added `content/content_pipeline/score.py`: deterministic weighted scoring, configured penalties, freshness mapping, and candidate ranking. - Added `content/config/sources.yaml`: seeded RSS/Atom discovery template for the daily run. - Added the `content-bot/` service (Compose profile `content`): Telegram long-polling bot with on-demand link drafts, Approve/Reject inline buttons, channel publishing, per-day caps, duplicate-post protection, and a daily editorial scheduler driven by the working-copy policy. - `install.sh` now offers the Content Bot interactively, asks for the bot token, operator IDs, publish channel, and writer model, and provisions the unprivileged container, secrets, and state directory automatically. - `manage.sh` gained `content-status`, `content-connect-instagram`, the interactive Content Bot group, and `./manage.sh logs content`. - Added `.github/workflows/publish-content-bot.yml`: pushing Content Bot source to `main` builds and publishes `afsharidevops/content-bot:0.1.0` (plus `:latest`) to Docker Hub. `install.sh` now pulls that published image instead of building the container locally, and the daily scheduler honors `daily_proposal_time` from `editorial-policy.yaml`. - Added `docs/CONTENT-PRODUCTION-GUIDE.md` and `docs/INSTAGRAM-SETUP.md`; Instagram remains a guided pending checklist. ### Fork additions — editorial loop and standalone deployment (2026-09-09) - Draft behavior is now policy-driven: on-demand freshness and the daily proposal cap are read from the working-copy editorial policy, and operator-sent links are never blocked by the freshness window or the daily cap. - Added the reply-based revision loop: replying to a proposal with edit notes and pressing Reject revises the draft in place; pressing Reject without notes discards it. Empty or unchanged writer revisions are surfaced to the operator instead of re-posting the original draft. - Draft titles render bold with RTL-safe ordering, and daily feed parsing is hardened to HTML titles and links instead of raw page text. - Seeded `content/config/sources.yaml` with vetted RSS/Atom feeds per category and an in-file guide for adding and pre-testing new feeds. - Added standalone Content Bot deployment: installer option 5 provisions only the Content Bot against an external OpenAI-compatible writer API (base URL, key, model), and `./install.sh --content-reconfigure` or `./manage.sh content-configure` reconfigures it while preserving existing components and data. - Writer calls tolerate null or truncated responses and honor `CONTENT_WRITER_MODEL=auto`, `CONTENT_WRITER_MAX_TOKENS`, and `CONTENT_WRITER_REASONING_EFFORT`. - Added the operator `/forget-link ` command (plus `/forget_link` alias) so a previously published link can be drafted again; the Telegram command menu is registered on startup in the default and private-chat scopes. ### Fork additions — media handoff, uploads, RTL-safe copy (2026-09-09) - The media question after a draft now offers four choices: text only, an AI-generated image, a photo the operator uploads, or a ready-to-use video prompt so the operator creates the clip elsewhere and uploads it. - Upload handling is guarded per draft: a Cancel button, wrong-kind rejection, size checks, and blocked new drafts while an upload is pending; uploaded files are stored locally, and uploaded photos are branded by Media Studio like AI images while uploaded videos stay unchanged. - Media preview buttons now match the media source: retry options for AI-generated media and Approve/Reject options for operator uploads. - Mixed Persian/Latin post bodies render RTL-safe in Telegram: every line that needs it carries a right-to-left mark, and writer prompts require paragraphs to open with a Persian word so Latin product names never flip the line. - Writer replies that arrive as legacy-charset mojibake are repaired automatically; replies that cannot be repaired are refused instead of being published as garbage. ### Fork additions — configurable corner brand chip (2026-09-09) - Added `media-studio/media_studio/branding.py`: a translucent rounded chip with the configured brand label is drawn in a corner of every raster artifact produced by an image driver. The chip is disabled when the label is blank, and jobs can opt out with `"params": {"brand": false}`. - Media Studio exposes `POST /brand` (raw image bytes in, branded bytes out); the Content Bot calls it for every uploaded photo so operator media carries the same brand as AI images. Branding failures never fail a job or an upload; the original file is kept. - Configuration: `MEDIA_STUDIO_BRAND_LABEL` (default `Locallab`, blank disables) and `MEDIA_STUDIO_BRAND_POSITION` (default `bottom-right`, also `bottom-left`, `top-right`, `top-left`), wired through `docker-compose.yml` and `.env.example`. - Media Studio gained Pillow as a runtime dependency for the overlay. --- ## Hermes Linux Stack — v0.5.9 Changelog — 2026-08-13 ### v0.5.9 release focus Visual Flow Connections across Workflow, Agent, Router Pipeline, and Knowledge Pipeline studios, while preserving Hermes execution trust boundaries. ### v0.5.9 release acceptance Automated candidate, API/persistence, regression, Compose, manifest, and full Smart Router smoke validation passed during release preparation. The release owner explicitly requested early finalization and waived the remaining manual browser light/dark rendering and mouse/trackpad interaction gate. The waived checks are not represented as passed. - Fixed direct Users, Policies, and Plugins create APIs returning HTTP 500 after commit because audit logging dereferenced expired SQLAlchemy rows outside their sessions. - Added the v0.5.9 visual-flow implementation work without changing the runtime release version. - Updated Hermes stack plugins for the current plugin registration API. - Persist Telegram enablement for `stack-execution-policy` and `stack-package-policy` during installation. - Updated `manage.sh` runtime diagnostics to verify the current stack plugin toolset names. - Added regression coverage for Telegram plugin-toolset persistence and runtime registration. - Implemented v0.5.9 Phase 1 shared graph contract for Workflow and Knowledge studios: port-aware edges, legacy graph normalization, typed compatibility, named outputs, drag-to-connect preview/target validation, selectable/deletable edges, dirty state, and server-side duplicate/self/cycle checks. - Completed the v0.5.9 visual-flow development candidate across all four studios: Agent and Router shared-canvas integration, persisted agent composition graphs, legacy graph synthesis, named Router branch outputs (classifier/condition/capability/health/approval), branch-aware runtime traversal, edge reconnection, drop-on-empty quick add, undo/redo, pan/zoom/fit controls, keyboard/pointer accessibility, explicit save states, and backward-compatible port-aware persistence. - Preserved the execution trust boundary: visual orchestration and Telegram UX do not grant Docker, SSH, signing-key, or Execution Admin authority. --- ## Hermes Linux Stack — v0.5.8 Changelog ### Release focus v0.5.8 is the visual-building and operator-UX release for the 9router branch. It keeps the v0.5.7 trust-separated execution design while making browser connectivity failures diagnosable and adding visual studios for workflows, agents, router pipelines, and knowledge pipelines. ### Execution & Approvals reliability - Execution Broker target advances to `0.1.3`. - The Operations Center now probes the Execution Admin `/health` endpoint before attempting key authentication. Network/bind/CORS failures are reported separately from key failures. - Added `./manage.sh configure-execution-admin-browser ORIGIN [PRIVATE_IPV4]` to configure a private bind plus exact browser origin without printing the Execution Admin key. - Execution Admin CORS preflight supports the Private Network Access response header for explicitly allowed origins. - The key remains browser-memory-only and is still sent directly from the operator browser to Execution Admin; Smart Router does not receive it. - Wildcard/public bind shortcuts are intentionally refused by the helper. ### Operations Center redesign - Reworked dark and light palettes around semantic surface/text/border tokens. - Reorganized navigation into Observe, Build, Tools, Routing, Access, and System. - Added Dify-inspired visual interaction patterns without copying Dify branding or assets. - Added Workflow Studio with draggable nodes, visible edges, node inspector, references, and save/edit lifecycle. - Added Agent Studio with visible Input → Knowledge → Agent → Tools → Answer path and configuration inspector. - Added Router Pipeline Studio for conditions, capability/health filters, scoring, load balance, route, retry, fallback, and approval stages. - Added Knowledge Pipeline Studio and persistent `v58_knowledge_pipelines` registry. - Added Publish & Monitor workspace that links API, Flight Deck, agent testing, and runtime status. - Improved Flight Deck light-mode surfaces, sidebar, controls, and status pills. ### Data and API - Smart Router runtime/control schema marker advances in place to `0.5.8`. - Compatibility database filename remains `control-v0.5.2.sqlite3`; persistent Operations Center state is upgraded in place. - New Operations API routes: - `GET/POST /control/api/knowledge-pipelines` - `PUT/DELETE /control/api/knowledge-pipelines/{id}` - Knowledge pipeline graph validation accepts only managed ingestion/indexing node types and validates node IDs, edge references, and graph limits. ### Image targets - Smart Router: `afsharidevops/hermes-smart-router:0.5.8` - Execution Broker: `afsharidevops/hermes-execution-broker:0.1.3` - Intended platforms: `linux/amd64`, `linux/arm64` ### Validation performed in packaging environment - Smart Router pytest suite: 96 passed per branch. - Execution Admin focused unittest suite: 5 passed per branch. - `manage.sh` UX tests: passed per branch. - `bash -n` for `manage.sh` and `install.sh`: passed. - Python compilation for modified Smart Router UI/control files: passed. - Operations Center JavaScript syntax (`node --check`): passed. The broader root Python test discovery also contains environment-dependent tests requiring `ssh-keygen` and a fully usable npm prefix. Those checks could not complete in this packaging environment and should be run in the normal CI/release host before production publication. ### Security invariant UI convenience must not collapse execution authority into Smart Router. Execution Admin continues to exclude the Ed25519 approval-signing private key, Docker socket, and SSH private credentials, while Smart Router continues to exclude the Execution Admin key and execution approval secrets. ### Post-release private-ingress hotfix - Execution Admin now joins a dedicated `execution-admin-ingress-net` in addition to the internal `execution-control-net`. - The control network remains `internal: true`; only Execution Admin receives the host-ingress bridge. - This allows `${EXECUTION_ADMIN_BIND_IP}:${EXECUTION_ADMIN_PORT}:8752` to be actually published on Docker while preserving broker isolation. - Smart Router and Execution Broker application images are unchanged by this Compose-only networking fix. - Smart Router publishing documentation/workflow now uses only the plain version tag and `latest`; no `v ` or SHA alias is emitted. - `MANIFEST.sha256` is regenerated from repository-tracked release files, removing stale ignored cache entries that are not present in a fresh clone. --- ## Hermes Linux Stack — v0.5.7 Changelog ### Focus v0.5.7 integrates secure execution administration with the v0.5.6 Operations Center without collapsing the execution-broker trust boundaries. ### Execution & Approvals - Added **System → Execution & Approvals** to Hermes Operations Center. - Added `execution-admin` mode to Hermes Execution Broker `0.1.2`. - Operations Center talks directly from the operator browser to the separate Execution Admin endpoint with a separate admin key. - Smart Router backend does not receive the Execution Admin key or dedicated Telegram approval-bot token. - Execution Admin does not mount the Ed25519 approval signing key, Docker socket, or SSH private credentials. - Added live redacted health for approver, Docker broker and SSH broker. - Added live enable/disable policy for already-deployed `local`, `docker`, and `ssh` execution capabilities. - Added Telegram execution approver management, constrained to IDs already present in `TELEGRAM_ALLOWED_USERS`. - Added write-only dedicated approval-bot token replacement. The token is never returned by the API. - Added protection preventing the execution approval bot from reusing the Hermes Telegram bot token when the Hermes token hash is synchronized. - Added broker control-secret rotation from the separate admin boundary. - Added redacted SSH profile listing without exposing private keys/passwords. - Added execution-admin audit events. - Every execution policy/admin mutation increments the policy generation, invalidating older pending capabilities/approvals. ### Dynamic execution policy - Added `EXECUTION_FEATURES_FILE` support to the Hermes execution policy plugin and execution brokers. - Added `EXECUTION_POLICY_GENERATION_FILE` support to Docker, SSH and approver modes. - Policy files are bind-mounted and rewritten in place to preserve host ownership and permissions. - Existing environment variables remain compatibility fallbacks. - First-time broker deployment remains a host `manage.sh` operation; the UI does not need Docker-socket authority. ### Management commands Added: ```text ./manage.sh enable-execution-admin ./manage.sh disable-execution-admin ./manage.sh execution-admin-status ./manage.sh show-execution-admin-key ./manage.sh rotate-execution-admin-key ``` The existing execution commands remain supported. ### Security defaults - Execution Admin binds to `127.0.0.1:8752` by default. - Browser CORS uses an exact allowlist (`EXECUTION_ADMIN_ALLOWED_ORIGINS`); wildcard origins are not used. - The admin credential is separate from Smart Router authentication. - Operations Center keeps the Execution Admin key only in page memory; it is not written to localStorage. - Bot token readback is not implemented. - Signing-key rotation and SSH credential creation/removal remain local operator/CLI operations. ### v0.5.6 platform foundations retained v0.5.7 retains the v0.5.6 light/dark UI, lifecycle fixes, hybrid vector RAG/pgvector support, Flight Deck traces, guardrails, router pipelines, workflows, prompt versioning, datasets/evaluations, model catalog, marketplace/onboarding and HA foundations. ### Compatibility - Smart Router runtime/schema marker: `0.5.7`. - Existing `control-v0.5.2.sqlite3` compatibility filename is preserved and upgraded in place. - Smart Router image target: `afsharidevops/hermes-smart-router:0.5.7`. - Execution Broker image target: `afsharidevops/hermes-execution-broker:0.1.2`. - Branches: `main` (9router) and `hermes-omniroute-linux-stack` (OmniRoute). --- ## Hermes Linux Stack v0.5.6 — Platform Foundations v0.5.6 advances Hermes from an operator-focused Smart Router into a broader self-hosted AI infrastructure platform while preserving the existing `control-v0.5.2.sqlite3` compatibility filename and explicit-model pass-through behavior. ### Major additions - Light/dark themes in Hermes Operations Center and Flight Deck, persisted in the browser. - Consistent lifecycle UX: Agents, Teams, and Groups use reversible Enable/Disable controls; permanent deletion is a separate destructive action. Group purge protects ACL references unless explicit cascade deletion is requested. Plugins and Skills have explicit enable/disable and permanent removal controls. - Hybrid Knowledge/RAG with lexical + vector retrieval, embedding indexing, score fusion/reranking, PostgreSQL `pgvector` acceleration where available, and a deterministic portable vector fallback for offline/development use. - Full request trace records for request/auth/authorization/guardrails/RAG/classification/routing/retry/fallback/result stages, visible in Flight Deck and Operations Center. - Guardrail engine foundations for prompt-injection indicators, PII indicators, content deny rules, tool allow-lists, and high-risk tool confirmation policy. Modes: `off`, `audit`, `enforce`. - Advanced router pipeline definitions with conditions, route stages, load balancing, retries, and fallback model chains. - Workflow graph registry and visual workflow preview for Agent/Team orchestration. - Prompt registry with immutable versions, activation/rollback, notes, and history. - Evaluation datasets, dataset items, and A/B evaluation-run definitions. - Model catalog synchronized from the upstream `/models` endpoint with capability/context/pricing/health/latency metadata where available. - Plugin/Skill marketplace view built on the safe catalog/registry model. - First-run Operations Center onboarding flow. - PostgreSQL + pgvector + Redis + two-router HA Compose example and smoke/load-test tooling. - Enterprise identity readiness page. OIDC remains the completed interactive login path; LDAP/SAML/SCIM are connector/provisioning foundations and require deployment-specific integration before production use. - Static public-docs starter with release screenshots and demo deployment example. ### Compatibility and non-regression rules - `model=auto` enters Smart Router automatic selection. - Explicit upstream model IDs pass through unchanged. - Existing SQLite data is upgraded in place; the compatibility DB filename is not renamed simply because the software version changes. - Catalog plugin installation does not download/execute arbitrary untrusted code. Lifecycle state, permissions metadata, endpoint configuration, and safe registration remain controlled by the operator. ### Production notes For real semantic vector RAG, configure an embeddings endpoint and use PostgreSQL with the `vector` extension. The built-in deterministic embedding fallback is useful for tests/offline operation but is not a substitute for a production embedding model. Run the HA smoke and load-test scripts against your own infrastructure before calling a deployment production-HA certified. --- ## Hermes Linux Stack v0.5.5 ### Operations Center runtime controls, agent lifecycle, skills, groups, and built-in docs v0.5.5 is an operator-control and usability release built on v0.5.4. It preserves the existing Smart Router data directory and the default `control-v0.5.2.sqlite3` compatibility filename while upgrading the Operations Center schema in place to `0.5.5`. #### Smart Router / Operations Center - Live UI editing for `router_mode`: `observe` or `route`. - Live UI editing for `router_policy`: `heuristic`, `calibrated`, or `learned`. - UI control for HA mode with a guard that requires Redis before HA can be enabled. - Runtime UI settings persist in the Operations database and override startup environment values until **Reset to environment** is used. - System page now reports the schema version and explains why the default DB filename can remain `control-v0.5.2.sqlite3`. - OIDC login button corrected to the actual `/api/auth/oidc/start` route. - ACL UI corrected to use backend subject type `virtual_key`; `group` subjects are now exposed. #### Agents - Create Agent errors are shown instead of failing silently. - Agent names and referenced Knowledge/Plugin/Skill IDs are validated. - Agents can be edited in the UI. - Agents can be disabled reversibly or permanently deleted. - Agent forms use existing Knowledge, Skill, and Plugin records instead of requiring blind CSV IDs. #### Skills and plugins - New reusable Skill registry with curated suggestions for Linux, Docker, networking, MikroTik, automation safety, and incident response. - Skills can be installed from the built-in catalog or registered manually, including commercial/license metadata without storing license secrets. - Skills assigned to an agent are injected into that agent's system context during runs. - Suggested Plugin catalog can install safe registry templates for GitHub MCP, read-only PostgreSQL, Kubernetes observation, and MikroTik observation. - Plugin catalog installation does not download or execute arbitrary code and installs suggested entries disabled by default. #### Access groups - New Access → Groups menu. - Groups contain Operations Center usernames. - ACL rules with `subject_type=group` now resolve real group membership. #### Help and documentation - New built-in **Docs** menu covers routing, system controls, users/keys/groups/ACLs, Knowledge, Memory, Agents, Skills, Plugins, Teams, upgrades, backups, and troubleshooting examples. - Memory page now explains scope behavior and warns against using memory as a secret store. #### Database compatibility The default filename remains: ```text sqlite:////data/control-v0.5.2.sqlite3 ``` This is intentional. v0.5.5 creates its new tables in the same database and updates the `schema_versions` marker to `0.5.5`, preserving v0.5.1/v0.5.2-era users, routes, keys, policies, budgets, knowledge, memory, agents, teams, plugins, ACLs, audit, and outcome records. New v0.5.5 tables include: ```text v55_runtime_settings v55_access_groups v55_skills v55_agent_skills ``` Back up the persistent Smart Router database before any production upgrade. --- ## v0.5.4 change summary — 9router - Fixed the hidden 200,000 TPM ceiling on `SMART_ROUTER_CLIENT_API_KEY`; trusted stack-client RPM/TPM/daily limits are explicit (2,000,000 TPM default). - Local quota 429 responses now identify Smart Router as the source, include current/limit/estimated values, and return `Retry-After`. - Redis/HA quota checks are atomic: denied requests no longer consume additional quota. - Virtual API-key RPM/TPM/daily limits can be edited in-place in `/control/` without rotating the key. - Visible **Control Plane** naming is replaced by **Hermes Operations Center**; `/control/` and legacy `SMART_ROUTER_CONTROL_*` variables stay compatible. - RAG knowledge tables can share the Operations DB or use a separate SQLite/PostgreSQL database through `SMART_ROUTER_KNOWLEDGE_DATABASE_URL`. - The Operations Center shows RAG storage health/backend and labels the built-in retriever accurately as lexical. - The persistent default filename remains `control-v0.5.2.sqlite3` so upgrades preserve state. - Runtime/package/image/chart defaults are `0.5.4`. --- ## v0.5.3 change summary — 9router ### User experience - `./manage.sh` now opens a grouped interactive manager by default. - Added dedicated Services, Smart Router, Hermes, n8n, Execution, Maintenance, and Security menu groups. - Smart Router mode/policy/time-window/feature choices are shown as numbered choices instead of requiring memorized values. - Direct v0.5.2 commands remain backward compatible for scripts and automation. ### Hermes Smart Router Flight Deck - Redesigned `/dashboard` as the Hermes Flight Deck with access state, time-window and auto-refresh selectors, routing-flow explanation, telemetry quality, route mix, and explicit zero/pricing states. - Redesigned `/control/` navigation into Observe, Routing, Access, Intelligence, and System groups. - Added dropdown/select controls for common finite-value choices such as roles, budget scopes, ACL effects, agent profiles, team strategy, plugin kind/risk, and API-key tiers. - Dashboard and Control Plane link to each other. ### Compatibility - Runtime/package version is 0.5.3. - Fresh installs pin `afsharidevops/hermes-smart-router:0.5.3`. - Existing v0.5.2 Control Plane SQLite/schema naming is intentionally preserved for in-place data compatibility. - Corrected the installer status message so it reports the selected Smart Router mode instead of always saying observation mode. --- ## v0.5.2 change summary — main - fixed router-info endpoint and retained compatibility alias - hardened installer secret rotation and Smart Router client-key synchronization - removed fixed Docker GID assumption - enabled control/client auth by default and disabled Open WebUI signup by default - made application image tags `.env`-overrideable while retaining `latest`/`main` defaults - added provider health/circuit breakers, shared Redis state/stickiness, OIDC/ACL/secrets foundations, outcome capture and provider quality metadata - added doctor/backup/restore/rollback/image-lock operations - added HA Compose and Helm foundations plus security CI - reconciled package version/documentation to Smart Router v0.5.2 See `docs/HERMES-SMART-ROUTER-v0.5.2-IMPLEMENTATION-STATUS.md` for items still open and not claimed complete. ### Post-review cleanup - Replaced the Docker execution GID 0 fallback with fail-closed sentinel GID 65534; `install.sh` still detects the actual Docker socket GID when available. - Removed a dead duplicate `/router/info` handler definition. - Deduplicated Redis/Authlib dependency constraints to their prior effective versions. - Removed duplicate long-range planning documentation from the active release tree. - Updated smoke validation for configurable Smart Router image tags (`latest` by default, pinnable via `.env`).