Blame
|
1 | --- |
||||||
| 2 | category: reference |
|||||||
| 3 | tags: |
|||||||
| 4 | - robot.wtf |
|||||||
| 5 | - v1 |
|||||||
| 6 | - sqlite |
|||||||
| 7 | - data-access |
|||||||
| 8 | last_updated: 2026-03-15 |
|||||||
| 9 | confidence: high |
|||||||
| 10 | --- |
|||||||
| 11 | ||||||||
| 12 | # V1-1/V1-2: SQLite Data Access Layer & TenantResolver Port |
|||||||
| 13 | ||||||||
| 14 | ## Summary |
|||||||
| 15 | ||||||||
| 16 | Ported the DynamoDB-backed data models, auth middleware, TenantResolver, and ManagementMiddleware from wikibot-io to robot.wtf using SQLite as the storage backend. Identity model changed from OAuth provider sub to ATProto DID. |
|||||||
| 17 | ||||||||
| 18 | **Branch:** `feat/v1-sqlite-port` in the robot.wtf repo |
|||||||
| 19 | **Commit:** `9861a0b` |
|||||||
| 20 | **Tests:** 55/55 passing |
|||||||
| 21 | ||||||||
| 22 | ## Files Created (21 total) |
|||||||
| 23 | ||||||||
| 24 | ### Data Access Layer |
|||||||
| 25 | | File | Description | |
|||||||
| 26 | |------|-------------| |
|||||||
| 27 | | `app/db.py` | SQLite connection factory (WAL mode, foreign keys, configurable via `ROBOT_DB_PATH`) | |
|||||||
| 28 | | `app/models/user.py` | UserModel: CRUD by DID, get_by_username, set_username with validation | |
|||||||
| 29 | | `app/models/wiki.py` | WikiModel: CRUD by slug, list_by_owner, scan_by_token (bcrypt) | |
|||||||
| 30 | | `app/models/acl.py` | AclModel: CRUD by (wiki_slug, grantee_did), list_by_wiki, upsert semantics | |
|||||||
| 31 | ||||||||
| 32 | ### Auth |
|||||||
| 33 | | File | Description | |
|||||||
| 34 | |------|-------------| |
|||||||
| 35 | | `app/auth/jwt.py` | PlatformJWT: RS256 sign/verify, issuer/audience = "robot.wtf" | |
|||||||
| 36 | | `app/auth/permissions.py` | Role-to-permission mapping (owner/editor/viewer) | |
|||||||
| 37 | | `app/auth/headers.py` | Proxy header construction for otterwiki | |
|||||||
| 38 | | `app/auth/acl.py` | AclEnforcer: check_access, check_public_access, check_bearer_token | |
|||||||
| 39 | | `app/auth/middleware.py` | AuthMiddleware: JWT from header or cookie, user resolution | |
|||||||
| 40 | ||||||||
| 41 | ### WSGI Middleware |
|||||||
| 42 | | File | Description | |
|||||||
| 43 | |------|-------------| |
|||||||
| 44 | | `app/resolver.py` | TenantResolver: Host header → wiki slug, auth, ACL, storage swap (otterwiki stubbed) | |
|||||||
| 45 | | `app/management/routes.py` | ManagementMiddleware: `/api/*` routes for wiki CRUD, ACL, token management | |
|||||||
| 46 | | `app/management/token.py` | MCP bearer token generation (secrets + bcrypt) | |
|||||||
| 47 | ||||||||
| 48 | ### Tests |
|||||||
| 49 | | File | Tests | |
|||||||
| 50 | |------|-------| |
|||||||
| 51 | | `tests/conftest.py` | Shared fixtures: in-memory SQLite, model instances, sample data | |
|||||||
| 52 | | `tests/test_models.py` | 35 tests: all CRUD ops, constraints, FK enforcement, bcrypt token scan | |
|||||||
| 53 | | `tests/test_auth.py` | 20 tests: JWT round-trip, expiry, wrong key, permissions, ACL enforcer, auth middleware | |
|||||||
| 54 | ||||||||
| 55 | ## Key Schema Differences from wikibot-io |
|||||||
| 56 | ||||||||
| 57 | | Aspect | wikibot-io | robot.wtf | |
|||||||
| 58 | |--------|-----------|-----------| |
|||||||
| 59 | | User PK | UUID | ATProto DID (text) | |
|||||||
| 60 | | Wiki PK | (owner_id, wiki_slug) composite | slug only | |
|||||||
| 61 | | ACL PK | (wiki_id, grantee_id) where wiki_id = "owner:slug" | (wiki_slug, grantee_did) | |
|||||||
| 62 | | Identity | OAuth provider + sub | ATProto DID + handle | |
|||||||
| 63 | | Storage | DynamoDB + boto3 | SQLite + sqlite3 stdlib | |
|||||||
| 64 | | API prefix | `/admin/*` | `/api/*` | |
|||||||
| 65 | | JWT issuer | wikibot.io | robot.wtf | |
|||||||
| 66 | | Tier/billing | Yes | No | |
|||||||
| 67 | ||||||||
| 68 | ## Architecture Notes |
|||||||
| 69 | ||||||||
| 70 | - **No boto3/DynamoDB references** anywhere in the codebase |
|||||||
| 71 | - **otterwiki imports are conditional** in resolver.py — uses try/except ImportError with stubs |
|||||||
| 72 | - **ManagementMiddleware** auth callback returns 501 (ATProto OAuth not yet implemented) |
|||||||
| 73 | - **TenantResolver** extracts wiki slug from subdomain directly (not username+path like wikibot-io) |
|||||||
| 74 | - **Foreign keys enforced** — wiki requires valid owner_did, ACL requires valid wiki_slug and grantee_did |
|||||||