Skip to content

[DO NOT MERGE]: citynode.app - #237

Closed
elliotBraem wants to merge 25 commits into
mainfrom
feat/chicago
Closed

[DO NOT MERGE]: citynode.app#237
elliotBraem wants to merge 25 commits into
mainfrom
feat/chicago

Conversation

@elliotBraem

Copy link
Copy Markdown
Contributor

Summary

Three primitives layered onto the bos CLI + deploy workflow so the deploy loop is harder to misconfigure and easier to recover from:

  1. Per-account/gateway deploy lock in FastKV (apps/<account>/<gateway>/lock/deploy.json). Acquired before publish, released in a finally block after publish confirmation. Concurrent dispatches now fail fast with status: "locked" and a conflict payload (owner, nonce, expires, txHash). bos publish keeps 10 min default; bos deploy uses 25 min default to cover publish + Railway redeploy. Override either with BOS_DEPLOY_LOCK_TTL_MS. Opt out per command with --no-deploy-lock.

  2. bos infra export that emits the resolved CI infra plan (env + services + account + gateway + project + generatedAt as JSON). Backed by buildCiInfraPlan(runtimeConfig, opts) in cli/infra.ts so the source of truth is the same place that already knows port assignments for api/auth/plugin Postgres + Redis. deploy.yml now consumes this in one step (bun run bos infra export --target ci --network mainnet > .ci-infra.json) and feeds $GITHUB_ENV from the resulting JSON, dropping the duplicated API_DATABASE_URL / AUTH_DATABASE_URL / CORS_ORIGIN literals.

  3. buildOriginMap reads the resolved runtime config (runtimeConfig.auth?.extendsRef + runtimeConfig.plugins[id].extendsRef) instead of re-parsing raw bos.config.json. Both fields are populated by the existing config resolution path (config.ts:833/1062), so the helper is now side-effect-free with respect to disk I/O. ~30 lines deleted.

Operator surface

  • bos deploy --no-deploy-lock — opt out of lock acquisition.
  • bos deploy lock inspect — print active lock (owner, nonce, expires, txHash).
  • bos deploy lock release — force-clear a stuck lock.
  • bos infra export [--target ci|local] [--network mainnet|testnet] — emit JSON to stdout.

Test status

  • Typecheck: clean.
  • bun run lint: clean (3 warnings, 10 infos — pre-existing).
  • 335 unit tests pass (12 new + 4 new TTL tests), same 3 pre-existing failures (typechecks api/ui with zero unexpected errors, completes init cleanly…) that predate this branch.

Changeset

.changeset/deploy-lock-and-infra-export.md (minor bump for everything-dev).

Files

M  .github/workflows/deploy.yml
M  packages/everything-dev/src/cli.ts
A  packages/everything-dev/src/cli/deploy-lock.ts
M  packages/everything-dev/src/cli/infra.ts
M  packages/everything-dev/src/contract.meta.ts
M  packages/everything-dev/src/contract.ts
M  packages/everything-dev/src/plugin.ts
M  packages/everything-dev/src/publish.ts
A  packages/everything-dev/tests/unit/deploy-lock.test.ts
A  packages/everything-dev/tests/unit/infra-export.test.ts
A  .changeset/deploy-lock-and-infra-export.md
M  ui/src/routes/_layout/_authenticated/stake.tsx  (biome auto-fix: missing semicolon)

Open follow-ups (out of scope for this PR)

  • Move the railway redeploy step inside bos deploy so child workflows shrink to one CLI call (bun run bos deploy --network mainnet --service app) and the lock spans publish + Railway. Requires the template deploy.yml regeneration.

elliotBraem and others added 16 commits August 13, 2026 13:55
… to dashboard

- add _admin pathless layout gating on admin role; tenant admin dashboard and system pages render as children via Outlet
- rename authenticated /home route to /dashboard; update sidebar, mobile tabs, user nav, and login redirect fallbacks
- move apps and things routes under the public layout
- changeset: ui-layout-mounts
…, remove nostr

- move login from public layout into new _anon pathless layout that redirects authed users to /dashboard and provides theme toggle header
- rename organization route group from /organizations to /orgs; move invitation acceptance to /orgs/invites/$id
- remove stale nostr entry from authenticated sidebar
- changeset: ui-anon-orgs
…n origin

- Add per-account/gateway FastKV deploy lock (apps/<account>/<gateway>/lock/deploy.json)
  acquired before publish and released in a finally block. Stale or concurrent
  dispatches fail fast with status "locked" and a conflict payload listing owner,
  nonce, expires, and txHash. opt out with --no-deploy-lock.
- bos publish holds the lock for 10 minutes by default; bos deploy holds it for
  25 minutes to cover publish + Railway redeploy. Override with
  BOS_DEPLOY_LOCK_TTL_MS.
- Add bos deploy lock inspect|release for ops visibility and stuck-lock recovery.
- Add bos infra export that emits the resolved CI infra plan (env + services +
  account/gateway/project/generatedAt) so CI consumers stop duplicating port and
  DATABASE_URL knowledge from cli/infra.ts.
