README.md markdown
78 lines 8.7 KB
Raw
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor ⚠ breaking 16 days ago

Knowtation Hub Bridge

GitHub connect + Back up now + index + search for the hosted product. Stores GitHub token per user; sync fetches vault from the ICP canister and pushes to the user’s repo. Roles, invites, workspace owner, vault access, and scope persist in Blobs (or DATA_DIR). See docs/MULTI-VAULT-AND-SCOPED-ACCESS.md and docs/TEAMS-AND-COLLABORATION.md; auth contract in docs/HUB-API.md.

Routes

  • GET /auth/github-connect?token=<jwt> — Redirect to GitHub OAuth (scope=repo). User must be authenticated (pass JWT in query or cookie). Callback stores token for user id.
  • GET /auth/callback/github-connect — GitHub OAuth callback (do not call directly).
  • POST /api/v1/vault/sync — Back up now. Requires Authorization: Bearer <jwt>. Body optional: { "repo": "owner/name" }. Fetches vault from canister, pushes to GitHub.
  • GET /api/v1/vault/github-status — Returns { github_connected, repo } for the authenticated user.
  • GET /api/v1/role — Returns { role, may_approve_proposals } for the authenticated user (gateway settings + approve gate). may_approve_proposals is true for admin; for evaluator it follows per-user blob hub_evaluator_may_approve and env HUB_EVALUATOR_MAY_APPROVE=1 when no row exists. Requires Bearer JWT.
  • GET /api/v1/roles — List roles (admin only). Returns { roles, evaluator_may_approve } (map of user id → boolean).
  • POST /api/v1/roles — Add or update role (admin only). Body { user_id, role, evaluator_may_approve? }. When role is evaluator, optional evaluator_may_approve (boolean) sets or clears the per-user approve flag in blob hub_evaluator_may_approve. Changing a user to a non-evaluator role removes their entry from that map.
  • POST /api/v1/roles/evaluator-may-approve — Admin only. Body { user_id, evaluator_may_approve: boolean }. Target user must already be evaluator.
  • GET /api/v1/invites — List pending invites (admin only).
  • POST /api/v1/invites — Create invite link (admin only). Body { role }. Returns { invite_url, token, role, created_at, expires_at }.
  • DELETE /api/v1/invites/:token — Revoke invite (admin only).
  • POST /api/v1/invites/consume — Consume an invite for the authenticated user. Body { token }. Adds user to roles and removes invite.
  • GET /api/v1/workspace — Returns { owner_user_id } (admin only). Canonical canister partition for the team when set.
  • POST /api/v1/workspace — Body { owner_user_id: string | null } (admin only). null disables delegation.
  • GET /api/v1/vault-access, POST /api/v1/vault-access — Same contract as Node Hub hub_vault_access.json (admin only).
  • GET /api/v1/scope, POST /api/v1/scope — Same contract as Node Hub hub_scope.json (admin only).
  • GET /api/v1/hosted-context — JWT. Returns effective canister user, allowed_vault_ids, scope, role, and may_approve_proposals for current X-Vault-Id (used by the gateway).
  • GET /api/v1/bridge-version — No auth. Deploy probe: { service, commit, deploy_id, context, netlify } (Netlify sets COMMIT_REF / DEPLOY_ID / CONTEXT). Use to confirm knowtation-bridge production matches main after a merge (separate from the gateway site).
  • POST /api/v1/index — Re-index vault (chunk → embed → sqlite-vec per effective user + vault). Requires Bearer JWT. Response includes vectors_deleted (sqlite rows removed for this vault before upsert) plus notesProcessed, chunksIndexed, embedding_input_tokens.
  • POST /api/v1/search — Semantic search. Body: { "query": "...", "limit?", ... }. Requires Bearer JWT.

Environment

