|
| 1 | +# HTML5 App Serving Route Implementation |
| 2 | + |
| 3 | +## Goal |
| 4 | +Implement server-side rendering for HTML5 applications, serving extracted ZIP content from blob storage via `/apps/{appId}/{path}` routes. Must work in both web and native Tauri modes. |
| 5 | + |
| 6 | +## Architecture |
| 7 | + |
| 8 | +**Doorway** = Projection layer (read-only view cache from P2P content-server layer, includes both DHT and storage backends) |
| 9 | +**elohim-storage** = Backend storage layer (SQLite metadata, blob storage) |
| 10 | + |
| 11 | +### Deployment Modes |
| 12 | + |
| 13 | +| Mode | App Serving | |
| 14 | +|------|-------------| |
| 15 | +| **Web** | Browser → Doorway `/apps/` → elohim-storage blob | |
| 16 | +| **Native (Tauri)** | Tauri → Local conductor `/apps/` → Local blob storage | |
| 17 | + |
| 18 | +The `/apps/` route lives in Doorway for web deployments, but native Tauri apps need equivalent local serving capability - either via embedded conductor or bundled assets. |
| 19 | + |
| 20 | +## Context |
| 21 | + |
| 22 | +### Current State |
| 23 | +- HTML5 app metadata stored inline in SQLite (`content_body`) |
| 24 | +- ZIP files exist in `genesis/docs/content/fct/` (e.g., `evolution-of-trust.zip` - 7MB) |
| 25 | +- Content JSON references ZIP via `metadata.localZipPath` |
| 26 | +- Angular `IframeRenderer` expects URL: `${baseUrl}/apps/${appId}/${entryPoint}` |
| 27 | +- Currently falls back to `fallbackUrl` since `/apps/` route doesn't exist |
| 28 | + |
| 29 | +### Content Structure |
| 30 | +```json |
| 31 | +{ |
| 32 | + "id": "simulation-evolution-of-trust", |
| 33 | + "contentFormat": "html5-app", |
| 34 | + "content": { |
| 35 | + "appId": "evolution-of-trust", |
| 36 | + "entryPoint": "index.html", |
| 37 | + "fallbackUrl": "https://ncase.me/trust/" |
| 38 | + }, |
| 39 | + "metadata": { |
| 40 | + "localZipPath": "docs/content/fct/evolution-of-trust.zip", |
| 41 | + "embedStrategy": "iframe", |
| 42 | + "securityPolicy": { |
| 43 | + "sandbox": ["allow-scripts", "allow-same-origin"], |
| 44 | + "csp": "default-src 'self'; script-src 'unsafe-inline'" |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +## Implementation Tasks |
| 51 | + |
| 52 | +### 1. Blob Storage for ZIPs (elohim-storage) |
| 53 | +Update seeder to upload ZIP files: |
| 54 | +- Read ZIP from `metadata.localZipPath` |
| 55 | +- Upload to blob storage via `/blob/` endpoint |
| 56 | +- Store returned `blob_hash` in content record |
| 57 | +- Track `blob_cid` for IPFS compatibility |
| 58 | + |
| 59 | +### 2. Doorway `/apps/` Route (Web Mode) |
| 60 | +Create `holochain/doorway/src/routes/apps.rs`: |
| 61 | + |
| 62 | +```rust |
| 63 | +// Route: GET /apps/{app_id}/{path:.*} |
| 64 | +// 1. Look up content by appId (via storage backend) |
| 65 | +// 2. Get blob_hash from content record |
| 66 | +// 3. Fetch ZIP from elohim-storage blob endpoint |
| 67 | +// 4. Extract requested file from ZIP (with caching) |
| 68 | +// 5. Serve with appropriate Content-Type |
| 69 | +``` |
| 70 | + |
| 71 | +### 3. Native Tauri Support |
| 72 | +Options for native app serving: |
| 73 | +- **Option A**: Bundle HTML5 apps as Tauri assets, serve via `tauri://` protocol |
| 74 | +- **Option B**: Local blob cache with embedded HTTP server |
| 75 | +- **Option C**: Extract to temp directory on first access, serve via file:// URLs |
| 76 | + |
| 77 | +IframeRenderer must detect mode and construct appropriate URL: |
| 78 | +```typescript |
| 79 | +// Web: https://doorway.example.com/apps/evolution-of-trust/index.html |
| 80 | +// Native: tauri://localhost/apps/evolution-of-trust/index.html |
| 81 | +// or: file:///path/to/extracted/evolution-of-trust/index.html |
| 82 | +``` |
| 83 | + |
| 84 | +### 4. ZIP Extraction Service |
| 85 | +Create extraction service (shared between doorway and native): |
| 86 | +- In-memory ZIP extraction using `zip` crate |
| 87 | +- LRU cache for extracted apps (configurable size limit) |
| 88 | +- Lazy extraction: only extract files as requested |
| 89 | +- Full extraction option for frequently accessed apps |
| 90 | + |
| 91 | +### 5. Content-Type Detection |
| 92 | +Map file extensions to MIME types: |
| 93 | +```rust |
| 94 | +".html" => "text/html" |
| 95 | +".js" => "application/javascript" |
| 96 | +".css" => "text/css" |
| 97 | +".png" => "image/png" |
| 98 | +".svg" => "image/svg+xml" |
| 99 | +".json" => "application/json" |
| 100 | +".wasm" => "application/wasm" |
| 101 | +``` |
| 102 | + |
| 103 | +## File Structure |
| 104 | +``` |
| 105 | +holochain/doorway/src/ |
| 106 | +├── routes/ |
| 107 | +│ ├── mod.rs # Add apps module |
| 108 | +│ └── apps.rs # NEW: /apps/ route handler |
| 109 | +└── services/ |
| 110 | + ├── mod.rs # Add app_extractor module |
| 111 | + └── app_extractor.rs # NEW: ZIP extraction + caching |
| 112 | +
|
| 113 | +elohim-app/src-tauri/ |
| 114 | +└── src/ |
| 115 | + └── apps.rs # Native app serving (if Option B/C) |
| 116 | +``` |
| 117 | + |
| 118 | +## API Design |
| 119 | + |
| 120 | +### GET /apps/{app_id}/{path} |
| 121 | +- **200**: File content with appropriate Content-Type |
| 122 | +- **404**: `{ "error": "not_found", "fallback": "https://..." }` |
| 123 | +- **500**: Extraction or blob fetch error |
| 124 | + |
| 125 | +### Cache Headers |
| 126 | +``` |
| 127 | +Cache-Control: public, max-age=31536000, immutable |
| 128 | +ETag: {blob_hash}-{file_path_hash} |
| 129 | +``` |
| 130 | + |
| 131 | +## Security Considerations |
| 132 | +- Path traversal prevention (no `..` in paths) |
| 133 | +- Content-Security-Policy from metadata |
| 134 | +- Sandbox attributes for iframe embedding |
| 135 | +- No execution of server-side code from ZIPs |
| 136 | +- Tauri: Ensure proper permissions for local file access |
| 137 | + |
| 138 | +## Testing |
| 139 | +1. **Web**: Upload ZIP to blob storage, request via Doorway |
| 140 | +2. **Native**: Bundle or cache ZIP locally, request via Tauri protocol |
| 141 | +3. Verify game loads in iframe in both modes |
| 142 | + |
| 143 | +## Dependencies |
| 144 | +- `zip` crate for extraction |
| 145 | +- `mime_guess` crate for Content-Type detection |
| 146 | +- Existing blob storage infrastructure |
| 147 | +- Tauri custom protocol handler (for native) |
| 148 | + |
| 149 | +## Related Files |
| 150 | +- `elohim-app/src/app/lamad/renderers/iframe-renderer/iframe-renderer.component.ts` |
| 151 | +- `elohim-app/src/app/lamad/content-io/plugins/html5-app/html5-app-format.plugin.ts` |
| 152 | +- `genesis/data/lamad/content/simulation-evolution-of-trust.json` |
| 153 | +- `genesis/docs/content/fct/evolution-of-trust.zip` |
| 154 | +- `holochain/doorway/src/routes/mod.rs` |
| 155 | +- `holochain/elohim-storage/src/blob_store.rs` |
0 commit comments