Properties
category: reference
tags:
  - phase-2
  - management-api
last_updated: 2026-03-13

P2-4: Management API

Branch: feat/P2-4-management-api (from phase-2) Commit: 1e80a07 Tests: 30 passing

What was built

WSGI middleware (ManagementMiddleware) that intercepts /admin/* requests before they reach the otterwiki Flask app. All routes require platform JWT authentication.

Files

File Purpose
app/management/routes.py WSGI middleware with 8 API endpoints
app/management/wiki_init.py Git repo init + bootstrap template writer
app/management/token.py MCP bearer token generation (secrets + bcrypt)
app/models/user.py Added get_by_email() scan method
tests/test_management_api.py 30 unit tests (moto + tmpdir)

API Endpoints

Method Path Description
POST /admin/wikis Create wiki (repo init, bootstrap, ACL, token)
GET /admin/wikis List user's wikis
GET /admin/wikis/{slug} Wiki details + MCP endpoint
DELETE /admin/wikis/{slug} Delete wiki (repo, DB records, ACLs)
POST /admin/wikis/{slug}/token Regenerate MCP bearer token
POST /admin/wikis/{slug}/acl Grant access (body: {email, role})
DELETE /admin/wikis/{slug}/acl/{grantee_id} Revoke access
GET /admin/wikis/{slug}/acl List ACL entries

Design decisions

  • WSGI middleware pattern — wraps the otterwiki app; if PATH_INFO starts with /admin, handle it; otherwise pass through.
  • Plain JSON responsesjson.dumps() with Content-Type: application/json, not Flask.
  • Ownership enforcement — delete, token regen, and ACL grant/revoke require role=owner in ACL table.
  • Tier limitswiki_count >= wiki_limit check on create; count incremented/decremented atomically.
  • Token shown once — plaintext returned only on create and regenerate; only bcrypt hash stored.
  • Repo cleanup on create failure — if init_wiki_repo fails, DB records are rolled back.
  • wiki_id format{owner_id}:{wiki_slug} (consistent with ACL model).
  • MCP endpoint — returns /{slug}/mcp for now (no username mapping yet).

Integration notes

To wire into the Lambda handler, wrap the otterwiki WSGI app:

from app.management.routes import ManagementMiddleware
wrapped = ManagementMiddleware(
    otterwiki_wsgi_app,
    auth_middleware=auth_middleware,
    user_model=users,
    wiki_model=wikis,
    acl_model=acls,
)
handler = make_lambda_handler(wrapped)

Not included (future work)

  • Lambda handler wiring (will be done when all Phase 2 components integrate)
  • Username-to-subdomain mapping for MCP endpoint URLs
  • Rate limiting on wiki creation
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