docs(260729-gjd): pre-dispatch plan for demo IndeeHub iframe + signer + preinstall
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2e54c3e40c
commit
db25545a9a
@ -0,0 +1,348 @@
|
||||
---
|
||||
phase: quick-260729-gjd
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- neode-ui/docker/nginx-demo.conf
|
||||
- neode-ui/docker/indee-demo-signin.js
|
||||
- neode-ui/Dockerfile.web
|
||||
- docker-compose.demo.yml
|
||||
- demo-deploy/docker-compose.yml
|
||||
- neode-ui/src/composables/useDemoIntro.ts
|
||||
- neode-ui/src/views/appSession/useAppIdentity.ts
|
||||
- neode-ui/mock-backend.js
|
||||
autonomous: true
|
||||
requirements: [QUICK-260729-GJD]
|
||||
must_haves:
|
||||
truths:
|
||||
- "A fresh demo session (clean browser, no localStorage) shows IndeeHub as an installed, running app in My Apps"
|
||||
- "Launching IndeeHub in the demo renders the real indee.tx1138.com site inside the in-app iframe session (no new tab, no external interstitial)"
|
||||
- "The embedded IndeeHub boots signed-in (active demo account visible, no login wall) and no identity-picker modal blocks the demo visitor"
|
||||
- "The non-demo (real node) build is byte-for-byte unaffected in behavior: indeedhub launch, identity picker, and NIP-07 bridge all work as before"
|
||||
- "The served demo content contains no occurrence of the private release-server IP (existing Docker build guards still pass)"
|
||||
artifacts:
|
||||
- "neode-ui/docker/nginx-demo.conf — new whole-origin reverse-proxy server block (port 2101) for indee.tx1138.com with framing headers stripped and sign-in script injected"
|
||||
- "neode-ui/docker/indee-demo-signin.js — demo-only localStorage seeding script with a labelled throwaway demo nsec"
|
||||
- "docker-compose.demo.yml and demo-deploy/docker-compose.yml — publish the new 2101 port"
|
||||
- "neode-ui/src/composables/useDemoIntro.ts — indeedhub moved from external-tab to iframe launch via the :2101 proxy origin"
|
||||
- "neode-ui/mock-backend.js — indeedhub present in staticDevApps as installed/running"
|
||||
key_links:
|
||||
- "demoAppUrl('indeedhub') → http://<demo-host>:2101/ → nginx :2101 server block → https://indee.tx1138.com upstream"
|
||||
- "nginx sub_filter → /__demo/indee-demo-signin.js → seeds indeedhub-accounts + indeedhub-active-account localStorage keys → IndeeHub boot-restore logs the visitor in"
|
||||
- "staticDevApps['indeedhub'] → structuredClone into per-session package-data → My Apps grid on fresh session"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Make IndeeHub a first-class app in the PUBLIC DEMO only: (1) the real site
|
||||
https://indee.tx1138.com/ renders inside the in-app iframe session (today it is
|
||||
frame-busted by `X-Frame-Options: SAMEORIGIN` and opens externally), (2) a demo
|
||||
visitor sees IndeeHub already signed in with a throwaway demo Nostr identity
|
||||
(no login wall, no identity-picker modal), and (3) IndeeHub appears as an
|
||||
already-installed, running app on a completely fresh demo session.
|
||||
|
||||
Purpose: the demo currently punts IndeeHub to a new tab with a login wall —
|
||||
the flagship media app looks broken/hostile to demo visitors.
|
||||
Output: demo-scoped changes across nginx-demo.conf, a new sign-in seed script,
|
||||
the two demo compose files, useDemoIntro.ts, useAppIdentity.ts, mock-backend.js.
|
||||
|
||||
All behavior changes are gated behind IS_DEMO / demo-image build paths. The
|
||||
real-node build must be completely unaffected.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/gsd-core/workflows/execute-plan.md
|
||||
@$HOME/.claude/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@CLAUDE.md
|
||||
@neode-ui/src/composables/useDemoIntro.ts
|
||||
@neode-ui/docker/nginx-demo.conf
|
||||
@neode-ui/Dockerfile.web
|
||||
@neode-ui/Dockerfile.backend
|
||||
@docker-compose.demo.yml
|
||||
@demo-deploy/docker-compose.yml
|
||||
@neode-ui/src/views/appSession/appSessionConfig.ts
|
||||
@neode-ui/src/views/appSession/useAppIdentity.ts
|
||||
@neode-ui/src/views/appSession/useNostrBridge.ts
|
||||
@neode-ui/src/stores/appLauncher.ts
|
||||
@neode-ui/mock-backend.js
|
||||
</context>
|
||||
|
||||
<verified_findings>
|
||||
Facts confirmed by inspection on 2026-07-29 (do not re-derive, but re-verify
|
||||
the live-bundle details marked "verify at exec time"):
|
||||
|
||||
- `curl -sI https://indee.tx1138.com/` → `X-Frame-Options: SAMEORIGIN`, no
|
||||
Content-Security-Policy header today (strip both defensively).
|
||||
- The live index.html loads a hashed module bundle from absolute-root paths
|
||||
(`/assets/index-*.js`, `/icons/...`, `/manifest.json`). This is why the old
|
||||
`/app/indeedhub/` path-prefix + sub_filter proxy broke (asset/router paths
|
||||
escape the prefix). A WHOLE-ORIGIN proxy on a dedicated port has no such
|
||||
problem — the SPA sees itself at `/` and every relative call just works.
|
||||
- The live bundle (assets/index-BMWtjRCn.js) uses applesauce-accounts:
|
||||
- localStorage key `"indeedhub-accounts"` = JSON array of serialized
|
||||
accounts (`Fe.toJSON`/`Fe.fromJSON`), restored on boot before UI renders.
|
||||
- localStorage key `"indeedhub-active-account"` = active account id.
|
||||
- A private-key account class exists whose `fromJSON` does
|
||||
`const t=Vn(e.signer.key); new mr(e.pubkey, new ai(t))` — i.e. shape is
|
||||
`{ id, type: "<verify at exec time>", pubkey, signer: { key: "<hex sk>" } }`
|
||||
plus common fields from `loadCommonFields` (verify exact `type` string and
|
||||
common fields by grepping the live bundle for `static type` / the class's
|
||||
`toJSON`). An `"extension"` account type also exists (fallback path).
|
||||
- The bundle expects NIP-07 as a real `window.nostr` object ("Signer
|
||||
extension missing" guard) — it does NOT contain the archipelago
|
||||
`nostr-request` postMessage client.
|
||||
- Parent-side NIP-07 plumbing already exists: `AppSession.vue` line ~423
|
||||
routes `nostr-request` messages to `useNostrBridge`, which calls
|
||||
`node.nostr-pubkey` (mocked in mock-backend.js) and `node.nostr-sign`
|
||||
(NOT implemented in mock-backend.js). Only needed for the fallback approach.
|
||||
- `useAppIdentity.ts`: `isIdentityAwareApp('indeedhub')` is true → on iframe
|
||||
load with no stored identity it opens the identity-picker modal. In the demo
|
||||
this is a blocking modal the visitor shouldn't see.
|
||||
- `appLauncher.ts openSession`: `IS_DEMO && isDemoExternal(appId)` is the only
|
||||
thing forcing indeedhub external; `NEW_TAB_APP_IDS` is already bypassed when
|
||||
`IS_DEMO && isDemoApp(appId)`. `AppSession.vue mustOpenNewTab` has the same
|
||||
two-clause shape. Removing indeedhub from `DEMO_EXTERNAL_URLS` while keeping
|
||||
`isDemoApp('indeedhub')` true flips it to the iframe path everywhere.
|
||||
- `mock-backend.js`: per-visitor session state is initialized via
|
||||
`md['package-data'] = structuredClone(staticDevApps)` (~line 5493), so
|
||||
adding an entry to `staticDevApps` (~line 828) makes it installed on every
|
||||
fresh session. `APP_PORTS`-style map at ~line 323 already has
|
||||
`'indeedhub': 8190`; an icon exists at `/assets/img/app-icons/indeedhub.png`.
|
||||
- `Dockerfile.web` copies `nginx-demo.conf` to `/etc/nginx/nginx.conf.template`
|
||||
and runs `docker-entrypoint-custom.sh` (env substitution) — read the
|
||||
entrypoint before editing so the new server block's nginx `$vars` survive
|
||||
templating the same way the existing blocks' do.
|
||||
- Both Docker builds already scrub + fail on any occurrence of the private
|
||||
release-server IP; nothing in this change may hardcode host IPs — build the
|
||||
iframe URL from `window.location.hostname`.
|
||||
- `indeedhub/` at repo root is a git submodule (not checked out) — NEVER stage
|
||||
any path under it. `indeedhub-demo/` is a prior standalone-build attempt
|
||||
(clones the GitHub fork, builds with VITE env); this plan supersedes it by
|
||||
proxying the LIVE site instead — leave that directory untouched.
|
||||
</verified_findings>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: nginx whole-origin proxy on :2101 + sign-in seed script + compose ports</name>
|
||||
<files>neode-ui/docker/nginx-demo.conf, neode-ui/docker/indee-demo-signin.js, neode-ui/Dockerfile.web, docker-compose.demo.yml, demo-deploy/docker-compose.yml, neode-ui/docker/docker-entrypoint.sh</files>
|
||||
<action>
|
||||
Add a second `server` block to nginx-demo.conf: `listen 2101;` that is a
|
||||
pure whole-origin reverse proxy of `https://indee.tx1138.com` — no path
|
||||
prefix, no URL rewriting (this is the fix for the documented sub_filter
|
||||
breakage: the SPA keeps its own absolute-root paths). In that block:
|
||||
`location / { proxy_pass https://indee.tx1138.com; }` with
|
||||
`proxy_ssl_server_name on;`, `proxy_ssl_name indee.tx1138.com;`,
|
||||
`proxy_set_header Host indee.tx1138.com;`,
|
||||
`proxy_http_version 1.1;` + WebSocket upgrade headers (reuse the existing
|
||||
`$connection_upgrade` map), `proxy_hide_header X-Frame-Options;` and
|
||||
`proxy_hide_header Content-Security-Policy;`. For HTML injection:
|
||||
`proxy_set_header Accept-Encoding "";` (upstream must not gzip or
|
||||
sub_filter no-ops), `sub_filter_types text/html;`, `sub_filter_once on;`,
|
||||
`sub_filter '</head>' '<script src="/__demo/indee-demo-signin.js"></script></head>';`
|
||||
— a classic (non-module) script injected at end of head still executes
|
||||
BEFORE the SPA's deferred module bundle, which is what the seeding needs.
|
||||
Add `location = /__demo/indee-demo-signin.js { root /usr/share/nginx/html; }`
|
||||
(or alias) inside the 2101 server so the seed script is served same-origin
|
||||
to the iframe. Update the comment block that currently explains why
|
||||
IndeeHub is not proxied (lines ~106-109) to describe the new :2101 design.
|
||||
|
||||
Create neode-ui/docker/indee-demo-signin.js: a small plain-JS classic
|
||||
script, clearly headed with a comment stating it is PUBLIC-DEMO-ONLY and
|
||||
that the embedded key is a freshly generated THROWAWAY demo identity, not
|
||||
a real secret. Generate ONE fresh secp256k1 keypair at implementation time
|
||||
(e.g. `node -e` with a tiny script using any available schnorr/secp lib, or
|
||||
a one-off `npx` of nostr-tools in the scratchpad — the generator itself is
|
||||
not committed) and embed hex sk + hex pk as constants. The script: if
|
||||
`localStorage.getItem('indeedhub-accounts')` is empty/absent, write the
|
||||
two keys IndeeHub's boot-restore reads — `indeedhub-accounts` (JSON array
|
||||
with ONE serialized private-key account: verify the exact `type` string
|
||||
and common-field shape against the live bundle per verified_findings, shape
|
||||
`{ id, type, pubkey, signer: { key } }` + whatever `loadCommonFields`
|
||||
round-trips, give it a friendly name/metadata like "Archy Demo" if the
|
||||
shape supports it) and `indeedhub-active-account` (that account's id).
|
||||
Because the script runs on the :2101 origin inside the iframe, this
|
||||
touches only the proxied app's isolated storage. IndeeHub then restores
|
||||
the account on boot and self-signs with its own bundled signer — no
|
||||
window.nostr and no parent bridge required. Do NOT define a partial
|
||||
`window.nostr` in this approach (a pubkey-only shim with a broken
|
||||
signEvent causes worse failures than no shim).
|
||||
|
||||
FALLBACK (only if live testing in Task-3 verification shows the seeded
|
||||
account shape is not accepted): seed an `"extension"`-type account
|
||||
instead, define a `window.nostr` postMessage client in this same script
|
||||
(request/response protocol matching useNostrBridge: post
|
||||
`{type:'nostr-request', id, method, params}` to `window.parent`, resolve on
|
||||
`{type:'nostr-response', id, ...}`), and implement `node.nostr-sign` /
|
||||
`identity.nostr-sign` in mock-backend.js with real schnorr signatures over
|
||||
the same throwaway key (add `nostr-tools` to neode-ui dependencies — it is
|
||||
pure JS and Dockerfile.backend runs `npm install` over package.json).
|
||||
Prefer the primary approach; only fall back with evidence.
|
||||
|
||||
Wire the plumbing: `EXPOSE 2101` in Dockerfile.web (the seed script is
|
||||
already inside `neode-ui/` so the existing `COPY neode-ui/ ./` +
|
||||
dist copy do NOT ship it — add an explicit
|
||||
`COPY neode-ui/docker/indee-demo-signin.js /usr/share/nginx/html/__demo/indee-demo-signin.js`
|
||||
in the nginx stage of Dockerfile.web; it lands only in the demo web image,
|
||||
never in real-node artifacts). Publish the port in docker-compose.demo.yml
|
||||
(`"2101:2101"` on neode-web) and demo-deploy/docker-compose.yml (use an
|
||||
env-overridable mapping consistent with its existing `DEMO_WEB_PORT`
|
||||
style, e.g. `"${DEMO_INDEE_PORT:-2101}:2101"`, and document it in that
|
||||
file's header comment). Read docker-entrypoint.sh first and make sure the
|
||||
new server block survives its template substitution exactly like the
|
||||
existing blocks (same escaping convention for nginx `$` variables); touch
|
||||
the entrypoint only if its substitution list needs it.
|
||||
|
||||
Do not put any host IP in any of these files; upstream hostname
|
||||
indee.tx1138.com is fine.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>docker run --rm -v "$PWD/neode-ui/docker/nginx-demo.conf:/etc/nginx/nginx.conf:ro" nginx:alpine nginx -t (or, if docker unavailable locally, `nginx -t -c` via a podman run — config must parse). Plus: grep -c "2101" neode-ui/docker/nginx-demo.conf docker-compose.demo.yml demo-deploy/docker-compose.yml neode-ui/Dockerfile.web — each ≥1; grep -q "indee-demo-signin" neode-ui/docker/nginx-demo.conf && grep -qi "throwaway" neode-ui/docker/indee-demo-signin.js</automated>
|
||||
</verify>
|
||||
<done>nginx config parses with the new :2101 whole-origin proxy block (framing headers stripped, sub_filter injection, WS upgrade); seed script exists with labelled throwaway demo key and idempotent localStorage seeding; both compose files publish 2101; demo web image copies the script and exposes the port; no host IPs added anywhere.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: demo frontend — iframe launch via :2101 and no identity-picker wall</name>
|
||||
<files>neode-ui/src/composables/useDemoIntro.ts, neode-ui/src/views/appSession/useAppIdentity.ts</files>
|
||||
<action>
|
||||
In useDemoIntro.ts: remove `indeedhub` from `DEMO_EXTERNAL_URLS` (delete
|
||||
the map entirely if it becomes empty, simplifying `isDemoExternal` to
|
||||
return false — keep the exported function so call sites in appLauncher.ts
|
||||
and AppSession.vue compile unchanged). Make `demoAppUrl('indeedhub')`
|
||||
return the proxied origin built at runtime:
|
||||
`${window.location.protocol}//${window.location.hostname}:2101/`
|
||||
(hostname, never a hardcoded host/IP — works on any deploy host). Keep
|
||||
`isDemoApp('indeedhub')` true (it must stay in the demoable set so the
|
||||
NEW_TAB bypass in appLauncher.openSession and AppSession.mustOpenNewTab
|
||||
keeps routing it into the in-app iframe session, and so the install
|
||||
button stays enabled). Update the file-header comment block that
|
||||
currently documents the external-tab workaround to describe the :2101
|
||||
whole-origin proxy design instead. SSR-safety is not a concern (Vite SPA)
|
||||
but guard `typeof window !== 'undefined'` if other tests import the module
|
||||
in node context — check the existing unit tests under
|
||||
src/views/appSession/__tests__/ and src/stores/__tests__/ for assertions
|
||||
about indeedhub being demo-external and update them to the new behavior.
|
||||
|
||||
In useAppIdentity.ts: gate the picker for the demo. Import IS_DEMO from
|
||||
useDemoIntro and in `onIframeLoadIdentity` / `handleIdentityRequest`,
|
||||
when IS_DEMO is true, never set `showIdentityPicker` — the demo visitor
|
||||
must not be interrupted by an identity modal (the embedded IndeeHub is
|
||||
already signed in via the seeded account from Task 1, and `sendIdentity`'s
|
||||
`identity.sign` RPC is not what logs it in). Real-node behavior
|
||||
(picker on first launch) is untouched because IS_DEMO is compile-time
|
||||
false there.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd neode-ui && npx vitest run src/views/appSession src/stores --silent 2>&1 | tail -5 (all green) && VITE_DEMO=1 npm run build && grep -rq "2101" dist/assets && npm run build && grep -rq "indee.tx1138.com" dist/assets && echo BUNDLE-OK</automated>
|
||||
</verify>
|
||||
<done>Demo build (VITE_DEMO=1) bundle contains the :2101 launch logic (grep hit proves the build didn't silently no-op — per CLAUDE.md); plain build still compiles and demo-gated branches do not alter non-demo behavior; unit tests updated and green; launching indeedhub in demo resolves to the same-host :2101 origin in the iframe session; identity picker suppressed only under IS_DEMO.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: mock backend — IndeeHub pre-installed on fresh demo sessions</name>
|
||||
<files>neode-ui/mock-backend.js</files>
|
||||
<action>
|
||||
Add an `indeedhub` entry to `staticDevApps` in mock-backend.js using the
|
||||
existing `staticApp({...})` helper: id `indeedhub`, title `Indeehub`
|
||||
(match the existing title map at ~line 537 and APP_TITLES), a short/long
|
||||
description consistent with the marketplace copy ("Bitcoin documentary
|
||||
streaming platform" per the existing entry), `state: 'running'`,
|
||||
`lanPort: 8190` (matches the existing port map), icon
|
||||
`/assets/img/app-icons/indeedhub.png`. Because per-session demo state is
|
||||
`structuredClone(staticDevApps)`, this alone makes it installed+running on
|
||||
every fresh session. Then reconcile the rest of the mock so nothing
|
||||
contradicts installed status: check the marketplace/available-apps mock
|
||||
responses and any install/uninstall handlers (~lines 540-740, 1900-1960,
|
||||
4900+) for `indeedhub` entries that would render it as not-installed or
|
||||
double-listed, and check `DEMO_APP_PAGES` does NOT grow an indeedhub
|
||||
placeholder (the demo launch URL bypasses /app/indeedhub/ entirely — the
|
||||
iframe goes to the :2101 origin). Keep the existing `node.nostr-pubkey`
|
||||
mock as-is unless Task 1's fallback path was taken (in which case align
|
||||
its pubkey with the throwaway demo key and add the sign handlers described
|
||||
there).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd neode-ui && node -e "const s=require('fs').readFileSync('mock-backend.js','utf8'); if(!/staticDevApps[\s\S]*?indeedhub:\s*staticApp/.test(s)) process.exit(1)" && (DEMO=1 timeout 20 node mock-backend.js & sleep 4; curl -s -X POST localhost:5959/rpc/v1 -H 'content-type: application/json' -d '{"method":"server.data","id":1}' -H 'cookie: demo=fresh' | grep -o '"indeedhub"' | head -1; kill %1 2>/dev/null) — expect an indeedhub hit in fresh-session package-data (adapt the RPC method/auth to what the mock actually serves; a login with the demo password first is fine)</automated>
|
||||
</verify>
|
||||
<done>A fresh demo session's package-data includes indeedhub as installed and running with launchable UI; My Apps shows it without an install step; no duplicate/contradictory indeedhub listing in marketplace mocks; mock backend boots cleanly with DEMO=1.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| demo nginx :2101 → indee.tx1138.com | demo host proxies an external site; upstream content is served under the demo host |
|
||||
| iframe (:2101 origin) ↔ parent (:2100 origin) | cross-origin; parent NIP-07 bridge only used in fallback path |
|
||||
| public visitors → demo host | anyone can drive the proxy |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-gjd-01 | Spoofing | throwaway demo nostr key | low | accept | key is a labelled public demo identity by design; generated fresh, never a real user key; anyone extracting it can only impersonate "the demo visitor" |
|
||||
| T-gjd-02 | Info disclosure | private release-server IP in served content | high | mitigate | no host IPs added in any changed file; iframe URL derived from window.location.hostname; existing Docker-build scrub+fail guards remain the backstop |
|
||||
| T-gjd-03 | Tampering | open reverse proxy on :2101 | medium | mitigate | proxy is pinned to a single upstream host (proxy_pass fixed hostname + proxy_ssl_name), no dynamic upstreams, no request-driven destinations — it cannot be used as an open proxy |
|
||||
| T-gjd-04 | Elevation | header stripping (X-Frame-Options/CSP) | low | accept | stripping applies only to the :2101 demo proxy of one known site, demo image only; real-node builds never carry this config |
|
||||
| T-gjd-SC | Tampering | npm installs | low | accept | primary path adds no dependencies; fallback path adds only nostr-tools (well-known, verify on npmjs.com before install) |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Local (executor, before commit):
|
||||
1. nginx config parses (Task 1 verify).
|
||||
2. Unit tests green; VITE_DEMO=1 build contains ":2101" logic; plain build
|
||||
unaffected (Task 2 verify). Note: demo-gated strings are tree-shaken out of
|
||||
the plain build — that is EXPECTED; the bundle-grep for demo strings must be
|
||||
done on the VITE_DEMO=1 build, which is exactly what the demo Docker image
|
||||
builds (Dockerfile.web defaults ARG VITE_DEMO=1).
|
||||
3. Fresh-session mock package-data includes indeedhub (Task 3 verify).
|
||||
4. Optional full-stack smoke: `docker compose -f docker-compose.demo.yml up
|
||||
--build` locally, browse http://localhost:2100 in a private window →
|
||||
login `entertoexit` → IndeeHub installed → launch → iframe renders the
|
||||
proxied site from http://localhost:2101 with a signed-in account.
|
||||
5. `git status` — confirm nothing under indeedhub/ is staged, ever.
|
||||
|
||||
Post-deploy on vps2 (orchestrator deploys; verify on http://146.59.87.168:2100):
|
||||
1. `curl -sI http://146.59.87.168:2101/` returns 200 with NO X-Frame-Options
|
||||
header and the injected `indee-demo-signin.js` tag in the HTML body
|
||||
(`curl -s http://146.59.87.168:2101/ | grep indee-demo-signin`). If the
|
||||
port is unreachable, the vps2 firewall needs 2101 opened — flag to
|
||||
orchestrator.
|
||||
2. Fresh private browser window → :2100 → login → IndeeHub shows installed/
|
||||
running on the dashboard/My Apps without any install action.
|
||||
3. Launch IndeeHub → renders inside the in-app iframe (panel/overlay), not a
|
||||
new tab; content browsable; no identity-picker modal.
|
||||
4. Signed-in check: IndeeHub header shows an active account (avatar/profile
|
||||
instead of a sign-in button). If the seeded account shape was rejected
|
||||
(login wall still visible), execute the documented fallback (extension
|
||||
account + window.nostr shim + mock signer) and redeploy.
|
||||
5. View-source/network spot-check: no occurrence of the private
|
||||
release-server IP in any served response.
|
||||
6. Repeat-visit check: reload the iframe once — a service worker registered by
|
||||
IndeeHub may serve cached HTML without the injected tag on later loads;
|
||||
that is acceptable because localStorage is already seeded on first load,
|
||||
but confirm sign-in persists.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Demo visitor on a fresh browser sees IndeeHub installed, launches it into
|
||||
the in-app iframe, and browses indee.tx1138.com content signed in — zero
|
||||
clicks spent on install/login/identity modals.
|
||||
- Real-node build behavior unchanged (all changes IS_DEMO- or demo-image-gated).
|
||||
- No secrets committed beyond the labelled throwaway demo key; nothing staged
|
||||
under indeedhub/; demo serves no private release-server IP.
|
||||
- Work committed in focused commits (infra / frontend / mock) with the
|
||||
Co-Authored-By trailer and pushed via gitea-ai per CLAUDE.md; docs left to
|
||||
the orchestrator.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/quick/260729-gjd-demo-make-indee-tx1138-com-work-in-the-a/260729-gjd-SUMMARY.md` when done.
|
||||
</output>
|
||||
Loading…
x
Reference in New Issue
Block a user