Skip to content

Commit 5e4e7c4

Browse files
committed
refactor(hub)!: upstream server shape plus one Corbits mount block
apps/hub takes upstream Interchange's hub shape exactly: server.ts is upstream's own composition (createAuth, DB_HOST/DB_PORT/DB_USER/ DB_PASSWORD/DB_NAME, no SESSION_SECRET or social sign-in) with one delimited block mounting every Corbits library the hub still needs (mailbox, memory, cron, artifacts, webhook-triggers, connections/OAuth/ MCP, workflow-authoring). index.ts is `export default await createHubServer()`, matching upstream. Deleted: config.ts (env parsing folds into server.ts, upstream-style), the static handler and HUB_STATIC_DIR (the web app now runs its own dev server), the ALLOW_PLAINTEXT_SECRETS credential-cipher fallback and social sign-in, notify-delivery.ts (dead: nothing mounted it), and the tests that assumed that surface (config, credential-cipher, static-handler tests; composition.test.ts rewritten against the new shape). scripts/dev.ts, scripts/e2e/harness.ts, and scripts/reset.ts move off apps/hub/src/config.ts's HubConfig onto the same env vars the hub itself now reads.
1 parent e6afac3 commit 5e4e7c4

14 files changed

Lines changed: 990 additions & 2997 deletions

‎.env.example‎

Lines changed: 51 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,32 @@
22
#
33
# cp .env.example .env
44
#
5-
# Every variable up to HUB_STATIC_DIR is required; everything below
6-
# it is optional (see its own comment). Boot validates the whole file
7-
# and reports every missing or malformed value at once — nothing is
8-
# silently defaulted.
5+
# DATABASE_URL, HUB_DATA_DIR, CREDENTIAL_ENCRYPTION_KEY, and
6+
# PRINCIPAL_KEY_ENCRYPTION_KEY are required; everything else is optional
7+
# (see its own comment). `bun run dev` reports the first missing or
8+
# malformed value it hits at boot — nothing is silently defaulted.
9+
#
10+
# The hub itself (apps/hub/src/server.ts) is upstream Interchange's own
11+
# hub shape: it reads DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME
12+
# individually rather than a single connection string, and mints its
13+
# own session auth with no SESSION_SECRET or social-provider config.
14+
# `bun run dev` derives those DB_* values from DATABASE_URL below so
15+
# this file only needs to state one URL.
916

1017
# Postgres connection URL for your local Postgres (see the README's
1118
# "Running locally" section for a one-time setup). Prefer an explicit
1219
# username (postgres://<user>@localhost:5432/workbench) — environments
1320
# without $USER cannot infer one.
1421
DATABASE_URL=postgres://localhost:5432/workbench
1522

16-
# The single public origin the stack serves from. `workbench setup` and
17-
# `workbench seed` also read this to find the hub, unless HUB_URL is
18-
# set separately.
23+
# The origin the Corbits connections/OAuth mount uses to build callback
24+
# URLs (github/gmail/huggingface connect, MCP OAuth). Unset falls back to
25+
# http://localhost:<PORT>, which is wrong once anything sits in front of
26+
# the hub — set this explicitly behind a reverse proxy (e.g. tailscale
27+
# serve), where BASE_URL is the public origin and PORT is the local port
28+
# the proxy forwards to.
1929
BASE_URL=http://localhost:3000
20-
21-
# Secret used to sign browser sessions; any string of at least 32
22-
# characters. The value below is fine for a laptop that only you can
23-
# reach; generate your own for anything else: openssl rand -hex 32
24-
SESSION_SECRET=insecure-dev-only-session-secret-0000
30+
# PORT=3000
2531

2632
# Directory where the hub keeps durable repo and asset state. Created
2733
# on boot if absent; relative paths resolve against the hub's working
@@ -36,11 +42,9 @@ HUB_DATA_DIR=.data/hub
3642
# commit can land on the working branch and delete the checkout.
3743
HUB_ALLOW_GIT_INSIDE_WORK_TREE=1
3844

39-
# Directory of built user-interface files the hub serves at BASE_URL.
40-
# Relative paths resolve against the hub's working directory (apps/hub
41-
# under `bun run dev`), so ../web/dist is the web app's build output;
42-
# produce it with `bun run build` before starting the hub.
43-
HUB_STATIC_DIR=../web/dist
45+
# The web app now runs its own dev server (`bun run dev` starts it
46+
# alongside the hub) rather than being served as a static build from the
47+
# hub's own origin — see apps/web/vite.config.ts's /api proxy.
4448

4549
# The administrator identity `bun run dev` seeds for local development:
4650
# the dev script signs this account up on the fresh hub, so a fresh
@@ -53,24 +57,10 @@ HUB_STATIC_DIR=../web/dist
5357
# feature it configures off; the hub never treats a partially-set group
5458
# as configured — it fails loudly at boot instead.
5559

