Skip to content
SHAMPOO

Manual · Chapter 4 of 19

SHAMPOO security and boundaries

SHAMPOO security and boundaries

Authentication

Every data call re-authenticates the session credential against live state: principal entry (active, generation) plus session entry (live, unexpired, profile-bound). Anything revoked, killed, expired, or replayed across profiles denies on its very next call with a generic authentication failed, identical for unknown profile, unknown client, and bad credential, so failures never say which part was wrong.

Bootstrap and enrolment

New principals join by one-time ticket, never by shared password:

  1. A conductor issues an enrolment ticket binding user, role, and projects server-side. The secret is shown once.
  2. The new spoke redeems it over an encrypted handshake, which provisions the principal and opens its first session in one atomic unit.
  3. The bootstrap secret lives in a 0600 file owned by the spoke; the ticket dies on use (exactly one winner under races).

Existing principals rotate the same way: re-issue the bootstrap secret, redistribute, confirm re-handshake. There is no in-place secret editing. See 12-authentication-and-enrolment.

Session binding

Sessions carry their profile permanently (bound at open, checked on every call beside identity and generation). A credential minted for one profile is useless in another. Sessions last one hour and renew at roughly 42–48 minutes with deterministic per-session jitter, so fleets never herd-renew; failed renewal backs off and then fails closed loudly rather than serving stale.

Project scope

Scope is enforced inside the platform's own query path, never by trusting the client. Conductor (and all-projects grants) see everything in the profile; everyone else sees exactly their granted projects. Ungranted content reads as absent: empty lists, zero counts, silent skips, never a "forbidden" that confirms existence. Assignee hints, task links, and search expansion are all scope-checked the same way.

Generic denials

Errors that would confirm or deny the existence of foreign content are reduced to uniform shapes: not found for missing-or-foreign objects, authentication failed for any credential problem, requires conductor role for privilege problems. Debugging your own boundary is fully supported (see 16-troubleshooting); probing another boundary always looks the same.

Revocation

Revoking a principal flips its entry and bumps its generation: every live session of that principal denies on its next operation, with no daemon restart. Killing a session ends exactly that session. Both are conductor actions, audited, effective immediately, and scoped to the profile whose entry changed; other profiles are unaffected by design.

Separate identities

Humans and agents never share a principal. Each assistant, each operator workflow, each service gets its own client_id with exactly the grants it needs. Sharing one identity would merge audit trails and force all-or-nothing revocation; separate identities make every action attributable and every decommission surgical.

The no-delete invariant

Durable platform history is never physically deleted. Where something must stop participating, its lifecycle state moves instead: tasks archive, knowledge voids (and can revive on re-creation), sessions end, runs discard, quorums cancel, links mark removed. Each transition keeps who, when, and why. Tests enforce the invariant mechanically: production code contains no physical deletion of durable state, and entries that are gone operationally remain historical truth.

Lifecycle instead of destruction is what makes the rest trustworthy: audits replay, revocations leave evidence, decisions keep their provenance, and "deleted" always means "retired with a record".

On this page