HERMES-GIELIE/api
nesquena-hermes c414c39f10
feat(extensions): token-v1 proxy→sidecar authentication boundary (#6331)
* feat(extensions): token-v1 proxy->sidecar auth boundary

Mint a per-extension secret core injects (X-Hermes-Sidecar-Token) on every
proxied request; sidecars validate it. Closes the hole where a loopback sidecar
port is reachable by any local process and cannot distinguish a proxied request
from a direct one.

- api/extension_sidecar_auth.py: per-extension token lifecycle (atomic mint,
  re-read-verified so an unpersisted token is never injected, per-request
  mtime-cached read for live rotation, path-escape-safe)
- manifest proxy_auth negotiation: absent=legacy, token-v1=enforce, unknown=fail-closed
- proxy: inject token, strip inbound + response x-hermes-*, auth-off posture
  (loopback-only local_unprotected, else 503), fail-closed when token unavailable
- consent-time auth_required in status payload; mint-on-consent
- docs/EXTENSIONS.md proxy_auth section
- 7 new tests (26/26 green)

* fix(extensions): address Codex+Fable gate on token-v1 (6 findings)

- align token-module extension-id grammar with core _EXTENSION_ID_RE (was
  narrower -> legally-named ext consented then 503'd forever)
- resolve token dir dynamically (mirror _extension_state_dir) -> real test
  isolation, no import-time STATE_DIR cache
- cross-process mint: O_CREAT|O_EXCL no-clobber claim (was os.replace clobber)
- rotation cache keyed on full fingerprint (ino/dev/mtime/ctime/size) +
  re-fingerprint after read -> same-size/mtime replacement no longer stale
- validate token format on read (url-safe, 16-256) -> malformed file can't
  leak via a ValueError echoed in a 502
- consent fails 503 when token can't be provisioned (was silent-swallow ->
  persisted consent then 503 forever)
- rename status auth_required -> posture enum (protected|local_unprotected):
  nothing is blocked for loopback, so 'required' was misleading
- panels.js: render local_unprotected warning on the consent row (+ CSS)
- docs: token-path resolution order, 401-vs-503, explicit 'legacy' acceptance
- tests: route-level token-injection+response-strip test; fix illusory isolation
  in token-module test; 27/27 green

* fix(extensions): close 2 token-mint races (Codex re-gate round 2)

- mint via temp-file + atomic os.replace (not O_CREAT|O_EXCL) so the final
  path is never observed empty/half-written — a concurrent loser can no longer
  read an empty token file and 503
- single _stable_read helper (fingerprint-read-refingerprint, bounded retry on
  mid-read change) used by BOTH ensure_token and current_token — a token that
  changes during the read is never returned or cached stale
- stress-verified: 20 concurrent first-mints converge on 1 persisted token; 27/27

* fix(extensions): atomic no-clobber token publish via os.link (Codex re-gate round 3)

os.replace fixed empty-file exposure but still clobbered cross-process: two
processes could both write+replace and a reader between them got a token no
longer on disk -> 401. Switch to the repo's TOCTOU-safe os.link create-or-fail
idiom (session_recovery.py:627): write temp -> link into place (fails if a
winner already published) -> loser drops its temp and reads the winner via
_stable_read. Proven: 16 concurrent PROCESSES converge on 1 persisted token,
all matching disk. 27/27 tests green.

* fix(extensions): resolve Frank+Greptile #6331 review — token-v1 fail-closed when auth off (consent+resolution), token-v1-only proxy_auth/posture status fields, fullmatch ext-id validator

* fix(extensions): finish Frank and Greptile sidecar review

---------

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
2026-07-20 17:49:23 +00:00
..
__init__.py
agent_health.py fix: fail closed on wrapped gateway pid helpers 2026-07-14 16:36:23 +08:00
agent_runtime.py fix(runtime): ignore in-memory agent module swaps 2026-07-16 22:28:35 +08:00
agent_sessions.py Release exp-v0.52.40: ACP sessions in sidebar (#5939) + date-stamped Claude 3.0 reasoning fix (#5934) (#5951) 2026-07-11 15:20:41 -07:00
auth.py Release exp-v0.52.43: trusted-header auth / reverse-proxy SSO (#5568, #3351) (#5956) 2026-07-11 16:34:14 -07:00
auth_oidc.py Release: OIDC allowlist whitespace fix (#6244, @webtecnica) (#6259) 2026-07-18 10:36:17 -07:00
background.py
background_process.py fix(wakeup): recover terminal process completions after restart (#6287) 2026-07-18 20:07:54 -07:00
clarify.py stage #4524: clarify 409-terminal (id-guarded, loading-cleared) + SSE-notify on expiry (#4504) + v0.51.534 CHANGELOG 2026-06-20 05:36:56 +00:00
commands.py fix(webui): guard non-dict resolve_moa_preset result in resolve_moa_config 2026-07-01 07:54:48 -05:00
compression_anchor.py Address compaction marker and redaction review 2026-06-26 00:12:09 +00:00
compression_recovery.py Live Stream: add compression recovery action 2026-07-04 21:42:32 +08:00
config.py Release: gateway-default MoA send (#5869, @rodboev) (#6365) 2026-07-20 14:29:22 +00:00
crash_visibility.py fix(webui): crash visibility — faulthandler + thread excepthook + exit audit (#4633) 2026-07-01 18:01:39 +00:00
dashboard_probe.py
extension_sidecar_auth.py feat(extensions): token-v1 proxy→sidecar authentication boundary (#6331) 2026-07-20 17:49:23 +00:00
extensions.py feat(extensions): token-v1 proxy→sidecar authentication boundary (#6331) 2026-07-20 17:49:23 +00:00
gateway_chat.py Release: terminal-error settlement timing/seal (#6323) + Docker experimental builds (#6329) (#6341) 2026-07-19 22:17:36 +00:00
gateway_restart.py fix: harden gateway restart recovery targeting 2026-07-14 16:01:56 +08:00
gateway_watcher.py fix(sessions): open state.db read-only for lineage reads + watcher projection 2026-07-08 20:17:45 +00:00
goals.py
helpers.py Release: raster-data-URI redaction fast-path (#6311, @inch772) (#6367) 2026-07-20 16:30:04 +00:00
kanban_bridge.py rebase(#2814): resolve i18n onto current master + add kanban_view_* keys to all 13 locales 2026-06-23 23:40:45 +00:00
metering.py feat(#6006): instrument send-to-first-token latency 2026-07-13 17:56:44 -04:00
models.py Release: content search no longer evicts the working-set cache (#6084, @ai-ag2026) (#6343) 2026-07-20 01:21:18 +00:00
oauth.py perf(providers): invalidate cache on oauth credential changes 2026-07-13 19:58:41 +08:00
office_documents.py #5142: office-doc preview + safe docx editing (#540), rebased on master; optional deps 2026-07-02 01:15:57 +00:00
onboarding.py Release exp-v0.52.42: ?profile= deep links (#5730) + Russian locale refresh (#5778) (#5953) 2026-07-11 15:58:47 -07:00
passkeys.py Release Batch D: terminal resource-leak fix + fsync atomic writers (#5835, #5843) (#5848) 2026-07-09 10:54:06 -07:00
paths.py fix(profiles): avoid config import cycle 2026-06-01 05:05:16 +00:00
plugin_providers.py Release v0.51.270 — Release IL (stage-u1 — un-hold batch: #3517 #3624 #3613) (#3674) 2026-06-05 11:35:26 -07:00
plugins.py fix(#2622): plugin card UX — legible Open button, single badge, visible toggle + reject protocol-relative tab.path 2026-06-02 03:58:02 +00:00
process_event_utils.py fix(wakeup): route async-delegation completions by origin + durable-claim delivery (#6283) 2026-07-19 02:26:55 +00:00
profiles.py fix(#5696): revert profiles cache TTL 60s->4s (stale profile rows); contributor-flagged follow-up 2026-07-06 20:40:08 +00:00
providers.py perf(providers): invalidate cache on oauth credential changes 2026-07-13 19:58:41 +08:00
request_diagnostics.py perf(webui/session-load-latency) tier4: fix CI failures — lint B009/B905 + _emit_slow format args 2026-07-09 04:22:21 +00:00
rollback.py fix(#4410): pre-check workspace leaf type via lstat + close fd on non-regular (Codex gate v2) 2026-06-18 19:22:29 +00:00
route_approvals.py fix(#5000): relay mirrored gateway approvals after stream loss 2026-06-27 06:03:24 +00:00
route_session_list_cache.py fix(cache): keep streaming TTLs above poll cadence 2026-07-15 20:59:59 +00:00
routes.py feat(extensions): token-v1 proxy→sidecar authentication boundary (#6331) 2026-07-20 17:49:23 +00:00
run_journal.py Release: run-journal summary cache (#6291, @sjungwon03) (#6355) 2026-07-20 09:12:32 +00:00
runner_client.py fix(gateway): correct approval URL and thread session history into runs API (#4479) 2026-06-19 13:16:39 -04:00
runtime_adapter.py
session_discoverability.py Release Batch D: terminal resource-leak fix + fsync atomic writers (#5835, #5843) (#5848) 2026-07-09 10:54:06 -07:00
session_events.py feat(sessions): carry changed session_id in session-list-changed events (#4221) (#4233) 2026-06-15 00:10:01 -07:00
session_export_html.py #4968: export chat to self-contained themed HTML, rebased on master 2026-07-02 02:36:09 +00:00
session_lifecycle.py fix(#5543): fire-and-forget commit_session_memory + drain-safe managed stop 2026-07-04 21:40:21 +00:00
session_ops.py fix: preserve lazy alignment semantics 2026-07-17 15:36:55 -06:00
session_recovery.py Merge latest master into compression recovery action 2026-07-05 10:23:15 +08:00
shares.py fix(share): reject dict-valued text-blocks and dict titles (re-gate edge) 2026-07-11 02:14:07 +00:00
skill_usage.py
sse_chunked.py stage #4447: opt-in chunked SSE for buffering proxies (#4447) + v0.51.524 CHANGELOG 2026-06-19 22:09:17 +00:00
startup.py
state_sync.py fix(state-sync): mirror WebUI API call counts 2026-07-07 19:48:19 +00:00
streaming.py Release: terminal-error settlement timing/seal (#6323) + Docker experimental builds (#6329) (#6341) 2026-07-19 22:17:36 +00:00
system_health.py fix: keep psutil optional for system health 2026-06-26 20:49:13 +00:00
terminal.py Release: broadcast terminal output to every viewer (#5836, @ai-ag2026) (#6340) 2026-07-19 22:08:29 +00:00
todo_state.py Release v0.51.261 — Release IC (stage-r11) (#3616) 2026-06-04 16:46:20 -07:00
turn_journal.py Release v0.51.326 — Release KP (#3618 + #3802 + #3762 + #3810) (#3816) 2026-06-07 23:18:51 -07:00
updates.py Release: pip-installable packaging metadata (#6337, @rodboev) (#6344) 2026-07-20 02:17:52 +00:00
upload.py Release: write Office upload sidecars (#5734, #540) (#5859) 2026-07-09 15:57:21 -07:00
usage.py
webui_session_db.py fix: preserve compression recovery child markers in sidecar rebuild 2026-07-05 06:45:50 +08:00
workspace.py #5142: office-doc preview + safe docx editing (#540), rebased on master; optional deps 2026-07-02 01:15:57 +00:00
workspace_git.py fix(workspace-git): hide git subprocess console windows on Windows (#5692) 2026-07-07 02:07:39 +00:00
worktrees.py fix(worktrees): unlock before remove so WebUI-created worktrees can be reaped 2026-07-13 17:47:58 +00:00