56-
# Per-IP rate limit on email sign-up. Defaults to 5 sign-ups per 60
57-
# seconds when unset.
58-
# SIGNUP_RATE_LIMIT_WINDOW_SECONDS=60
59-
# SIGNUP_RATE_LIMIT_MAX=5
60-
61-
# Per-account rate limit on email sign-in, keyed on the target email
62-
# rather than client IP (client IP can't be trusted as a sign-in key in
63-
# this deployment — see sign-in-rate-limit.ts). Defaults to 10 attempts
64-
# per 60 seconds when unset — deliberately looser than better-auth's
65-
# built-in 3-per-10-seconds default, which is tight enough that a mistyped
66-
# password can lock an account out mid-window. See CL-6494.
67-
# SIGNIN_RATE_LIMIT_WINDOW_SECONDS=60
68-
# SIGNIN_RATE_LIMIT_MAX=10
69-
70-
# Self-serve signup is ungated: stock Interchange composition carries no
71-
# signup mode, domain allowlist, or email-verification gate, so anyone
72-
# can register (rate-limited only — see SIGNUP_RATE_LIMIT_* above). Gate
73-
# access in the composition that embeds the hub if a deployment needs it.
60+
# Self-serve signup is ungated: stock Interchange composition (createAuth)
61+
# carries no signup mode, domain allowlist, rate limit, or
62+
# email-verification gate, so anyone can register. Gate access in the
63+
# composition that embeds the hub if a deployment needs it.
7464

7565
# Provider API keys are no longer read from the environment: hub boot
7666
# plants nothing, and setting ANTHROPIC_API_KEY or any other provider's
@@ -91,49 +81,24 @@ HUB_STATIC_DIR=../web/dist
9181
# tooling, never part of a real user's workflow set.
9282
# WORKBENCH_SEED_CATALOG_TEST_WORKFLOWS=
9383

94-
# Behind a reverse proxy (e.g. tailscale serve), BASE_URL is the public
95-
# origin and PORT is the local port the proxy forwards to.
96-
# PORT=3000
97-
98-
# OAuth sign-in. Each provider is an independent pair — set both values
99-
# to turn its button on at the sign-in screen, leave both unset to
100-
# leave it off. Setting only one half of a pair fails the hub at boot,
101-
# naming the missing value, rather than silently disabling the
102-
# provider. Email/password sign-in always stays available regardless.
103-
#
104-
# Google: create an OAuth client at
105-
# https://console.cloud.google.com/apis/credentials, with an authorized
106-
# redirect URI of <BASE_URL>/api/auth/callback/google.
107-
# GOOGLE_CLIENT_ID=
108-
# GOOGLE_CLIENT_SECRET=
109-
#
110-
# GitHub: create an OAuth app at
111-
# https://github.com/settings/developers, with an authorization
112-
# callback URL of <BASE_URL>/api/auth/callback/github.
113-
# GITHUB_CLIENT_ID=
114-
# GITHUB_CLIENT_SECRET=
115-
11684
# The `github` connector stores a personal access token today (Plugins
11785
# page and the in-room connect-github card are PAT-first — CL-6345). Create
11886
# a token at https://github.com/settings/tokens with the `repo` scope and
11987
# paste it in the connect UI; no OAuth App is required for that path.
12088
#
121-
# Optional — reserved for a future GitHub App / hosted one-click connect
122-
# (CL-6343, not the shipped welcome mat). A SEPARATE OAuth App from
123-
# GITHUB_CLIENT_ID above (which only signs people in to Workbench itself).
124-
# If you register one early: create it at https://github.com/settings/developers
125-
# with an authorization callback URL of <BASE_URL>/api/tenants/ — GitHub
126-
# matches any redirect under that prefix; the flow's actual callback would
127-
# be tenant-scoped
89+
# Optional — a hosted one-click GitHub connect (CL-6343, not the shipped
90+
# welcome mat). Create an OAuth App at
91+
# https://github.com/settings/developers with an authorization callback
92+
# URL of <BASE_URL>/api/tenants/ — GitHub matches any redirect under that
93+
# prefix; the flow's actual callback is tenant-scoped
12894
# <BASE_URL>/api/tenants/<tenant-id>/connections/oauth/github/callback
12995
# (CL-6394). Leave both unset for normal PAT-first deploys.
13096
# GITHUB_APP_CLIENT_ID=
13197
# GITHUB_APP_CLIENT_SECRET=
13298

13399
# Gmail connector (read, draft, send — sending always waits for approval):
134100
# create an OAuth client at https://console.cloud.google.com/apis/credentials
135-
# (a separate client from GOOGLE_CLIENT_ID's sign-in app) with the Gmail
136-
# API enabled. Google requires EXACT-match redirect URIs, so register the
101+
# with the Gmail API enabled. Google requires EXACT-match redirect URIs, so register the
137102
# tenant-scoped callback for each tenant that will connect:
138103
# <BASE_URL>/api/tenants/<tenant-id>/connections/oauth/gmail/callback
139104
# The connect asks for the gmail.modify scope with offline access (the
@@ -232,27 +197,20 @@ HUB_STATIC_DIR=../web/dist
232197
# webhook-trigger signing secrets, and the onboarding OAuth connect state
233198
# (PKCE verifier) sealed between /start and /callback so it survives a hub
234199
# restart in between. 64 hex characters (32 bytes, AES-256): openssl rand
235-
# -hex 32. Required for any real deployment — the hub refuses to boot
236-
# without it. Leave unset and instead set ALLOW_PLAINTEXT_SECRETS below
237-
# for local dev/test.
238-
# CREDENTIAL_ENCRYPTION_KEY=
200+
# -hex 32. Required unconditionally — the hub refuses to boot without it,
201+
# with no dev-only unencrypted fallback. The value below is fine for a
202+
# laptop that only you can reach; generate your own for anything else.
203+
CREDENTIAL_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
239204

