NEARBuilders / NEARBuilders/everything-dev

v2 platform services: deploy service, gasless publish, grafting, OTA runtime, sandboxes [spec]

Open
#274 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

ready-for-agent
Dominant language
TypeScript
Stars
5
Forks
6
Avg merge
1m
Merged PRs (30d)
1

Description

Blocking predecessor: the oRPC v2 / Effect 4 migration epic — #248 (all 21 tickets). This spec covers everything after it, on the v2 integration branch cut from the citynode fork (ahead of upstream).

Problem Statement

Deploying an everything.dev application today means depending on third-party platform vendors with incompatible, gate-kept permission models. Bundles go to Zephyr Cloud — unreliable in practice (upload failures, retry loops, CI hangs) and permissioned by Zephyr-org + GitHub-repo membership. The alternative (self-managed Cloudflare R2, as attempted in the citynode fork's feature/cloudflare-cdn-alchemy branch) recreates the same gatekeeping with shared bucket credentials. Every path forces developers, tenants, and agents to hold accounts and credentials with hosting vendors before they can ship, and config publish requires locally-stored NEAR keys via a multi-step near-cli ritual. Meanwhile the runtime itself is static: the host boots once from an on-chain config snapshot, so shipping a change means a redeploy, not a transaction. This blocks the everything.dev vision — a graph of applications that extend one another, each with their own APIs and plugins, where a NEAR wallet is the only credential anyone needs.

Solution

The platform becomes its own service provider. A deploy plugin on the official everything.dev deployment serves as the bundle host: any wallet-holder (gated by organization membership) uploads built workspaces through an authenticated API and receives content-addressed URLs plus integrity hashes that land in their config. Publishing to FastKV is a single gasless wallet approval relayed by the platform's existing relayer. The runtime gains an OTA mode: a static container watches its account's on-chain config and hot-swaps changed remotes in memory, so a deploy is visible within seconds without a restart. Web plugin route grafting — already proven by the beta-v2 prototypes — lands in the real host, so applications compose from independently deployed UI plugins, tenants and members override by plugin URL, and the extends chain becomes a verified, multi-level node graph. Sandboxes (per-account containers from the same image, PGlite-backed) make "check out, deploy, see it live" real for workshop attendees and agents, with a pre-spawned pool requiring no orchestrator. The service provider itself (buckets, machines, DNS) is declared as one alchemy program, self-hostable and portable across Cloudflare, Fly, and Hetzner. Zephyr is removed. A NEAR wallet is the only human credential; an API key is the only agent credential.

User Stories

  1. As a platform developer, I want to run one command that builds every workspace and uploads it to the platform's deploy service, so that I never manage a CDN account or vendor credential again.
  2. As a platform developer, I want the deploy command to write bundle URLs and integrity hashes into my config automatically, so that the config always reflects what was actually deployed.
  3. As a platform developer, I want to publish my config to FastKV by approving a single transaction in my wallet, so that I keep no private keys on disk and run no CLI key rituals.
  4. As a platform developer, I want the publish transaction to also record a deployment manifest on-chain, so that every deploy is auditable and any previous deploy can be re-pinned as a rollback.
  5. As an AI agent, I want to deploy and publish through the platform's MCP tools with an API key, so that I can ship changes to a live app autonomously, headlessly.
  6. As a tenant member (organization member of a city node), I want to deploy my UI customizations and see them at my own subdomain, so that I can iterate on the shared app without touching the official deployment.
  7. As a tenant member, I want deploy rights to be governed by my organization membership, so that access control is just "who's in the org" — no vendor dashboards, no repo membership lists.
  8. As a city node operator, I want my city's dedicated host to serve member subdomains over free wildcard TLS, so that every member gets HTTPS without per-hostname certificates or paid certificate add-ons.
  9. As a city node operator, I want members to override only UI-level plugins, so that no unverified server code from members ever executes in my host process.
  10. As a sovereign tenant, I want to take an entire application config via extends, publish it under my own NEAR account, and deploy my own host, so that I can graduate from a member subdomain to a fully self-owned deployment whenever I want.
  11. As a sovereign tenant, I want to provision my own databases by running the platform's alchemy program on my own cloud accounts, so that my data layer is as self-owned as my host.
  12. As a platform maintainer, I want official CI to deploy through the same deploy service under the platform account, so that there is exactly one storage path and one set of CDN credentials in existence — held only by the service.
  13. As a platform maintainer, I want the deploy service to enforce per-account quotas, a MIME allowlist, and streaming uploads, so that a public upload endpoint on my domain cannot be abused for cost, reputation, or memory damage.
  14. As a platform maintainer, I want member and tenant bundles served from a separate domain than official ones, so that user-uploaded content never shares the official CDN's reputation surface.
  15. As a workshop attendee, I want to write code, run one deploy command, and see my app update at my sandbox URL within seconds, so that the build-deploy-see loop feels live without me signing up for anything but a NEAR wallet.
  16. As a workshop attendee, I want my sandbox to run the exact same container image as production hosts, so that what I demoed is provably what production runs.
  17. As an application user, I want a host that hot-swaps new versions of remotes without dropping my in-flight requests, so that deploys never interrupt me.
  18. As an application user, I want a failed deploy to roll back to the last-good configuration, so that a bad publish can never take the app down.
  19. As a plugin author, I want my web plugin's routes grafted into host mount points with zero host-side configuration, so that adding a plugin to an app is a config reference, not host routing code.
  20. As a plugin author, I want auth, locale, and the typed API client injected into my routes through router context, so that my plugin behaves as if it were part of one codebase.
  21. As a plugin author, I want route collisions across plugins to resolve first-wins with namespaced route ids, so that independently deployed plugins can never break each other's URLs.
  22. As an app composer, I want extends chains to nest multiple levels with per-hop verification, so that a city platform can extend the base platform, a city extends the city platform, and a member extends the city — each hop cryptographically checked.
  23. As an app composer, I want chain depth capped and cycles rejected at publish time, so that resolution cost stays bounded and no request ever discovers a loop.
  24. As a platform operator, I want the whole provider — buckets, sandbox machines, DNS — declared as one alchemy program, so that "self-host everything.dev" is a single deploy command on any supported cloud.
  25. As a platform operator, I want idle sandboxes to sleep, so that a hundred workshop attendees cost nothing when idle.
  26. As a developer on the v2 branch, I want the oRPC v2 / Effect 4 migration to land first (epic #248), so that everything after it — the deploy service especially — is built Effect-native from day one.
  27. As an upstream maintainer of everything-dev, I want this work delivered on an integration branch cut from the citynode fork (which is ahead), so that platform changes flow upstream as reviewable PRs.

Implementation Decisions

  • Sequencing and branch: all work lands on a v2 integration branch cut from the citynode fork's main (ahead of upstream); PRs flow up to NEARBuilders/everything-dev; the oRPC v2 + Effect 4 migration (epic #248, 21 tickets) is the blocking predecessor — the deploy service and everything after is built on the migrated, Effect-native plugin API.
  • Deploy service is a platform plugin with an oRPC contract (upload, finalize, list/read deployments), Drizzle storage in its own plugin schema, and eligibility middleware. Because every oRPC route is auto-derived into OpenAPI and MCP tools, the deploy API is agent-callable with an API key by construction.
  • Eligibility = organization membership, checked in-process against the auth plugin via the plugins client; the check is a seam designed to admit a second criterion (node subscription) later. Deployments and subdomain bindings are namespaced per organization.
  • Storage via S3-compatible PUTs (aws4fetch) with scoped, service-held tokens — never account-wide credentials in plugin env. Content-hashed immutable paths give dedupe, infinite cacheability, and natural SRI. Explicit content-type map and immutable cache-control headers on upload (the failure that stalled the earlier Cloudflare attempt). Only the service touches CDN credentials; official CI uploads through the same service under the platform account to the official domain, member uploads to a separate member domain.
  • Hardening is day-one: streaming (non-buffering) uploads, concurrency cap, per-account size and file-count quotas, MIME allowlist (scripts, styles, maps, fonts, images, manifests), robots noindex on the member domain.
  • Publish is split from deploy: a deploy command builds Zephyr-free, uploads, and writes URLs + local-SRI integrity into the config; publish pushes config to FastKV. The human path signs a FastKV write as a wallet-approved delegate action relayed gaslessly by the existing ephemeral relayer (requires whitelisting the FastKV registry contract and sizing gas/deposit for ~10KB writes); the local function-call key path stays first-class for CI and headless agents. One transaction writes both the config key and a per-deploy manifest key (on-chain ledger mirroring the service's deployment records).
  • Wallet-in-terminal signing is a spike with a browser-popup fallback — do not block the milestone on it.
  • Zephyr removal: bundler plugins, deploy-output parsing, and catalog pins are removed across all workspaces; a provider seam is retained so additional storage providers (IPFS notably — viable once watchers cache bundles locally at swap time) slot in later without architecture change.
  • Grafting lands as prototyped: host grafts web-plugin route trees into a mount registry derived from pathless layouts; reparent only the subtree root; namespace route ids per plugin; join the MF shared-singleton scope; first-wins collision policy; SSR by exclusion (session-gated mounts never server-render); typed API client and auth injected through root-route context. The two beta-v2 prototypes are the specification — their findings, including the corrections they produced, carry over.
  • Extends chains are multi-level with per-hop verification (publisher attestation + SRI + binding permission flags), depth-capped at five, cycles rejected at publish; resolution flattens chains at publish time first, with a strategy slot for runtime walking later.
  • Member subdomains (<user>.<city>.<domain>) resolve through the existing data-driven binding table (30s-cached host resolver), extend the city runtime, and may override UI and plugin-UI remotes only — matching exactly the request-scoped override surface the fixed-core host supports; SSR stays per-binding gated. Wildcard TLS via DNS-only wildcard CNAME plus ACME-challenge delegation to the machine host's auto-issued wildcard certificate — free at any depth, no paid certificate manager.
  • OTA runtime: --watch mode polls the account's FastKV read URL (~5s), validates the trust chain (on-chain config signature + per-remote SRI), and swaps only remotes whose URLs changed. Candidate design — shadow-flip: import new remotes alongside old, build the new graph, verify, flip the request handler atomically, grace-period the old graph — is gated on a runnable hot-swap lifecycle prototype before implementation; rollback re-imports the cached last-good URLs.
  • Sandboxes are per-account containers running the same static image with --watch, boot-parameterized only by account and gateway env. Database tiering: PGlite embedded (same Postgres dialect as dev/test/prod, bounded tens-of-MB RAM, disposable with the sandbox) for workshop/disposable sandboxes; Neon provisioned via alchemy for production tenants; the engine is selected at boot through the existing DB driver seam. Workshop tier ships as a pre-spawned pool (manually deployed machines + wildcard DNS) — no orchestrator code before the demo loop works; a spawn dashboard via the provider program is a later phase.
  • The provider is one alchemy program: buckets, sandbox machines, and DNS declared together, self-hostable by sovereign tenants on their own cloud accounts, portable across supported providers (Cloudflare, Fly, Hetzner). Effect 4 (from the predecessor migration) is what allows alchemy as a direct dependency rather than a sandboxed sidecar.

Testing Decisions

  • Good tests exercise external behavior at a seam — the served app, the contract surface, the published registry payload — never plugin internals or storage implementation details.
  • The unifying seam is the publish→serve loop: deploy + publish to (a stubbed or test-network) FastKV, host loads it, the served app reflects it. The existing regression stack (stack-start + browser specs) is the highest practical vehicle for this and for grafting, member subdomains, and OTA verification ("config change → app changes without restart, no dropped requests").
  • The deploy plugin is tested through its oRPC contract with the pglite pattern used by existing plugin suites; the storage provider is a swappable adapter so tests run against a local provider while asserting the contract-visible outcomes (deterministic content-hashed URLs, recorded content-type and cache-control, quota enforcement, MIME rejection, noindex headers).
  • Grafting tests are the prototype's headless verify scripts promoted into the real test suites (composition, SSR exclusion, collision policy), plus browser specs through the data-testid conventions already established in the regression suite.
  • Member subdomain resolution extends the existing host suite at the request-runtime seam (hostname + bindings → runtime config, permission gating, SRI verification, per-hop chain verification with the depth cap and cycle rejection).
  • Publish tests assert at the registry-write boundary (entries include config + manifest keys; confirmation loop re-reads equality) with delegate-action signing covered as units; relayer integration is exercised in the e2e loop on testnet.
  • Prior art: api/host/everything-dev unit suites, the two beta-v2 prototype verify scripts, the regression stack's admin and settings specs, and the existing publish test suite in the CLI package.

Out of Scope

  • The oRPC v2 / Effect 4 migration itself — tracked entirely by upstream epic #248 (this spec's blocking predecessor).
  • app.ts authoring and its evaluation/backwards-compat questions (wayfinder tickets 04, 05) — the config surface remains the current one.
  • UI-to-UI route inheritance (inheritRoutes) — designed, deliberately sequenced after grafting is load-bearing, gated on its own prototype (wayfinder ticket 09).
  • The sandbox orchestrator dashboard and spawn API — pre-spawned pool first; orchestrator is a follow-up spec.
  • IPFS as a storage provider — seam only; adoption after watchers exist.
  • Native (React Native) target, offline service-worker layers, and browser-executed client plugins — separate plans.
  • Node-graph discovery UI (registry browsing beyond the existing view-as-prefix-scan model).

Further Notes

  • Honest risk register: hot-swap lifecycle (in-flight requests, plugin scope teardown, DB pool handoff, ESM disposal) is the hardest engineering here and is prototype-gated; relayer gas sizing and terminal wallet signing are spikes with fallbacks; the public upload endpoint is treated as untrusted user-generated content from day one.
  • The wayfinder map (decisions 11–17) and tickets 08–11 in the fork's plans/wayfinder/ are the decision record behind this spec; the fork's plans/ directory is gitignored, so decision records travel in these issue bodies, not the repo.
  • Bundle delivery never routes through the provider's API once uploaded — content is edge-cached object storage, so a down deploy service blocks new deploys, never traffic.
  • Cost posture: object storage with free egress (~pennies/GiB-month for thousands of deploys), machines sized to the ~300–500MB app-process floor with sleep-on-idle, micronear relay gas per publish.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Begin with the blocking migration epic #248, then inspect the v2 integration branch and the two beta-v2 grafting prototypes named in this spec. The work is complete only when the deploy service, gasless publish, OTA runtime, grafting, extends verification, member domains, and sandboxes described here are implemented and validated; this issue does not name files or tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, typescript
Domain
backend-api-design, cloud, databases, devops, frontend, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.