---
id: WF-PFM-SEC-007
title: Suspend, reinstate, revoke or delete a membership
trigger: An administrator suspends, reinstates, revokes or deletes a reachable membership.
product_area: platform
module: security
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
- CAP-PFM-SEC-009
- CAP-PFM-SEC-008
- CAP-PFM-SEC-016
moves:
- STATE-PFM-SEC-004
- STATE-PFM-SEC-005
- STATE-PFM-SEC-006
crosses:
- SVC-PFM-SEC-security
idempotency: Repeated revocation retains its first timestamp. Removal work identifies pool and username; provider
  not-found is successful absence. Replayed consumer work converges, though a crash after provider success
  and before acknowledgement can repeat history. Dispatcher claims lapse for work not accepted by the queue.
concurrency: Membership changes transact with related records and applicable lockout guards. Durable removal
  rows are written for revocation/deletion and dispatched across membership boundaries; suspension/reinstatement
  remain separate post-commit single attempts.
hops:
- n: 1
  actor_or_service: Administrator
  action: Select the status-dependent action on a reachable membership.
  anchor: react-console:components/security/users/user-actions.tsx
  seam: sync
  on_failure: Display authority, state or validation refusal.
  left_inconsistent: No committed change follows a refused action.
  observer: The administration screen explains the outcome.
  provenance: observed
  source: react-console:lib/security/api.ts:suspendUser
- n: 2
  actor_or_service: Security service (one transaction)
  action: Commit membership change, invitation/assignment withdrawal, former-user preservation when deleting,
    security history and durable removal work where required; apply lockout guards.
  anchor: services-platform:modules/security/services/membership/lockout.ts:guardedAgainstLockout
  seam: sync
  on_failure: Roll back reference or lockout failures and audit the refusal outside the failed transaction.
  left_inconsistent: No partial guarded database change survives.
  observer: The caller and security history expose the refusal.
  provenance: observed
  source: services-platform:modules/security/services/invitations/revoke-user.ts (header)
- n: 3
  actor_or_service: Security service (suspend and reinstate, after commit)
  action: For suspension or reinstatement, attempt provider disabling or enabling once after commit.
  anchor: services-platform:modules/security/services/invitations/suspend-user.ts
  seam: sync
  on_failure: Log the provider problem and return its separate result; no automatic retry is implemented here.
  left_inconsistent: Membership and provider state can disagree until repaired.
  observer: The API reports provider trouble; the observed console success notice does not prove synchronization.
  provenance: observed
  source: services-platform:modules/security/services/invitations/suspend-user.ts (header, step 2)
- n: 4
  actor_or_service: Removal dispatcher (on nudge and every minute)
  action: Claim removal rows, publish without an open transaction and delete only accepted queue submissions.
  anchor: services-platform:modules/security/services/dispatch/dispatch-pool-account-removals.ts
  seam: schedule
  on_failure: Leave refused rows until their claim expires and the dispatcher tries again.
  left_inconsistent: Unpublished work leaves the provider account unchanged.
  observer: Dispatcher logs and pending rows expose the delay.
  provenance: observed
  source: services-platform:modules/security/services/dispatch/dispatch-pool-account-removals.ts (header)
- n: 5
  actor_or_service: Removal consumer (SQS)
  action: Consume queued provider disabling or deletion and append completion history.
  anchor: services-platform:modules/security/services/dispatch/remove-pool-account.ts
  seam: queue
  on_failure: Treat not-found as success; other errors retry after visibility timeout and can dead-letter after
    eight receives.
  left_inconsistent: The provider account can remain enabled until retry or operator redrive succeeds.
  observer: Consumer errors and dead-letter alarms expose exhaustion.
  provenance: observed
  source: services-platform:modules/security/services/dispatch/remove-pool-account.ts (header)
- n: 6
  actor_or_service: Security service (after commit)
  action: Submit a post-commit authorizer flush request.
  anchor: services-platform:modules/security/services/authorizer-flush/submit-flush-job.ts
  seam: state_machine
  on_failure: Log submission failure without undoing membership state.
  left_inconsistent: Earlier decisions remain usable within cache/context expiry bounds.
  observer: Flush job monitoring records attempted acceleration.
  provenance: observed
  source: services-platform:modules/security/services/authorizer-flush/submit-flush-job.ts (header)
branches:
- Reinstatement checks delegation over retained assignments and does not revive withdrawn invitations.
- HQ staff actions use the shared membership capabilities and relevant last-Rhythm-Administrator protection.
- Six older routes still expose shared capabilities without a customer path boundary and remain recorded debt.
waits:
- The removal dispatcher runs every minute and receives a post-commit nudge; queue backlog, retries and dead-letter
  recovery prevent a completion bound.
evidence:
- 'services-platform at ad3f6a65: modules/security/services/invitations/suspend-user.ts, revoke-user.ts, user-deletion.ts;
  services/membership/lockout.ts, last-administrator.ts; services/dispatch/*; aws/resources/security-pool-account-removals.sqs.ts;
  aws/logic/security-pool-account-removals.lambda.ts; docs/queued-work.md.'
- 'react-console at baf6dae: components/security/users/user-actions.tsx; components/console/hq/staff/staff-detail.tsx.'
examined_at: '2026-10-23'
examiner: codex
controls: [TC-AVL-001, TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CHG-005, TC-CHG-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-003, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-003, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Suspend, reinstate, revoke or delete a membership

## What It Does

The workflow separates database authority from provider synchronization. Revocation and deletion persist queue work atomically with membership changes; suspension and reinstatement issue one provider call after commit.

## The Flow

```mermaid
flowchart TD
    H1["Select membership action"]
    H2["Commit guarded database changes"]
    H3["Attempt suspension synchronization"]
    H4["Dispatch durable removal"]
    H5["Consume provider removal"]
    H6["Request cache flush"]
    H1 --> H2
    H2 -->|suspend or reinstate| H3
    H2 -->|revoke or delete| H4
    H4 --> H5
    H2 -. where submitted .-> H6
    H4 -->|unaccepted rows retried| H4
    H5 -->|retry within budget| H5
```

The diagram separates the relevant success, alternative and failure paths. The numbered hop rows retain the detailed authority checks, delegated work and retry limits.

## When Something Fails

Fresh authorization observes membership changes. Cached decisions and signed contexts can lag within their expiry bounds, and provider removal can require dead-letter redrive. The console’s successful action message is not evidence that provider synchronization succeeded.

Repeated revocation retains its first timestamp. Removal work identifies pool and username; provider not-found is successful absence. Replayed consumer work converges, though a crash after provider success and before acknowledgement can repeat history. Dispatcher claims lapse for work not accepted by the queue.

Membership changes transact with related records and applicable lockout guards. Durable removal rows are written for revocation/deletion and dispatched across membership boundaries; suspension/reinstatement remain separate post-commit single attempts.

## Open Design Questions

The recorded findings retain any stronger planning expectation, missing writer or unproved recovery path. Observed behavior does not approve those outstanding product choices.
