Roles and authority

A role is a name a definition uses in its guards. Membership is store state that decides who is in that role today.
Declaring a role
:process/roles
{:triager {:members ["seb"]}
:approver {:members ["alice" "bob"]}}
Guards refer to the name:
:guards [[:signed-by :triager [:category]]]
That guard holds when the fact at [:category] was asserted by a member of
:triager. It is a statement about evidence — who put this claim on the
record — rather than a permission check performed somewhere else.
Membership lives in the register
A definition declares which roles exist and who starts in them. The store’s register decides who is in one now:
tik roles # who gates what, with effective members
tik roles add approver carol
tik roles remove approver bob
The register overrides a definition role by role, and takes effect on
in-flight tickets immediately — no version bump, no reprocess. That is
what makes a departure actually remove authority and a hire actually confer
it, rather than leaving a departed member able to sign every ticket minted
before they left.
Overriding is whole-role: the register’s entry replaces the definition’s
members for that role rather than merging with them, because a departure
has to be expressible. The first tik roles add on a role says so.
A store with no register derives exactly as before, so a definition’s declared members are a working default rather than something to restate.
Resolution takes no now. Time-aware validity — “was this actor a member
last March” — is a separate concern that wants signed bindings rather than
a mutable file, so a re-derivation at a past instant reads today’s
membership.
Separation of duties
Two guards express the common controls without a policy engine.
Four eyes. :different-person holds when two facts are present and
were asserted by distinct actors:
[:different-person [:proposal] [:approval]]
Nobody approves their own proposal, and explain says so by name when
they try — the reason carries the actor whose signature already counted,
so anyone else re-asserting one path breaks the tie.
Fresh evidence. :attested-within holds when an attestation of a claim
exists and is recent enough:
[:attested-within {:claim :ci-green} "P7D"]
A replayed attestation from last month is cryptographically valid and fails this guard honestly, which is exactly the distinction a stale-evidence control needs.
An empty role is a dead end
A role with no members can never sign, so every stage behind it is
unreachable. explain says that rather than leaving somebody waiting:
To reach :approved (unreachable):
⊘ fact [:sign-off] must be asserted by a member of role :auditor
— nobody can ever do this
The fix is tik roles add, or a definition that does not demand a
signature nobody can give.
Signing
Authority only means something once writes are signed. Register a signer once, then let every write carry authorship:
tik actor add alice ~/.config/tik/id_ed25519.pub
export TIK_KEY=~/.config/tik/id_ed25519
Signatures are detached sidecars from ssh-keygen -Y, checked by
tik verify with stock OpenSSH. A signature is an authorship claim, which
is why tik sign refuses to sign somebody else’s events.