Roles vs. verified kinds
These are two independent dimensions. One controls what you can DO; the other controls what fans SEE on a profile.
role (authorization)
| Role | Powers |
|---|---|
member | Regular fan. No admin tool access. |
mod_candidate | Probationary mod (30-day window). |
moderator | Can act on reports + scoped board mod actions. |
admin | Full daily admin: compose, manage tour/badges/venues, hide/lock board posts, verify users. |
superadmin | Studio-level: cost caps, proxy management, role grants, category creation, tour sync. |
verifiedKind (public-facing identity badge)
| Kind | Display |
|---|---|
band_member | Verified band member checkmark on profile. |
band_staff | Verified staff badge. |
tour_crew | Verified tour crew badge. |
Common mistake: marking someone as band_staff does NOT give them admin access. That's identity-only. For them to use this dashboard, set role=admin (or whatever auth tier you intend).
Recommended setup for OneRepublic team
| Person | Role | Verified Kind |
|---|---|---|
| You (sthreelabs) | superadmin | none (or band_staff if you want) |
| Band's primary manager (if trusted with platform keys) | superadmin | band_staff |
| Other working staff | admin | band_staff |
| Ryan / band members who post | admin | band_member |
| Tour crew with tool access | admin | tour_crew |
| Staffer who only needs a badge, no tools | member | band_staff |
Setting up a new staff member
- They sign into the iOS app first (creates their user row via Sign in with Apple).
- Go to Users in this dashboard → search their username.
- Click Set role → choose
admin(ormoderatorif more limited). - Click Verify → choose the right kind:
band/staff/crew. - Send them
admin.onerepublic.fncnx.com(or whatever your admin domain is) — they sign in with the same Apple ID and they're in.
If their iOS sign-in is brand-new and the system has zero admins, the worker auto-bootstraps them as admin. So always create your account first; then add other admins via the Users page (which is gated to superadmin-only).
Setting up proxy posting (manager posts as band)
Use case: social media manager publishes a post that appears in the feed as if Ryan wrote it. Audit log records the manager pressed publish.
- Both users (manager + the post-as identity) must exist in the Users table and be active.
- The post-as user must be verified — usually as
band_member. - The manager must have an active role (admin / moderator / verified staff or crew).
- Go to Proxies page.
- Pick the manager from the first dropdown, the post-as identity from the second.
- Add a note (e.g., "Tour manager — covers band-account posts") and grant.
- Manager signs into this dashboard → composer dropdown shows the post-as options.
Revoking a proxy stops future posts but leaves past posts attributed (audit retention).
Promoting another superadmin
Only do this for someone you trust with platform-level keys (cost caps, proxy grants, role changes). Treat it like handing them the master keys to the building.
- Users page → find them.
- Set role →
S(superadmin).
Demoting yourself out of superadmin is blocked by lockout protection. If you ever need to remove yourself, demote yourself to admin first (which is also blocked — you'd have to grant another superadmin first and have them demote you).
Cost caps + degraded mode
Every AI moderation call, image classification, and translation logs a row to cost_events. The system auto-rolls up hourly and daily, and watches the spend against two caps:
- Soft cap — when crossed, you get a push notification (TODO: not yet wired).
- Hard cap — when crossed, the system auto-engages degraded mode: text moderation queues everything to human review (skipping the AI hop); image moderation continues. At UTC midnight, degraded mode auto-clears.
Defaults: $50/day soft, $200/day hard. Adjust on the Costs page. You can also force degraded mode on/off manually if needed.
A viral moment (album drop weekend) typically burns $20–60 in 48h at the current pricing. A normal active day at OneRepublic-scale (~10K posts/day across all surfaces) is $50–150/month total.
Board moderation + categories
Category structure
8 default categories shipped. Order: Inner Circle (pinned, members-only) / Introductions / Music / Tour & Shows / Meet & Greet / Fan Creations / Memories / General.
Tour & Shows is special
- Auto-creates one thread per show from Laylo data (lazy-bootstraps on first visit + daily cron resync).
- Sort: next upcoming show pinned top with "NEXT UP" badge, then upcoming ASC, then past DESC.
- Fan-created threads with no show attached fall to bottom.
Moderation roles
- App mods (role
moderatoror higher) — can hide/lock/pin in any category. - Category mods — scoped to ONE category. Promoted from trusted superfans. Can hide/lock/pin within their category but can't ban or warn. Grant from the Board page.
Composer constraints
Text + images only. URLs auto-stripped on submit (scalper / phishing defense). No audio, no video. Max 4 images per post, 10MB each.
Multilingual moderation
Llama Guard 3 handles 8 languages natively (en/fr/de/hi/it/pt/es/th). Other languages route through Gemini 2.5 Flash translate → Llama Guard. False-positive self-harm flags get a lyric-whitelist bypass.
Tour sync from Laylo
Source of truth: Laylo drop ID set in worker env (LAYLO_DROP_ID). The worker fetches this nightly + lazy-bootstraps on first Tour & Shows board visit.
- New show added in Laylo → new board thread auto-created next sync.
- Date / venue updated → existing thread's denormalized fields refresh and title rewrites.
- Show removed from Laylo → thread is NOT deleted (don't lose conversation history if a date temporarily disappears).
Manual sync: Board → ↻ Sync tour shows button. Adds ?refresh=1 to bypass the 5-minute Laylo cache.
IAP product-ID naming rules
Product IDs are permanent. ASC binds reporting history to them; renaming or reusing breaks longitudinal data forever. Decide the convention BEFORE creating the first ASC product.
Pattern: com.sthreelabs.<app>.<category>.<item>[.<tier>]
Examples:
com.sthreelabs.onerepublic.membership.innercircle.monthlycom.sthreelabs.onerepublic.membership.innercircle.annualcom.sthreelabs.onerepublic.flair.badge.foundingcom.sthreelabs.onerepublic.sticker.pack.encorecom.sthreelabs.onerepublic.meetgreet.entry.london2026
Full spec at IAP_REPORTING.md in the worker repo.
Deployment quick reference
| What | Where | Deploy command |
|---|---|---|
| Worker (API) | ~/Development/dev/onerepublic-worker | npx wrangler deploy |
| Admin SPA (this dashboard) | ~/Development/dev/onerepublic-admin | npx wrangler pages deploy . --project-name=onerepublic-admin |
| iOS app | ~/Development/dev/onerepublic/OneRepublic | Xcode Archive → Distribute → App Store Connect |
| D1 migrations | worker repo | npx wrangler d1 migrations apply onerepublic-db --remote |
| Secrets | worker repo | npx wrangler secret put NAME |
| One-off SQL | worker repo | npx wrangler d1 execute onerepublic-db --remote --command "..." |