● zegit · proves
Identity & certificates
No passwords, no long-lived tokens: your identity is a passkey, and signing runs on short-lived certificates minted at login. One touch covers a working session — and the same mechanism lets an agent work under your identity, with everything it signs cryptographically attributed to you.
Why short-lived certificates
Signing every AoV directly with a passkey would mean a hardware touch per commit — unusable, especially for an agent loop. Parking a long-lived signing key on disk is the opposite failure: the credential that outlives laptops, employments, and incident timelines. zegit takes the middle path, analogous to sigstore keyless signing but passkey-bound rather than OIDC-bound: authenticate once with the passkey, receive a certificate that lives for hours, sign freely until it expires.
The payoff is that there is no long-lived secret worth stealing. A compromised machine yields a credential that dies within hours, traces to one specific login ceremony, and can be revoked centrally in the meantime.
Two deployments, one model
Without ZEGIT_CP_URL, zg runs a local IAM — a single-machine simulation with a local CA that issues real certificates through the same code paths. With a CP URL set, the ceremony runs against the Control Plane. The chain model — leaf → Root CA — is identical either way.
Minting a certificate
Enrollment (zg auth enroll) happens once per identity: a WebAuthn credential-creation ceremony records your passkey and role. From then on, each login is:
- You run
zg auth login— a passkey touch produces a WebAuthn assertion. - The issuer verifies the assertion, generates a fresh ed25519 keypair, and signs a leaf certificate binding that key to your identity and role.
- Cert and key land at
~/.config/zegit/cert/{cert,key}.pem(override the home withZEGIT_HOME).zg validatesigns AoVs with that key until it expires.
zg auth login # passkey ceremony → short-lived cert
zg auth status # identity, role, time remaining
zg auth login --validity 30m # narrower window than the role defaultWhat is in the leaf
| Part | Value |
|---|---|
| Subject CN | The enrolled identity — e.g. jonas@acme. |
| Subject OU | The role — developer / maintainer / owner / provisioner (an IaC principal that may manage inventory but never attest code); a second OU=breakglass marks a break-glass leaf. With zg auth login --org the leaf is scoped to one organization and carries your role there. |
| Key | A fresh ed25519 keypair generated at login, never reused across sessions. |
| Validity | The role's lifetime — see below. |
| Chain | Leaf → ZeGit Root CA. The chain is embedded in every AoV, so verification works offline. |
More privilege, shorter cert
Default lifetimes: developer 8h, maintainer 1h, provisioner 1h, owner 5m. A developer touches the passkey about once a working day; an owner cert barely outlives the command that needed it. The profile — Ed25519 keys, EKU codeSigning, role-to-lifetime table — is fixed in code, not configuration; the Control Plane enforces it on issuance.
Delegating to an agent
The leaf is deliberately a bearer credential, scoped in time and role — and that is the delegation primitive. You mint it with a passkey touch; an agent working on your machine (mongoose, Claude Code, a script) signs with it. There is no agent enrollment step and no secret handoff protocol: the agent simply runs zg validate, which picks up the leaf you minted.
The result is a two-layer attribution that answers the two questions an auditor actually asks — who vouches for this? and who did the work? — separately, without conflating them:
| Layer | Question it answers | Bound to |
|---|---|---|
| DSSE signature + cert chain | Who vouches for this validation? | Your passkey identity — non-repudiable. |
actor in the evidence record | Who actually did the work? | The agent — kind: agent plus harness and model. |
| The cert validity window | When could this have been signed? | The hours you delegated, and nothing more. |
zg auth login # you — one touch, 8h developer cert
mongoose run "fix the flaky test" # the agent edits, runs zg validate,
# and signs AoVs as you
zg auth status # how much delegation window remainsWhat never delegates
Release approval is excluded by construction: zg tr approve demands a fresh passkey touch regardless of any cached certificate. An agent holding your leaf can validate, sign AoVs, and push branches — it can never approve or finalize a release. The human stays in the release gate even when everything else is delegated.
Treat the cert directory the way you treat an SSH agent socket: anything running as your user during the window can sign as you. That is the designed trade — the mitigations are the short TTL, role scoping, central revocation, and the fact that approvals never ride on it.
Standing automation identities
For a headless runner or a standing agent with no human at the keyboard, delegation-by-borrowed-leaf doesn't work — there is no passkey to touch when the cert expires. Automation identities close that gap: an owner registers the agent's ed25519 public key with the Control Plane, and the agent then obtains its own short-lived certs by signing a single-use server challenge — no passkey involved.
# on the agent host — generate a keypair:
zg auth automation-keygen
# an owner registers the public key:
zg auth automation-register --identity ci@acme --pubkey <base64>
# the agent, whenever it needs a cert:
zg auth automation-login --identity ci@acmeAn automation identity is registered as developer (it can sign AoVs and push branches) or provisioner (an IaC principal that manages inventory and credentials but is refused as an AoV signer on protected refs) — never as maintainer or owner, so it can never approve or finalize a release tag. Its identity is its own (e.g. ci@acme) — attribution names the automation, and the registration itself is an owner action in the audit log.
Ending a session
zg auth logout ends the session on this machine — it removes the leaf and key, the Control Plane session cookie and the gateway SSH certificate, and keeps the enrolment. zg auth logout --purge resets the machine entirely. Sessions are also server-side since v0.0.169: an owner can end any identity's sessions from the Control Plane.
Expiry, revocation, and the deny list
A fresh push is verified strictly at the current time: an expired leaf means the gateway rejects the AoV, and a fresh zg validate re-signs it. Evidence the gateway already accepted is judged on the accepted-evidence clock as well — bundles and zg aov inspect keep verifying after the leaf expires, because the chain was valid when the gateway took it (see the AoV notes). Before expiry, revocation covers the window: the Control Plane publishes a signed revocation list (by certificate serial; there is no CRL/OCSP — lifetimes are minutes to hours), and gateways refresh it continuously. A gateway whose list is staler than the maximum (60s) fails closed for protected refs. The matching rejections are ZG_ERR_CERT_EXPIRED, ZG_ERR_CERT_REVOKED and ZG_ERR_REVOCATION_LIST_STALE.
The root of trust
Every leaf chains to the ZeGit Root CA — created by zg ceremony generate (default validity three years) for self-hosted deployments, with a two-custodian YubiHSM ceremony (zg ceremony hsm) for hardware-resident keys. Because the chain is embedded in every AoV and every bundle, an auditor needs exactly one artifact besides the evidence itself: the root certificate. Archive it with your bundles and they verify offline, years later, with no live infrastructure.
Next
Authoritative reference, generated from the component repos. Spot something stale? Tell us.