Repository navigation
Writable access store: members, friends and capabilities managed by admin commands #25
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestsecurityAuth, access control, privacy, secrets — needs human reviewAuth, access control, privacy, secrets — needs human review
Description
Context
config/members.yaml is read-only today and edited by hand. Admin commands (#26) will change who is a member or friend, so the file has to become bot-managed. Decided: Pixel's own data is the source of truth for tiers. Discord roles are not read for tiers (in prod there's one role for everyone, and a member might use Pixel from another platform later).
Goal
Members, friends and their capabilities live in a bot-managed file that admin commands can change safely, behind an interface so a database (#15) can replace it later.
Scope
- A store behind a small interface. The current file-based tier source reads from it.
- Schema: today's entries (
discordId,tier, optionalnote), plus an optionalcapabilitieslist per person (see Generic capability system for restricted features #28). Backwards compatible. The same validation applies: IDs are quoted strings, unknown keys are rejected, and no duplicates. - Safe writes:
- atomic (temp file and rename), and serialised inside the process
- re-read just before writing, so hand edits aren't lost
- the file's comments and order are preserved
- the previous version is kept as a
.bak
- The in-memory view changes only after a successful write. A failed write changes nothing.
- A missing file still stops startup. Never silently "everyone is a guest". A lost file is a loud outage that's restored from a backup.
- Every change goes through one function that writes an audit event to the log: who did it (ID, display name, handle), who it was done to, before and after, and when.
Acceptance criteria
- A tier or capability change is written atomically and appears in the file, with comments and unrelated entries untouched
- A failed write leaves both the file and the in-memory view unchanged, and the caller gets a clear error
- A hand edit made while the bot runs survives the next write
- Concurrent changes can't corrupt the file or lose an update
- The previous version is kept as a
.bakon every write - A missing file stops startup. An empty
members: []file is fine - Every change is logged with actor, target, before and after
- The file stays gitignored, and docs are updated
Considerations
- Durability: the file can no longer be a read-only Key Vault mount. In Azure it needs a writable persistent volume with snapshots (Terraform module for Pixel on Azure Container Apps + dev environment #9)
- It contains personal data, so it stays out of git
- Hand edits made while the bot runs are picked up on the next write or restart
- Moving to Postgres later should be a swap behind the interface (Persistence: PostgreSQL and migrations #15)
References
docs/identity-and-access.md,services/access-config.ts
Decisions made
- Demoting keeps the entry and its capabilities with
tier: guest. A guest has no tier, so every tier-gated command refuses them. Capabilities will need a minimum tier (Generic capability system for restricted features #28). - No lock: the read, edit, write and swap has no
await, so changes cannot interleave. Pixel is single-instance. - Admins are never written. A change targeting an admin is refused.
- Audit: structured log event plus a Sentry breadcrumb. A dedicated audit log is Admin audit log #31.
/admin reloadre-reads both files and keeps the old data if they are invalid.
Implemented in #32.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestsecurityAuth, access control, privacy, secrets — needs human reviewAuth, access control, privacy, secrets — needs human review
Activity