240205
# Seals every principal's per-principal signing key at rest, under its own
241206
# key so it rotates independently of CREDENTIAL_ENCRYPTION_KEY. 64 hex
242-
# characters (32 bytes, AES-256): openssl rand -hex 32. Required for any
243-
# real deployment — the hub refuses to boot without it. Leave unset and
244-
# instead set ALLOW_PLAINTEXT_SECRETS below for local dev/test.
245-
# PRINCIPAL_KEY_ENCRYPTION_KEY=
207+
# characters (32 bytes, AES-256): openssl rand -hex 32. Required
208+
# unconditionally, same as CREDENTIAL_ENCRYPTION_KEY above.
209+
PRINCIPAL_KEY_ENCRYPTION_KEY=1111111111111111111111111111111111111111111111111111111111111111
246210

247-
# Dev/test-only opt-out of the CREDENTIAL_ENCRYPTION_KEY and
248-
# PRINCIPAL_KEY_ENCRYPTION_KEY requirements above:
249-
# set to boot without a key, storing those secrets unencrypted with a boot
250-
# warning. Uncommented here so a fresh checkout's `bun run dev` keeps
251-
# working out of the box. Safe to inherit as-is: the hub refuses to boot
252-
# with this set unless BASE_URL is also a loopback address (localhost /
253-
# 127.0.0.1 / ::1), so it can never take effect against a real deployment
254-
# even if this file is copied verbatim.
255-
ALLOW_PLAINTEXT_SECRETS=1
211+
# Maximum size, in bytes, of a tool-package tarball the package-registry
212+
# PUT endpoint accepts. Defaults to 10 MiB when unset.
213+
# HUB_MAX_TARBALL_BYTES=10485760
256214

257215
# Artifacts plane (@corbits/artifacts) uses DATABASE_URL too — different
258216
# schema only (`artifacts.*` FKs into public.tenant/principal). Nothing to
@@ -276,17 +234,14 @@ ALLOW_PLAINTEXT_SECRETS=1
276234
# e2b one remote E2B sandbox per allocation. Requires E2B_API_KEY
277235
# and E2B_TEMPLATE (see packages/e2b-sandbox-sidecar/README.md).
278236
#
279-
# SIDECAR_DEFAULT_PROVISIONER picks which listed id exclusive placements
280-
# provision on; optional when exactly one id is listed, required when
281-
# listing more than one.
237+
# Listing more than one id registers all of them; the first listed is the
238+
# default an exclusive placement provisions on.
282239
#
283-
# Adding another backend is three steps, no hub surgery: implement
284-
# SidecarProvisioner in its own package (copy packages/process-provisioner
285-
# — the allocation lifecycle itself comes from @corbits/sandbox-sidecar),
286-
# add its id to SIDECAR_PROVISIONER_IDS in apps/hub/src/config.ts, and add
287-
# a case to buildSidecarProvisioner in apps/hub/src/index.ts.
240+
# Adding another backend is: implement SidecarProvisioner in its own
241+
# package (copy packages/process-provisioner — the allocation lifecycle
242+
# itself comes from @corbits/sandbox-sidecar), then add a case for its id
243+
# to buildSidecarProvisioner in apps/hub/src/server.ts.
288244
# SIDECAR_PROVISIONERS=process
289-
# SIDECAR_DEFAULT_PROVISIONER=process
290245
# DOCKER_PROVISIONER_IMAGE=
291246

292247
# Overrides for the "process" backend, both optional. Unset, it spawns
@@ -298,9 +253,10 @@ ALLOW_PLAINTEXT_SECRETS=1
298253

299254
# HUB_SIDECAR_WEBSOCKET_URL overrides the ws(s):// address a provisioned
300255
# sidecar dials back to reach this hub. Leave unset to derive it from
301-
# BASE_URL — fine when the hub and its sidecars can reach each other at
302-
# that address, which is always true for the "process" backend (a child
303-
# process on the hub's own host). Set this explicitly whenever
256+
# PORT (ws://127.0.0.1:<PORT>/api/sidecars/ws) — fine when the hub and its
257+
# sidecars can reach each other at that address, which is always true for
258+
# the "process" backend (a child process on the hub's own host). Set this
259+
# explicitly whenever
304260
# SIDECAR_PROVISIONERS includes "docker" or "e2b": a container's own
305261
# "localhost" is itself, not the hub host, and an E2B sandbox is a remote
306262
# machine that needs a publicly reachable wss:// URL or tunnel.

0 commit comments

Comments
 (0)