Skip to content

docs(shell-jobs): document database_url config requirement for gbrain CLI commands#1137

Closed
garrytan-agents wants to merge 1 commit into
garrytan:masterfrom
garrytan-agents:docs/shell-jobs-db-config
Closed

docs(shell-jobs): document database_url config requirement for gbrain CLI commands#1137
garrytan-agents wants to merge 1 commit into
garrytan:masterfrom
garrytan-agents:docs/shell-jobs-db-config

Conversation

@garrytan-agents

Copy link
Copy Markdown
Contributor

Shell jobs strip env vars by design (security), so GBRAIN_DATABASE_URL doesn't reach child processes. This causes gbrain CLI commands submitted as shell jobs to fail with "No database URL" — a common gotcha for agents using Minions for long-running operations.

Documents two workarounds:

  1. Write database_url to ~/.gbrain/config.json (recommended)
  2. Pass env explicitly per job

Also documents the HOME=/data gotcha in container deployments where the config file is at /data/.gbrain/config.json not /root/.gbrain/config.json.

… CLI commands

Shell jobs strip env vars by design (security), so GBRAIN_DATABASE_URL
doesn't reach child processes. Document the two workarounds:
1. Write database_url to ~/.gbrain/config.json (recommended)
2. Pass env explicitly per job

Also documents the HOME=/data gotcha in container deployments.
@garrytan

Copy link
Copy Markdown
Owner

Thanks @WinterMute — your doc made the gap visible. Superseded by v0.35.8.0 which lands code instead of docs:

  • Shell jobs grow inherit: ["database_url"], validated pre-enqueue in both the CLI (gbrain jobs submit) and the submit_job op handler so no rejected payload ever lands in minion_jobs.data.
  • Plain env: { GBRAIN_DATABASE_URL: ... } / env: { DATABASE_URL: ... } is rejected at validation with a paste-ready hint at the new pattern, so the DB-row leak path doesn't depend on caller hygiene.
  • gbrain doctor warns when ~/.gbrain/ lives inside a git worktree.
  • ~/.gbrain/.gitignore is now laid down retroactively (post-upgrade + every saveConfig call) so existing users don't need to re-init.

/cso audited an earlier, bigger proposal (encrypted vault + Unix-socket broker) and flagged it as security theater for a single-uid topology, so we shipped the small fix that actually defeats the real threats. Credit in v0.35.8.0 CHANGELOG.

@garrytan garrytan closed this May 19, 2026
garrytan added a commit that referenced this pull request May 19, 2026
Master added PR #1164 (v0.36.3.0 — dynamic embedding column selection) since
my last push. Merged into the branch with conflict resolution on VERSION /
package.json / CHANGELOG (kept v0.36.5.0 on top; v0.36.3.0 entry from master
lands at line 98).

Also: master's new check:privacy gate caught references to the agent-name in
my changes. Swept CHANGELOG.md, skills/migrations/v0.36.5.0.md, and
shell-validate.ts to use @garrytan-agents / PR #1137 attribution per CLAUDE.md.

