docs: fix capitalization losses + one text corruption from the contractions pass
Review pass on top of the previous commit (85a94671) turned up three
real issues its own vale-only verification didn't catch:
- 'has not' -> 'hasn't' fired inside 'has nothing', corrupting
docs/swarm/README.md into 'hasn'thing to say upward'. Restored
'has nothing'.
- ~44 hits where the original text was genuinely sentence-initial
('It is', 'That is', 'They are', 'What is', 'Do not', ...) got
contracted in lowercase, silently de-capitalizing the start of a
sentence. Checked each one's real surrounding text and capitalized
the ones that needed it.
- Confirmed (by reading the actual text, not just trusting vale) that
the 3 remaining 'is not'/'has not'/'it is' hits after those fixes are
vale's own false positives: it matches these as a bare substring with
no trailing word boundary, so it also fires on 'is nothing', 'has
nothing', and the already-correct 'it isn't'. Left those alone --
there's no real issue in the text.
Re-verified with a fresh vale run: the remaining 17 hits are exactly
the union of the 14 legitimate ALL-CAPS/bold-emphasis skips from the
first commit and these 3 false positives. Full accounting in
/agents/iris/state/haiku-batches/contractions-report.md.
This commit is contained in:
parent
78021ce982
commit
ff377b0dee
15 changed files with 47 additions and 47 deletions
|
|
@ -58,7 +58,7 @@ declarations.
|
|||
the PAT injected?** → [`integrations/github.md`](integrations/github.md)
|
||||
(operator content up top; the `gh`/git-push + notification-poller
|
||||
mechanics are in a collapsed "Implementation" section at the bottom).
|
||||
- **What is `/knowledge`? How does the hive-wide knowledge repo sync,
|
||||
- **What's `/knowledge`? How does the hive-wide knowledge repo sync,
|
||||
and how do I contribute a document?** → [`integrations/knowledge.md`](integrations/knowledge.md).
|
||||
- **What does `hivectl` do? Provisioning, gateway users, container
|
||||
shells?** → [`tools/hivectl.md`](tools/hivectl.md) (the curated guide);
|
||||
|
|
|
|||
|
|
@ -476,9 +476,9 @@ Only present on the one host running
|
|||
so it survives restarts and redeploys.
|
||||
|
||||
- `webhook-secret` — the HMAC key the swarm's forge webhooks are signed
|
||||
with. **Keep it.** it's handed to Forgejo when a hook is registered,
|
||||
with. **Keep it.** It's handed to Forgejo when a hook is registered,
|
||||
so replacing the file means every subsequent delivery fails
|
||||
verification until the hook is re-registered with the new value. it's
|
||||
verification until the hook is re-registered with the new value. It's
|
||||
generated automatically on first start; there is nothing to configure.
|
||||
|
||||
If the file is unreadable at startup the daemon still starts and logs
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ On by default, and listens on `httpsPort` (default 443) on every vhost beside th
|
|||
|
||||
The issuer is a **host-held hive CA**, not a bare self-signed leaf. A host service (`hive-tls-ca.service`, from the `hive-tls` module) generates a long-lived CA (`services.hyperhive.deploy.hive-controller.tls.caValidityDays`, default ~20y) under `services.hyperhive.deploy.hive-controller.tls.stateDir` (default `/var/lib/hive-tls`), then signs a gateway **leaf** (`leafValidityDays`, default 30d) with it. `hive-gateway-self-signed-cert` then imports the leaf into nginx's state dir (`/var/lib/hive-gateway/tls/{cert,key}.pem`).
|
||||
|
||||
⚠️ **don't collapse that import unit into pointing nginx at the CA dir.**
|
||||
⚠️ **Don't collapse that import unit into pointing nginx at the CA dir.**
|
||||
It does two jobs, and skipping it has taken the gateway down in production
|
||||
before. It re-modes the leaf (`hive-tls-ca` writes the key `0600
|
||||
root:root`; nginx's pre-start `nginx -t` runs as the *nginx user*, so a
|
||||
|
|
@ -455,7 +455,7 @@ legacy single-proxy block and all traffic is forwarded to the agent
|
|||
daemon as before.
|
||||
|
||||
**`extraFiles`**: per-agent `hyperhive.frontend.extraFiles` are in
|
||||
`mergedDist`, not in the base `cfg.frontend` dist. they're not under
|
||||
`mergedDist`, not in the base `cfg.frontend` dist. They're not under
|
||||
the nix-store `alias` path, so requests for them fall through
|
||||
`try_files` to `@<name>_dynamic` and are served by the agent daemon
|
||||
as before.
|
||||
|
|
@ -542,7 +542,7 @@ The daemon hashes passwords with BCrypt (cost 12) and writes
|
|||
`$2y$`-prefixed hashes that nginx accepts natively. No external
|
||||
`htpasswd` binary is required.
|
||||
|
||||
**what's not gated:** per-agent UI routes emitted into `agents.conf`
|
||||
**What's not gated:** per-agent UI routes emitted into `agents.conf`
|
||||
(served under `/agent/<name>/`) inherit no auth from `/` — nginx
|
||||
applies `auth_basic` per-location. Full per-agent coverage is a
|
||||
follow-up.
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ to all containers — agents and service containers alike.
|
|||
`HOST_ADDRESS` is the bridge gateway IP (the address part of
|
||||
`HIVE_NETWORK_SUBNET`, via `lifecycle::bridge_gateway_ip` — taken verbatim
|
||||
so a non-`.1` operator override still resolves to wherever the bridge
|
||||
actually lives). it's **load-bearing**: nixos-container's container-side
|
||||
actually lives). It's **load-bearing**: nixos-container's container-side
|
||||
network setup only installs a default route (`ip route add default via
|
||||
$HOST_ADDRESS`) when `HOST_ADDRESS` is non-empty. In bridge mode the
|
||||
host-side address/route setup is skipped, so writing it only affects the
|
||||
|
|
@ -257,7 +257,7 @@ wiring is runtime:
|
|||
gateway IP) into each container's `/etc`.
|
||||
- the `hyperhive-isolated-dns` oneshot (`nix/agent-modules/network.nix`), gated on that
|
||||
marker, rewrites `/etc/resolv.conf` to `nameserver <gateway-ip>` at boot.
|
||||
it's ordered `before` the harness (`hive-ag3nt`), the matrix daemon, and
|
||||
It's ordered `before` the harness (`hive-ag3nt`), the matrix daemon, and
|
||||
`tea-login` so the resolver is correct before the first DNS lookup.
|
||||
|
||||
**Why isolation is safe**: hive-c0re's control-plane sockets are unix
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ complete a migration.
|
|||
Two things it's not, both worth stating because both are common
|
||||
assumptions:
|
||||
|
||||
- **it's not the swarm controller**, and doesn't depend on one. it's
|
||||
- **It's not the swarm controller**, and doesn't depend on one. It's
|
||||
a NixOS host role: a btrfs subvolume tree, a socket-activated
|
||||
receiver, and the `wg-hive` interface the swarm module already brings
|
||||
up. that's why it can be deployed before any controller exists.
|
||||
- **it's not a backup product.** It happens to hold the data a backup
|
||||
up. That's why it can be deployed before any controller exists.
|
||||
- **It's not a backup product.** It happens to hold the data a backup
|
||||
would hold, and it should be operated accordingly (see
|
||||
[Operating it](#operating-it)) --- but nothing in it does scheduling,
|
||||
verification, or restore orchestration.
|
||||
|
|
@ -56,7 +56,7 @@ services.hyperhive.swarm.snapshotStore = {
|
|||
Two deliberate asymmetries in that pair, both liable to be misread as
|
||||
inconsistency:
|
||||
|
||||
- **`address` has no default.** it's a deployment fact a pushing hive
|
||||
- **`address` has no default.** It's a deployment fact a pushing hive
|
||||
can't derive, and a wrong guess means streaming an agent's state at
|
||||
whatever happens to answer. Unset, a push fails naming this option.
|
||||
- **`port` does default** (`51821`), because it's a convention both
|
||||
|
|
@ -64,7 +64,7 @@ inconsistency:
|
|||
coordination, not a guess.
|
||||
|
||||
Note the option lives under `swarm.*` while the receiving host's lives
|
||||
under `services.hyperhive.snapshotStore`. that's the distinction the
|
||||
under `services.hyperhive.snapshotStore`. That's the distinction the
|
||||
two namespaces carry throughout: `swarm.*` describes _the swarm_ as seen
|
||||
from this host, and a bare `services.hyperhive.<service>` describes _a
|
||||
role this host performs_. A store host sets both --- one to run the
|
||||
|
|
@ -78,7 +78,7 @@ and no credential argument, because the mesh is the authentication.
|
|||
|
||||
## The mesh is the authentication
|
||||
|
||||
There are no certificates here, and no key material of its own. that's
|
||||
There are no certificates here, and no key material of its own. That's
|
||||
deliberate rather than an omission.
|
||||
|
||||
WireGuard's cryptokey routing already binds a peer's source address to
|
||||
|
|
@ -116,7 +116,7 @@ The rule that matters:
|
|||
|
||||
Validation is an allowlist --- `[A-Za-z0-9_-]+` and nothing else. No
|
||||
slash and no dot means neither directory traversal nor an absolute path
|
||||
can survive it. it's deliberately an allowlist and not a list of
|
||||
can survive it. It's deliberately an allowlist and not a list of
|
||||
forbidden characters: a blocklist only ever excludes the attacks
|
||||
somebody already thought of.
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ nobody runs it day to day, so it rots and is discovered broken in
|
|||
production.
|
||||
|
||||
⚠️ **The assumption to keep true over time:** the store host runs
|
||||
nothing else. that's true on day one and quietly false the day someone
|
||||
nothing else. That's true on day one and quietly false the day someone
|
||||
notices the box has spare disk. Nothing in the config objects when it
|
||||
stops being true.
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ container build:
|
|||
`DeployWindow` — the deploy subtree's root, which holds it across every
|
||||
phase below it (it declares `Resource::MetaWindow`). Two meta
|
||||
mutations can therefore never interleave, so no commit lands inside another
|
||||
node's staged window. it's a queue resource rather than a runtime mutex
|
||||
node's staged window. It's a queue resource rather than a runtime mutex
|
||||
because a resource is held by a subtree root across its whole subtree, which
|
||||
a `MutexGuard` (bounded by one executor fn) can't — that's what lets a
|
||||
multi-node deploy own one window. For the same reason the window must stay
|
||||
|
|
@ -237,7 +237,7 @@ than to one DAG shape it happens to appear in. A **brace** is the one sanctioned
|
|||
exception: a pure-resource-holder root that declares on behalf of a subtree
|
||||
coordinated with itself, whose members then declare nothing.
|
||||
|
||||
it's forced rather than stylistic. Declaring a resource means _"I need this
|
||||
It's forced rather than stylistic. Declaring a resource means _"I need this
|
||||
exclusively"_, and the agent lease is single-unit — so **two siblings that both
|
||||
declared it could never run concurrently.** For a subtree whose whole point is
|
||||
concurrency (`Prebuild` beside the `Signal` → `Drain` quiesce window), declaring
|
||||
|
|
@ -349,7 +349,7 @@ the event's whole job.
|
|||
## Container view
|
||||
|
||||
`container_view.rs` maintains an in-memory snapshot of every nixos-container's
|
||||
systemd service state. it's polled on coordinator startup and re-scanned after
|
||||
systemd service state. It's polled on coordinator startup and re-scanned after
|
||||
every lifecycle operation (spawn, rebuild, kill) so the dashboard always reflects
|
||||
the actual container status without a live `nixos-container list` call on each
|
||||
render.
|
||||
|
|
@ -527,7 +527,7 @@ wants it.
|
|||
|
||||
Two things to know about the weights:
|
||||
|
||||
- they're **hive-wide** — unlike the caps there is no per-agent
|
||||
- They're **hive-wide** — unlike the caps there is no per-agent
|
||||
override in `meta/resource-limits.json`, so every agent carries the
|
||||
same value and the weight does _not_ rank agents against each other.
|
||||
What `80` buys is that agents yield to everything **not** on this
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ credential.** Neither tier can tell a container's genuine Claude Code stats
|
|||
from anything else shaped like OTLP arriving on that port — including data
|
||||
smuggled out in resource attributes on an otherwise-legitimate export.
|
||||
|
||||
that's a **different risk from the one the collector fixes**, and strictly
|
||||
That's a **different risk from the one the collector fixes**, and strictly
|
||||
smaller than what preceded it: before, every agent held the upstream credential
|
||||
itself, so it could do all of the above _and_ use the token anywhere else. The
|
||||
collector removes the token and keeps the pipe. Agents are inside the trust
|
||||
|
|
@ -122,7 +122,7 @@ read. `0600` protects it from other containers, not from the agent itself. As
|
|||
long as the direct path stays _selectable_, that hole stays selectable; an
|
||||
option that can reintroduce it's a hole with extra steps.
|
||||
|
||||
**Why the tiers stay separate on one box.** they're not collapsed when
|
||||
**Why the tiers stay separate on one box.** They're not collapsed when
|
||||
co-located: an all-local hive is a statement about _where_ processes run, not
|
||||
about the shape of the deployment. A boundary that disappears locally is one
|
||||
the local deployment stops testing.
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ services.hyperhive.swarm.hives = {
|
|||
```
|
||||
|
||||
One attrset describing **every** hive in the swarm, **including this
|
||||
one**, keyed by that hive's `hiveName`. it's meant to be _identical on
|
||||
one**, keyed by that hive's `hiveName`. It's meant to be _identical on
|
||||
every host_ — write it once, share it, and each host reads it correctly
|
||||
because `services.hyperhive.hiveName` says which entry is itself.
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ evaluates cleanly points at a real machine that isn't the one you meant.
|
|||
> inside a swarm comes from the swarm root ([`ca.md`](ca.md)): every
|
||||
> hive chains to it, so one anchor replaces per-hive pinning entirely.
|
||||
> What that genuinely drops is trusting a hive whose root this swarm
|
||||
> does _not_ own — another swarm's, or one keeping its own CA. that's
|
||||
> does _not_ own — another swarm's, or one keeping its own CA. That's
|
||||
> a cross-swarm problem and wants a mechanism designed for it. (An
|
||||
> earlier `certFingerprint` field existed for exactly that gap, pinning
|
||||
> a peer's TLS leaf for hive-c0re's own peer HTTPS checks — removed
|
||||
|
|
@ -183,7 +183,7 @@ hiveName = "pr1ma"; hiveName = "edge";
|
|||
swarm.hives = { … }; swarm.hives = { … }; # byte-identical
|
||||
```
|
||||
|
||||
that's the point of the shape, and it removes a class of bug rather
|
||||
That's the point of the shape, and it removes a class of bug rather
|
||||
than saving typing: a per-host peer list let two hosts hold _different_
|
||||
facts about the same third hive — a stale endpoint, a rotated
|
||||
fingerprint — with nothing to detect the disagreement. One entry per
|
||||
|
|
@ -276,7 +276,7 @@ service it points at: `services.hyperhive.swarm.snapshotStore.{address,
|
|||
port}` tells this hive where the swarm's `btrfs receive` endpoint is, so
|
||||
`hivectl agent <name> subvol snapshot push` has somewhere to stream to.
|
||||
|
||||
it's genuinely swarm-scoped rather than per-peer — a swarm has exactly
|
||||
It's genuinely swarm-scoped rather than per-peer — a swarm has exactly
|
||||
one store, because the receiver keys destinations by _agent_ so a
|
||||
migrating agent keeps one unbroken incremental chain. See
|
||||
[snapshot-store.md](../networking/snapshot-store.md).
|
||||
|
|
@ -303,7 +303,7 @@ still knows its own state — you just can't see it from here.
|
|||
|
||||
A hive publishes only once it has been given the three
|
||||
`swarm.statusPublish` coordinates below. A hive that has not reads
|
||||
`never_reported` — it's not broken, it just hasn'thing to say upward.
|
||||
`never_reported` — it's not broken, it just has nothing to say upward.
|
||||
|
||||
| freshness | what to do about it |
|
||||
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ hive's own CA, and that hive CA signs the gateway leaf. A peer that
|
|||
trusts the root once validates every hive in the swarm, present and
|
||||
future, instead of being pinned to each one by hand.
|
||||
|
||||
that's the whole point of the hierarchy — it turns per-peer trust from
|
||||
That's the whole point of the hierarchy — it turns per-peer trust from
|
||||
O(n²) hand-pinning into one anchor per swarm.
|
||||
|
||||
## Two provisioning modes, one structure
|
||||
|
|
@ -49,7 +49,7 @@ swarm-services leaf can, and this host signs that one only under
|
|||
and every client sees a name mismatch.
|
||||
|
||||
So `hive-tls` emits an eval-time **warning** naming the uncovered names.
|
||||
it's deliberately not an assertion: this module can see what _it_ is
|
||||
It's deliberately not an assertion: this module can see what _it_ is
|
||||
able to issue, but not an operator-installed sub-CA in
|
||||
`swarm.ca.stateDir`, an external ACME setup, or a certificate delivered
|
||||
by any other means. If you have arranged one, the warning is expected
|
||||
|
|
@ -89,7 +89,7 @@ host:
|
|||
scp /var/lib/swarm-ca/root.pem <peer-host>:/var/lib/swarm-ca/root.pem
|
||||
```
|
||||
|
||||
that's the point of the hierarchy: the root is installed **once per
|
||||
That's the point of the hierarchy: the root is installed **once per
|
||||
swarm host**, not once per peer, so a hive joining later needs no edit on
|
||||
the hives already running. A hive whose cert already chains to a public
|
||||
CA has nothing to install — `peer-config` says so instead.
|
||||
|
|
@ -98,7 +98,7 @@ Handing a peer this hive's own `ca.pem` would not work even as a
|
|||
one-off: once a hive CA is an intermediate under the swarm root, it's
|
||||
no longer something a verifier can build a chain _to_ — OpenSSL won't
|
||||
terminate a chain at a trusted non-self-signed certificate without
|
||||
`-partial_chain`. that's why the tool distributes the root, not a
|
||||
`-partial_chain`. That's why the tool distributes the root, not a
|
||||
per-hive file.
|
||||
|
||||
## Adopting the hierarchy on an existing hive
|
||||
|
|
@ -117,7 +117,7 @@ Adoption happens by itself, once. `hive-tls-ca.service` notices that
|
|||
`ca-previous.pem`, and re-issues under the root; the next leaf is signed
|
||||
by the new CA.
|
||||
|
||||
it's safe to automate here precisely because this is the
|
||||
It's safe to automate here precisely because this is the
|
||||
all-on-one-host shape: every consumer is on this box, so "when will they
|
||||
have refreshed" is knowable rather than guessed.
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ build log isn't something anyone reads twice.
|
|||
|
||||
To keep the current CA on purpose — a hive that deliberately stays
|
||||
outside the hierarchy, or one mid-migration — `touch` the marker file
|
||||
named in the message. that's a decision, and it's recorded as one.
|
||||
named in the message. That's a decision, and it's recorded as one.
|
||||
|
||||
A hive with **no** root configured at all isn't affected by any of
|
||||
this: it self-signs exactly as it always has.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ so the store is a perfectly good place for it.
|
|||
|
||||
**The generator and the reader are usually in different containers.** They share
|
||||
the host's network namespace, which makes them feel co-located, but their
|
||||
filesystem roots are separate. that's why delivery is a **host-side copy rather
|
||||
filesystem roots are separate. That's why delivery is a **host-side copy rather
|
||||
than a bind mount**: `nixos-container` refuses to start when a bind source is
|
||||
missing, and a secret minted on another container's first boot doesn't exist
|
||||
yet. Binding it would make one container wait on a file that waits on a
|
||||
|
|
@ -59,7 +59,7 @@ neither is a renaming of the other.
|
|||
| the secret store's unseal material | the HSM/TPM under `deploy.bao.seal = "pkcs11"`; openbao itself under `"shamir"` | in the token; or held by whoever ran `bao operator init`, which is what `"shamir"` means and why it's stated rather than inferred | n/a — only the host running the store seals anything |
|
||||
|
||||
The three keys authelia mints for itself are generated in-container precisely
|
||||
because nothing outside that container ever reads them. **that's the test worth
|
||||
because nothing outside that container ever reads them. **That's the test worth
|
||||
applying to any secret added here** — and the client secret's plaintext half is
|
||||
the one row that fails it, which is the entire reason a delivery step exists.
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ secret isn't the container that reads it.
|
|||
The copy is only made when authelia is enabled **on this host** and something
|
||||
published is being scraped; otherwise no secret is needed and none is placed.
|
||||
|
||||
⚠️ **don't read that delivery unit as the only way this collector is fed.**
|
||||
⚠️ **Don't read that delivery unit as the only way this collector is fed.**
|
||||
Whether it authenticates follows the _credential_, never another service's
|
||||
placement: a swarm collector may run on a host that holds neither store and no
|
||||
authelia, and then the secret is an ordinary operator-provided file named in
|
||||
|
|
@ -108,12 +108,12 @@ admit anyone to the queue, so it belongs wherever the responder runs and nowhere
|
|||
else.
|
||||
|
||||
A hive that sets neither the public keys nor `autoGenerateCallout` fails at
|
||||
eval, naming the option it wants. that's deliberate: a queue that started
|
||||
eval, naming the option it wants. That's deliberate: a queue that started
|
||||
without them would accept `CONNECT {"user":"auth"}` from anyone sharing the
|
||||
host's network namespace, and nothing would look wrong until somebody connected.
|
||||
|
||||
**All four or none** — the seed paths are required too, not just the public
|
||||
keys. they're two halves of the same pair: the server verifies with the public
|
||||
keys. They're two halves of the same pair: the server verifies with the public
|
||||
half, the responder signs with the private one. Supplying only the public keys
|
||||
used to pass eval and leave the queue with an auth-callout nobody answers, which
|
||||
refuses every client rather than degrading — and a refusal reaches the client as
|
||||
|
|
@ -163,7 +163,7 @@ internal CA deletes that file and names its own paths in
|
|||
`deploy.bao.serverCertFile` / `clientCaFile`; the store itself has no opinion.
|
||||
A hive that reads from a store on **another** machine names the reader's half —
|
||||
`clientCertFile`, `clientKeyFile`, `serverCaFile` — and places that leaf by hand.
|
||||
it's the one credential that can't come out of the store, being what opens it;
|
||||
It's the one credential that can't come out of the store, being what opens it;
|
||||
everything else a hive needs does.
|
||||
|
||||
## The constraint that decides where the root lives
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ services.hyperhive.deploy.allSwarmServices = true;
|
|||
|
||||
**`deploy.allSwarmServices` is what "the swarm's shared services run
|
||||
here" means: every once-per-swarm service that's _optional_ takes its
|
||||
`enable` from it.** that's the whole rule, stated once — the per-service
|
||||
`enable` from it.** That's the whole rule, stated once — the per-service
|
||||
sections below don't repeat it, so a service that stops deriving is a
|
||||
visible difference rather than one more paragraph saying the same thing.
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ between "an operator's page" and "anyone with a session".
|
|||
## What it costs to be reachable
|
||||
|
||||
The apex is published to the hive's resolver like every other swarm
|
||||
service, so agent containers can resolve it. that's deliberate and it's
|
||||
service, so agent containers can resolve it. That's deliberate and it's
|
||||
not a hole: **reachability isn't the access control here.** An agent
|
||||
that resolves the name and connects still has no operator session, and
|
||||
the subrequest denies it.
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ agent's container uid/gid** — `o=--x` is traverse without listing, and
|
|||
both sockets are `0666`, which is all a dialer needs.
|
||||
|
||||
**Ownership is declared, not repaired.** The tmpfiles.d entry written by
|
||||
`SyncAgentTmpfiles` names the uid/gid directly. don't add a chown
|
||||
`SyncAgentTmpfiles` names the uid/gid directly. Don't add a chown
|
||||
alongside it: `d` re-applies on every boot _and_ every agent
|
||||
spawn/destroy, so ownership set afterwards is reverted the next time any
|
||||
agent changes — which is exactly how this dir spent a long time at
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
The sections below document specific mechanisms (the state-file endpoint,
|
||||
nixbld isolation, privilege separation). This section frames the model they
|
||||
serve: **what hyperhive defends, what it deliberately doesn't, and where the
|
||||
operator is accepting risk.** it's the reference for "is it safe to give an
|
||||
operator is accepting risk.** It's the reference for "is it safe to give an
|
||||
agent capability X?".
|
||||
|
||||
### The trust boundary is the container, not credential storage
|
||||
|
|
@ -66,7 +66,7 @@ checkpoints**, not about sandboxing the agent from its own tools:
|
|||
merges restricted to the operators team + a required operators-team approval
|
||||
(`apply_operator_branch_protection` / the config-repo equivalent) — and an
|
||||
agent (a write collaborator, not a repo admin) can neither change those
|
||||
settings nor merge its own PR. it's **not** set up for external VCS (GitHub
|
||||
settings nor merge its own PR. It's **not** set up for external VCS (GitHub
|
||||
etc.), though — there, operator-merge is process + accepted risk, not a
|
||||
technical control.
|
||||
- **Approvals** — config changes, schedule additions, and other
|
||||
|
|
@ -161,7 +161,7 @@ services — including the core dashboard at `127.0.0.1:<dashboard_port>`, which
|
|||
has no application-layer auth of its own (see [`docs/scheduler/ci.md`](../scheduler/ci.md)). The `0600`
|
||||
token policy bounds file reads; network isolation bounds network reach.
|
||||
|
||||
**what's NOT exposed**:
|
||||
**What's NOT exposed**:
|
||||
|
||||
- `/home/<name>/.claude/` — mode `0700`, owned by the per-agent
|
||||
user `<name>`. nixbld users can't read it.
|
||||
|
|
@ -171,7 +171,7 @@ token policy bounds file reads; network isolation bounds network reach.
|
|||
can't read it.
|
||||
|
||||
**Policy**: all credential files written to agent state directories MUST be mode
|
||||
`0600` or stricter. don't create world-readable secret files in agent state dirs.
|
||||
`0600` or stricter. Don't create world-readable secret files in agent state dirs.
|
||||
|
||||
### Long-term fix
|
||||
|
||||
|
|
|
|||
|
|
@ -852,7 +852,7 @@ life). Two consequences for anything rendering it:
|
|||
string; don't switch on specific values. `restarting` in
|
||||
particular no longer exists, because no node kind is unique to a
|
||||
restart.
|
||||
- it's **not** exclusively operator-initiated, and **not** limited
|
||||
- It's **not** exclusively operator-initiated, and **not** limited
|
||||
to rebuild-shaped work — `running_transients()` on the backend is
|
||||
a status-only test (any `Running` node whose payload names a
|
||||
non-empty agent lights a pill), so work the operator never
|
||||
|
|
|
|||
Loading…
Reference in a new issue