---
status: current
platform: robot.wtf (VPS)
---

Extracted from the original wikibot.io design. AWS-specific content archived at [[Archive/AWS_Design/Auth]].

See also: [[Design/VPS_Architecture]], [[Design/Data_Model]].

---

## Auth Overview

**ATProto OAuth login** — users authenticate via their ATProto identity (Bluesky handle/DID). The platform runs a self-hosted OAuth 2.1 Authorization Server for MCP connections.

Three auth paths, all converging on the same identity resolution and ACL check:

1. **Browser session**: ATProto OAuth → platform issues a session JWT (signed with our RS256 key) → middleware validates JWT on each request → resolves user → checks ACL → sets Otterwiki headers.
2. **MCP OAuth (Claude.ai)**: Self-hosted OAuth 2.1 AS handles DCR, PKCE, token issuance, JWKS. Claude.ai discovers `/.well-known/oauth-protected-resource`, authenticates, presents access token. Per-wiki authorization in our middleware.
3. **Bearer token (Claude Code, API clients)**: Token in `Authorization` header → middleware hashes token, looks up in DB → resolves to user + wiki → checks ACL.

The platform middleware is the single authentication boundary. Everything downstream trusts it.

## MCP Auth

**OAuth 2.1 (Claude.ai)**: Self-hosted AS (authlib-based) provides DCR, PKCE, AS metadata, token endpoint, and JWKS. The MCP endpoint serves `/.well-known/oauth-protected-resource` pointing to the local AS. Per-wiki authorization happens in middleware — the AS identifies the user, middleware checks wiki access. See [[Design/VPS_Architecture]] for implementation details.

**Bearer token (Claude Code / API)**: Each wiki gets a unique MCP bearer token, stored as a bcrypt hash in the platform DB. The user sees the token once (at creation) and can regenerate it from the dashboard. Usage: `claude mcp add --transport http`.

## ACL Model

Simple role-based model:

| Role | Read | Write | Delete | Manage ACL | Delete wiki |
|------|------|-------|--------|------------|-------------|
| viewer | yes | no | no | no | no |
| editor | yes | yes | yes | no | no |
| owner | yes | yes | yes | yes | yes |

Wiki creator is always owner. Owners can grant viewer/editor access to other registered users.

## Authorization Flow

**Layer 1 — Platform middleware** (before Otterwiki sees the request):

1. Resolve user identity from JWT or bearer token
2. Resolve wiki from request routing
3. Look up ACL: does this user have a grant on this wiki?
4. If no grant and wiki is not public → 403
5. Map ACL role to Otterwiki permission headers:

| ACL role | `x-otterwiki-permissions` header |
|----------|----------------------------------|
| viewer | `READ` |
| editor | `READ,WRITE,UPLOAD` |
| owner | `READ,WRITE,UPLOAD,ADMIN` |
| anonymous (public wiki) | Synthetic user with `READ` only |

6. Set headers: `x-otterwiki-email`, `x-otterwiki-name`, `x-otterwiki-permissions`
7. Forward to Otterwiki

**Layer 2 — Otterwiki** (`AUTH_METHOD=PROXY_HEADER`):

- Reads headers, creates ephemeral user object per request
- No local user database — all identity comes from headers
- Enforces READ/WRITE/UPLOAD/ADMIN based on the permissions header

**For MCP and API paths**, Otterwiki is not involved in auth — the handlers read the git repo directly. Authorization happens entirely in Layer 1.

### Public wiki access

The platform middleware injects a synthetic anonymous user with READ permission for public wikis. Otterwiki config stays identical for all wikis:

```python
AUTH_METHOD = "PROXY_HEADER"
READ_ACCESS = "APPROVED"          # always — public access handled by middleware
WRITE_ACCESS = "APPROVED"
ATTACHMENT_ACCESS = "APPROVED"
DISABLE_REGISTRATION = True       # no Otterwiki-level registration
```

## Otterwiki Admin Panel — Section Disposition

The wiki owner (ACL role `owner`) gets `ADMIN` permission → access to `/-/admin/*`.

| Admin section | Disposition | Reason |
|--------------|-------------|--------|
| Application Preferences | **Keep** | Wiki branding: site name, description, logo, favicon, language |
| Sidebar Preferences | **Keep** | UI layout: sidebar shortcuts, custom menu items |
| Content and Editing | **Keep** | Git workflow: commit message mode/template, page name casing, WikiLink style |
| Repository Management | **Disable** | Conflicts with platform Git management |
| Permissions and Registration | **Disable** | Conflicts with platform auth (middleware-managed) |
| User Management | **Disable** | No local user database in ProxyHeaderAuth mode |
| Mail Preferences | **Disable for MVP** | SMTP notifications — revisit later |

**Implementation**: Override admin navigation template to hide disabled sections. Return 404 from disabled routes in middleware (defense in depth).
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9