Zoth end-to-end — the walkthrough you must narrate flawlessly
One access request, from login to GitHub collaborator to automatic expiry. This is THE interview story.
Know your own system
This is the single most important article of the even days. "Walk me through what happens when X" is the question that exposes whether you built something or assembled something. Narrate this until it's reflexive. Every step is real code — I name the files.
Step 0 — Authentication
POST /api/v1/auth/login → JWT HS256, claims {sub: userID, tid: tenantID, email, roles, iat, exp} (1 h expiry, secret ≥ 32 bytes enforced at startup). On every subsequent request, middleware.authenticateJWT parses the token, then — crucially — re-resolves the user's role from the database rather than trusting the claim, so role changes and user deletion take effect immediately. API keys are the machine path (X-API-Key, SHA-256-hashed with 8-char prefix, write-scope enforcement on non-GET). Tenant context comes from tid and is asserted by middleware.Tenant().
Step 1 — Request creation
POST /api/v1/access-requests with {resource_id, justification} → jit.Service.RequestAccess (internal/services/jit/service.go):
- Tenant check: resource is fetched via
GetResourceByIDAndTenant(INNER JOIN through integrations — resources have no tenant_id of their own; ownership flows through their parent integration). - Policy evaluation:
policy.EvaluateForRequestloads the tenant's active policies ordered bypriority DESCand returns the first whosematch_conditionsmatch the resource + the user's groups. No match → the request proceeds unrouted (any member can decide); matched →policy_idis stamped on the request. - RAG enrichment (if
GEMINI_API_KEYset): risk score, approval likelihood, ETA, suggested duration are computed and stored on the request row (rag_risk_scoreetc.) — advisory fields, never decisions. - Suspicious-activity check: four signals (temporal anomaly, privilege escalation on HIGH/CRITICAL sensitivity, velocity spike for first-time requesters, cross-domain pattern) — if any trigger: audit row +
alert.suspiciousevent. It never blocks. - Insert the
access_requestsrow with statusPENDING. - Route approval:
approval.RouteApproval— the recursive CTE finds the manager chain; the first manager gets aPENDINGapprovals row.
Step 2 — The decision
POST /access-requests/{id}/approve by the manager:
- Self-decision is rejected (you can't approve your own request — remediation of audit finding C-2/C-3).
- Authority check (
checkDecisionAuthority): the decision-maker must be the routed pending approver; unrouted requests may be decided by any member and are auditedauthority: "UNROUTED". - The decision runs
decide→ conditional update toAPPROVED→ and immediately callsactivateApprovedin the same flow, so the request becomesACTIVEandexpires_at(frompolicy.MaxDurationMinutes, default 60 min) lands in one atomicUPDATE ... SET status='ACTIVE', expires_at=$4 WHERE id=$1 AND status=$2.APPROVEDis never a resting state for human approvals — approval is activation. - Audit row
ACCESS_GRANTEDis appended to the hash chain. - The
access.grantedevent is not published directly — it's written tooutbox_eventsin the same logical unit, and the relay (5 s tick,FOR UPDATE SKIP LOCKED) publishes it to thezoth.accesstopic exchange with routing keyaccess.granted.
Step 3 — Provisioning (the emitter)
cmd/emitter consumes from queue emitter.access (bound with keys access.granted/revoked/expired, prefetch 10):
- Dedup:
PostgresDeduper.Seen—INSERT INTO processed_events ON CONFLICT DO NOTHING; zero rows affected = duplicate → Ack. Fail-closed on errors. - Resolve:
PlatformBridge.resolveloads user + resource + the resource's integration, verifies the event's tenant matches the integration's tenant (the bridge tenant check), decryptscredentials_encrypted(AES-256-GCM) — per call, never cached. - Grant:
platforms.Service.GrantAccess→ zlink connector → e.g. GitHub: resolve username (viaSearchUsersby email), thenRepositories.AddCollaborator(owner, repo, username, {Permission: "push"})for write access. Retry: 3 attempts, 500 ms base, exponential with jitter, 30 s cap. - Errors after 5 retries → dead-letter to
emitter.access.dlqfor inspection.
Step 4 — Expiry (the loop)
RunExpiryLoop (ticker, 30 s): ListExpiredAccessRequests (WHERE status='ACTIVE' AND expires_at < now() LIMIT 100) → conditional transition to EXPIRED → access.expired outbox event → audit row. Race with a concurrent revoke? The conditional update makes one of them win with ErrConflict swallowed as "a concurrent transition beat us — safe with multiple replicas."
Step 5 — The revoke lands on GitHub
The emitter consumes access.expired exactly like a manual revoke: RemoveCollaborator. Idempotent by design — zlink.ErrNotFound from the platform ("already gone") is treated as success, because the goal state is achieved. This is the sentence that makes a security interviewer smile: "revocation converges, it doesn't error."
The 30-second version (memorize)
"User authenticates with a tenant-scoped JWT; creates a request against a tenant-owned resource; a priority-ordered JSONB policy matches it; the recursive-CTE manager chain routes approval; approval auto-activates with an expiry in one atomic conditional update; the grant event goes through a transactional outbox to RabbitMQ; the emitter dedupes, decrypts the integration's credentials, and calls GitHub/Azure/Notion through the zlink connector interface with retries and a DLQ; a 30-second loop expires access automatically; drift detection reconciles actual platform state every 5 minutes; and every step appends to a hash-chained audit log."
Bulletproof your resume
Say these answers aloud before moving on
These are the follow-ups an interviewer will actually ask about your projects.
Why does approval auto-activate instead of having a separate activation step?
Two reasons: human approvals are the moment authority is exercised, so making APPROVED a resting state adds a second race window (activate-vs-revoke) for zero benefit; and it lets expires_at be set in the same UPDATE that flips status — one atomic statement, no window where access is ACTIVE without an expiry (that was audit finding L-4's fix). The service retains a separate ActivateRequest method with an 'only approved can activate' guard for future API-driven activation, but no route exposes it today.
What if RabbitMQ is down when the approval happens?
Nothing is lost: the event is in outbox_events, committed with the business write. The relay ticks every 5 s, and its publishFn returns an error when the broker is unreachable, which aborts the batch without marking anything published — the backlog simply waits. The API even tolerates a broker-less boot. Broker availability affects provisioning latency, never correctness.
How do you know the GitHub grant actually happened?
Three layers: the emitter only Acks after zlink returns success (failures retry up to 5 times then DLQ); CheckAccess exists on the connector (IsCollaborator on GitHub) for direct verification; and the drift service periodically reconciles expected ACTIVE requests against actual platform state, publishing drift.detected events and audit rows when they disagree. Event-driven + reconciliation is the belt-and-suspenders pattern for side effects you don't fully control.