Skip to content

Writable access store: members, friends and capabilities managed by admin commands #25

Closed

Description

@NinjaLikesCheez

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, optional note), plus an optional capabilities list 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 .bak on 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

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 reload re-reads both files and keeps the old data if they are invalid.

Implemented in #32.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecurityAuth, access control, privacy, secrets — needs human review

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions