- Rust 97.4%
- Nix 2.1%
- Just 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Projection::{permitted_repositories, permitted_operations} and
IssueRequest::resource_repositories authorized the same way Vedanta's own
LifecycleCommand used to: two flat sets, unioned across every resource the
grant named. Projection now carries permitted_resources: Vec<ResourceGrant>
(Vedanta's own type) and IssueRequest names one resource_kind/resource_id
pair — a lease request always asks for exactly one resource — so authorize
looks up that one (kind, id) entry and holds the requested operations to
its own set, never the grant's total. build_command and the mirrored
DeployedGrant/Delegation wire type (crate::vedanta) carry the same shape
through to the signed command. The live bao_dev acceptance test still
passes un-ignored.
Co-Authored-By: Teyla <[email protected]>
|
||
| fixtures | ||
| openbao-plugin | ||
| openspec | ||
| src | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CONTEXT.md | ||
| devenv.lock | ||
| devenv.nix | ||
| devenv.yaml | ||
| flake.lock | ||
| flake.nix | ||
| justfile | ||
| module.nix | ||
| README.md | ||
Vedanta
The private identity and resource-credential effector for the Ting. Vedanta owns additive principal operations and performs bounded credential lifecycle effects only when commanded by OpenBao; sibling Ting/Jostoph owns subtractive principal lifecycle. Named and scoped by Ting/Ting#2, whose first open scope item was literally "Naming. This service needs a name (sibling to 'Jostoph' the steward)."
Built on jostoph-rs, the same way
Jostoph itself is: an Event in, a Ruling per gate, gates written as
ascent! Datalog programs (via Deduction), resolved deny-biased into one
Decision, audited. A ruling declares effects; it never performs one —
the substrate has no credentials and no knowledge of Kanidm, Forgejo, or
OpenBao. Executing an allowed effect is this binary's job, not the
substrate's. Private lifecycle commands additionally cross the authenticated,
signed, deny-biased boundary in src/private_api.rs before any adapter can
mutate a native issuer.
Why this exists
Ting/Ting#2's invariant: agent identity is pooled, not
one-shot-per-spawn — a small set of reusable slots reassigned across many
ephemeral drone containers, so a persona looks longer-lived than any one
container's life. A slot is only available for reissue if there is no
live previous holder — liveness-checked mutual exclusion, not a fixed
TTL.
Privilege is split on purpose. Providers and flake-ops realize the durable drone pool and immutable AssignmentGrants; OpenBao authorizes and owns leases; Vedanta validates OpenBao's deployed projection and performs the narrow native credential effect. Jostoph owns subtractive principal lifecycle. A queen seat authorizes a drone grant but neither queen nor drone calls Vedanta or native token administration directly.
For the swarm-alpha pilot, all ten UUID-pinned pool principals have grants
delegated to the runtime swarm-alpha/queen seat. A leased drone has no
Forgejo account. Vedanta causes Forgejo to mint a distinct read-only,
repository-restricted token under the active queen holder's account
(agent.teyla initially), and OpenBao delivers it once to the drone. The
queen never receives the credential. Renewal creates a successor; revocation
deletes exact token IDs and blocks worker reuse while deletion is unverified.
What's already settled, from lar.ad/flake-ops
The two consumer-side schemas this service mints against are written, in
lar.ad/flake-ops's docs/:
docs/identity-pool-schema.md— the pool.uid = worker-<uuid>(a UUID, not a sequential N — no pre-sized range for this service to coordinate against, no collision to avoid minting concurrently), one group (agent-pool) as the pool's only enumeration, three custom lldap attributes (pool_holder,pool_checkout_at,pool_generation). Display name convention: 4+ words, generated, deliberately whimsical (John the Third of the Root Beer) — a human scanning lldap can tell a rotating slot from a persistent identity by word count alone.docs/librefang-hand-identity-schema.md— a second, DELIBERATELY SEPARATE schema for long-lived named personas (librefang's hands — not pool slots, no checkout/reissue cycle).uid = "a+<hand>", one mutable attribute (librefang_node) recording current assignment, not part of the identity. Display name: 1-2 words, a real name (Freki).- The
a+/sa+prefix split:a+marks an agent/hand identity (persistent, has a persona);sa+marks a service account (a machine credential with no persona attached — e.g. a default/ad-hoc identity for a deployment with no more specific principal). Surfaced bylar.ad/flake-ops's own need for exactly this (LIBREFANG_SA_TOKEN,sub = "sa+librefang") before this repo had it — recorded there first, landing here now. - The JWT claims contract (
modules/hosts/modron/configuration.nix):sub(principal),groups(comma-joined STRING, not a JSON array — the gate's meta claims carry primitives only),gen(pool tenancy generation — omitted entirely for hands/service accounts, which have no checkout to name a generation of),aud(the gate this token is for),exp. - Authentication-token lifetime, for non-pool principals: pool checkout
tokens are ~24h,
reissued at every checkout. Hands and service accounts have no checkout
event to hang a short expiry off — owner call (2026-08-09, via
lar.ad/flake-ops) is ~30 days, manually reminted, until this service exists to do it on a schedule instead.
lar.ad/flake-ops's secrets/mint-identity-token.nu is the manual stand-in
for the older principal-authentication mint path — a hand-run step crypto jwt sign wrapper, same claims contract, same signing key
(secrets/identity-pool.yaml, operator-held). What Vedanta replaces is the
"operator runs a script by hand" part, not that claims contract. It is not
the Forge capability lease: the pilot's Forge token lasts at most 30 minutes
and is issued only through OpenBao's private command.
What this repo does not decide yet
- The liveness-check mechanism for pool reissue (heartbeat, TTL, or a read
against the container runtime —
docs/identity-pool-schema.md's answer: the runtime is authoritative, lldap records attribution only, with a 4×-lifetime disaster-recovery backstop; this service is what acts on that answer, not what re-derives it). - Where the principal-authentication JWT signing key actually lives once this is a standing service — today it is operator-held and decrypted by hand per mint. This is separate from private lifecycle command authentication: OpenBao signs those commands and Vedanta consumes only deployed public verification keys.
- Integration with "start a swarm" (queen's bootstrap workflow) — this service owns only the identity slice of that workflow, not the whole thing.
Development
cargo test
cargo clippy --all-targets -- -D warnings