Skip to content

Agents

An agent is a member of the Department of Professional Standards with an account in the system. The authoritative record is the agents row, keyed by the agent's Discord user ID (discord_id is the primary key — it is known from the moment onboarding begins, unlike agent_id which does not exist until training completes).

The agent record

FieldPurposeWho sets it
discord_id (PK)Canonical identityBot at onboarding start
nameDisplay nameBot; admin correction
statusactive | inactive | onboardingBot (onboarding), admin (corrections)
agent_rankRank (see below)Director-gated admin only — the bot cannot set rank
security_id, agreement_idOnboarding identifiers (bot-generated)Bot
agent_idDPS agent identifier (assigned after training)Bot
clearance_level1–5Bot (initial grant after training); Director-gated admin changes
clearance_idClearance identifier (bot-generated)Bot
is_trainerTraining-eligibility flagAdmin (admin_agents)

Roles and ranks

The rank hierarchy, lowest → highest (RANK_ORDER in authz.py):

text
Trial Agent → Probationary Agent → Agent → Senior Agent
   → Head Investigator → Lead Agent → Director → Department Director

Rank gates, checked against the live agents row on every request:

Rank setGrants
Senior Agent + (SUPERVISOR_RANKS)Supervisor reports, supervisor queue, audit visibility, trainer eligibility
Head Investigator + (REASSIGN_RANKS)Case reassignment
Director / Department Director (DIRECTOR_RANKS)Director-only admin capabilities (admin_reports, admin_schema, access management), rank/clearance changes

Ranks are stored as agent_rank and validated against the fixed set; anything else is rejected by the database (INVALID_RANK).

Clearance

Clearance is an integer 1–5 mapping to capabilities via CLEARANCE_POLICY (see Permissions). It is separate from rank: an agent's clearance can be 5 while their rank is Lead Agent — Director-only capabilities additionally require Director rank, so a demotion revokes them immediately even if clearance_level is untouched.

Onboarding

Onboarding is driven by the bot, which generates every identifier itself through the /generate/* endpoints:

  1. POST /agents/create (bot or manage_agents) creates the row with discord_id and status onboarding; everything else starts null.
  2. As each phase completes, the bot PATCHes whitelisted fields onto the row (PATCH /agents/<discord_id>/update): security_id/agreement_id after Phase 2, agent_id/clearance_id/clearance_level after training.
  3. The training-eligibility rule: only agents whose status is exactly onboarding appear in the Training Center trainee selector; completing training transitions the status to active.

There is no agents-equivalent timeline: onboarding step history lives on Discord, not in the API.

Permissions and gates

OperationGate
List / view agentsview_agents (1)
Create agent (onboarding start)manage_agents (5) or bot API key
Bot onboarding field updatesAPI key; restricted to the onboarding field whitelist; rank is not bot-settable (rejected with 400 — closes the static-key privilege-escalation path)
Agent record correction from the dashboardadmin_agents (4) + admin allowlist + required reason
Change agent_rank / clearance_levelDirector rank required (checked in the API and inside rpc_admin_agent_update against the live row)
Modify yourselfBlocked entirely (SELF_PROMOTION_BLOCKED, 403) — an admin can never edit their own record (prevents soft-locking yourself out of the site)
Modify agents above your rankBlocked (RANK_MANAGE_REQUIRED / hierarchy guard)
Set rank above Head InvestigatorDirector only

Administrative updates

Dashboard-side agent changes go through the audited admin path:

  • PATCH /agents/<discord_id>/update (browser session) → require_admin("admin_agents") → reason required → rpc_admin_agent_update.
  • PATCH /admin/agents/<discord_id> → the admin Agents tab → same RPC.

rpc_admin_agent_update (migrations 028 + 034) is the single authoritative mutation for agent records:

  • Field whitelist: name, status, agent_rank, security_id, agreement_id, agent_id, clearance_level, clearance_id, is_trainer.
  • Validation: status ∈ {active, inactive, onboarding}; rank ∈ the fixed set; clearance 1–5; non-empty name/status/rank.
  • Guards: clearance changes are Director-only and never on yourself; rank changes are never on yourself; Director-rank targets are Director-only; other targets follow admin_requester_can_manage_rank (up to Head Investigator for active admins with clearance ≥ 4).
  • Writes the before/after admin_audit_log row (action agent.update) and the canonical agent.updated event.

The bot's onboarding updates flow through the same RPC via rpc_agent_set_onboarding_fields, which uses a reserved system-actor lane (system:botghost-onboarding) that skips only the human self-edit/Director clearance guard — rank is filtered out of the allowed fields entirely, and every write is audited under the system identity (api-key / BotGhost (API key)).

What an agent is NOT allowed to modify

  • Own record — never (self-modification guard, enforced in the DB).
  • Own rank or clearance — covered by the same guard.
  • Rank via the bot key — the bot cannot set agent_rank through any path.
  • Clearance via the bot key above 1–5 — bounded and validated; initial grants only, and only for onboarding agents.
  • Anyone above their own rank — hierarchy guard in the API and the RPC.
  • Non-whitelisted fields — anything outside the whitelist is FIELD_NOT_ALLOWED (400).

Restrictions against unauthorized privilege escalation

The escalation surface is deliberately closed:

  1. The former direct bot rank write is gone; rank changes require a Director via the dashboard, and the RPC re-checks Director rank from the live agents table.
  2. Clearance changes require Director rank (not just clearance 5) and can never target the requester.
  3. The admin allowlist (admin_users) is required on top of clearance, and access management (grant/revoke/remove) is Director-only with a required reason and audit row.
  4. Directors cannot be removed from System Administration (DIRECTOR_PROTECTED).
  5. All of these checks exist in authz.py and inside the RPCs, so a route bug cannot bypass the database authority.