--- 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. Fix submitted as `fix/permissions-admin-guard` — PR pending to redimp. Red/green verified. ## Panel Status ### Already working (no changes needed) | Panel | Route | Notes | |-------|-------|-------| | Application Preferences | `/-/admin` | SITE_NAME, SITE_DESCRIPTION, SITE_LOGO, etc. SERVER_NAME hidden in PLATFORM_MODE. | | 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` | ✅ **Gated in PLATFORM_MODE** (2026-03-17). Route, nav link, pull webhook, and auto-push/pull all disabled. SSH key storage and outbound connections blocked. May re-enable with sandboxing if demand emerges. | ### Phase 1: Permissions panel ✅ DEPLOYED (2026-03-17) `READ_ACCESS` / `WRITE_ACCESS` / `ATTACHMENT_ACCESS` configurable via Otterwiki admin. Resolver enforces by intersecting with platform ACL. Registration fields hidden in PLATFORM_MODE. `READ_ACCESS` replaces `is_public` as sole source of truth for anonymous access. ### Phase 2: User Management (consolidated) **Key decision (2026-03-17): Remove the "Collaborators" tab from the platform dashboard entirely. All user/access management happens in Otterwiki's User Management panel.** This consolidates three layers (platform ACL, wiki preferences, per-wiki roster) into two places the wiki owner actually sees: the Permissions panel and the User Management panel. **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. **Admin workflow:** 1. Admin opens User Management panel in Otterwiki's admin 2. Enters a DID handle (e.g., `@alice.bsky.social`) to add a user 3. Sets per-user flags: role (viewer/editor/admin), `is_approved` 4. User can now access the wiki at whatever level their flags grant **Wiki admins can create other wiki admins.** #### Consolidating platform ACL into User Management Currently, the platform dashboard has a "Collaborators" tab that manages the `acls` table in robot.db (granting owner/editor/viewer roles). Phase 2 moves this into Otterwiki's User Management panel: - **Remove the Collaborators tab** from the dashboard (`wiki_settings.html`) - **Remove the Collaborators API endpoints** from the management API (or deprecate) - **User Management becomes the single place** to manage who can access the wiki and at what level - **The per-wiki `user` table replaces the platform `acls` table** as the source of truth for per-wiki access - **The resolver reads from the per-wiki user table** instead of (or in addition to) the platform ACL table - **The platform ACL ceiling is enforced implicitly:** the wiki owner's own role (from the `wikis` table) is the ceiling. The owner can grant up to their own level but not beyond. #### Migration path 1. On Phase 2 deploy, migrate existing `acls` entries into per-wiki `user` tables 2. Remove Collaborators tab from dashboard 3. Remove or deprecate ACL management API endpoints 4. Resolver reads permissions from per-wiki user table #### `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 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 #### Required changes **Otterwiki fork:** 1. Re-enable User Management + User Edit panels in PLATFORM_MODE 2. Customize add-user form: accept DID handle, hide password fields 3. Override `ProxyHeaderAuth.get_all_user()` to query per-wiki user table 4. Map user flags to roles (viewer/editor/admin) for display **robot.wtf:** 1. Remove Collaborators tab from `wiki_settings.html` 2. Remove or deprecate ACL management endpoints in `management/routes.py` 3. Extend `_apply_wiki_access_restrictions()` to query per-wiki user table for APPROVED level and per-user role 4. Migration script: copy `acls` entries to per-wiki `user` tables 5. Resolver reads per-wiki user table for permission computation ### Keep disabled: Mail Preferences Per-wiki SMTP config is a security/spam risk. Notification mechanism (email vs. BlueSky DMs) is an open question. ## Implementation Order 1. ~~Permissions panel~~ ✅ Deployed 2. ~~Hide SERVER_NAME~~ ✅ Deployed 3. ~~Remove is_public toggle~~ ✅ Deployed 4. ~~User Management~~ ✅ Deployed (2026-03-17) — platform ACL removed entirely, per-wiki user table is sole permission source, role UI (admin/editor/viewer) deployed, APPROVED access level functional, dashboard collapsed to single-page card 5. ~~Repository Management~~ ✅ Gated in PLATFORM_MODE (2026-03-17) — route, nav, webhook, auto-push/pull all disabled ### Remaining disabled - **Mail Preferences** — per-wiki SMTP is a security/spam risk. Notification mechanism (email vs. BlueSky DMs) is an open question.
