- app.tempomusic.actor.defs defs The viewer's analytics consent preferences. Private appview data — never written to the public PDS repo. Business fields only: the last-write-wins clock travels as a sibling `updatedAtMicros` field on the get/put envelopes, never inside this document.
- app.tempomusic.actor.deleteAccount procedure Schedule the requesting viewer's account for deletion. Soft-deletes the profile and cascades soft-delete on every post, reply and like owned by the viewer in a single transaction. The viewer can log back in within 30 days and call restoreAccount to undo. After 30 days the account is hard-deleted irreversibly and the content is anonymised. See ADR-009 for the full retention lifecycle.
- app.tempomusic.actor.getAnalyticsConsent query Fetch the requesting viewer's analytics consent preferences. The viewer DID is read from the service-auth JWT (`iss` claim). `preferences` and `updatedAtMicros` are absent together when the viewer has never stored preferences — the client then hydrates its early-access defaults.
- app.tempomusic.actor.getDeletionState query Return the requesting viewer's account-deletion state so the client's bootstrap gate can decide between showing the app, the dated restoration screen, or the terminal 'window passed' variant (TEM-492). The viewer DID is read from the service-auth JWT (`iss` claim) — no explicit parameter, matching the appview's `getAnalyticsConsent` convention. When the profile row has been hard-deleted at day 30 the endpoint returns `deleted: false` (indistinguishable from an account that was never deleted — in
- app.tempomusic.actor.profile record Customizable profile attributes for a Tempo actor. Stored as a singleton per repository under the literal record key 'self'.
- app.tempomusic.actor.putAnalyticsConsent procedure Store the requesting viewer's analytics consent preferences with last-write-wins semantics on the sibling updatedAtMicros clock (client wall-clock microseconds): the server keeps the document with the highest value and always returns the winning document with its clock, which the client must adopt as its new local state. The viewer DID is read from the service-auth JWT (`iss` claim).
- app.tempomusic.actor.restoreAccount procedure Restore the requesting viewer's previously soft-deleted account. Clears deleted_at on the profile and unconditionally on every post, reply and like owned by the viewer (including content the viewer soft-deleted individually before the account cascade). Moderation-held content stays in moderation hold. Only valid within the 30-day restoration window opened by deleteAccount; outside the window the profile has been hard-deleted and the cascade content is anonymised, so restoration is impossible. Se
- app.tempomusic.actor.searchActorsTypeahead query Accounts whose display name or handle starts with `q`, case-insensitively, for the mention picker. At most `limit` results, ordered: exact handle match first, then display-name prefix matches, then handle prefix matches, each group by display name then DID. Filtered for the viewer: accounts in a block relation with the viewer in either direction, sanctioned accounts (suspended or banned), soft-deleted or moderation-held profiles and the viewer's own account are never returned. The viewer DID is
- app.tempomusic.embed.defs defs Width/height ratio of an image or video. Used by clients to reserve layout space before media loads and to choose appropriate display dimensions.
- app.tempomusic.embed.external object Embedded external link with preview metadata (Open Graph-style title, description, and thumbnail).
- app.tempomusic.embed.images object Image embed. Currently limited to one image per post; maxLength may be increased in future versions without breaking existing records.
- app.tempomusic.embed.video object Video embed. One video per post.
- app.tempomusic.feed.defs defs An album, EP, single, or any long-form release.
- app.tempomusic.feed.getPost query Fetch a single Tempo post by its AT-URI.
- app.tempomusic.feed.getPostReplies query Paginated flat list of replies whose root is the given post URI, in chronological order. When the request carries a service-auth JWT, the viewer DID is read from its `iss` claim and each returned reply carries a viewer projection; anonymous requests receive replies without viewer state.
- app.tempomusic.feed.getTimeline query Paginated chronological feed of timeline items. Each item is discriminated by `$type` (today only `app.tempomusic.feed.post`; future variants such as `app.tempomusic.feed.review` plug in additively). When the request carries a service-auth JWT, the viewer DID is read from its `iss` claim and each returned item carries a viewer projection; anonymous requests receive items without viewer state.
- app.tempomusic.feed.like record A like record. Subject is a strongRef to either an app.tempomusic.feed.post or an app.tempomusic.feed.reply. strongRef is collection-agnostic by design — these constraints live in AppView validation, not in the lexicon schema.
- app.tempomusic.feed.post record A music-related post sharing what the user is experiencing.
- app.tempomusic.feed.reply record A text reply attached to a Tempo post. Reply records form threads via the reply ref; the music subject lives on the root post, not on the reply itself.
- app.tempomusic.moderation.createReport procedure Submit a user-initiated report on a post, reply, or actor account. The reporter DID is read from the authenticated principal. Reports are dedupe-forever on (reporter, subject): a duplicate submission returns the existing report id with `deduped: true` and no new row is inserted. Per-reporter rate limit applies (5/hour, 50/day). Reports are server-side records only — they are NEVER published as PDS records, never broadcast on the firehose, and are only readable by moderators via the admin surface
- app.tempomusic.moderation.defs defs Report subject identifying an actor account.
- app.tempomusic.moderation.listModerationNotices query List the authenticated user's own moderation notices (takedown, suspension, ban), newest first. Prepared for future client consumption; delivery channels (push/email) are out of scope. The subject is always the caller — there is no parameter to read another user's notices.
- app.tempomusic.notification.defs defs A single notification surfaced to the recipient. Server-projected from indexed like, reply and post records; recipients are scoped server-side from the viewer parameter.
- app.tempomusic.notification.getUnreadCount query Count of unread notifications for the requesting viewer. The viewer DID is read from the service-auth JWT (`iss` claim) — no `?viewer=` query parameter.
- app.tempomusic.notification.listNotifications query Paginated notifications addressed to the requesting viewer, newest first. The viewer DID is read from the service-auth JWT (`iss` claim) — no `?viewer=` query parameter.
- app.tempomusic.notification.updateSeen procedure Mark the requesting viewer's notifications as seen up to seenAt. Server clamps the stored value to max(stored, received) so the marker is monotonic. The viewer DID is read from the service-auth JWT (`iss` claim) — no `?viewer=` query parameter.
- app.tempomusic.staff.banAccount procedure Permanently ban an account: all of its content disappears from the public read surface and no surface can lift the state. Escalating an active suspension replaces it. Requires the `superAdmin` role. Writes an `accountBanned` audit row. Federation caveat: the ban blocks this DID — re-registration under a fresh DID is structurally possible in AT Protocol.
- app.tempomusic.staff.createInviteCode procedure Generate a batch of single-use PDS invite codes (TEM-502). Requires the caller to have the `superAdmin` role. Each call requests `quantity` codes from the PDS (`com.atproto.server.createInviteCode` with `useCount=1`), writes one `inviteCodeCreated` audit row per successfully-created code (payload = SHA-256 hash of the suffix + `use_count`, never the plain code), and returns the suffix-only codes. When the PDS returns a transient failure mid-batch the handler stops iterating and returns the succe
- app.tempomusic.staff.defs defs AppView projection of a moderation_audit row. The audit log is append-only and never purged; entries survive hard-deletion of the actor account.
- app.tempomusic.staff.disableInviteCodes procedure Disable one or more PDS invite codes (TEM-502). Requires the caller to have the `superAdmin` role. Codes are provided as suffix-only strings; the handler re-prepends the env prefix stored under `APPVIEW_PDS_INVITE_CODE_PREFIX` before calling `com.atproto.admin.disableInviteCodes`. On success, writes one `inviteCodeDisabled` audit row per code (payload = SHA-256 hash of the suffix, never the plain code).
- app.tempomusic.staff.dismissReport procedure Close a report without acting on the subject. Valid from `open` (fast-path obvious junk) and from `underReview`. Writes a single `reportDismissed` audit row. Fast-path dismissal directly from `open` does NOT emit a `reportTriaged` audit row first — only the dismissal entry is written.
- app.tempomusic.staff.getAccountModeration query Current account-level moderation state of a DID (suspension or ban), or an empty object when the account is unrestricted. A lapsed suspension is reported with its (past) `until` — interpretation is the caller's. Requires the `moderator` role.
- app.tempomusic.staff.getReport query Fetch a single report by id, including its subject snapshot as captured at intake. Requires the caller to be an active moderator.
- app.tempomusic.staff.getSubjectContent query Moderator-grade fetch of a report subject's stored record and lifecycle state, INCLUDING rows the public read surface hides: taken-down content, soft-deleted rows inside the 30-day evidence window, and tombstoned rows (record is then null — 'purged' — and the console shows a banner instead of content). ADR-009 future-work item 'expose retained content through the moderation surface'. Requires the caller to be an active moderator. When media attachments ship, CSAM-quarantined blobs are NEVER rend
- app.tempomusic.staff.getSubjectModerationHistory query Audit timeline for a single subject (post URI, reply URI, or account DID). Returns every moderation_audit row referencing the subject, in reverse-chronological order. Used by the moderator UI to answer 'what has ever happened to this content/account'. Requires the caller to be an active moderator.
- app.tempomusic.staff.grantStaffRole procedure Add a DID to the moderators allowlist with the given role. Requires the caller to have the `superAdmin` role. Writes a `moderatorGranted` audit row only when a new row is inserted or a revoked row is reactivated. Idempotent: granting a DID that already holds the same active role returns 200 with `granted: false` and no audit row.
- app.tempomusic.staff.listAuditEvents query Global audit journal browser for the moderation console. Returns moderation_audit rows in reverse-chronological (created_at, id) keyset order, optionally filtered by acting moderator, action type, or subject DID. Complements getSubjectModerationHistory (per-subject view) with the 'what has the team been doing' view. Requires the caller to be an active moderator.
- app.tempomusic.staff.listInviteCodes query List PDS invite codes on-demand via the PDS (`com.atproto.admin.getInviteCodes`). No local mirror — the source of truth is the PDS. Requires the caller to have the `superAdmin` role. Codes are returned as suffix-only strings (the env prefix stored under `APPVIEW_PDS_INVITE_CODE_PREFIX` has been stripped). Optional `createdBy` filter narrows the listing to codes emitted by a specific staff DID (applied server-side by the AppView — the admin endpoint has no such parameter); when omitted every code
- app.tempomusic.staff.listReports query Cursored listing of moderation reports for the queue view. Filterable by status, subject type, reason, reporter, and subject author. Sorted by (created_at DESC, id DESC). Requires the caller to be an active moderator (any role).
- app.tempomusic.staff.listStaff query List currently-active moderators. Audit transparency: any moderator may see who else holds privileges. Set `includeRevoked: true` to include historical (revoked) rows. Sorted by (granted_at DESC, did). Requires the caller to be an active moderator (any role).
- app.tempomusic.staff.restoreSubject procedure Flip `moderation_status` back to 0 on a previously taken-down subject row. Writes a `subjectRestore` audit row. Trivially symmetric with takedownSubject — both act on the subject row only; lifting an account-wide sanction is unsuspendAccount's job (bans are permanent). Requires the caller to be an active moderator.
- app.tempomusic.staff.revokeStaffRole procedure Mark a moderator row as revoked. The DID can no longer pass the moderator gate (latency = the moderator-cache TTL, default 30s). Requires the caller to have the `superAdmin` role. A super-admin cannot revoke themselves. Writes a `moderatorRevoked` audit row only when an active row was actually revoked. Idempotent: revoking a DID that is not currently an active moderator returns 200 with `revoked: false` and no audit row.
- app.tempomusic.staff.suspendAccount procedure Suspend an account: all of its content disappears from the public read surface (feeds, single fetches, threads, notifications) while the row is active. Reversible via unsuspendAccount; optionally time-boxed via `until` (evaluated at read time — a lapsed suspension readmits content with no further action). Re-suspending replaces the current suspension (new reason/expiry, new audit row). Requires the `moderator` role. Writes an `accountSuspended` audit row.
- app.tempomusic.staff.takedownSubject procedure Flip `moderation_status` on a subject row to a non-zero takedown class. Read paths immediately refuse to serve the subject (404 on getPost / getReply / actor reads). Re-takedown against an already-taken-down subject is permitted as a severity escalation (e.g. `reported` → `legalHold`) or to amend the reason; each call writes a new audit row. Scoped to the subject row ONLY: an account takedown flips the profile row (offensive avatar/bio case) and does not touch the account's content — hiding ever
- app.tempomusic.staff.triageReport procedure Claim a report for review. Transitions status `open` → `underReview` atomically; if the report has already been claimed by another moderator, returns `AlreadyTriaged`. Writes a `reportTriaged` audit row. Requires the caller to be an active moderator.
- app.tempomusic.staff.unsuspendAccount procedure Lift an account suspension: deletes the current state row (history stays in the audit log) and instantly readmits the account's content on the public surface. Also accepts a LAPSED suspension (removes the stale row). Cannot lift a ban. Requires the `moderator` role. Writes an `accountUnsuspended` audit row.