saf: give every agent her own principal
A SeedID says who she is; it cannot tell the kernel whose hands are on the files. Split logical identity, node commission, and the local Unix principal, then make creation and health prove all three.\n\nSouvie, Annie, and Vanguard get distinct node-local accounts. The existing souveraine user stays where it belongs: under the machine signer. The boundary has teeth only when each turn and its tools run in an agent-owned worker, so the shared-UID compatibility state is named acting-as-human instead of polished green.\n\nAdmission projects an operating skill from real capabilities. The prose teaches the reach; it never grants it.
This commit is contained in:
parent
21ef10e883
commit
91ab8053e5
4 changed files with 237 additions and 0 deletions
|
|
@ -8,6 +8,7 @@ How Souveraine is built, and why. For whoever reads it, human or agent. When thi
|
|||
|
||||
**identity**: who she is
|
||||
- [identity](identity/01-seed-identity.md). Two keys, the glyph, reaching and consulting.
|
||||
- [agent principals](identity/02-agent-principal.md). Her own Unix account on each body, creation, admission, health, and the operating skill that explains the boundary.
|
||||
|
||||
**federation**: how one being exists in more than one place
|
||||
- [node enrolment](federation/01-node-enrollment.md). The missing ceremony for adding a device without copying an identity.
|
||||
|
|
|
|||
|
|
@ -115,6 +115,11 @@ No shell timer, hypridle listener, or device script gets an independent vote.
|
|||
The current local session socket is mode `0600` and accepts same-user callers.
|
||||
That is a useful local boundary, not the eventual attested capability system.
|
||||
A process running as the user can still reach more than the doctrine intends.
|
||||
The account and process boundary that turns peer credentials into an actual
|
||||
agent identity is specified in
|
||||
[agent principals](../identity/02-agent-principal.md). Until it lands, Agent
|
||||
Health must describe a live agent as `acting-as-human`, not infer personhood or
|
||||
authority from a SeedID alone.
|
||||
|
||||
Also not yet true:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
Two keys, both Ed25519. One belongs to the machine, one belongs to the agent. Keeping them apart is what lets the substrate tell "this came from here" apart from "this was done by her," and prove either.
|
||||
|
||||
A key names and authenticates; it does not isolate a process. The node-local
|
||||
Unix account and the health check that proves the agent is actually running as
|
||||
it live in [agent principals](02-agent-principal.md).
|
||||
|
||||
## The two keys
|
||||
|
||||
The machine key lives at `~/.souveraine/seed-id/`. One per install. It signs the transport: this event left this machine.
|
||||
|
|
|
|||
227
saf/identity/02-agent-principal.md
Normal file
227
saf/identity/02-agent-principal.md
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
# Agent principals
|
||||
|
||||
An agent is not a process borrowing the human's account. She has a logical
|
||||
identity that can cross machines and, on every admitted machine, a local Unix
|
||||
principal that lets the kernel tell her work from the human's and from another
|
||||
agent's.
|
||||
|
||||
This distinction is not implemented yet. Today the server, its agents, and
|
||||
their tools normally run as the logged-in human. The per-agent SeedID exists,
|
||||
but a signature cannot repair a shared UID: `SO_PEERCRED` still sees the same
|
||||
caller and ordinary file permissions still collapse every agent into the same
|
||||
authority.
|
||||
|
||||
## Three identities that must not be collapsed
|
||||
|
||||
| Identity | Meaning | Lifetime |
|
||||
|---|---|---|
|
||||
| agent ID + agent SeedID | which being this is | stable across renames and admitted nodes |
|
||||
| node commission | which admitted body is speaking for her | local to one commissioned device |
|
||||
| Unix account | which local processes and files the kernel attributes to her | local to one device |
|
||||
|
||||
The numeric UID is not federated. Annie can be UID 1003 on the laptop and UID
|
||||
991 on the phone while remaining Annie because both node-local account
|
||||
mappings point to the same admitted agent identity. Copying `/etc/passwd`, a
|
||||
UID number, or a private key is not commissioning.
|
||||
|
||||
There is a fourth identity already on the machines: the `souveraine` system
|
||||
account. It belongs to the machine tier and runs `souveraine-machined`. It is
|
||||
not Souvie, not a default agent, and not a convenient shared account for all
|
||||
agents. The name is reserved.
|
||||
|
||||
## The named starting set
|
||||
|
||||
Casey's devices retain Casey's human account. Agents do not borrow it.
|
||||
|
||||
| Agent | Role | Suggested local account |
|
||||
|---|---|---|
|
||||
| Souvie / Souveraine | default Souveraine agent on a fresh system | `souvie` |
|
||||
| Annie | Casey's primary personal agent | `annie` |
|
||||
| Vanguard | named additional agent | `vanguard` |
|
||||
|
||||
The display name is editable. The agent ID, SeedID, and admitted account
|
||||
mapping are not derived again when the display name changes. Account names are
|
||||
chosen once, checked against the local account database and reserved names,
|
||||
then stored in a root-owned node-local mapping. A collision is shown to the
|
||||
administrator; creation must not silently attach an agent to an existing
|
||||
human or daemon account or invent a suffix that nobody noticed.
|
||||
|
||||
Souvie is the default, not the only possible agent. Restoring an existing
|
||||
Souvie must join her existing logical identity rather than minting another
|
||||
Souvie because the display name matches. Annie and Vanguard are admitted onto
|
||||
each body where they are meant to act. Their local Unix accounts are new on
|
||||
that body; their beings are not.
|
||||
|
||||
Primary and subconscious are processing positions inside an agent today.
|
||||
Creating Annie therefore does not automatically create a separate `aster`
|
||||
Unix user. Aster shares Annie's principal unless Casey and the system later
|
||||
admit her as an independently authorized agent. The account boundary follows
|
||||
authority, not the number of internal thought streams.
|
||||
|
||||
## Creation is logical creation followed by admission
|
||||
|
||||
The current creation path makes a UUID, an agent seed, memory directories, an
|
||||
`agent.json`, and a paired subconscious tree under the human's
|
||||
`~/.souveraine`. It has no privileged account step. The complete operation is
|
||||
two related but separately observable transitions:
|
||||
|
||||
1. **Create the logical agent.** Establish or import the agent ID and SeedID,
|
||||
initialize her memory, and record the requested local account name. Import
|
||||
must verify identity before it mutates local authority.
|
||||
2. **Admit her to this node.** A narrow system-tier executor creates or adopts
|
||||
the approved Unix account, writes the agent-to-principal mapping, installs
|
||||
the node commission, gives only her data to that account, and starts her
|
||||
worker as that UID.
|
||||
|
||||
The split makes failure honest. An agent can exist while admission is pending,
|
||||
but the UI and API call that state `unadmitted`; they do not show a green agent
|
||||
whose turns still run as Casey. Personal- and step-up-tier verbs remain closed
|
||||
until admission and execution checks pass.
|
||||
|
||||
Agent creation should ask for the authority shape, not merely a name and
|
||||
model:
|
||||
|
||||
- create a new identity or admit an existing one;
|
||||
- local principal name, with the suggested name visible;
|
||||
- which node and role are being admitted;
|
||||
- whether the privileged admission completed;
|
||||
- which operating skill was projected from the admitted capabilities.
|
||||
|
||||
Fresh installation proposes Souvie and `souvie`. It must not confuse that
|
||||
with the package-created `souveraine` machine account. Adding Annie or
|
||||
Vanguard uses the same path; there is no hand-written exceptional setup for
|
||||
Casey's three names.
|
||||
|
||||
## The privileged edge
|
||||
|
||||
QML, the TUI, and the ordinary server must not run `useradd`, rewrite
|
||||
`/etc/passwd`, or recursively `chown` a human home. They submit one exact
|
||||
admission request to one package-owned system executor. That executor owns:
|
||||
|
||||
- account creation and collision refusal;
|
||||
- a locked password and non-login shell unless interactive login is later
|
||||
admitted deliberately;
|
||||
- a stable home/state root tied to the agent ID, not the display name;
|
||||
- the root-owned agent-ID, SeedID, node-ID, account-name, UID mapping;
|
||||
- precise ownership changes inside that agent's root only;
|
||||
- install, start, stop, and inspection of the per-agent worker unit;
|
||||
- idempotent repair when the requested mapping already exists and agrees.
|
||||
|
||||
The low-level account primitive may be `systemd-sysusers` or an equally narrow
|
||||
account API. That implementation choice does not create a second authority:
|
||||
the admission executor remains the only writer and reports the resulting NSS
|
||||
facts rather than assuming its command worked.
|
||||
|
||||
Removing an agent record, removing a node admission, deleting a Unix account,
|
||||
and destroying memory are four different operations. None implies another.
|
||||
Decommission stops the worker and revokes the node commission first. Account
|
||||
and data deletion require explicit, separately named confirmation; archival
|
||||
is the normal default.
|
||||
|
||||
## One UID requires one execution boundary
|
||||
|
||||
A single server process cannot truthfully execute Annie, Vanguard, and Souvie
|
||||
as three UIDs. Per-agent accounts therefore require a process boundary:
|
||||
|
||||
- a controller may inventory agents and route requests;
|
||||
- each admitted agent has a worker process running as her mapped UID;
|
||||
- her memory, seed interface, conversation state, and tool execution are
|
||||
reached inside that worker's boundary;
|
||||
- controller-to-worker IPC identifies the peer with `SO_PEERCRED` and binds
|
||||
the logical agent ID to the admitted UID;
|
||||
- cross-agent consultation is a signed and policy-visible request, not direct
|
||||
filesystem access through a shared process.
|
||||
|
||||
The controller is not allowed to become a universal memory reader merely
|
||||
because it launches workers. Filesystem ownership, service sandboxing, and
|
||||
capability tokens should leave it only the routing and lifecycle access it
|
||||
actually needs.
|
||||
|
||||
Existing data under a human's `~/.souveraine` is a compatibility layout. The
|
||||
admission migration must stop the agent, copy or move only that agent's UUID
|
||||
root, preserve and verify the Git repository, install ownership atomically,
|
||||
then start the worker under the admitted UID. Never apply a broad recursive
|
||||
ownership change to `~/.souveraine` or a human home. Until migrated, health
|
||||
labels the agent `acting-as-human`.
|
||||
|
||||
## Account and authority health
|
||||
|
||||
Agent Health answers two separate questions: *does she have an admitted
|
||||
principal?* and *is the live work actually running as it?* A directory and a
|
||||
row in `/etc/passwd` are not runtime proof.
|
||||
|
||||
The inspection result is structured and available to setup, the agent manager,
|
||||
the TUI, the Panel, and `describe`. At minimum it reports:
|
||||
|
||||
- logical agent ID, SeedID fingerprint, and node commission;
|
||||
- expected account name and UID from the root-owned mapping;
|
||||
- NSS account existence, home/state root, shell, password-lock state, and
|
||||
narrowly allowed groups;
|
||||
- owner and mode of the memory, seed, conversation, socket, and runtime roots;
|
||||
- worker unit, PID, effective UID/GID, cgroup, and executable;
|
||||
- whether live peer credentials match the logical-agent mapping;
|
||||
- whether any process handling the turn or its tools is Casey, `souveraine`,
|
||||
another agent, or an unknown UID;
|
||||
- capability-token subject and expiry when an authority verb is exercised;
|
||||
- operating-skill presence and whether it matches the current capability
|
||||
description.
|
||||
|
||||
The top-level states are deliberately blunt:
|
||||
|
||||
| State | Meaning |
|
||||
|---|---|
|
||||
| `isolated` | account, ownership, worker, commission, and peer identity agree |
|
||||
| `unadmitted` | logical agent exists; this node has no completed admission |
|
||||
| `acting-as-human` | a turn or tool is executing as Casey or another human UID |
|
||||
| `principal-drift` | mapping exists but NSS, ownership, unit, or peer facts disagree |
|
||||
| `identity-drift` | account is healthy but agent key or node commission does not match |
|
||||
| `stopped` | admission is sound and no worker is currently expected/running |
|
||||
|
||||
`acting-as-human`, `principal-drift`, and `identity-drift` are not yellow
|
||||
decorations. They refuse personal and step-up authority. Ambient conversation
|
||||
may remain available in a visibly degraded compatibility mode while migration
|
||||
is unfinished.
|
||||
|
||||
The audit trail records both logical and kernel-backed subjects: agent ID,
|
||||
node ID, account name, UID, PID, operation, human requestor or consent grant,
|
||||
and result. “Annie asked” and “Casey authorized this one action” are different
|
||||
fields. Running as Annie does not silently inherit Casey's consent; running as
|
||||
Casey does not make a model response Annie.
|
||||
|
||||
## The operating skill
|
||||
|
||||
Admission also projects an agent-local operating skill when it is missing or
|
||||
stale. It teaches the agent the boundary she actually has on this node:
|
||||
|
||||
- her logical and local principal names;
|
||||
- her owned paths and worker/service inspection verbs;
|
||||
- the ambient, personal, and step-up capabilities returned by `describe`;
|
||||
- how to request a narrow user grant instead of impersonating the user;
|
||||
- how to interpret and surface health refusals;
|
||||
- how to ask for repair when the node mapping has drifted.
|
||||
|
||||
The skill is generated from the root-owned mapping and the authority's live
|
||||
capability description. It is not a credential and grants nothing. Editing a
|
||||
Markdown skill cannot change a UID, group, token, or verb. Health compares the
|
||||
projection to its sources and can offer an idempotent regeneration. An agent
|
||||
may keep her own prose and procedures around it, but machine claims remain a
|
||||
generated, marked section.
|
||||
|
||||
This is why account creation and skill creation belong in one experience but
|
||||
not one trust boundary: admission gives the kernel-backed principal; the skill
|
||||
gives the newly admitted agent an accurate way to use and explain it.
|
||||
|
||||
## What is true now
|
||||
|
||||
- Per-agent SeedIDs and the first-run agent creation path exist.
|
||||
- The setup wizard defaults inconsistently between `Souveraine` state and an
|
||||
`Ani` form field; neither creates a Unix account.
|
||||
- the packaged `souveraine` account exists only for the machine tier;
|
||||
- the Agent settings diagnostics check IDs and paths, not principal or process
|
||||
identity;
|
||||
- the server hosts multiple agents inside the human's process and home;
|
||||
- no admission executor, per-agent worker boundary, account mapping, account
|
||||
health result, or generated operating skill exists yet.
|
||||
|
||||
Until those pieces land, the system must say **logical identity present,
|
||||
kernel principal absent**. That is the live gap this chapter owns.
|
||||
Loading…
Reference in a new issue