# app.tempomusic.staff.defs

> Published by [lexicons.tempomusic.fan](https://lexicon.garden/identity/did:plc:pxsuqmultto34oks44m2lgxm)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:pxsuqmultto34oks44m2lgxm/app.tempomusic.staff.defs)
- [Documentation](https://lexicon.garden/lexicon/did:plc:pxsuqmultto34oks44m2lgxm/app.tempomusic.staff.defs/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:pxsuqmultto34oks44m2lgxm/app.tempomusic.staff.defs/examples)

## Definitions

### `app.tempomusic.staff.defs#staffRole`

**Type**: `string`

Privilege level on the admin surface. `superAdmin` is the only role permitted to grant or revoke other moderators.

**Known Values**:
- `moderator`
- `superAdmin`

**Constraints**: maxLength: 100

### `app.tempomusic.staff.defs#staffView`

**Type**: `object`

AppView projection of a row in the moderators table.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `did` | `string` (did) | Yes |  |
| `role` | `ref` → `#staffRole` | Yes |  |
| `grantedAt` | `string` (datetime) | Yes |  |
| `revokedAt` | `string` (datetime) | No | When set, the row is no longer active; the gate denies the DID. Listed in listStaff only when explicitly requested via the includeRevoked parameter. |
| `grantedByDid` | `string` (did) | No | DID of the super-admin who granted this row. Absent for bootstrap rows seeded from the TEMPO_STAFF_BOOTSTRAP_DIDS env var. |
| `revokeReason` | `string` | No |  |

### `app.tempomusic.staff.defs#reportView`

**Type**: `object`

AppView projection of a single report row.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | `integer` | Yes |  |
| `status` | `ref` → `#reportStatus` | Yes |  |
| `details` | `string` | No | Free-text context provided by the reporter. Absent when not provided. |
| `subject` | `union` | Yes |  |
| `createdAt` | `string` (datetime) | Yes |  |
| `triagedAt` | `string` (datetime) | No |  |
| `reasonType` | `ref` → `app.tempomusic.moderation.defs#reasonType` | Yes |  |
| `resolvedAt` | `string` (datetime) | No |  |
| `reporterDid` | `string` (did) | Yes |  |
| `triagedByDid` | `string` (did) | No |  |
| `resolvedByDid` | `string` (did) | No |  |
| `resolutionNote` | `string` | No |  |
| `subjectResolvedAt` | `string` (datetime) | No | Timestamp at which the daily resolver job flipped subjectKnownAtIntake from false to true. Absent when intake was already known. |
| `subjectKnownAtIntake` | `boolean` | Yes | True when the subject was already projected into the AppView at the moment of intake. False when the resolver job had to catch up; combined with subjectResolvedAt to distinguish the two known-at-intake variants. State combinations: (a) `true` + subjectResolvedAt absent = known at intake; (b) `true` + subjectResolvedAt present = resolver flipped after the fact; (c) `false` = still unresolved. |

### `app.tempomusic.staff.defs#reportStatus`

**Type**: `string`

Lifecycle state of a report. `open` → `underReview` → terminal `actioned` | `dismissed`. Current implementations MUST NOT emit any value outside this set; future audit-clarity states (e.g. resolvedDuplicate) require an additive lexicon update plus an implementation that writes them.

**Known Values**:
- `open`
- `underReview`
- `actioned`
- `dismissed`

**Constraints**: maxLength: 100

### `app.tempomusic.staff.defs#inviteCodeUse`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `usedAt` | `string` (datetime) | Yes |  |
| `usedBy` | `string` (did) | Yes |  |

### `app.tempomusic.staff.defs#takedownClass`

**Type**: `string`

Classification of a takedown action. All three classes make the subject invisible to read paths identically; the distinction is downstream retention/purge policy. `reported` is the standard moderation hold. `tagged` is reserved for future classifier-driven holds with auto-purge after a sweep. `legalHold` is exempt from any future purge endpoint and is audit-mandatory.

**Known Values**:
- `reported`
- `tagged`
- `legalHold`

**Constraints**: maxLength: 100

### `app.tempomusic.staff.defs#auditEntryView`

**Type**: `object`

AppView projection of a moderation_audit row. The audit log is append-only and never purged; entries survive hard-deletion of the actor account.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | `integer` | Yes |  |
| `reason` | `string` | No | Free-text reason provided by the actor. |
| `subject` | `union` | No |  |
| `actorDid` | `string` (did) | Yes | DID of the moderator who performed the action, or of the reporter for `reportCreated` entries. |
| `reportId` | `integer` | No | Linked report row when relevant (report-lifecycle entries, takedowns with closeAttachedReports). |
| `createdAt` | `string` (datetime) | Yes |  |
| `actionType` | `ref` → `#staffActionType` | Yes |  |
| `afterStatus` | `integer` | No | moderation_status value after the action, when applicable. 0 = live, 1 = reported, 2 = tagged, 3 = legalHold. |
| `beforeStatus` | `integer` | No | moderation_status value before the action, when applicable. 0 = live, 1 = reported, 2 = tagged, 3 = legalHold. |
| `takedownClass` | `ref` → `#takedownClass` | No | For subjectTakedown actions, the class chosen by the moderator. Equivalent to afterStatus but in symbolic form; populated to spare clients from translating the integer code. |

### `app.tempomusic.staff.defs#inviteCodeView`

**Type**: `object`

AppView projection of a single PDS invite code, sourced on-demand from `com.atproto.admin.getInviteCodes`. No local mirror.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `code` | `string` | Yes | Suffix only — the env prefix has been stripped by the server before surfacing. |
| `uses` | `array` | No |  |
| `disabled` | `boolean` | Yes | True when the code has been disabled by a super-admin via disableInviteCodes. |
| `available` | `integer` | Yes | Number of remaining uses. `1 - uses.length` for the single-use codes emitted by this feature. |
| `createdAt` | `string` (datetime) | Yes |  |
| `createdBy` | `string` (did) | Yes | DID of the staff account that generated this code. |
| `forAccount` | `string` (did) | No | When the code was minted for a specific DID (unused by this feature — the staff panel only generates unbound codes). |

### `app.tempomusic.staff.defs#staffActionType`

**Type**: `string`

Categorical action recorded in the moderation audit log. Covers report-lifecycle transitions, subject status changes, moderator-role mutations, and invite-code lifecycle (TEM-502).

**Known Values**:
- `reportCreated`
- `reportTriaged`
- `reportDismissed`
- `reportClosedByCascade`
- `subjectTakedown`
- `subjectRestore`
- `moderatorGranted`
- `moderatorRevoked`
- `accountSuspended`
- `accountUnsuspended`
- `accountBanned`
- `reportReopened`
- `inviteCodeCreated`
- `inviteCodeDisabled`

**Constraints**: maxLength: 100

## Raw Schema

```json
{
  "id": "app.tempomusic.staff.defs",
  "defs": {
    "staffRole": {
      "type": "string",
      "maxLength": 100,
      "description": "Privilege level on the admin surface. `superAdmin` is the only role permitted to grant or revoke other moderators.",
      "knownValues": [
        "moderator",
        "superAdmin"
      ]
    },
    "staffView": {
      "type": "object",
      "required": [
        "did",
        "role",
        "grantedAt"
      ],
      "properties": {
        "did": {
          "type": "string",
          "format": "did"
        },
        "role": {
          "ref": "#staffRole",
          "type": "ref"
        },
        "grantedAt": {
          "type": "string",
          "format": "datetime"
        },
        "revokedAt": {
          "type": "string",
          "format": "datetime",
          "description": "When set, the row is no longer active; the gate denies the DID. Listed in listStaff only when explicitly requested via the includeRevoked parameter."
        },
        "grantedByDid": {
          "type": "string",
          "format": "did",
          "description": "DID of the super-admin who granted this row. Absent for bootstrap rows seeded from the TEMPO_STAFF_BOOTSTRAP_DIDS env var."
        },
        "revokeReason": {
          "type": "string",
          "maxLength": 5000,
          "maxGraphemes": 500
        }
      },
      "description": "AppView projection of a row in the moderators table."
    },
    "reportView": {
      "type": "object",
      "required": [
        "id",
        "subject",
        "reporterDid",
        "reasonType",
        "status",
        "subjectKnownAtIntake",
        "createdAt"
      ],
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "status": {
          "ref": "#reportStatus",
          "type": "ref"
        },
        "details": {
          "type": "string",
          "maxLength": 500,
          "description": "Free-text context provided by the reporter. Absent when not provided.",
          "maxGraphemes": 500
        },
        "subject": {
          "refs": [
            "app.tempomusic.moderation.defs#postSubject",
            "app.tempomusic.moderation.defs#replySubject",
            "app.tempomusic.moderation.defs#accountSubject"
          ],
          "type": "union"
        },
        "createdAt": {
          "type": "string",
          "format": "datetime"
        },
        "triagedAt": {
          "type": "string",
          "format": "datetime"
        },
        "reasonType": {
          "ref": "app.tempomusic.moderation.defs#reasonType",
          "type": "ref"
        },
        "resolvedAt": {
          "type": "string",
          "format": "datetime"
        },
        "reporterDid": {
          "type": "string",
          "format": "did"
        },
        "triagedByDid": {
          "type": "string",
          "format": "did"
        },
        "resolvedByDid": {
          "type": "string",
          "format": "did"
        },
        "resolutionNote": {
          "type": "string",
          "maxLength": 5000,
          "maxGraphemes": 500
        },
        "subjectResolvedAt": {
          "type": "string",
          "format": "datetime",
          "description": "Timestamp at which the daily resolver job flipped subjectKnownAtIntake from false to true. Absent when intake was already known."
        },
        "subjectKnownAtIntake": {
          "type": "boolean",
          "description": "True when the subject was already projected into the AppView at the moment of intake. False when the resolver job had to catch up; combined with subjectResolvedAt to distinguish the two known-at-intake variants. State combinations: (a) `true` + subjectResolvedAt absent = known at intake; (b) `true` + subjectResolvedAt present = resolver flipped after the fact; (c) `false` = still unresolved."
        }
      },
      "description": "AppView projection of a single report row."
    },
    "reportStatus": {
      "type": "string",
      "maxLength": 100,
      "description": "Lifecycle state of a report. `open` → `underReview` → terminal `actioned` | `dismissed`. Current implementations MUST NOT emit any value outside this set; future audit-clarity states (e.g. resolvedDuplicate) require an additive lexicon update plus an implementation that writes them.",
      "knownValues": [
        "open",
        "underReview",
        "actioned",
        "dismissed"
      ]
    },
    "inviteCodeUse": {
      "type": "object",
      "required": [
        "usedBy",
        "usedAt"
      ],
      "properties": {
        "usedAt": {
          "type": "string",
          "format": "datetime"
        },
        "usedBy": {
          "type": "string",
          "format": "did"
        }
      }
    },
    "takedownClass": {
      "type": "string",
      "maxLength": 100,
      "description": "Classification of a takedown action. All three classes make the subject invisible to read paths identically; the distinction is downstream retention/purge policy. `reported` is the standard moderation hold. `tagged` is reserved for future classifier-driven holds with auto-purge after a sweep. `legalHold` is exempt from any future purge endpoint and is audit-mandatory.",
      "knownValues": [
        "reported",
        "tagged",
        "legalHold"
      ]
    },
    "auditEntryView": {
      "type": "object",
      "required": [
        "id",
        "actorDid",
        "actionType",
        "createdAt"
      ],
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "reason": {
          "type": "string",
          "maxLength": 5000,
          "description": "Free-text reason provided by the actor.",
          "maxGraphemes": 500
        },
        "subject": {
          "refs": [
            "app.tempomusic.moderation.defs#postSubject",
            "app.tempomusic.moderation.defs#replySubject",
            "app.tempomusic.moderation.defs#accountSubject"
          ],
          "type": "union"
        },
        "actorDid": {
          "type": "string",
          "format": "did",
          "description": "DID of the moderator who performed the action, or of the reporter for `reportCreated` entries."
        },
        "reportId": {
          "type": "integer",
          "minimum": 1,
          "description": "Linked report row when relevant (report-lifecycle entries, takedowns with closeAttachedReports)."
        },
        "createdAt": {
          "type": "string",
          "format": "datetime"
        },
        "actionType": {
          "ref": "#staffActionType",
          "type": "ref"
        },
        "afterStatus": {
          "type": "integer",
          "minimum": 0,
          "description": "moderation_status value after the action, when applicable. 0 = live, 1 = reported, 2 = tagged, 3 = legalHold."
        },
        "beforeStatus": {
          "type": "integer",
          "minimum": 0,
          "description": "moderation_status value before the action, when applicable. 0 = live, 1 = reported, 2 = tagged, 3 = legalHold."
        },
        "takedownClass": {
          "ref": "#takedownClass",
          "type": "ref",
          "description": "For subjectTakedown actions, the class chosen by the moderator. Equivalent to afterStatus but in symbolic form; populated to spare clients from translating the integer code."
        }
      },
      "description": "AppView projection of a moderation_audit row. The audit log is append-only and never purged; entries survive hard-deletion of the actor account."
    },
    "inviteCodeView": {
      "type": "object",
      "required": [
        "code",
        "available",
        "disabled",
        "createdBy",
        "createdAt"
      ],
      "properties": {
        "code": {
          "type": "string",
          "maxLength": 128,
          "description": "Suffix only — the env prefix has been stripped by the server before surfacing."
        },
        "uses": {
          "type": "array",
          "items": {
            "ref": "#inviteCodeUse",
            "type": "ref"
          }
        },
        "disabled": {
          "type": "boolean",
          "description": "True when the code has been disabled by a super-admin via disableInviteCodes."
        },
        "available": {
          "type": "integer",
          "minimum": 0,
          "description": "Number of remaining uses. `1 - uses.length` for the single-use codes emitted by this feature."
        },
        "createdAt": {
          "type": "string",
          "format": "datetime"
        },
        "createdBy": {
          "type": "string",
          "format": "did",
          "description": "DID of the staff account that generated this code."
        },
        "forAccount": {
          "type": "string",
          "format": "did",
          "description": "When the code was minted for a specific DID (unused by this feature — the staff panel only generates unbound codes)."
        }
      },
      "description": "AppView projection of a single PDS invite code, sourced on-demand from `com.atproto.admin.getInviteCodes`. No local mirror."
    },
    "staffActionType": {
      "type": "string",
      "maxLength": 100,
      "description": "Categorical action recorded in the moderation audit log. Covers report-lifecycle transitions, subject status changes, moderator-role mutations, and invite-code lifecycle (TEM-502).",
      "knownValues": [
        "reportCreated",
        "reportTriaged",
        "reportDismissed",
        "reportClosedByCascade",
        "subjectTakedown",
        "subjectRestore",
        "moderatorGranted",
        "moderatorRevoked",
        "accountSuspended",
        "accountUnsuspended",
        "accountBanned",
        "reportReopened",
        "inviteCodeCreated",
        "inviteCodeDisabled"
      ]
    }
  },
  "$type": "com.atproto.lexicon.schema",
  "lexicon": 1
}
```