- Update .github/workflows/deploy.yml to consume the export and drop the
  hardcoded API_DATABASE_URL / AUTH_DATABASE_URL / CORS_ORIGIN env block.
- buildOriginMap now reads runtimeConfig.plugins[id].extendsRef and
  runtimeConfig.auth?.extendsRef instead of re-parsing raw bos.config.json.
- Tests for the lock helpers, the CI plan builder, the resolved-config origin
  lookup, and the per-command TTL resolution.

Co-authored-by: opencode <opencode@local>
Remove FastKV-backed deploy lock feature:
- Delete bos deploy lock acquire/release/inspect commands
- Remove lock logic from publishToFastKv
- Remove lockConflict from PublishResult and DeployResult schemas
- Concurrent deploys now follow last-write-wins semantics (harmless for Railway redeploy)

Keep bos infra export command:
- Emits {env, services, account, gateway, project, generatedAt} JSON
- buildOriginMap reads runtimeConfig.*.extendsRef (no raw JSON re-parse)

Tests: 263 pass, failures are pre-existing (auth types, template property)
@elliotBraem elliotBraem changed the title feat(cli): lock deploys in FastKV, expose infra export, resolve plugin origin [DO NOT MERGE]: citynode.app Aug 17, 2026
Replace the legacy citynode model with three orthogonal services and a
matching UI surface.

API
- New NodesService: hierarchical node management with a recursive CTE
  for subtree lookup, parent-cycle prevention on create/update, and
  per-node validator scoping.
- New ValidatorsService (replaces CityNodesService): multi-validator per
  node, exact-one default enforcement via db.transaction, and
  resolveForStaking which descends the subtree first and walks
  ancestors as a fallback. resolveByAccountId looks up a single
  validator by its NEAR account id.
- Rewritten TenantsService: createBinding, verifyCustomDomain via HTTP
  HEAD, and setPrimaryBinding wrapped in db.transaction. authorizedTenant
  helper centralizes personal vs org tenant checks.

Robustness
- Shared toOrpcError extracted into api/src/lib/errors.ts so route
  handlers stop hand-typing error maps.
- isUniqueViolation(error) detects Postgres code 23505 instead of
  string-sniffing the message.
- HOSTNAME_REGEX validates DNS hostnames independently of NEAR
  account ids in createBinding and the binding preflight.

Schema
- Drop node_tags (orphaned by the citynode removal) and regenerate
  migrations as a single clean snapshot (4 tables: tenants, nodes,
  validators, domain_bindings). The self-referencing nodes.parent_id
  FK uses AnyPgColumn for the typed reference.

Host
- BindingResolver's TenantBinding interface gained tenantId so resolve
  flows can tie a hostname back to a tenant record. Mock updated to
  keep the integration test green.

UI
- Public landing directory reads listValidators instead of
  listLegacyCityNodes.
- Stake page: CityNodeCard -> ValidatorCard, query keys -> validators,
  resolver hooks -> resolveValidatorByAccountId.
- Admin tenants wizard rebuilt around nodes / validators / bindings,
  and tenant header affordances reference tenant.id instead of the
  now-defunct subdomain field.
- Landing page queries listRootNodes, renders root-node directory cards
  (name, kind badge, slug) with subdomain links + skeleton/empty states
- New public country page at /n/$slug aggregates children via
  listChildren and shows stake guidance from resolveStakingValidators
  (own validator CTA, or stake-to-city links for children with validators)
- Stake page rewritten around resolveStakingValidators: selectable
  validator list with isDefault pre-selected, role/protocol badges,
  community secondary styling, inherited-validator banner, no-validator
  fallback with child links. Node resolved from ?node= param or hostname.
  Stake transaction + onramp flows unchanged; broken admin CRUD removed.
- Rewrite admin tenant creation page from placeholder into full wizard:
  inline org creation (if no active org), node details (kind, cascading
  parent dropdown via listRootNodes + listChildren, slug, name), tenant +
  binding form with auto-generated hostname and live bindingPreflight
- On submit: createTenant → createNode → createBinding (blocking, with
  rollback via deleteNode + deleteTenant on failure), then non-blocking
  deploy steps for NEAR subaccount and registry config publish
- Export StepList + useStepper from @/components barrel
- Extend api/tests/setup.ts: optional plugins map param on getPluginClient,
  role param on orgContext (enables requireOrgRole in integration tests)
- Add api/tests/integration/wizard.test.ts: 5 tests covering full chain,
  rollback on duplicate hostname, nested hierarchy, preflight availability
…Nodes product

- Landing: 'What are CityNodes?' hero + root-node directory + Apply button
- New /apply route (external redirect to citynode.app/apply)
- Remove apps browser cruft (4 routes deleted, apps tab stripped from profile)
- Things index → typed DataTable demo
- Mobile responsive fixes (auth-shell double-padding, simple-header overflow)
- README + skill.md rewritten for CityNodes product surface
@elliotBraem
elliotBraem deleted the feat/chicago branch August 20, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant