diff --git a/docs/external-access-and-websites.md b/docs/external-access-and-websites.md index abd4e867..52f7e2ef 100644 --- a/docs/external-access-and-websites.md +++ b/docs/external-access-and-websites.md @@ -100,7 +100,34 @@ been created. The earlier standalone proxy route/certificate were removed. A new route now connects the dashboard-published synthetic site to `https://free.archipelago.builders` through Yaya. Trusted TLS, exact page bytes, `/rpc` returning 404, and traversal rejection (400) pass. The route remains for UAT; -full revoke/restart qualification is still in progress. +FIPS/HTTPS revocation retained the Tor publication and archive, and Tor +revocation retained HTTPS. Republishing retained the onion hostname. The existing +Yaya Tor client timed out after republish, while a separate fresh Tor client +fetched the exact restored page; do not treat the first timeout as a confirmed +publisher defect or a universal reachability pass. Temporary notice logging was +removed and the isolated test client was stopped after qualification. + +A management-service restart retained exact project/archive state, all app +container IDs/states, and native Bitcoin/LND PIDs/start times. Public HTTPS and +Tor both returned exact content after that restart. A full machine reboot is +separate and awaits the operator's recovery arrangement. The actual dashboard +Blossom iframe passed profile selection, explicit denial with zero uploads, and +an approved local upload through the canonical signer. Physical companion +acceptance remains separate; this was a browser iframe test. + +The operator requested a further UX pass informed by all existing guides. The +seven existing goal guides, help tree, shared walkthrough and onboarding patterns +were reviewed. The revised flow uses concise visitor-oriented multiselect cards, +an AIUI-first creation path, optional HTML/model controls, a saved-version preview, +explicit Save and continue, and contextual Help entries. The Home shortcuts now +respect the same dedicated guide routes as the Setup cards. The final polish is active on Framework. The production typecheck and build pass, +as do 23 focused tests. The deployed flow again passed real draft save, local +Blossom archive/readback, FIPS publishing and the mobile overflow check, with zero +external browser requests. It preserves original Setup visuals, a single main +Save and continue action, keyboard focus/scroll handling, and visible revoke +controls even when an already-published route is deselected. +No local Ollama service responded on Framework; live model generation remains +unqualified. No proprietary fallback was used or added. New source work includes local Blossom website archives, explicit app-only guest credentials, and an on-demand HTTPS check against exact published page bytes. diff --git a/neode-ui/src/components/SetupWalkthrough.vue b/neode-ui/src/components/SetupWalkthrough.vue index b18e481c..f84f79cd 100644 --- a/neode-ui/src/components/SetupWalkthrough.vue +++ b/neode-ui/src/components/SetupWalkthrough.vue @@ -1,14 +1,22 @@ + + diff --git a/neode-ui/src/data/helpTree.ts b/neode-ui/src/data/helpTree.ts index 664940e4..b354dc01 100644 --- a/neode-ui/src/data/helpTree.ts +++ b/neode-ui/src/data/helpTree.ts @@ -82,6 +82,18 @@ export const helpTree: HelpSection[] = [ content: 'Share files and media with connected peers through the Content section in Web5. Add content from your Cloud storage, set it as free or paid (ecash-gated), and connected peers can browse and access your catalog. For paid content, peers pay with ecash micropayments — the sats appear in your wallet instantly.', relatedPath: '/dashboard/web5', }, + { + id: 'external-access-guide', + label: 'Allowing External Connections', + content: 'Open Setup → Allow external connections. Choose how visitors will connect: FIPS, an HTTPS domain through your own gateway, or Tor. You can select more than one. Save your choices, then choose a supported app and create an expiring guest token. Share that token privately. Guests cannot use it to sign in to your dashboard. Check the app address from the visitor’s device; saved settings alone do not confirm a working connection.', + relatedPath: '/dashboard/setup/external-access', + }, + { + id: 'website-guide', + label: 'Publishing a Website', + content: 'Open Setup → Publish a website. The guide reuses your saved connections and skips Blossom installation when it is already installed. Create a page with AIUI, use Continue to website setup on its HTML preview, then review and save the draft. You can keep a signed copy in local Blossom. Choose an address and explicitly publish each connection. For Nostr, review the exact page, profile identity, storage server and relays before sharing: public copies may remain even after a removal request. FIPS and Tor do not require a purchased domain.', + relatedPath: '/dashboard/setup/website', + }, { id: 'self-hosting', label: 'Self-Hosting', diff --git a/neode-ui/src/services/publishing.ts b/neode-ui/src/services/publishing.ts index 8a5861e4..c46839cb 100644 --- a/neode-ui/src/services/publishing.ts +++ b/neode-ui/src/services/publishing.ts @@ -29,10 +29,10 @@ export interface DnsPlan { } export interface HttpsCheck { hostname: string; sha256: string; checked_at: string } export const PUBLISH_ROUTES: { id: PublishRoute; title: string; description: string }[] = [ - { id: 'fips', title: 'FIPS network', description: 'Reach your node through FIPS. Visitors need a FIPS connection or a configured LAN gateway.' }, - { id: 'public-web', title: 'Public web', description: 'An HTTPS address for ordinary browsers, using your selected gateway or a direct public connection.' }, - { id: 'tor', title: 'Tor', description: 'An onion address controlled by your node. Visitors use Tor Browser.' }, - { id: 'nostr', title: 'Nostr / nsites', description: 'Publish a static website through Nostr and Blossom. Public copies may remain after you unpublish.' }, + { id: 'fips', title: 'FIPS network', description: 'Use your node’s FIPS address without a public gateway.' }, + { id: 'public-web', title: 'Public web', description: 'Use a domain and a reverse proxy you control.' }, + { id: 'tor', title: 'Tor', description: 'Your node controls its onion address and keeps it when you unpublish.' }, + { id: 'nostr', title: 'Nostr / nsites', description: 'Share a signed static copy through Nostr and Blossom.' }, ] export const publishing = { status: () => rpcClient.call({ method: 'publishing.status', maxRetries: 1 }), diff --git a/neode-ui/src/views/Home.vue b/neode-ui/src/views/Home.vue index 3d4744a3..c033e44b 100644 --- a/neode-ui/src/views/Home.vue +++ b/neode-ui/src/views/Home.vue @@ -260,7 +260,7 @@
- + {{ goal.title }}
diff --git a/neode-ui/src/views/publishing/PublishingSetup.vue b/neode-ui/src/views/publishing/PublishingSetup.vue index 1187b3c7..cc8ad64e 100644 --- a/neode-ui/src/views/publishing/PublishingSetup.vue +++ b/neode-ui/src/views/publishing/PublishingSetup.vue @@ -79,6 +79,21 @@ const walkthroughSteps = computed(() => websiteMode.value ? [ { id: 'sharing', title: 'Choose an app and grant access', description: 'Give a guest access to one supported app with an expiry date. Your dashboard stays private.', complete: false }, ]) +const routeHints: Record = { + fips: 'For people connected to FIPS · No domain needed', + 'public-web': 'For ordinary browsers · Domain and gateway needed', + tor: 'For Tor Browser · No domain needed', + nostr: 'For nsite gateways · Public copies may remain', +} +const continueLabel = computed(() => walkthroughStep.value === 'storage' ? 'Continue without installing' : 'Save and continue') +const continueDisabled = computed(() => walkthroughStep.value === 'connections' ? !selected.value.length : ['design', 'domain'].includes(walkthroughStep.value) && (!current.value || !html.value.trim())) +async function continueSetup(next: string) { + if (walkthroughStep.value === 'connections' && websiteMode.value && !current.value) await saveSharedConnections() + else if (['connections', 'design', 'domain'].includes(walkthroughStep.value)) await save() + if (!error.value) walkthroughStep.value = next +} +const publishedRoutes = computed(() => [current.value?.fips_publication ? 'FIPS' : '', current.value?.tor_publication ? 'Tor' : '', current.value?.nsite_receipt?.accepted_relays.length ? 'Nostr' : ''].filter(Boolean)) + const routes = computed(() => PUBLISH_ROUTES.filter(r => websiteMode.value || r.id !== 'nostr')) async function perform(work: () => Promise) { @@ -289,7 +304,7 @@ onMounted(refresh)