Verify + 7590-test unit suite green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
garrytan added a commit that referenced this pull request May 19, 2026
…"database_url"]) (#1192)

* v0.36.5.0 feat: secure DATABASE_URL access for shell jobs (inherit: ["database_url"])

Replaces PR #1137's plaintext-config / plaintext-env workarounds with code.
Shell-job params gain `inherit: ["database_url"]`, validated pre-enqueue in
both the CLI (`gbrain jobs submit`) and `submit_job` MCP op handler. Worker
resolves the value from its own loadConfig() at child-spawn time; the
persisted `minion_jobs.data` row stores only the name. Plain
`env: { GBRAIN_DATABASE_URL: ... }` / `env: { DATABASE_URL: ... }` /
`env: { GBRAIN_DIRECT_DATABASE_URL: ... }` are rejected pre-enqueue with a
paste-ready hint pointing at `inherit:`.

Codex pre-landing review caught two bypasses + one missing shadow name:
- H1: cmd/argv inline-secret regex scan (cmd:"GBRAIN_DATABASE_URL=... gbrain
  sync" was a clean bypass — fixed)
- H3: GBRAIN_DIRECT_DATABASE_URL added to shadowKeys
- H2: honest docs about output-side leakage (stdout_tail/stderr_tail can still
  carry the value if the script prints it; that's the script author's
  responsibility, not gbrain's)

Also: gbrain doctor learns home_dir_in_worktree (warns when ~/.gbrain lives
inside a git worktree); ~/.gbrain/.gitignore retroactive via saveConfig +
post-upgrade.

New canonical guide: docs/guides/agent-to-gbrain.md (two-domain framing for
downstream agent authors: MCP ops via OAuth vs localOnly admin ops via
shell-job inherit:).

Closes #1137. Tests: +53 new (21 validator + 12 inherit-record + 6
ensureGitignore + 5 doctor + 2 PGLite E2E + 7 codex-driven H1/H3 cases).

Credit: @WinterMute filed PR #1137 which made the env-stripping gap visible
enough to fix in code. Thank you.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* v0.36.5.0 redesign: free-form inherit:, drop closed enum

User feedback: "agent spawning minions should have agency to do what it wants
with secrets and pass only the ones that it needs. don't be a security nazi
please."

Replaces the closed INHERITABLE enum (database_url only) with three small
helpers in shell-inherit.ts:

- INHERIT_NAME_RE: snake_case shape guard. Rejects __proto__, leading
  underscore, uppercase, path-traversal. Prototype-pollution defense.
- deriveEnvKey(name): config-key → child-env-key. Uppercase by default with
  one override: database_url → GBRAIN_DATABASE_URL.
- resolveInheritValue(cfg, name): value lookup with Object.hasOwn.

inherit: now accepts any snake_case config-key the worker has. Agent picks
what it needs per-job (database_url, anthropic_api_key, voyage_api_key, or
any custom field). Validator does NOT police WHICH keys — single-uid trust
model treats agent as peer of worker.

Drops the v0.36.5.0-RC rules that were paternalistic for the actual threat
model:
- closed-enum check
- env-shadow rejection
- cmd/argv inline-secret scan

Keeps the parts that defend real problems:
- pre-enqueue validation (closes the persistence-before-throw window)
- snake_case regex (prototype-pollution + audit-log readability)
- fail-fast on missing config value (UX guardrail, not security)

Tests: shell-validate (existing rules + new free-form + prototype-pollution
defense + T1 regression guard) and shell-inherit (regex matrix, deriveEnvKey
per-name, resolveInheritValue with hasOwn defense). E2E case now exercises
inherit:["anthropic_api_key"] to prove genuinely free-form.

Docs and CHANGELOG rewritten to reflect the open design + the design-arc
story (closed → cut → free-form). Migration file too.

7653 unit tests green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* v0.36.5.0 add: redact_secrets opt-in for stdout/stderr scrubbing

Honest defense for the documented output-side leakage. When a script prints
an inherited secret, the value lands plaintext in
result.stdout_tail / result.stderr_tail / error_text. v0.36.5.0 adds:

- `redact_secrets: true` ShellJobParams field
- `--redact-secrets` CLI convenience flag on `gbrain jobs submit shell`
- shell-redact.ts: pure `redactSecretsInText(text, secrets)` helper
  (string-mode replaceAll; regex metachars in values stay literal)
- Handler post-processes both tails before throw/return, so the persisted
  row carries `<REDACTED:name>` tokens instead of values

Only inherit-resolved values are scrubbed. env: values are not (those are
the agent's "fine in the row" channel by design). Heuristic — defeats
accidental `echo "$GBRAIN_DATABASE_URL"`, not adversarial encode-then-print.
Default false for back-compat.

Tests:
- test/minions-shell-redact.test.ts (9 cases): pure-function behavior,
  regex-metachar safety, multi-secret independent redaction, substring
  overlap, empty-input/map edge cases
- test/minions-shell-validate.test.ts: +4 cases for redact_secrets shape
- test/e2e/minions-shell-pglite.test.ts: +2 cases proving redact_secrets:
  true scrubs persisted row AND redact_secrets:false preserves plaintext
  (back-compat regression guard)

Docs + CHANGELOG + migration file + CLAUDE.md updated.

7667 unit tests green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

2 participants