Variable Required Description
CANISTER_URL Yes Canister HTTP URL (e.g. https://<canister-id>.ic0.app).
SESSION_SECRET or HUB_JWT_SECRET Yes Same as gateway: verify JWT and encrypt stored tokens.
HUB_BASE_URL Yes (prod) Public URL of this bridge (for OAuth callback). E.g. https://bridge.knowtation.com.
HUB_UI_ORIGIN No Origin of Hub UI (post-connect redirect). Defaults to HUB_BASE_URL. Must match the host users actually use (e.g. https://www.knowtation.store if they use www; otherwise redirect can land on the wrong host and show the landing page).
HUB_UI_PATH No Path under origin where the Hub lives (e.g. /hub). Default /hub. Empty = root. Redirects after Connect GitHub use this so users land on the Hub.
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET No GitHub OAuth for "Connect GitHub". Use a separate GitHub App or same as gateway.
DATA_DIR No Directory for tokens, per-user vector DBs, and roles/invites (default: repo data/). Ignored on Netlify when Blobs are used.
HUB_ADMIN_USER_IDS No Comma-separated user IDs (e.g. google:123,github:456) who are admin on hosted (bootstrap; can also add admins via POST /api/v1/roles). Should match gateway's HUB_ADMIN_USER_IDS so Settings shows the correct role.
HUB_EVALUATOR_MAY_APPROVE No Set to 1 so evaluators without an explicit row in blob hub_evaluator_may_approve may approve proposals. Per-user false in the blob still denies.
BRIDGE_PORT or PORT No Port (default 3341).
EMBEDDING_PROVIDER No ollama (default), openai, or voyage. On Netlify/serverless, prefer openai or voyage — the default Ollama URL is http://localhost:11434, which the function cannot reach.
EMBEDDING_MODEL No Model name (defaults: nomic-embed-text / Ollama, text-embedding-3-small / OpenAI, voyage-4-lite / Voyage).
VOYAGE_API_KEY No Required if EMBEDDING_PROVIDER=voyage.
OLLAMA_URL No Ollama API base URL (default http://localhost:11434). Must include http:// or https://. Must be reachable from this process. On Netlify, use a public Ollama endpoint or switch to OpenAI. https://ollama.com is the marketing site, not the API.
OLLAMA_API_KEY No Required for Ollama Cloud; add Authorization: Bearer header.
OPENAI_API_KEY No Required if EMBEDDING_PROVIDER=openai. Set this on the bridge site for hosted Hub Re-index / Search.
INDEXER_CHUNK_SIZE, INDEXER_CHUNK_OVERLAP No Chunking params (default 2048, 256).

Run locally

The bridge is an API server (default http://localhost:3341), not a separate browser app. It requires a real CANISTER_URL and SESSION_SECRET (or HUB_JWT_SECRET) — if npm start exits immediately, those are missing. Copy hub/bridge/.env.example into the repository root .env (the bridge loads ../../.env automatically) and fill in your canister URL and secret.

cd hub/bridge
npm install
# Option A — vars in repo root .env (recommended; see .env.example)
npm start
# Option B — export in shell
export CANISTER_URL=https://<canister-id>.ic0.app
export SESSION_SECRET=your-secret
export HUB_BASE_URL=http://localhost:3341
export GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=...
npm start

Self-hosted Hub (npm run hub → usually http://localhost:3333) uses hub/server.mjs and local files; it does not use the bridge unless you run the gateway with BRIDGE_URL pointing at this process.

Hub UI (hosted) must call this bridge for Connect GitHub and Back up now. Either set a separate bridge URL in the UI config, or run gateway and bridge on the same host and have the gateway proxy /api/v1/vault/sync and /auth/github-connect to the bridge.

Netlify Blobs (persistence on Netlify)

When the bridge is deployed as a Netlify function (netlify/functions/bridge.mjs), tokens, per-user vector DBs, roles/invites, and hub_evaluator_may_approve (per-evaluator approve permission) are stored in Netlify Blobs (store name: bridge-data) so they persist across cold starts. Use a second Netlify site with Package directory deploy/bridge. Enable Blobs for that site in the Netlify dashboard (Site configuration → Data & storage or Build & deploy). The wrapper defaults to eventual blob consistency; set NETLIFY_BLOBS_CONSISTENCY=strong on the bridge site if you need read-your-writes immediately after index (see Netlify Blobs consistency). Locally, or if Blobs are not available, the bridge falls back to DATA_DIR (filesystem).

Reference

File History 2 commits
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor 16 days ago