Skip to content

Reposition around team collisions, and run the API on a function platform - #34

Merged
amsultan2010 merged 2 commits into
mainfrom
crosscode-pricing-growth-plan-v3
Aug 4, 2026
Merged

Reposition around team collisions, and run the API on a function platform#34
amsultan2010 merged 2 commits into
mainfrom
crosscode-pricing-growth-plan-v3

Conversation

@amsultan2010

Copy link
Copy Markdown
Owner

What this changes

Docs and marketing site: repositions Crosscode around its actual audience — shared projects whose team members all run coding agents — replacing landing-page copy that had drifted to solo-first ("You don't need a team" in the hero, a FAQ entry calling team-necessity "the most common misread"). Rewrites the hero, subtitle, comparison table, benefits heading, use-case card, pricing intro and two FAQ entries; rewrites README's "Who it's for"; and sharpens the product-scope decision and Phase 10 design intent in BUILD_INSTRUCTIONS.md so the doc and the site finally agree.

Coordination service: adds a serverless path so the API can run as functions alongside the marketing site instead of needing its own always-on host. createRequestHandler() exposes the router without a Node server around it, serverless.ts builds store + JWKS once per instance with a no-op broadcast gateway, and apps/docs-site/api/[...path].ts plus vercel.json route /api/* into it. apps/service/src/main.ts remains the persistent-process entrypoint self-hosters run — this is an addition, and both paths share one router rather than forking route logic. Migration 012 adds a rate_limits table with a single-statement takeRateLimit().

Why

The site was describing a user the product is not shaped for: invites, roles, seats, presence, claims, handoffs and per-workspace autonomy policy are all team machinery. Working alone still counts when the project is shared — teammates' agents will land work on it again — but a repository only one person will ever touch has nothing to coordinate with, and plain Git is the better tool.

On the service side, running the API from the existing Vercel project removes an always-on host, a Docker deployment, and the api. DNS record from the critical path. The durable rate limiter is not optional for that: an in-memory counter gives every instance its own full budget, which would turn the pairing-claim throttle into N x 10/min for N warm instances.

Testing

  • pnpm build passes
  • pnpm test passes (304 passed)
  • pnpm test:postgres passes (24 passed)

Three new Postgres tests cover the durable limiter: one budget spent across callers with buckets kept independent, the limit holding when the same bucket is spent concurrently (this one fails against a read-then-write implementation), and window rollover plus pruning.

Note for reviewers running the Postgres suites locally: pick your own container name and port. Several agents are working this repo concurrently on one Docker daemon, and reusing the default means docker run silently fails to bind and the suite connects to somebody else's schema.

Security / trust-boundary impact

Yes — one item, deliberately, and it is the reason migration 012 exists. POST /v1/pairing-codes/claim is unauthenticated, so its per-IP throttle is the only thing standing between an attacker and brute-forcing a 40-bit code space (Contract A). That budget is now spent against the database when durableRateLimits is set, in one INSERT ... ON CONFLICT DO UPDATE so concurrent instances cannot interleave a read and a write and both conclude they are under the limit. It is on for serverless and off for a single persistent process, where it would only buy a round-trip per request. Every other route stays in memory on purpose: being approximate about a courtesy limit costs nothing.

The rate_limits table has RLS enabled with no policies, which denies every non-service role by default — it is service-internal bookkeeping no member ever selects from. Auth, RLS on member-facing tables, checkpoint/materialization safety and excluded-path handling are untouched.

One known gap, called out rather than hidden: a function platform cannot hold /v1/stream open, so broadcasts become no-ops and live push degrades to the daemon's existing unconditional poll until Supabase Realtime carries the notification instead. Nothing breaks — the daemon already polls on a timer — but "instant" becomes "next poll" on a serverless deployment.

Related issues

Follows #32 and #33.

amsultan2010 and others added 2 commits August 4, 2026 02:54
The landing page had drifted to solo-first -- "You don't need a team" in the
hero, a FAQ entry calling team-necessity "the most common misread" -- which
contradicts the product-scope decision in BUILD_INSTRUCTIONS.md and describes
a user the product is not shaped for. Invites, roles, seats, presence, claims,
handoffs, and per-workspace autonomy policy are all team machinery.

The audience is shared projects whose members all run coding agents. Working
alone today still counts when the project is shared -- teammates' agents will
land work on it again, and the proposals waiting on return are the point. A
repository only one person will ever touch is explicitly out of scope; there is
nothing to coordinate with and plain Git is the better tool.

Rewrites the hero, subtitle, hero microcopy, comparison table, benefits
heading, use-case card, and pricing intro on the landing page; replaces the
"do I need a team" FAQ with "what if I'm the only one working on it today";
rewrites README's "Who it's for"; and sharpens the product-scope decision plus
Phase 10's design intent so the doc and the site finally agree.

Also adds createRequestHandler() to the service and a serverless adapter, so a
function platform can run the same routing and auth rather than a forked copy.
Incomplete on purpose: broadcasts become no-ops without a persistent process,
and the in-memory rate limiter counts per instance -- the pairing-code limit is
a brute-force defense over a 40-bit code space and must move to the database
before any serverless deployment is real.

Verified: pnpm build, pnpm test (304 passed).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…limits

Adds a serverless path so the coordination service can run as Vercel functions
alongside the marketing site instead of needing its own always-on host.
apps/service/src/main.ts stays the persistent-process entrypoint self-hosters
run; this is an addition, not a replacement, and both share one router.

- createRequestHandler() exposes the route handler without a Node server
  wrapped around it, so the platform's (IncomingMessage, ServerResponse) pair
  runs the identical routing and auth rather than a forked copy.
- serverless.ts builds store + JWKS once per instance and passes a no-op
  broadcast gateway, because a function has nowhere to hold /v1/stream open.
  Live push therefore degrades to the daemon's existing poll until Supabase
  Realtime carries the notification instead.
- apps/docs-site/api/[...path].ts plus vercel.json route /api/* into it. The
  Vercel project's root directory is already apps/docs-site, so the site build
  is untouched.

The part that is not optional: an in-memory counter gives every instance its
own full budget, which turns the pairing-claim throttle -- the brute-force
defence over a 40-bit code space -- into N x 10/min for N warm instances.
Migration 012 adds a rate_limits table and takeRateLimit() spends the budget in
one statement, so concurrent instances cannot interleave a read and a write and
both conclude they are under the limit. Enabled via durableRateLimits, on for
serverless and off for a single persistent process, where it would only buy a
round-trip per request. Other routes stay in memory deliberately: being
approximate about a courtesy limit costs nothing.

Verified: pnpm build, pnpm test (304 passed), pnpm test:postgres (24 passed,
including one that fails without the single-statement counter).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crosscode Ready Ready Preview Aug 4, 2026 8:08am

@amsultan2010
amsultan2010 merged commit 4d7392f into main Aug 4, 2026
4 checks passed
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