Repository navigation
feat(backend)!: require THINKER_ENCRYPTION_KEY, separate from BETTER_AUTH_SECRET - #150
Conversation
…AUTH_SECRET
At-rest encryption no longer falls back to the session secret.
- crypto.util: getEncryptionSecret() reads only THINKER_ENCRYPTION_KEY;
getDecryptionSecrets() is [key, PREVIOUS]. New assertEncryptionKeyConfig()
throws a StartupConfigError when the key is missing, under 32 chars or
equal to BETTER_AUTH_SECRET, or PREVIOUS is set but under 32 chars.
- main.ts runs the check before the app and its migrations start; config
errors exit 1 with their own message instead of the migration hint. The
legacy-key check uses the same error type.
- EncryptApiKeys migration and the re-encryption service no longer mention
or read BETTER_AUTH_SECRET.
- Compose requires the key (${VAR:?}); env examples, Docker README and the
developer guide document it as required. Docker smoke and CI e2e set one.
- Tests: unit coverage for the gate and a known-ciphertext fixture with a
PREVIOUS rotation; e2e boots the real server with a missing, short,
equal and short-PREVIOUS key and checks the exit and message, and checks
a provider key saved through the API does not decrypt with
BETTER_AUTH_SECRET. Unit and e2e setup files supply a test key.
BREAKING CHANGE: THINKER_ENCRYPTION_KEY must be set (32+ chars, different
from BETTER_AUTH_SECRET) or the server does not start.
Refs #27
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #150: require THINKER_ENCRYPTION_KEY with no BETTER_AUTH_SECRET fallback (#27)Diff reviewed: Summary
TestsUnit coverage in Findingsshould-fix 1: there is no upgrade path for installs that relied on the fallback. In nit 2: stale comment at nit 3: the doc comment at High-risk criteria that apply
Nothing blocking. qa-docker is required for this runtime change: it should cover boot refusal and a normal boot with both keys set. Should-fix 1 must be fixed or tracked in an issue linked in the PR before merge. CI on the reviewed SHA: all 19 check runs completed with success (CodeQL, Docker build amd64/arm64, Compose smoke, install.sh e2e, Lint, Typecheck, Build, Unit backend/cli/shared/frontend, Backend e2e UTC and America/Sao_Paulo, Changeset check, Scripts, dependency-review). Head revisado: f32a40f |
|
Review should-fix 1 and the nits are tracked in #152. |
qa-docker report for PR #150Image
Flaky: the first e2e run took 30 minutes with a host load average above 120, because other QA runs were going at the same time. Both failures were hook timeouts and passed on rerun. Finding (non-blocking): Tested SHA: f32a40f |
* feat(cli)!: rename the CLI to thinker with THINKER_* variables - Package thinker-cli (private, no publishConfig), bin thinker -> bin/thinker.js. - Env vars THINKER_URL, THINKER_API_KEY, THINKER_AGENT_KEY, THINKER_AGENT_URL; the old names are not read (no aliases). - Config and agent-key store under ~/.config/thinker/; default origin stays http://localhost:2099. Fix the stale "then Cloud" resolution comment. - User-Agent thinker-cli/<version>; usage, hints and messages say thinker. - Embedded skill renamed thinker-cli and rewritten for a self-hosted Thinker instance; regenerated skill-content.gen.ts. - routing test detects the gateway's peacock error tag for any product word, so it keeps working across the server-side rename of that tag. - README: local install (npm install -g ./packages/cli or npm pack) and a note that the upstream CLI's config directory (including leftover telemetry files) is neither read nor migrated. - Root: changesets ignore list and check-changesets message use thinker-cli; lockfile updated; dev guide CLI section refreshed. BREAKING CHANGE: the CLI command, environment variables and config directory are renamed; run thinker login again. Refs #23 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(docker)!: rebrand image, compose and installer to Thinker - docker-compose.yml: project/service `thinker`, Postgres user/db `thinker`, volumes thinker_pgdata and thinker_request_recordings (no legacy pins), `# thinker-compose-version: 1`, no legacy 3001 section, TRUST_PROXY exposed with a `loopback` default so clients on a private network cannot spoof X-Forwarded-For to dodge the per-IP rate limit. - install.sh: THINKER_INSTALLER_SOURCE / THINKER_INSTALLER_SKIP_PULL (no aliases), raw source patrickbrandao/thinker, default dir $HOME/thinker, thinker compose marker, `docker compose logs -f thinker`, LAN note via HOST_BIND_ADDRESS. - Dockerfile: Thinker OCI labels, drop the stale base.digest label, ENV PORT=2099 and healthcheck fallback 2099, ship /app/LICENSE and a THIRD_PARTY_NOTICES generated offline before node_modules is pruned. - Backend: single DEFAULT_PORT = 2099 constant replaces every 3001 fallback; spec fixtures follow. - DOCKER_README.md: minimal rebrand (no upstream stars badge, API Bot banner or manifest.build links). - docker-smoke.yml: drop the Compose override and the patched compose copy; tag the local build tmsoftbrasil/thinker:ci and select it with THINKER_VERSION=ci (restores the image-tag check), new service, volume and container names, and check the installer writes two distinct secrets. docker.yml checks LICENSE, THIRD_PARTY_NOTICES and PORT=2099. BREAKING CHANGE: MANIFEST_INSTALLER_* are no longer read; the Compose project, service and volume names changed. Refs #141 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(n8n)!: rebrand the n8n node as n8n-nodes-thinker - git mv integrations/n8n-nodes-manifest -> integrations/n8n-nodes-thinker; package n8n-nodes-thinker 0.1.0, repository/homepage/author point at this repo. Lockfile: only the root name/version lines change. - Nodes Thinker and LmChatThinker, credential ThinkerApi (thinkerApi), base URL default http://localhost:2099. - Both nodes refuse an API key that does not start with thk_ before sending any request. - THINKER_ATTRIBUTION_HEADERS with User-Agent n8n-nodes-thinker; the backend caller classifier recognises the new UA. - Provisional "T" logo until #10. - dist/ removed from git and ignored; root nodes/ and credentials/ mirrors deleted. - CHANGELOG restarted at 0.1.0; README documents npm pack and a manual community-node install. LICENSE.md keeps the MNFST, Inc. notice (MIT). BREAKING CHANGE: node, credential and package names change; existing n8n workflows must be rewired to the Thinker nodes and credential. Closes #24 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(dev): describe the unpublished thinker-cli package Refs #23 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(docker): keep the upgraded compose file readable (0644) mktemp creates the replacement with mode 0600; the compose file carries no secrets, so restore the usual 0644 after the atomic move. Found by qa-docker. Refs #26 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(deploy)!: rebrand community deploy templates to Thinker - render.yaml, app.json, .do/deploy.template.yaml, Dockerfile.heroku and deploy/* (fly, coolify, gcp, aws, easypanel, apple-containers, koyeb, digitalocean, heroku) use the image tmsoftbrasil/thinker:0.1 (Apple Containers follows THINKER_VERSION like Compose) and generate or ask for THINKER_ENCRYPTION_KEY; since #138 the backend refuses to boot with only MANIFEST_ENCRYPTION_KEY. - Drop the dead MANIFEST_MODE everywhere, manifest.build links, the upstream logo and the AWS quick-create link to Manifest's S3 bucket. - deploy/aws/manifest.yaml -> deploy/aws/thinker.yaml; Apple Containers: MANIFEST_ENV_FILE / MANIFEST_PG_VOLUME / MANIFEST_RECORDINGS_VOLUME -> THINKER_* (no aliases), container names thinker-postgres/thinker-app, Postgres digest read from docker/docker-compose.yml (Dependabot keeps that one current) with a synced fallback; Coolify Postgres pinned by digest. - Every TUTORIAL.md starts with the PT-BR "community-maintained, not tested in CI" notice; root templates carry it as a comment. - CONTRIBUTING.md: Thinker naming, thk_ keys, Docker Hub image and the tag-driven release flow; the stale Codecov badge goes. BREAKING CHANGE: MANIFEST_ENV_FILE, MANIFEST_PG_VOLUME and MANIFEST_RECORDINGS_VOLUME are no longer read by deploy/apple-containers/start.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(frontend): rebrand the dashboard to Thinker (#25) - Thinker name, logo (brand assets, cache-busted with ?v=1) and favicon on every page; index.html title and OG/Twitter tags with no absolute URLs. - Setup guides: OpenCode, OpenClaw disable, MCP clients use the `thinker` provider/server name; n8n uses n8n-nodes-thinker; Craft points at a custom OpenAI-compatible endpoint; HeaderTierModal placeholder x-thinker-tier. - Links: Docs and Help point to the Thinker repository (docs, issues); Manifest terms, privacy, support email, blog, discussion and the modelparams.dev report action are removed. - Web Storage keys move to thinker*; pre-rename keys are deleted on load; lazy-reload survives blocked sessionStorage. - Gateway marker `thinker` gets the Thinker icon and label. - Routing pricing toast explains when catalog sync is off (#75 item 5). - Playground headers: shared validators, warnings for duplicate and over-cap rows, announcement reset on close, adapter note (#105 items 4-5). - Tests use thk_ keys and x-thinker-tier as data (#141 frontend part). - Docker smoke checks <title>Thinker</title>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore!: rename every remaining Manifest reference to Thinker Sweep the identifiers, types, files, MCP tool names, routing reasons, analytics fields, error messages, docs, URLs and test fixtures that still carried the upstream brand, on top of the folded rebrand branches. - MCP tools manifest_* -> thinker_*; MCP server name thinker. - ManifestError/ManifestErrorCode/formatManifestError/ManifestRequest and friends -> Thinker*; files manifest-error, manifest-runtime and manifest-error-docs renamed. - routing_reason manifest_* -> thinker_* with an append-only migration (RenameThinkerRoutingReasons1803600000000, up/down). - origin=manifest filter -> origin=thinker; manifest_errors, manifest_lift_pct, manifest_error_code/message -> thinker_*. - Error docs links point to docs/erros.md in the repository (new PT-BR page) instead of manifest.build; OpenRouter HTTP-Referer and email footers point to the repository; default dashboard URL is http://localhost:2099. - Dev seed login admin@thinker.example.com / thinker. - Templates use THINKER_RECORDING_* (backend side in #148) and the Compose file, Apple Containers script and Docker Smoke require THINKER_ENCRYPTION_KEY (#150). - README links point to patrickbrandao/thinker; upstream-only badges, banners and the hosted cloud section removed. Kept on purpose: upstream attribution, legacy guards (legacy-env, legacy-storage, absence assertions), frozen storage-format salts, applied migrations, and generic "manifest" meanings (OCI manifests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(backend): no hosted gateway host left in backend production files Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(backend): escape error text in SSE and friendly bodies, add nosniff CodeQL flagged reflected XSS / exception text as HTML on the playground SSE writer, the friendly SSE response and the proxy stream-error frames. Those bodies now go through safeJsonStringify, which writes <, >, &, U+2028 and U+2029 as \uXXXX escapes: the JSON every client parses is unchanged. SSE responses and the friendly JSON response also send X-Content-Type-Options: nosniff, and the stream-error path declares text/event-stream when no header was sent yet. Also drop the duplicated THINKER_BASE_URL and stale TODO(#31) in Docker Smoke, and document that 0.1 is fresh-install only for Manifest users. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Closes #27
What
crypto.util.ts:getEncryptionSecret()reads onlyTHINKER_ENCRYPTION_KEY.getDecryptionSecrets()returns[key, PREVIOUS].BETTER_AUTH_SECRETis no longer read in this file.New
assertEncryptionKeyConfig()/encryptionKeyConfigProblems()throw aStartupConfigErrorwhen:BETTER_AUTH_SECRET;THINKER_ENCRYPTION_KEY_PREVIOUSis set but under 32 chars.Error messages name the variables, never their values, and include the
openssl rand -hex 32hint.main.tsruns the check inbootstrap()before the app and migrations start. A config error exits 1 withConfiguration error: ...instead of the migration hint. The legacy-key check now throws the same error type.EncryptApiKeysmigration andsecret-reencryption.service.tsno longer read or mention the fallback.docker/docker-compose.ymlnow requires the key with${THINKER_ENCRYPTION_KEY:?...}.Docs:
docker/.env.example,packages/backend/.env.example,DOCKER_README.md(docker run examples now pass the key) anddocs/dev/manifest-guide.md.CI: the e2e job env and docker-smoke both set a key. Docker-smoke also checks that install.sh writes the key.
The changeset ships inside 0.1.0 and bumps nothing, like feat(backend)!: rename client-visible identifiers to THINKER_*, thk_ and x-thinker-* #138.
Not done here (out of my scope):
docker/install.shis unchanged, sodocker/.env.examplekeeps the commented# THINKER_ENCRYPTION_KEY=line it rewrites. The issue's switch to an uncommented line and the "--upgradewithout the key" abort still belong to devops. The release notes indocs/releases/0.1.0.md(docs-ptbr) also need an entry for this change.Tests
BETTER_AUTH_SECRET.test/support/unit-env.tsandtest/support/e2e-env.tssupply a test key.test/encryption-key-required.e2e-spec.tsboots the realsrc/main.tswith the key missing, short, equal toBETTER_AUTH_SECRET, and with a short PREVIOUS. Each run must exit non-zero with the expected message. It also saves a provider key through the API and checks that it decrypts with the key and not withBETTER_AUTH_SECRET.npm run lint(0 errors),npm run typecheckandnpm run buildpass for packages/backend;npx jest: 475 suites, 9096 tests passed;TZ=UTC npm run test:e2e:docker: 72 suites, 604 tests passed.qa-docker is needed (runtime boot behavior changed).
🤖 Generated with Claude Code