{
"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
}