Ech0 Hub
April 25, 2026 · View on GitHub
Frontend for hub.ech0.app: it loads a static instance registry JSON, fetches each Ech0 deployment’s posts API in parallel in the browser, and merges results into a single unified timeline.
How to join Ech0 Hub
Want your public Ech0 instance to appear on hub.ech0.app? Two steps:
-
Configure CORS on your instance. Allow the Hub origin to call your instance API — set
Access-Control-Allow-Origin(or the equivalent on your reverse proxy / CDN) to permithttps://hub.ech0.app. If you also develop the Hub locally, additionally allowhttp://localhost:5173. If you cannot modify your instance, run a reverse proxy on the Hub domain so requests are same-origin. -
Open the registration issue. Submit via Register on Ech0 Hub (GitHub sign-in required) and fill in:
- Instance ID — lowercase letters, digits, and hyphens only; globally unique within Hub.
- Instance URL — your deployment base URL, no trailing slash (e.g.
https://memo.example.com).
Once submitted, CI parses the issue and opens a PR that updates
public/hub.json. After a maintainer merges, your instance shows up in the Hub timeline.
Requirements: your instance must be reachable over HTTPS, expose the standard Ech0 API, and run version ≥ 4.4.0 (Hub gates aggregation on the version returned by
/healthz). Instances that fail the health check or are unreachable are dropped from the merged timeline with a visible reason.
Architecture
| Aspect | Description |
|---|---|
| CSR | Content is rendered after data is fetched in the browser; no server-side templates. |
| SPA | Vue 3 + vue-router single-page app; route changes do not full-page reload. |
| PWA | vite-plugin-pwa provides the Web App Manifest and Service Worker (installable on desktop/home screen, static asset caching per vite.config.ts). |
For a detailed task list as the implementation evolves, see the repo root: docs/superpowers/plans/2026-04-05-ech0-hub-csr-spa-pwa.md.
Registration automation workflow
The workflow file is .github/workflows/hub-register-from-issue.yml. It automates issue-to-PR registration with these triggers:
issues.opened: runs when a new issue is created.issues.reopened: runs when an existing issue is reopened.workflow_dispatch: manual recovery path by passingissue_numberfrom the Actions UI.
Execution logic:
- For
opened/reopened, the job only runs when the issue has thehublabel. - For
workflow_dispatch, it fetches the target issue byissue_numberand validates thathublabel exists. - It parses
Instance IDandInstance URLfrom the issue body, validates format and URL scheme (https), then updateshub/public/hub.json. - It creates a PR on branch
hub/issue-<issue_number>with commit titlechore(hub): add instance .... - The PR body includes
Closes #<issue_number>, so merging the PR automatically closes the source issue. - It comments on the issue with the created PR link for status tracking.
Repository settings required for PR creation from Actions:
Settings -> Actions -> General -> Workflow permissionsmust be set to Read and write permissions.- Enable Allow GitHub Actions to create and approve pull requests.
Instance registry public/hub.json
At runtime Hub requests /hub.json from the same origin (in dev, Vite serves it from public/). Each instance needs only id and url:
{
"instances": [
{ "id": "my-instance", "url": "https://your-ech0-origin.example.com" }
]
}
**id**: Short identifier for the instance (source labeling and UI).**url**: API root without a trailing slash; aggregated requests use{url}/api/echo/query(same as the main project’sinternal/router). Health checks use**GET {url}/healthz** on the same host (seeinternal/router/resource.go; not under/api).
Health checks and version gate
- For each instance, call
**GET {url}/healthz, parsedata.versionwhen**code === 1(same contract asHealthzininternal/handler/common/common.go). - Only instances whose version is ≥ 4.4.0 (same semantics as
Versionininternal/model/common/common.go) participate in post aggregation. - If the check fails or the request errors, show the reason on the page and exclude that instance from the timeline.
Body / images and reuse from web
- Markdown & images: Vite
resolve.aliasmaps@to the repo’sweb/src, reusingTheMdPreview(viaMarkdownRenderer) andTheImageGallery; gallery requests pass the instancebaseUrl, matching the main Hub use case. - Global styles & i18n: The Hub entry imports
webtheme SCSS,virtual:uno.css, andvue-i18n(currentlyzh-CNstrings) to drive those components. - Extension: Aggregated feeds omit posts with
extension(filtered insrc/composables/useHubMergeFeed.ts); Hub does not show Extension cards or related wrappers.
Cross-origin (CORS)
When the browser on hub.ech0.app calls each instance, **/api/* and /healthz** (among others) must allow the Hub origin (e.g. Access-Control-Allow-Origin: https://hub.ech0.app). If you cannot change the instance, use a reverse proxy on the Hub domain (same-origin avoids CORS).
Development & build
pnpm install
pnpm dev
pnpm build
pnpm preview
- Local dev defaults to
http://localhost:5173; add that origin to instance CORS for debugging.
Stack
Vue 3, TypeScript, Vite, vue-router, vite-plugin-pwa, vue-i18n, UnoCSS; shares some UI with web/ in this repo (see above).
Aggregation flow (summary)
GET /hub.json→ instance list.GET {url}/healthz→ candidates with version ≥ 4.4.0.- For each candidate,
POST {url}/api/echo/querywith a body matching the main project’sEchoQueryDto; success whencode === 1anddata.itemsis the post array (seeinternal/model/common/result.go). - Merge results, sort by
created_atdescending; surface failures or partial errors in the UI.
Repository layout
This directory is the hub/ package in the monorepo. It shares Git history with the main Ech0 (Go backend) project but builds and deploys independently.