Properties
category: reference
tags: [architecture, admin, permissions, multi-tenancy]
last_updated: 2026-03-17
confidence: high

Admin Panel Re-enablement

Analysis of which Otterwiki admin panels can be re-enabled in robot.wtf's multi-tenant platform, now that per-wiki SQLite databases are deployed.

Background

Otterwiki has 8 admin panels. Three are gated by PLATFORM_MODE (added in feat/P2-8-admin-panel-hiding). Per-wiki databases landed on 2026-03-17, meaning each wiki's preferences, drafts, user, and cache tables are now isolated.

Upstream bug found

handle_permissions_and_registration in otterwiki/preferences.py is missing the has_permission("ADMIN") guard that every other admin handler has. A non-admin authenticated user can POST and overwrite READ_ACCESS/WRITE_ACCESS settings. Fix submitted as fix/permissions-admin-guard on our fork — PR pending to redimp. Red/green verified: POST returns 302 (succeeds) without fix, 403 with fix.

Panel Status

Already working (no changes needed)

Panel Route Notes
Application Preferences /-/admin SITE_NAME, SITE_DESCRIPTION, SITE_LOGO, etc. All stored in per-wiki preferences. update_app_config() reloads from the right DB. app_renderer sees changes via live reference to app.config.
Sidebar Preferences /-/admin/sidebar_preferences Pure preferences. Works.
Content and Editing /-/admin/content_and_editing COMMIT_MESSAGE, WIKILINK_STYLE, etc. Works.
Repository Management /-/admin/repository_management GIT_WEB_SERVER, remote push/pull. Works but git remote push/pull + SSH key storage is a security concern — wiki owners storing arbitrary SSH keys that the platform executes. Consider gating push/pull behind a platform-level flag.

Flag: SERVER_NAME in Application Preferences could break Flask's URL generation in multi-tenant mode. Should be hidden or read-only in PLATFORM_MODE.

Phase 1: Permissions panel (in progress)

Route: /-/admin/permissions_and_registration

What it controls:

  • READ_ACCESS — ANONYMOUS / REGISTERED / APPROVED
  • WRITE_ACCESS — same options
  • ATTACHMENT_ACCESS — same options
  • Registration settings (DISABLE_REGISTRATION, AUTO_APPROVAL, etc.) — not applicable to ATProto auth

Permission model (three layers):

  1. Platform ACL grants the ceiling (owner/editor/viewer → READ, WRITE, UPLOAD, ADMIN)
  2. Per-wiki preferences (READ_ACCESS/WRITE_ACCESS/ATTACHMENT_ACCESS) restrict further
  3. Per-user flags in per-wiki user table (Phase 2) restrict further still

Wiki owners can restrict but never escalate beyond the platform ACL ceiling. ADMIN permission is never affected by wiki preferences.

READ_ACCESS as source of truth for public access: The wiki's READ_ACCESS preference replaces the separate is_public flag in robot.db. If READ_ACCESS=ANONYMOUS, anonymous users get through. Otherwise, authentication is required. One place to manage access, in Otterwiki's native admin UI.

APPROVED level: Treated as REGISTERED until Phase 2 (user tracking). This is safe — more restrictive than intended, never less.

MCP bearer tokens: Full access regardless of wiki preferences. MCP tokens are platform-granted; wiki owners shouldn't be able to break their own integrations.

Changes (two repos):

  1. otterwiki fork (wikibot-io): Remove @platform_mode_disabled, hide registration fields in template, server-side guard for registration saves
  2. robot.wtf resolver: _apply_wiki_access_restrictions() intersects ACL permissions with wiki preferences before injecting proxy headers

Phase 2: User Management

Routes: /-/admin/user_management, /-/user/<uid>

Design: Explicit roster, not activity tracking

The per-wiki user table is an explicit roster managed by wiki admins, not an activity log. Users only exist there because an admin added them. No passive tracking — if a wiki is publicly readable, authenticated visitors are not recorded.

Admin workflow:

  1. Admin opens User Management panel
  2. Enters a DID handle (e.g., @alice.bsky.social) to add a user
  3. Sets per-user flags: is_approved, allow_read, allow_write, allow_upload, is_admin
  4. User can now access the wiki at whatever level their flags grant (intersected with wiki preferences and platform ACL ceiling)

Wiki admins can create other wiki admins. No reason to restrict this — the platform ACL is still the outer boundary.

email field decision — deferred

Otterwiki's User model keys on email. Two options:

  • Appropriate email for DID handles — quick, minimal schema divergence, but blocks future email notifications
  • Add a handle column — cleaner, but more fork divergence and schema migration work

Decision deferred. Future notification mechanism (email vs. BlueSky DMs) affects this choice.

Permission flow with APPROVED level

Once Phase 2 lands, the APPROVED access level becomes fully functional:

  1. Wiki owner sets READ_ACCESS=APPROVED in Permissions panel
  2. Resolver checks per-wiki user table for the authenticated user's handle
  3. If found and is_approved=True: READ permission granted
  4. If not found or not approved: READ stripped (along with WRITE, UPLOAD per dependency chain)

Required changes

  1. User Management UI: Admin enters DID handles to add users, sets flags. Hide password/email-editing fields in PLATFORM_MODE.
  2. get_all_user() override: ProxyHeaderAuth.get_all_user() must query the per-wiki user table instead of returning [current_user].
  3. Resolver permission check: Extend _apply_wiki_access_restrictions() to query per-wiki user table when access level is APPROVED.
  4. User Edit page: Same PLATFORM_MODE treatment — hide password actions, show handle.

Keep disabled: Mail Preferences

Per-wiki SMTP config is a security/spam risk. If mail is needed, platform provides a shared service. Notification mechanism (email vs. BlueSky DMs) is an open question.

Implementation Order

  1. Permissions panel — in progress (Phase 1)
  2. Hide SERVER_NAME — quick template fix (opportunistic)
  3. User Management — explicit roster + APPROVED level (Phase 2)