|
| 1 | +# Database Mirrors |
| 2 | + |
| 3 | +Ephemeral database mirrors let developers work on isolated copies of community knowledge databases without running a local server. Instead of full ephemeral backends (Docker containers, port allocation, proxies), mirrors provide lightweight SQLite copies that can be created and destroyed in seconds. |
| 4 | + |
| 5 | +## How it Works |
| 6 | + |
| 7 | +``` |
| 8 | +Developer's CLI OSA Backend |
| 9 | +┌──────────────┐ ┌────────────────────────┐ |
| 10 | +│ osa ask │ X-Mirror-ID │ │ |
| 11 | +│ --mirror │ ──────────────> │ Middleware sets │ |
| 12 | +│ abc123 │ │ ContextVar │ |
| 13 | +│ │ │ │ │ |
| 14 | +│ │ │ v │ |
| 15 | +│ │ │ get_db_path("hed") │ |
| 16 | +│ │ │ returns: │ |
| 17 | +│ │ │ mirrors/abc123/hed.db │ |
| 18 | +│ │ │ (instead of │ |
| 19 | +│ │ │ knowledge/hed.db) │ |
| 20 | +└──────────────┘ └────────────────────────┘ |
| 21 | +``` |
| 22 | + |
| 23 | +One shared backend, many lightweight database copies. All search, sync, and tool code works unmodified because every database access flows through a single function (`get_db_path`) that checks a `ContextVar`. |
| 24 | + |
| 25 | +## Quick Start |
| 26 | + |
| 27 | +```bash |
| 28 | +# Create a mirror of the HED and BIDS databases |
| 29 | +osa mirror create -c hed -c bids --label "testing-new-prompt" |
| 30 | + |
| 31 | +# Use with: osa ask "question" -a hed --mirror abc123def456 |
| 32 | +# Mirror created: abc123def456 |
| 33 | + |
| 34 | +# Ask questions against your mirror |
| 35 | +osa ask -a hed "How do I annotate a button press?" --mirror abc123def456 |
| 36 | + |
| 37 | +# Interactive chat with your mirror |
| 38 | +osa chat -a hed --mirror abc123def456 |
| 39 | + |
| 40 | +# Re-sync from public sources into your mirror |
| 41 | +osa mirror sync abc123def456 --type github |
| 42 | + |
| 43 | +# When done, clean up |
| 44 | +osa mirror delete abc123def456 |
| 45 | +``` |
| 46 | + |
| 47 | +## What Works vs. What Doesn't |
| 48 | + |
| 49 | +### Works with mirrors (no local server needed) |
| 50 | + |
| 51 | +- Querying existing community databases with different data |
| 52 | +- Re-syncing from public sources (GitHub, papers, etc.) into mirror |
| 53 | +- Using BYOK with any LLM provider |
| 54 | +- Multiple developers working independently |
| 55 | +- Testing retrieval quality with modified data |
| 56 | + |
| 57 | +### Requires a local server |
| 58 | + |
| 59 | +- **Schema changes**: New tables/columns need new Python code |
| 60 | +- **New community from scratch**: Server has no routes for unregistered communities |
| 61 | +- **Modified tool/search code**: Remote server runs deployed code, not your branch |
| 62 | +- **Modified sync pipeline**: Same; remote runs old sync logic |
| 63 | +- **Local LLM**: LLM must be reachable from server |
| 64 | + |
| 65 | +For these cases, use `osa mirror pull` to download mirror databases locally, then run `osa serve`: |
| 66 | + |
| 67 | +```bash |
| 68 | +# Download mirror databases locally |
| 69 | +osa mirror pull abc123def456 |
| 70 | + |
| 71 | +# Run local server with the downloaded data |
| 72 | +osa serve |
| 73 | +``` |
| 74 | + |
| 75 | +## CLI Commands |
| 76 | + |
| 77 | +### `osa mirror create` |
| 78 | + |
| 79 | +Create a new ephemeral database mirror. |
| 80 | + |
| 81 | +```bash |
| 82 | +osa mirror create -c hed -c bids |
| 83 | +osa mirror create -c hed --label "testing-new-prompt" --ttl 24 |
| 84 | +``` |
| 85 | + |
| 86 | +| Option | Description | Default | |
| 87 | +|--------|-------------|---------| |
| 88 | +| `--community, -c` | Community ID to include (repeatable) | Required | |
| 89 | +| `--label, -l` | Human-readable label | None | |
| 90 | +| `--ttl` | Hours until mirror expires (1-168) | 48 | |
| 91 | +| `--api-key, -k` | OpenRouter API key | From config | |
| 92 | +| `--api-url` | Override API URL | From config | |
| 93 | + |
| 94 | +### `osa mirror list` |
| 95 | + |
| 96 | +List all active mirrors. |
| 97 | + |
| 98 | +```bash |
| 99 | +osa mirror list |
| 100 | +``` |
| 101 | + |
| 102 | +### `osa mirror info` |
| 103 | + |
| 104 | +Show detailed information about a mirror. |
| 105 | + |
| 106 | +```bash |
| 107 | +osa mirror info abc123def456 |
| 108 | +``` |
| 109 | + |
| 110 | +### `osa mirror delete` |
| 111 | + |
| 112 | +Delete a mirror and its databases. |
| 113 | + |
| 114 | +```bash |
| 115 | +osa mirror delete abc123def456 |
| 116 | +osa mirror delete abc123def456 --yes # skip confirmation |
| 117 | +``` |
| 118 | + |
| 119 | +### `osa mirror refresh` |
| 120 | + |
| 121 | +Re-copy production databases into an existing mirror (resets to current production state). |
| 122 | + |
| 123 | +```bash |
| 124 | +osa mirror refresh abc123def456 |
| 125 | +osa mirror refresh abc123def456 -c hed # refresh only HED |
| 126 | +``` |
| 127 | + |
| 128 | +### `osa mirror sync` |
| 129 | + |
| 130 | +Run sync pipeline against a mirror's databases (populates from public sources). |
| 131 | + |
| 132 | +```bash |
| 133 | +osa mirror sync abc123def456 # sync all types |
| 134 | +osa mirror sync abc123def456 --type github # sync only GitHub |
| 135 | +``` |
| 136 | + |
| 137 | +Supported sync types: `github`, `papers`, `docstrings`, `mailman`, `faq`, `beps`, `all`. |
| 138 | + |
| 139 | +### `osa mirror pull` |
| 140 | + |
| 141 | +Download mirror databases locally for offline development. |
| 142 | + |
| 143 | +```bash |
| 144 | +osa mirror pull abc123def456 |
| 145 | +osa mirror pull abc123def456 -c hed -o ./data/knowledge |
| 146 | +``` |
| 147 | + |
| 148 | +## API Endpoints |
| 149 | + |
| 150 | +All mirror endpoints require authentication (BYOK or admin key). |
| 151 | + |
| 152 | +| Method | Endpoint | Description | |
| 153 | +|--------|----------|-------------| |
| 154 | +| `POST` | `/mirrors` | Create a mirror | |
| 155 | +| `GET` | `/mirrors` | List active mirrors | |
| 156 | +| `GET` | `/mirrors/{id}` | Get mirror metadata | |
| 157 | +| `DELETE` | `/mirrors/{id}` | Delete a mirror | |
| 158 | +| `POST` | `/mirrors/{id}/refresh` | Re-copy from production | |
| 159 | +| `POST` | `/mirrors/{id}/sync` | Run sync pipeline | |
| 160 | +| `GET` | `/mirrors/{id}/download/{community}` | Download SQLite file | |
| 161 | + |
| 162 | +### Create Mirror |
| 163 | + |
| 164 | +```bash |
| 165 | +curl -X POST https://api.osc.earth/osa/mirrors \ |
| 166 | + -H "Content-Type: application/json" \ |
| 167 | + -H "X-OpenRouter-Key: your-key" \ |
| 168 | + -d '{"community_ids": ["hed", "bids"], "ttl_hours": 48, "label": "my-test"}' |
| 169 | +``` |
| 170 | + |
| 171 | +Response: |
| 172 | + |
| 173 | +```json |
| 174 | +{ |
| 175 | + "mirror_id": "abc123def456", |
| 176 | + "community_ids": ["hed", "bids"], |
| 177 | + "created_at": "2026-03-07T12:00:00+00:00", |
| 178 | + "expires_at": "2026-03-09T12:00:00+00:00", |
| 179 | + "label": "my-test", |
| 180 | + "size_bytes": 1048576, |
| 181 | + "expired": false |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +### Query with Mirror |
| 186 | + |
| 187 | +Include the `X-Mirror-ID` header with any existing endpoint: |
| 188 | + |
| 189 | +```bash |
| 190 | +curl -X POST https://api.osc.earth/osa/hed/ask \ |
| 191 | + -H "Content-Type: application/json" \ |
| 192 | + -H "X-OpenRouter-Key: your-key" \ |
| 193 | + -H "X-Mirror-ID: abc123def456" \ |
| 194 | + -d '{"question": "What is HED?"}' |
| 195 | +``` |
| 196 | + |
| 197 | +## Resource Limits |
| 198 | + |
| 199 | +| Limit | Default | |
| 200 | +|-------|---------| |
| 201 | +| Max mirrors total | 50 | |
| 202 | +| Max mirrors per BYOK user | 2 | |
| 203 | +| Max TTL | 7 days (168 hours) | |
| 204 | +| Default TTL | 48 hours | |
| 205 | + |
| 206 | +Expired mirrors are automatically cleaned up by the server every hour. Admin key holders are only subject to the global mirror cap. |
| 207 | + |
| 208 | +## Architecture |
| 209 | + |
| 210 | +### Storage Layout |
| 211 | + |
| 212 | +``` |
| 213 | +data/ |
| 214 | + knowledge/ # Production databases (unchanged) |
| 215 | + hed.db |
| 216 | + bids.db |
| 217 | + mirrors/ # Ephemeral copies |
| 218 | + abc123def456/ # mirror_id |
| 219 | + _metadata.json # Timestamps, label, communities |
| 220 | + hed.db # Copy of production hed.db |
| 221 | + bids.db # Copy of production bids.db |
| 222 | +``` |
| 223 | + |
| 224 | +### ContextVar Routing |
| 225 | + |
| 226 | +All database access flows through `get_db_path(project)` in `src/knowledge/db.py`. When a mirror is active: |
| 227 | + |
| 228 | +1. Request arrives with `X-Mirror-ID: abc123` header |
| 229 | +2. Middleware validates the mirror exists and isn't expired |
| 230 | +3. Middleware sets a `contextvars.ContextVar` for the request |
| 231 | +4. `get_db_path("hed")` returns `data/mirrors/abc123/hed.db` instead of `data/knowledge/hed.db` |
| 232 | +5. All search, sync, and tool functions work unmodified |
| 233 | +6. Middleware resets the ContextVar when the request completes |
| 234 | + |
| 235 | +### Security |
| 236 | + |
| 237 | +- Mirror IDs are validated against path traversal (alphanumeric, hyphens, underscores only, max 64 chars) |
| 238 | +- Community IDs are validated at the API boundary |
| 239 | +- Per-user rate limits prevent resource exhaustion |
| 240 | +- Expired mirrors return HTTP 410 Gone |
| 241 | +- Auto-cleanup runs hourly |
0 commit comments