hot fixes to utc-6
This commit is contained in:
20
.claude/memory/tailscale_servers.md
Normal file
20
.claude/memory/tailscale_servers.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Tailscale Servers
|
||||
description: Archipelago Tailscale servers (archipelago-2, archipelago-3) — hostnames, SSH access, and deploy notes
|
||||
type: reference
|
||||
---
|
||||
|
||||
## Tailscale Servers
|
||||
|
||||
- **archipelago-2**: `archipelago@archipelago-2.tail2b6225.ts.net`
|
||||
- SSH key auth works (`~/.ssh/archipelago-deploy`)
|
||||
- Has Node.js, npm, Cargo/Rust, Podman — can do full builds
|
||||
- Deploy: `ARCHIPELAGO_TARGET="archipelago@archipelago-2.tail2b6225.ts.net" ./scripts/deploy-to-target.sh --live`
|
||||
|
||||
- **archipelago-3**: `archipelago@archipelago-3.tail2b6225.ts.net` (IP: 100.124.105.113)
|
||||
- SSH key auth works (key added 2026-03-12)
|
||||
- Has Podman only — NO Node.js, NO Rust/Cargo
|
||||
- Cannot build on-server; must copy pre-built binary + frontend tarball
|
||||
- Deploy method: SCP binary from archipelago-2 or local, upload frontend tarball, extract to `/opt/archipelago/web-ui/`
|
||||
|
||||
**How to apply:** For archipelago-2, use the standard deploy script with `ARCHIPELAGO_TARGET`. For archipelago-3, copy pre-built artifacts (binary + frontend tarball) since it lacks build tools.
|
||||
103
.claude/plans/shiny-bouncing-raven.md
Normal file
103
.claude/plans/shiny-bouncing-raven.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Plan: Fix Iframe Apps, Detail Pages, Kiosk, Identity Pairing, NIP-07
|
||||
|
||||
## Context
|
||||
|
||||
Three web-only apps (BotFights, 484 Kitchen, Arch Presentation) show black screens in iframe despite nginx reverse proxies being set up. The kiosk on .228 isn't running. Web-only apps need proper detail pages. The user wants Nostr identity formally paired with DID and NIP-07 browser integration for frictionless login to embedded apps.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Fix iframe black screen (HIGH)
|
||||
|
||||
**Root cause**: Proxied HTML contains root-relative paths (`href="/css/main.css"`). Browser resolves these against the origin root, not `/ext/botfights/`, so all assets 404.
|
||||
|
||||
**Fix**: Add `sub_filter` to nginx proxy blocks to rewrite root-relative paths.
|
||||
|
||||
**File**: `image-recipe/configs/nginx-archipelago.conf` (6 location blocks — 3 HTTP, 3 HTTPS)
|
||||
|
||||
Key additions per block:
|
||||
```nginx
|
||||
proxy_set_header Accept-Encoding ""; # Disable gzip so sub_filter works
|
||||
sub_filter_once off;
|
||||
sub_filter_types text/html text/css application/javascript;
|
||||
sub_filter 'href="/' 'href="/ext/{app}/';
|
||||
sub_filter 'src="/' 'src="/ext/{app}/';
|
||||
sub_filter 'action="/' 'action="/ext/{app}/';
|
||||
sub_filter "href='/" "href='/ext/{app}/";
|
||||
sub_filter "src='/" "src='/ext/{app}/";
|
||||
```
|
||||
|
||||
Deploy + nginx reload. Verify in browser DevTools (Network tab — no 404s on assets).
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Detail pages for web-only apps (MEDIUM)
|
||||
|
||||
**Problem**: Clicking a web-only app card navigates to `/dashboard/apps/{id}`. AppDetails.vue can't resolve it because web-only apps aren't in `store.packages` or `dummyApps`.
|
||||
|
||||
**Fix**:
|
||||
1. Add 7 web-only apps to `dummyApps` in AppDetails.vue (botfights, nwnn, 484-kitchen, call-the-operator, arch-presentation, syntropy-institute, t-zero) — same pattern as IndeeHub
|
||||
2. Add URL mappings in AppDetails.vue `appUrls` for all 7 (if not already present)
|
||||
3. Hide uninstall/start/stop buttons for web-only apps in AppDetails.vue
|
||||
|
||||
**Files**: `neode-ui/src/views/AppDetails.vue`
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Kiosk on .228 (MEDIUM)
|
||||
|
||||
**Problem**: Code exists but was never installed on server. No X11/Chromium packages.
|
||||
|
||||
**Steps** (SSH to .228, no code changes):
|
||||
1. `sudo apt-get install -y xorg chromium unclutter xinit`
|
||||
2. `cd ~/archy && sudo ./scripts/setup-kiosk.sh archipelago`
|
||||
3. `sudo systemctl enable --now archipelago-kiosk.service`
|
||||
4. Verify on monitor
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Pair Nostr identity with DID (LOW)
|
||||
|
||||
**Current state**: Ed25519 (DID) and secp256k1 (Nostr) are separate key pairs, both generated at startup. Not formally linked.
|
||||
|
||||
**Fix**: Include the Nostr secp256k1 pubkey in the DID Document as an additional verification method:
|
||||
- Modify `did_document_from_pubkey_hex()` in `identity.rs` to accept optional Nostr pubkey
|
||||
- Add `EcdsaSecp256k1VerificationKey2019` entry to `verificationMethod` array
|
||||
- Pass Nostr pubkey from server startup context
|
||||
|
||||
**Files**: `core/archipelago/src/identity.rs`, `core/archipelago/src/server.rs`
|
||||
|
||||
---
|
||||
|
||||
## Task 5: NIP-07 Nostr login via iframe injection (EXPLORATORY)
|
||||
|
||||
**Goal**: Web apps in iframe (like IndeeHub) can call `window.nostr.getPublicKey()` and `window.nostr.signEvent()` for frictionless Nostr login.
|
||||
|
||||
**Approach**: Inject a `window.nostr` shim into proxied pages via `sub_filter`, communicating with the parent Archipelago frame via `postMessage`.
|
||||
|
||||
**Steps**:
|
||||
1. Create `neode-ui/public/nostr-provider.js` — implements `window.nostr` interface, uses `postMessage` to parent
|
||||
2. Add `sub_filter '</head>' '<script src="/nostr-provider.js"></script></head>';` to nginx ext proxy blocks
|
||||
3. Add `postMessage` listener in AppLauncherOverlay that handles `nostr-getPublicKey` and `nostr-signEvent` by calling backend RPC
|
||||
4. Backend already has `identity.nostr-sign` and `node.nostr-pubkey` RPC endpoints
|
||||
|
||||
**Security**: Validate postMessage origin, prompt user before signing, never expose secret key to frontend.
|
||||
|
||||
**Files**: new `neode-ui/public/nostr-provider.js`, `image-recipe/configs/nginx-archipelago.conf`, AppLauncherOverlay component, `neode-ui/src/stores/appLauncher.ts`
|
||||
|
||||
---
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. Task 1 — fix iframe black screen (deploy nginx)
|
||||
2. Task 2 — detail pages (deploy frontend)
|
||||
3. Task 3 — kiosk on .228 (SSH ops)
|
||||
4. Task 4 — DID+Nostr pairing (deploy backend)
|
||||
5. Task 5 — NIP-07 injection (deploy full)
|
||||
|
||||
## Verification
|
||||
|
||||
- Task 1: Open BotFights/484 Kitchen/Arch Presentation in iframe — page renders with styles and interactivity
|
||||
- Task 2: Click web-only app card → detail page shows with title, description, launch button, no container buttons
|
||||
- Task 3: .228 monitor shows kiosk app grid
|
||||
- Task 4: `node.did` RPC returns DID Document with Nostr pubkey in verificationMethod
|
||||
- Task 5: Open IndeeHub in iframe, browser console `window.nostr.getPublicKey()` returns hex pubkey
|
||||
125
.claude/skills/add-web-app/SKILL.md
Normal file
125
.claude/skills/add-web-app/SKILL.md
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
name: add-web-app
|
||||
description: Add an external website as a web-only app to Archipelago (no container needed)
|
||||
disable-model-invocation: true
|
||||
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
|
||||
argument-hint: "[app-id] [url]"
|
||||
---
|
||||
|
||||
Add an external website ($ARGUMENTS) as a web-only app to Archipelago.
|
||||
|
||||
Web-only apps are external websites embedded in the Archipelago UI via iframe. They have no Docker container — they're bookmarks to public websites with full app-like detail pages.
|
||||
|
||||
## Architecture
|
||||
|
||||
External websites that set `X-Frame-Options` or CSP headers blocking iframe embedding are proxied through nginx on **dedicated ports** (one port per site). This approach:
|
||||
- Strips X-Frame-Options so the iframe works
|
||||
- Serves the site at root `/` so SPA routing works correctly
|
||||
- Does NOT use subpath proxying (`/ext/app/`) which breaks SPAs
|
||||
- Optionally injects NIP-07 nostr-provider.js for Nostr login
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Choose a port
|
||||
|
||||
Pick an unused port in the 8900-8999 range. Current allocations:
|
||||
- 8901: botfights.net
|
||||
- 8902: 484.kitchen
|
||||
- 8903: present.l484.com
|
||||
|
||||
### 2. Add nginx proxy server block
|
||||
|
||||
Add a new `server` block to `image-recipe/configs/nginx-archipelago.conf` at the end:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen {PORT};
|
||||
server_name _;
|
||||
location / {
|
||||
proxy_pass https://{DOMAIN};
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host {DOMAIN};
|
||||
proxy_set_header Accept-Encoding "";
|
||||
proxy_ssl_server_name on;
|
||||
proxy_hide_header X-Frame-Options;
|
||||
proxy_hide_header Content-Security-Policy;
|
||||
proxy_hide_header Cross-Origin-Embedder-Policy;
|
||||
proxy_hide_header Cross-Origin-Opener-Policy;
|
||||
proxy_hide_header Cross-Origin-Resource-Policy;
|
||||
sub_filter '</head>' '<script src="/nostr-provider.js"></script></head>';
|
||||
sub_filter_once on;
|
||||
}
|
||||
location = /nostr-provider.js {
|
||||
alias /opt/archipelago/web-ui/nostr-provider.js;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Add to appLauncher.ts EXTERNAL_PROXY_PORT
|
||||
|
||||
In `neode-ui/src/stores/appLauncher.ts`, add the domain-to-port mapping:
|
||||
|
||||
```typescript
|
||||
const EXTERNAL_PROXY_PORT: Record<string, number> = {
|
||||
// ... existing entries
|
||||
'{DOMAIN}': {PORT},
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Add to Apps.vue WEB_ONLY_APP_URLS and WEB_ONLY_APPS
|
||||
|
||||
In `neode-ui/src/views/Apps.vue`:
|
||||
1. Add to `WEB_ONLY_APP_URLS`: `'{app-id}': 'https://{DOMAIN}'`
|
||||
2. Add to `WEB_ONLY_APPS` with a synthetic `PackageDataEntry`:
|
||||
- state: `'running'`
|
||||
- manifest with id, title, version, description
|
||||
- static-files with icon path
|
||||
|
||||
### 5. Add to dummyApps.ts
|
||||
|
||||
In `neode-ui/src/utils/dummyApps.ts`, add a full `PackageDataEntry` with:
|
||||
- Long description (for detail page)
|
||||
- Website URL in manifest
|
||||
- Icon path
|
||||
|
||||
### 6. Add to AppDetails.vue WEB_ONLY_APP_URLS
|
||||
|
||||
In `neode-ui/src/views/AppDetails.vue`, add to the `WEB_ONLY_APP_URLS` map.
|
||||
|
||||
### 7. Add app icon
|
||||
|
||||
Place icon at `neode-ui/public/assets/img/app-icons/{app-id}.{png|webp|svg}`
|
||||
|
||||
### 8. Deploy
|
||||
|
||||
```bash
|
||||
# Build frontend
|
||||
cd neode-ui && npm run build
|
||||
|
||||
# Deploy nginx config
|
||||
scp image-recipe/configs/nginx-archipelago.conf archipelago@192.168.1.228:/tmp/
|
||||
ssh archipelago@192.168.1.228 "sudo cp /tmp/nginx-archipelago.conf /etc/nginx/sites-available/archipelago && sudo nginx -t && sudo systemctl reload nginx"
|
||||
|
||||
# Deploy frontend
|
||||
rsync -az --delete --exclude aiui --exclude claude-login.html web/dist/neode-ui/ archipelago@192.168.1.228:/opt/archipelago/web-ui/
|
||||
```
|
||||
|
||||
### 9. Verify
|
||||
|
||||
1. Open Archipelago UI
|
||||
2. Web-only app appears in My Apps (sorted alphabetically before container apps)
|
||||
3. Click app card -> detail page with title, description, launch button, no container buttons
|
||||
4. Click Launch -> iframe loads the external website correctly
|
||||
5. All assets load (no 404s in Network tab)
|
||||
6. `window.nostr` available in iframe console (NIP-07)
|
||||
|
||||
## Files Modified
|
||||
|
||||
| File | What to add |
|
||||
|------|-------------|
|
||||
| `image-recipe/configs/nginx-archipelago.conf` | New server block with proxy |
|
||||
| `neode-ui/src/stores/appLauncher.ts` | EXTERNAL_PROXY_PORT entry |
|
||||
| `neode-ui/src/views/Apps.vue` | WEB_ONLY_APP_URLS + WEB_ONLY_APPS entries |
|
||||
| `neode-ui/src/views/AppDetails.vue` | WEB_ONLY_APP_URLS entry |
|
||||
| `neode-ui/src/utils/dummyApps.ts` | Full PackageDataEntry for detail page |
|
||||
| `neode-ui/public/assets/img/app-icons/` | App icon file |
|
||||
Reference in New Issue
Block a user