* 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>
25 KiB
Upgrading Downstream Agents
GBrain ships skills in skills/. Downstream agents (custom OpenClaw deployments,
agent forks of any kind) often copy these skill files into their own workspace and
diverge over time — adding agent-specific phases, removing irrelevant ones, tightening
language. Once that happens, gbrain can't push updates to those forks. The agent has
to apply the diffs by hand.
This doc lists the exact diffs each downstream agent needs to apply when upgrading. Cross-reference against your fork's local skill files.
Why this exists
gbrain upgrade ships the new binary. gbrain post-upgrade [--execute --yes] runs
the schema migrations and backfills the data. But the skill files themselves
that tell the agent how to behave — those are user-owned. If your ~/git/<your-agent>/workspace/skills/brain-ops/SKILL.md
says # Based on gbrain v0.10.0 at the top, it doesn't know about v0.12.0 features.
The agent will keep manually calling gbrain link after every put_page (now redundant —
auto-link does it), miss out on gbrain graph-query for relationship questions, and
not know to backfill the structured timeline.
How to apply
- Identify your forked skill files. Typically at
~/git/<your-agent>/workspace/skills/or wherever your agent's skill directory lives. - For each skill listed below, find the matching phase/section in your fork.
- Apply the diff (paste the new block in the indicated location).
- Update the version banner at the top of your fork (
# Based on gbrain v0.12.0). - Verify: ask the agent to write a test page and confirm the response includes
auto_links: { created, removed, errors }.
Total time: ~10 minutes for all four skills.
1. brain-ops/SKILL.md
Where: Insert a new ### Phase 2.5 section immediately after ### Phase 2: On Every Inbound Signal.
Why: Phase 2.5 declares that auto-link runs automatically. Without this, the
agent's mental model says it must call gbrain link after every put_page, which
is now redundant and can cause double-add warnings.
### Phase 2.5: Structured Graph Updates (automatic)
Every `put_page` call automatically extracts entity references and writes them
to the graph (`links` table) with inferred relationship types. Stale links
(refs no longer in the page text) are removed in the same call. This is
"auto-link" reconciliation.
- No manual `add_link` calls needed for ordinary page writes.
- Inferred link types: `attended` (meeting -> person), `works_at`, `invested_in`,
`founded`, `advises`, `source` (frontmatter), `mentions` (default).
- The `put_page` MCP response includes `auto_links: { created, removed, errors }`
so the agent can verify outcomes.
- To disable: `gbrain config set auto_link false`. Default is on.
- Timeline entries with specific dates still need explicit `gbrain timeline-add`
(or batch via `gbrain extract timeline --source db`).
Also update the Iron Law section. If your fork still says "Back-links maintained on every brain write (Iron Law)" without qualification, append:
**v0.12.0 update:** Auto-link satisfies the Iron Law for entity-reference links
on every `put_page`. The agent's Iron Law obligation is now: include the
entity reference in the page content (e.g., `[Alice](people/alice)`); auto-link
handles the structured row. Manual `add_link` calls are reserved for
relationships you can't express in markdown content.
2. meeting-ingestion/SKILL.md
Where: Append to the end of ### Phase 3: Attendee enrichment.
Why: Eliminates redundant gbrain link calls per attendee (auto-link handles them
when the meeting page references attendees as [Name](people/slug)).
**Note (v0.12.0):** Once the meeting page is written via `gbrain put`, the
auto-link post-hook automatically creates `attended` links from the meeting
to each attendee whose page is referenced as `[Name](people/slug)`. You don't
need to call `gbrain link` for attendees. You DO still need `gbrain timeline-add`
for dated events (auto-link only handles links, not timeline entries).
Where: In ### Phase 4: Entity propagation, the line "Back-link from entity page
to meeting page" can be replaced with:
4. Entity references in the meeting page body auto-create the link via auto-link.
For incoming references on the entity page (entity page → meeting page), edit
the entity page to mention the meeting and `put_page` it — auto-link handles
the rest.
3. signal-detector/SKILL.md
Where: Append to the end of ### Phase 2: Entity Detection.
Why: Same logic as brain-ops — eliminates manual gbrain link after writing
originals/ideas pages that reference people or companies.
**Auto-link (v0.12.0):** When you write/update an originals or ideas page that
references a person or company, the auto-link post-hook on `put_page`
automatically creates the link from the new page to that entity. You don't
need to call `gbrain link` manually. Timeline entries still need explicit calls.
4. enrich/SKILL.md
Where: Replace ### Step 7: Cross-reference with the v0.12.0 version.
Why: Step 7 used to be primarily about creating links between related entity pages. With auto-link, that's automatic. Step 7 is now about content updates, not link creation.
Old (delete):
### Step 7: Cross-reference
- Update company pages from person enrichment (and vice versa)
- Update related project/deal pages if relevant context surfaced
- Check index files if the brain uses them
- Add back-links manually via `gbrain link` for any new entity references
New (paste):
### Step 7: Cross-reference
- Update company pages from person enrichment (and vice versa)
- Update related project/deal pages if relevant context surfaced
- Check index files if the brain uses them
**Note (v0.12.0):** Links between brain pages are auto-created on every
`put_page` call (auto-link post-hook). Step 7 focuses on content
cross-references (updating related pages' compiled truth with new signal
from this enrichment), not on creating links. Verify via the `auto_links`
field in the put_page response (`{ created, removed, errors }`).
Timeline entries still need explicit `gbrain timeline-add` calls.
After all four diffs are applied
-
Bump the version banner at the top of each forked file:
# Based on gbrain v0.12.0 skills/<skill-name>, extended with <your-agent>-specific config -
Run the v0.12.0 backfill (this populates the graph for your existing brain):
gbrain post-upgradeThe v0.12.0 release wires post-upgrade to call
apply-migrations --yesautomatically, which runs the v0_12_0 orchestrator (schema → config check →extract links --source db→extract timeline --source db→ verify). Idempotent; cheap when nothing is pending. -
Verify auto-link works: ask the agent to write a test page that references
[Some Person](people/some-person). Confirm the put_page response includesauto_links: { created: 1, removed: 0, errors: 0 }. -
Verify graph traversal works:
gbrain graph-query people/some-well-connected-person --depth 2Should return an indented tree of typed edges.
v0.12.2 hotfix (data-correctness, no skill edits)
v0.12.2 is a Postgres data-correctness hotfix. No forked skill files need to change — the skill contracts are unchanged. But you DO need to run the migration, and you should know about one behavior change in markdown parsing.
1. Run the migration (Postgres-backed brains)
gbrain upgrade
The v0_12_2 orchestrator runs gbrain repair-jsonb automatically. It rewrites
rows where jsonb_typeof = 'string' across pages.frontmatter, raw_data.data,
ingest_log.pages_updated, files.metadata, and page_versions.frontmatter.
Idempotent, safe to re-run. PGLite brains no-op cleanly.
Verify after upgrade:
gbrain repair-jsonb --dry-run --json # expect totalRepaired: 0
2. Recover any truncated wiki articles
If your brain imported wiki-style markdown before v0.12.2, some pages were
silently truncated (any standalone --- in body content was treated as a
timeline separator). Re-import from source:
gbrain sync --full
The new splitBody rebuilds compiled_truth correctly.
3. Know the splitBody contract going forward
splitBody now requires an explicit timeline sentinel. Recognized markers
(priority order):
<!-- timeline -->(preferred — whatserializeMarkdownemits)--- timeline ---(decorated separator)---directly before## Timelineor## Historyheading (backward-compat)
A bare --- in body text is now a markdown horizontal rule, not a timeline
separator. If your agent writes pages with a bare --- delimiter, migrate to
<!-- timeline --> — the serializeMarkdown helper already does this.
4. Wiki subtypes now auto-typed
inferType now auto-detects five additional directory patterns as their own
page types (previously they all defaulted to concept):
| Path pattern | New type |
|---|---|
/wiki/analysis/ |
analysis |
/wiki/guides/ |
guide |
/wiki/hardware/ |
hardware |
/wiki/architecture/ |
architecture |
/writing/ |
writing |
If your skills or queries filter by type=concept and expect wiki content in
that bucket, update them to include the new types.
v0.13.0 — Frontmatter Relationship Indexing
Verdict: no action required for most skills. v0.13 projects YAML frontmatter fields into the graph as typed edges. The ingestion API is unchanged — keep calling put_page with frontmatter the way you do today; the graph auto-populates behind the scenes.
Three skills get an optional new phase if you want to consume the new auto_links.unresolved response field. Without this, unresolvable frontmatter names silently skip (same as v0.12 behavior).
1. meeting-ingestion/SKILL.md (optional)
Where: Add a new section after "Phase 3: Write Meeting Page".
### Phase 3.5: Check for unresolved attendees (v0.13+)
After `put_page`, inspect `response.auto_links.unresolved` — an array of frontmatter
references that did not resolve to existing pages. For meetings, this usually means
attendees you haven't created a person page for yet.
If `unresolved.length > 0`:
- Option 1 (create pages now): trigger an enrichment pass to build the missing people pages.
- Option 2 (defer): log the unresolved names to the enrichment queue for later.
- Option 3 (accept the gap): the attendee edge will not be created until a page exists.
Re-running `gbrain extract links --source db --include-frontmatter` after creating
the page fills in the missing edges.
2. enrich/SKILL.md (optional)
Where: Add to the enrichment trigger list.
### Drain unresolved frontmatter names (v0.13+)
If any `put_page` response includes `auto_links.unresolved` entries, the enrichment
tier should pick up those (field, name) pairs and try to create the missing entity
pages. Example flow:
1. signal-detector captures a meeting with `attendees: [Alice Known, Unknown Person]`
2. put_page returns `auto_links.unresolved = [{field: 'attendees', name: 'Unknown Person'}]`
3. enrichment tier consumes `Unknown Person` → web search → creates `people/unknown-person.md`
4. The next put_page (or a backfill run) wires up the `attended` edge automatically
3. idea-ingest/SKILL.md (optional)
Where: Same pattern as meeting-ingestion — check auto_links.unresolved after put_page, route names to enrichment.
Unchanged skills (no diffs needed)
- brain-ops/SKILL.md — auto-link mechanics are internal; the write path stays the same.
- signal-detector/SKILL.md — signal capture path unchanged.
- query/SKILL.md —
traverse_graphnow returns richer results automatically. - daily-task-manager/SKILL.md, briefing/SKILL.md, citation-fixer/SKILL.md, media-ingest/SKILL.md — unchanged.
New edge types you can filter in graph queries
v0.13 edges carry new link_type values. If your fork has graph-query skills that filter by type, these are now available:
works_at(person → company) — fromcompany:,companies:, orkey_people:founded(person → company) — fromfounded:invested_in(investor → deal/company) — frominvestors:orlead:led_round(lead → deal) — fromlead:yc_partner(partner → company) — frompartner:attended(person → meeting) — fromattendees:discussed_in(source → page) — fromsources:source(page → source) — fromsource:related_to(page → target) — fromrelated:orsee_also:
Migration timing
gbrain upgrade takes 2-5 min on a 46K-page brain (one-time). Runs out-of-process via gbrain post-upgrade. If your agent holds a DB connection during the upgrade, reconnect after; otherwise keep serving.
Type normalization NOT in v0.13
Legacy rows with link_type='attendee' or link_type='mention' coexist with new 'attended' / 'mentions' rows. Your queries filtering on old type names keep working. A separate opt-in gbrain normalize-types command in v0.14 handles the rename.
v0.14.0 shell jobs (optional adoption, no skill edits)
Adds a shell job type to Minions so deterministic cron scripts (API fetch, token
refresh, scrape + write) move off the LLM gateway. Zero tokens per fire. ~60%
gateway CPU headroom at typical scale. Feature is off by default, existing
installs keep running exactly as they did before. Nothing breaks.
To adopt, follow skills/migrations/v0.14.0.md. The short version:
- Set
GBRAIN_ALLOW_SHELL_JOBS=1on the worker process, thengbrain jobs work(Postgres). On PGLite, every crontab invocation uses--followfor inline execution; no persistent worker. - Classify each of your host's cron entries: LLM-requiring (keep on gateway) vs
deterministic (candidate for shell). Typical splits:
- Deterministic → shell:
ycli-token-refresh,x-oauth2-refresh,x-garrytan-unified,calendar-sync-to-brain,github-pulse,frameio-scan,flight-tracker,x-raw-json-backfill. - LLM-requiring → stay:
social-radar,content-ideas,adversary-vacuum,ea-inbox-sweep,morning-briefing,brain-maintenance.
- Deterministic → shell:
- For each deterministic cron, rewrite as:
3 13,16,19,22,1,4,7,10 * * * \ gbrain jobs submit shell \ --params '{"cmd":"node scripts/your-script.mjs","cwd":"/data/.openclaw/workspace"}' \ --max-attempts 3 --timeout-ms 300000 - Watch
gbrain jobs get <id>for exit_code / stdout_tail / stderr_tail on each fire. Compare against pre-migration behavior before approving the next batch.
No skill edits required. The handler runs worker-side; skill files don't change. If your host exposed custom handlers via the plugin contract (v0.11.0), they still work the same way.
Iron rule: never auto-rewrite the operator's crontab. Every rewrite is
per-cron, human-approved, with a diff. If you want automation later, the
upcoming gbrain crontab-to-minions <file> helper is P1 in TODOS.
v0.16.0: durable agent runtime
v0.15 ships gbrain agent run / gbrain agent logs, a new subagent handler
type in Minions, and a plugin contract for host-repo subagent defs. None of the
existing skills need surgery. The question for downstream agents is how to
adopt the new runtime, not how to patch around a breaking change.
1. Run a worker with an Anthropic key
The subagent handlers (subagent and subagent_aggregator) are always
registered on the worker. No separate opt-in flag — ANTHROPIC_API_KEY is
the natural cost gate (no key, the SDK call fails on the first turn), and
who-can-submit is already protected (PROTECTED_JOB_NAMES + trusted-submit:
MCP callers get permission_denied; only gbrain agent run can insert
these rows).
ANTHROPIC_API_KEY=sk-ant-... gbrain jobs work
Worker startup prints:
[minion worker] subagent handlers enabled
2. Ship your subagents as a plugin (OpenClaw + similar)
Move your custom subagent definitions out of your gbrain fork and into your own repo as a plugin. Concretely:
~/<your-agent>/gbrain-plugin/
├── gbrain.plugin.json
└── subagents/
├── meeting-ingestion.md
├── signal-detector.md
└── daily-task-prep.md
gbrain.plugin.json:
{
"name": "your-openclaw",
"version": "2026.4.20",
"plugin_version": "gbrain-plugin-v1"
}
Each subagents/*.md is a plain-text agent definition — YAML frontmatter +
body-as-system-prompt. Recognized frontmatter fields: name, model,
max_turns, allowed_tools (must subset the derived brain-tool registry).
Turn it on:
export GBRAIN_PLUGIN_PATH="$HOME/<your-agent>/gbrain-plugin"
Worker startup prints [plugin-loader] loaded '<name>' v<ver> (N subagents)
per plugin; any rejection (bad manifest, unknown tool in allowed_tools,
version mismatch) shows up as a loud warning at startup, not a silent dispatch-
time failure. See docs/guides/plugin-authors.md for the full contract.
3. Replace ephemeral subagent runs with durable ones
If your agent currently spawns ephemeral subagents (OpenClaw Agent(), ad-hoc
Anthropic API calls, etc.) for work that should survive crashes, sleeps, or
worker restarts, migrate those to gbrain agent run. The durability is free:
gbrain agent run "analyze my last 50 journal pages for recurring themes" \
--subagent-def analyzer --fanout-manifest manifests/journal-pages.json
Every turn persists to subagent_messages, every tool call is a two-phase
ledger, and gbrain agent logs <job> shows where it died + what the last
successful call returned. No more "re-run from scratch because the session
context evaporated."
4. put_page from subagents writes under an agent namespace
If you adopted the v0.15 subagent runtime, note that put_page calls
originating from a subagent's tool dispatch MUST target
wiki/agents/<subagent_id>/.... The schema shown to the model enforces this
on first try; a server-side fail-closed check rejects anything else. This
does NOT affect your skill files, CLI put_page calls, or MCP put_page —
only tool-dispatched writes from inside an LLM loop.
Aggregation output (the final "here's what all N children found" brain page) goes via a separate trusted CLI path, not through a subagent tool call, so it can write anywhere you want.
Iron rule: never grant an agent write access beyond its namespace. The server-side check exists because dispatcher bugs happen; treat it as defense in depth, not the primary boundary.
v0.22.4 — frontmatter-guard adoption
1. Stop hand-rolling frontmatter validators
If your fork has scripts that call js-yaml directly to validate brain page
frontmatter, replace them with gbrain frontmatter validate calls. The CLI
covers the seven canonical error classes and ships a --json envelope that's
stable across releases.
- # Custom validator script
- node scripts/validate-frontmatter.mjs <path>
+ gbrain frontmatter validate <path> --json
For consumers that need the validator inside another script, import from
gbrain's markdown export instead of duplicating logic:
import { parseMarkdown } from 'gbrain/markdown';
const parsed = parseMarkdown(content, filePath, { validate: true, expectedSlug });
for (const err of parsed.errors ?? []) {
// err.code: MISSING_OPEN | MISSING_CLOSE | YAML_PARSE | SLUG_MISMATCH |
// NULL_BYTES | NESTED_QUOTES | EMPTY_FRONTMATTER
}
2. Drop any references to lib/brain-writer.mjs
If your fork's skills or scripts referenced an aspirational
lib/brain-writer.mjs (it never shipped — the spec was in PR #392 and never
landed), replace those references with the gbrain CLI. The frontmatter-guard
skill lives at skills/frontmatter-guard/SKILL.md and points at
gbrain frontmatter validate / audit / install-hook.
3. Wire the doctor subcheck into your health pipeline
gbrain doctor now reports frontmatter_integrity automatically. If your
fork has a custom health pipeline (e.g. a daily Slack post about brain
health), pull from gbrain doctor --json and surface the
frontmatter_integrity row counts.
4. (Optional) Install the pre-commit hook on brain repos
For sources backed by git, the v0.22.4 install-hook helper drops a pre-commit script that blocks commits with malformed frontmatter:
gbrain frontmatter install-hook
Skip this if your brain isn't a git repo or if your downstream agent already
enforces validation at write time. See docs/integrations/pre-commit.md for
the full recipe.
5. Migration ergonomics — read pending-host-work.jsonl
After gbrain apply-migrations --yes runs the v0.22.4 audit, your agent
should read ~/.gbrain/migrations/pending-host-work.jsonl (filter to
migration === "0.22.4") and walk each entry's command field. Each entry
points to a per-source gbrain frontmatter validate <source_path> --fix
command — surface counts to the user, get explicit consent, then run.
The migration is audit-only. It never mutates brain content during
apply-migrations. Your agent runs the fix command with user consent.
Future versions
When gbrain ships a new version, this doc will be updated with the diffs for that version. Each new version appends a section; old sections stay so you can catch up multiple versions at once.
To check what your fork is missing:
diff <(grep -A3 "Based on gbrain" ~/<your-fork>/skills/brain-ops/SKILL.md) \
<(grep "v[0-9]" ~/gbrain/skills/migrations/ | tail -3)
v0.36.5.0 — Free-form secret inheritance for shell jobs calling gbrain CLI
The change. Shell-job params get a new inherit: field. Pass any
snake_case config-key name on it; the worker resolves the value from its
loadConfig() at child-spawn time and injects it into the child env. Names
land in the row; values never persist from inherit:. Validation runs
pre-enqueue in both submit paths (CLI + submit_job op), so a malformed
payload never lands in minion_jobs.data.
Why. Pre-v0.36.5.0, agents that wanted to call gbrain from shell jobs
had to either write database_url to ~/.gbrain/config.json plaintext or
pass env: { GBRAIN_DATABASE_URL: "..." } per-job. Both left plaintext
secrets somewhere — disk or DB row. inherit: keeps names in the row and
resolves values at spawn time.
What your agent can do. inherit: is free-form. Pass any config-key:
{
"cmd": "gbrain sync --skip-failed && gbrain embed --stale",
"cwd": "/data/gbrain",
"inherit": ["database_url", "anthropic_api_key", "voyage_api_key"]
}
The env-key name in the child is derived by uppercasing the config-key:
database_url → GBRAIN_DATABASE_URL, anthropic_api_key →
ANTHROPIC_API_KEY, voyage_api_key → VOYAGE_API_KEY, etc. The validator
does NOT police which config keys you inherit — the agent is in the same
uid as the worker, so it's the agent's call.
You can still use env:. v0.36.5.0 does not forbid env:{ ANYTHING }.
If you have a reason to put a value in the row plaintext (a non-secret
correlation token, or a secret you know is OK to persist), pass it via
env:. Prefer inherit: when you want the value out of the row.
Worker setup (one-time, per host):
gbrain config set database_url postgresql://...(or any other key you want available for inherit)- OR put the key in
~/.gbrain/config.jsondirectly - OR set
GBRAIN_DATABASE_URL/DATABASE_URL/ per-provider env on the worker process
If the worker can't resolve a requested name, the validator fail-fasts at
submit time with gbrain config set <X> hint. No more silent "No database
URL" failures in child stderr minutes after submission.
Also new. A gbrain doctor check home_dir_in_worktree warns if
~/.gbrain/ lives inside a git worktree. A retroactive ~/.gbrain/.gitignore
(single line *) is now laid down by every saveConfig() call AND by
gbrain post-upgrade, so existing users get coverage without re-running
gbrain init. Honest scope: the .gitignore covers casual git add but does
NOT cover already-tracked files, screenshots, backups, or git add -f.
Strategy framing. For agent-to-gbrain calls, the new canonical guide is
docs/guides/agent-to-gbrain.md. Two distinct surfaces: HTTP MCP via OAuth
for ops with MCP equivalents (search, query, put_page, etc.), and shell
job + inherit: for localOnly admin ops (sync, embed, dream,
doctor, etc.). Not a fallback hierarchy — pick by op.
Errors to handle (your agent submits shell jobs; surface these clearly):
| Error | What it means | Agent action |
|---|---|---|
shell: inherit must be an array of config-key names |
inherit wasn't an array. |
Pass "inherit": ["database_url", ...]. |
shell: inherit entries must be non-empty strings |
Element was empty, non-string, or null. | Use snake_case config-key names. |
shell: inherit name "<X>" must match [a-z][a-z0-9_]* |
Name failed snake_case regex (uppercase, leading underscore, etc.). | Use the config-key verbatim — database_url, not DATABASE_URL. |
shell: inherit requested "<X>" but worker has no <X> configured |
Worker can't resolve the name from its loadConfig(). |
Run gbrain config set <X> <value> on the worker host. |