Skip to content

Security hardening (v0.1 → v0.2): scoped auth, non-root container, verified supply chain#1

Open
mikeumus wants to merge 4 commits into
mainfrom
harden/security-v0.2
Open

Security hardening (v0.1 → v0.2): scoped auth, non-root container, verified supply chain#1
mikeumus wants to merge 4 commits into
mainfrom
harden/security-v0.2

Conversation

@mikeumus

Copy link
Copy Markdown

Internal hardening PR against our fork (not upstream). This is the integration base for embedding Hermes-on-Cloudflare into the Divinci app, and the source branch for a later coordinated upstream contribution (handled via PlaydaDev's private security-disclosure process, per their SECURITY.md).

What changed

Turns the audited v0.1 into a hardened v0.2. Full mapping in HARDENING.md.

Auth — two-level tokens (API_TOKEN chat / ADMIN_TOKEN admin), fail-closed (503 when unset unless ALLOW_UNAUTHENTICATED=true), constant-time compare, mandatory HERMES_GATEWAY_TOKEN, optional rate limiting, proxy strips its own credentials before forwarding.

Container — non-root hermes user via gosu, Node download over verified TLS with pinned SHA-256, pinned Hermes clone with no silent unpinned fallback, tight secret perms.

Supply chain / maturity — SHA-pinned GitHub Actions + permissions: contents: read, committed lockfile, production-tree npm audit gate, and the first test suite (23 auth tests).

Verification

  • npm run typecheck — clean
  • npm test — 23/23 passing
  • npm audit --omit=dev — 0 vulnerabilities
  • Container changes are review + bash -n verified only; not image-build verified (see caveat below).

Needs live-Sandbox validation before we depend on it

The non-root privilege drop assumes the Sandbox control plane launches the startup script as root (so it can chown before dropping to hermes). On a real deploy, confirm (a) gateway/dashboard run as hermes and can read ~/.hermes, and (b) POST /api/instance/restart (execs kill -9 1) still works. Details in HARDENING.md.

Not for upstream as-is

Per PlaydaDev's SECURITY.md, the vulnerability-class fixes go to them via a private security advisory, not a public PR. This PR stays internal to Divinci-AI.

mikeumus and others added 4 commits July 16, 2026 12:21
Replace the single optional bearer token with a two-level model:
- chat level (API_TOKEN) gates /v1/* and /api/* baseline
- admin level (ADMIN_TOKEN, falls back to API_TOKEN) gates the
  destructive/introspective control routes (restart, stop, logs)

Auth now fails closed: with no token configured, protected routes
return 503 unless ALLOW_UNAUTHENTICATED=true (explicit dev opt-in).
Tokens are compared in constant time via SHA-256 digests.

Make HERMES_GATEWAY_TOKEN mandatory (requireGatewayToken) so the
Worker never sends an empty bearer, and add optional per-client rate
limiting (CHAT_RATE_LIMITER / ADMIN_RATE_LIMITER, no-op when unbound).
The dashboard proxy strips the Worker's own Authorization header and
hw_token cookie before forwarding to the container.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Run the Hermes gateway + dashboard as an unprivileged `hermes` user
  (privileges dropped via gosu after the root-run startup writes config).
- Fetch the Node.js tarball over verified TLS (drop `curl -k`) and check
  it against a pinned per-arch SHA-256.
- Remove the silent unpinned fallbacks on the Hermes git clone and the
  [web,pty] install; the build now fails hard on a bad ref. Add an
  optional HERMES_COMMIT build-arg to pin an immutable commit.
- Tighten secret perms: ~/.hermes is 0700, .env is 0600 under umask 077;
  require HERMES_GATEWAY_TOKEN (no `change-me` default).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- First test suite (vitest): 23 tests covering constant-time compare,
  token parsing, fail-closed behavior, and chat-vs-admin privilege
  separation.
- Commit package-lock.json; CI uses `npm ci`.
- Pin all GitHub Actions to commit SHAs; add `permissions: contents: read`,
  an `npm test` step, and a production-tree `npm audit` gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README + SECURITY.md describe the fail-closed two-token model, the
  mandatory gateway secret, non-root agent, and rate limiting.
- Correct docs/custom-domain.md, which described a dashboard gateway-token
  auto-injection the code never performed.
- Add HARDENING.md mapping each change to its rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant