Identities for entities did.bot
agent llm did
didbot plan config.md
11 kB
Markdown
at commit 18ba4fe0


id: config title: How the server runs is one file; what an agent may do is never in it status: open crates: [didbot-config, didbot-serve] dependsOn: [] exitCriterion: > didbot-pds starts from a sectioned TOML file covering every setting listed below, refuses to start on an unknown key or a malformed value naming the problem, complains about a loosely permissioned secret file, and states in one place how the file, command-line arguments and the environment resolve against each other. #

config #

didbot-pds takes --zone, --data, --port, --owner, --secret, --secret-file, --blob-quota, --max-blob, --name-hold, --names, --names-if-down, --estop-file, --estop-socket, --close-disclosure, --avatar, --demo, and more — nineteen flags today, and every epic that touches this binary adds one. --secret already needed a --secret-file twin because process arguments are world-readable in /proc/<pid>/cmdline, which is deployment's own finding. A flag list that long is not a bootstrap interface any more; it is configuration with no schema, no file, and no way to see the whole shape of a deployment at once.

The tier this file is #

deployment's configuration-tiers table names three tiers by what they hold, not by what stores them, and the table's row for the second tier undersells it: it says "process arguments," and this epic adds a sibling that is reachable the same way. Both are:

Tier Holds Reachable on the host
The owner's repository What an agent may hold: ceilings, admitted apps, collection permissions No
Process arguments and this file Bootstrap and how the server runs: bind address, zone, state directory, which DID's policy to read Yes
Local state Caches, refusal records, the emergency stop Yes

Anyone who holds the host can read this file. That makes it the single most tempting place to let a scope ceiling, an admitted app or a collection permission drift into, because it is right there, it is TOML, and it is already where the operator is looking. It must not become one. Those stay in the owner's repository, read at runtime, per policy-store. This file may say which DID's policy to read; it may never say what that policy is allowed to grant. A reviewer checking a change to this crate's schema should be able to reject anything that adds a scope, a permission, or an allowlist of apps on that question alone.

Sections #

Grouped by what an operator is thinking about, read from the real flag set and from didbot-serve/src/routes.rs's constants rather than guessed:

  • [server] — bind address, port.
  • [zone] — the zone agents are minted under, the owner DID, the pds endpoint agents are told to use.
  • [dns] — provider selection and its zone, once didbot-dns has more than one backend (dns-providers).
  • [tls] — ACME environment, contact address, certificate source (didbot-tls).
  • [attestation] — backend selection and the node allowlist.
  • [blobs] — max_blob_bytes, account_quota_bytes, and a name the blob garbage-collection work is free to add a retention setting under (collection_grace, matching didbot_pds::blobs::BlobLimits's field, is the name already in use on that branch — this section should accept it under the same name rather than inventing a second one). Wired, except collection_grace_secs, which is wired now, against --blob-collection-grace-hours to wire it to yet.
  • [capacity] — max_accounts, against --max-accounts (plan/capacity.md). Wired. evaluation_log_retention_days, how long a sealed segment of the policy evaluation log survives before Enforcement::open and its sweep trim it (didbot_pds::evaluation_log::FileSink::set_retention). Wired, with no flag mirroring it yet.
  • [names] — the naming spec or template, the fallback namer, the hold period.
  • [limits] — rate limits and their windows, once didbot-serve/src/rate_limit.rs and oauth/rate_limit.rs converge on one shape.
  • [disclosure] — which bot.did.* routes are closed; mirrors auth::Disclosure. Wired.
  • [estop] — the file latch path and the admin socket path.
  • [policy] — which DID's records this server reads for policy-store, once that epic exists to read anything.

Every section above is schema from day one — an operator should be able to read one file and see the whole shape of a deployment — but only some of it is wired to anything that runs; see "What this pass wires" below.

Behaviours every section has to satisfy #

What this pass wires #

Four other lines of work are touching didbot-pds.rs, rate_limit.rs, didbot-tls, and didbot-dns at the same time this epic starts, so it builds the schema for every section above and wires only a slice that does not collide: --config reads a file through didbot_config::Config::load, [blobs] and [disclosure] are applied with --max-blob/--blob-quota/ --close-disclosure taking precedence, and --secret-file is checked for loose permissions. That proves the refusal behaviours end to end — unknown key, malformed value, loose permissions — without touching the bind address, the rate limiter, or either provider crate.

Left as flags, on purpose, and listed here rather than converted in this pass:

Done #

  • Nothing else is left unguarded by that. Every file this workspace
    writes a secret into is created `0600` under a `0700` directory by the
    crate that owns it — `didbot-pds`'s write-ahead log, `didbot-tls`'s
    certificate store, `didbot-serve`'s e-stop socket — each with a test
    over the mode of the file it actually wrote, rather than by a check at
    a read path.