Skip to main content

Module invite

Module invite 

Source
Expand description

Persistent invite-token store (issue #756). Persistent invite-token store (issue #756 / Layer 6 gateway).

Invite tokens are short opaque secrets that grant a one-time-ish right to mint a principal. The kernel never stores the raw token — it stores SHA-256 of the URL-safe base64 form. Redemption hashes the incoming token and compares against the persisted set.

§On-disk layout

$ASTRID_HOME/etc/invites.toml:

[[invite]]
token_hash = "..."         # hex(sha256(token)) — 64 hex chars
group = "agent"
remaining_uses = 1
expires_at_epoch = 1234567890
issued_at_epoch = 1234560000
metadata = "alice's tablet"

Atomic writes via write-then-rename. The file is owned by the daemon UID and chmod 0600 — same posture as ~/.astrid/run/system.token.

§Threat model

  • Read-only leak: an attacker who reads invites.toml sees token hashes, not tokens. They cannot redeem.
  • Write leak: an attacker who can write invites.toml wins anyway — they can plant a hash whose pre-image they know. This matches the existing groups.toml / profile.toml threat model (operator-trusted system files; file-system perms gate access).
  • Replay: each redemption decrements remaining_uses; reaching zero removes the entry under the kernel’s admin_write_lock.
  • Wall-clock expiry: enforced at redeem time. Expired entries are removed lazily (next prune) — no background sweeper is spun up for what is at most a few-hundred-entry file.
  • Side-channel on lookup: the redeem path uses constant-time comparison on the hash bytes.

Structs§

Invite
On-disk persisted invite record. The raw token is NEVER stored — only its SHA-256.
InviteStore
File-backed invite store. Read-modify-write with atomic rename; concurrent mutators must serialise externally (the kernel uses admin_write_lock).

Enums§

InviteStoreError
Errors surfaced by InviteStore operations.

Constants§

MAX_EXPIRY_SECS
Hard cap on a single token’s lifetime. Mirrors the issue’s “max 30 days” guidance; longer-lived invites should issue a fresh token rather than carry one forever.
TOKEN_RAW_LEN
Length of the random token portion in bytes (192 bits → 32 chars URL-safe base64, comfortably exceeding the 128-bit work factor we need against online brute force given the per-IP redeem rate-limit at the gateway).

Functions§

ct_hash_eq
Constant-time hash comparison. Both inputs must be hex-encoded SHA-256 (64 hex chars). Returns false on any length mismatch without leaking the position via short-circuit.
generate_token
Generate a random URL-safe-base64 token. Uses the OS CSPRNG.
hash_token
Hash a token for storage / lookup. Hex-encoded SHA-256.
now_epoch
Current wall-clock as seconds since Unix epoch. Saturating on the (impossible) pre-1970 case so the returned u64 never wraps.
prune_expired
Borrow-checked helper: prune the in-place list, returning the count removed. Expired entries (wall-clock expiry passed) and consumed entries (remaining_uses == 0) both go.
prune_file
Same conventions as prune_expired but keyed on path — used by the handlers under the admin write lock.