Skip to content

Automate Cantelop Fleet and database setup in the production script - #22

Open
stepandel wants to merge 4 commits into
mainfrom
claude/hosted-setup-fleet-db
Open

stepandel wants to merge 4 commits into
mainfrom
claude/hosted-setup-fleet-db

Conversation

@stepandel

Copy link
Copy Markdown
Owner

Why

Follow-up to #21. I used service/deploy/production-env.sh to set up hosted production. Two of its steps still needed hand-copied credentials:

  • the Cantelop Fleet step, which needs Fleet tokens and package IDs;
  • the database step, which needs the Neon owner URL.

Running it also exposed a recursion bug.

What changed

  • cantelop section is automatic (new service/deploy/cantelop-fleet.mjs). It uses the owner credential from cantelop login to:

    • create or reuse the juiced Fleet;
    • issue a short-lived publisher principal, register the App's active release as the package, then revoke the publisher;
    • issue separate API and worker principals with the scopes from Cantelop's managed-fleet docs;
    • write the Fleet ID, both tokens, the package ID and the runtime ID to Vercel without printing them.

    Safeguards:

    • It refuses a release whose runtime differs from this CLI build (npm run build:runtime) and disables the package it just registered.
    • Package versions are <cli version>-r<App release number>, since a version can't be reused once registered.
    • The helper refuses to run unless the setup script calls it, and never prints to a terminal.
  • Fix: the cantelop shell function shadowed the CLI, so its cantelop releases call recursed into itself. It now uses command cantelop.

  • database section can read the owner URL from Vercel. With OWNER_FROM_VERCEL=1, it reads JUICED_OWNER_DATABASE_URL from the development environment into a temporary file and deletes the file straight away.

    • Before running it, connect the Neon resource to development only (vercel integration resource connect juiced-db juiced -e development --prefix JUICED_OWNER_).
    • Disconnect it afterwards, so the owner URL never reaches production.
  • docs/managed-service.md describes the automated Fleet step.

For reviewers

  • Cantelop API workarounds: owner access tokens last 60 seconds, so each owner call gets a fresh one from /api/cli/token. Cantelop's Fleet route returns 503 control_unavailable for an expired or invalid owner token rather than 401, so an unexpected 503 there usually means auth.
  • Principals aren't revoked on rerun: rerunning the cantelop section issues new API and worker principals and leaves the old ones active. Revoke them via DELETE /v1/fleets/{flt}/principals/{id} after redeploying.
  • Branch history: this branch is cut from main with the three commits cherry-picked, because Make Stripe optional so hosted sign-in can launch without billing #21 was squash-merged.

Testing

All three sections ran against production in this setup:

  • cantelop created Fleet flt_01e88a7c…, registered package 0.2.0-r24 with runtime rt_c50272… (matching CLI 0.2.0), issued both principals and revoked the publisher. A check of the log found no tokens.
  • database ran the migrations, created both roles, confirmed both can connect, and stored their URLs. The Neon resource was then disconnected.

After redeploying, /ready returns 200, GitHub browser sign-in reaches /account, and juiced login with juiced whoami shows an active account. Before that, I dry-ran the database section twice against a local PostgreSQL (with DRY_RUN=1), confirming it's safe to repeat.

🤖 Generated with Claude Code

stepandel and others added 3 commits October 21, 2026 15:11
The cantelop section now uses the owner's `cantelop login` credential to
create or reuse the juiced Fleet, register the active App release as the
package, and issue API and worker principals, writing the tokens to Vercel
without printing them. It refuses a release whose runtime differs from
this CLI build and revokes the one-off publisher principal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cantelop section is a shell function of the same name, so its
`cantelop releases` call recursed into itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OWNER_FROM_VERCEL=1 pulls JUICED_OWNER_DATABASE_URL from the development
environment, where the Neon resource is connected only for the setup,
into a temporary file that is removed straight away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
juiced Ready Ready Preview Nov 2, 2026 6:54pm UTC

Request Review

Main's Fleet SDK migration (#38) replaced the individual Fleet variables
with one CANTELOP_APP_CONFIG integration document and moved package
publication into `cantelop deploy`. Keep main's `cantelop` section and
drop cantelop-fleet.mjs, which wrote the retired variables. Keep the
OWNER_FROM_VERCEL database option and document it instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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