Six new admin-RPC handlers that complement the live-policy auth
rewrite. Each follows the canonical create_new_policy.ts pattern
(single JSON-stringified param, kind-24134 response, one prisma
mutation, no per-call ACL — validateRequestFromAdmin gates them
via the admin-npub allowlist).
Policy-level mutations propagate to every KeyUser bound to the
policy at the next sign-time check (the point of #11):
add_policy_rule { policyId, rule: {method, kind?, maxUsageCount?} }
remove_policy_rule { ruleId }
update_policy { policyId, patch: {name?, expiresAt?} }
Per-KeyUser override mutations (override layer):
add_signing_condition { keyUserId, condition: {method, kind?, allowed} }
remove_signing_condition { conditionId }
Surgical token revocation without nuking the KeyUser:
revoke_token { tokenId }
The 'all' kind literal is honored as a wildcard in add_policy_rule
for parity with the SigningCondition override-layer convention.
Param shapes ratified by webapp in
#11 (comment).
refs: #11
Shifts sign-time authorization from materialized SigningCondition
snapshots (frozen at token-bind time) to a layered model:
1. fetch KeyUser; missing → undefined
2. KeyUser.revokedAt set → false
3. SigningCondition override layer (explicit reject, per-(method,
kind) grant/deny) → return matching row's allowed value
4. live policy join: KeyUser → Token (revokedAt IS NULL) →
Policy → PolicyRule. Match on method + kind (exact /
'all' / NULL defensive) → true
5. else → undefined (caller may still prompt admin)
Backwards-compatible. The existing applyToken fan-out of
SigningCondition rows continues to populate step 3 for legacy
auth, so already-bound users keep working with no behavior change.
New users will still go through that fan-out until the follow-up
trim (#12). The key win: PolicyRule mutations via the upcoming
companion RPCs (add_policy_rule / remove_policy_rule / etc.)
propagate live to every KeyUser bound to that policy, rather than
requiring per-user backfill RPCs that don't exist.
Algorithm ratified by webapp in
#11 (comment).
refs: #11
Pre-requisite for the live-policy auth rewrite in #11. The new
revoke_token admin RPC needs a way to mark a single Token as
revoked without nuking the whole KeyUser (revoke_user) or
conflating with future expiry cleanup (deletedAt).
Nullable DateTime — existing rows default to NULL (active), no
data migration needed.
refs: #11