cybrix-deploy
Prerequisites
Before deploying, ensure the user has an API token. Check in this order:
CYBRIX_DEPLOY_TOKEN— set automatically by Claude Code from userConfig keychain (no action needed).- Environment variable
CYBRIX_TOKEN. - File
~/.config/cybrix/token. - File
.cybrix/tokenin the project (gitignored).
If none exist, instruct the user exactly like this:
No Cybrix token found. Get one free at https://app.cybrix.cc/dashboard (Step 3 — "Save your API token" → Generate).
Once you have it, you can either:
- Paste it here and I'll use it for this deploy (easiest)
- Run
export CYBRIX_TOKEN=<token>in your terminal to set it for this session- Run
echo <token> > ~/.config/cybrix/tokento save it permanently
Then wait for the user to provide the token. If they paste it directly in chat, use it immediately — do not require them to re-run any command. The project is ready to deploy once the token is available.
Deployment workflow
Step 1 — Detect project type (heuristic)
Do not rely on a framework whitelist. Instead, look for signals and classify the project as static, server, or unknown.
Static signals (proceed with deploy):
package.jsonhas abuildscript AND output lands indist/,out/,build/,public/,_site/, or.output/public/next.config.{js,ts,mjs}withoutput: 'export'oroutput: 'static'astro.config.{js,ts,mjs}present (default mode is static)vite.config.{js,ts}without SSR plugins_config.yml(Jekyll),config.tomlorhugo.toml(Hugo),.eleventy.js/eleventy.config.js(Eleventy),zola.toml(Zola)- Only HTML/CSS/JS files at root, no server entry point
Server signals (refuse — see below):
Dockerfileordocker-compose.yml(unless it only copies a staticdist/)main.go,server.go, or any*.gocontainingnet/httporListenAndServemain.py,app.py,server.pywithuvicorn,gunicorn,flask,fastapiimports, or aif __name__ == '__main__'block callingapp.run/serve/asyncio.runmain.rsorserver.rswithactix,axum,rocket,warp, ortokio::mainpackage.jsonwith astartscript that runsnode/tsx/bunon a server file (NOTnext startin a static-export config)Gemfilewithpuma,unicorn,rails, orsinatrapom.xmlorbuild.gradlewithspring-boot.csprojwith ASP.NET
Database signals (warn but allow if everything else is static):
*.sqlfiles,migrations/folder,prisma/schema.prisma,drizzle.config.*,DATABASE_URLreferenced in source- A static site hitting a hosted DB from the browser is unusual but valid. Warn the user, don't refuse.
When refusing (server signals detected):
This looks like a project that needs a server runtime — I detected
<specific signal, e.g. "main.go with net/http" or "Dockerfile with EXPOSE">.Cybrix currently supports static sites only. Your options:
- Convert to a static export (e.g. Next.js
output: 'export', Astro, Hugo).- Use a service that supports backends: Railway, Fly.io, Render.
- Tell me to deploy anyway if you think the detection is wrong.
Always allow option 3 — heuristics are imperfect and the user knows their project.
Static output directories to check, in order: dist, out, public,
_site, build, .output/public.
Step 2 — Scan environment variables
After confirming the project is static but BEFORE running the build, scan for environment variables the build will need.
2a. Read .env files — parse KEY=value format, skip comments (#)
and blank lines. Files to check: .env, .env.local, .env.production,
.env.example.
2b. Grep source code for build-time env var references:
- JS/TS:
process.env.X,import.meta.env.X - Look in
src/,app/,pages/,components/— any*.{js,jsx,ts,tsx,vue,svelte} - Pay extra attention to
NEXT_PUBLIC_*,VITE_*,PUBLIC_*,REACT_APP_*— these are baked into the bundle at build time
2c. Cross-reference keys found in code against keys present in .env files to find what the build needs.
2d. Show the user:
I detected the following environment variables your build needs:
NEXT_PUBLIC_API_URL (in .env.local, used in 3 files) VITE_STRIPE_KEY (in .env.local, used in src/checkout.ts)
These need to be set before the build. How would you like to provide them?
- Paste them here (sent encrypted with the deploy)
- Set them later in the dashboard
- Skip (build may fail or site may not work correctly)
If the user picks option 1, ask for each value one at a time. Include
them in the multipart POST to /v1/deploys as an env_vars field
(JSON map: {"KEY": "value", ...}).
2e. Warn about missing variables — if a var is referenced in code but not in any .env file:
⚠
AUTH_SECRETis referenced in your code but not in any .env file. Provide it now or the build may fail.
2f. Refuse to forward secrets in client-exposed vars — if a key with
a client prefix (NEXT_PUBLIC_*, VITE_*, REACT_APP_*, PUBLIC_*)
looks like a secret (*_SECRET, *_PRIVATE_KEY, DATABASE_URL,
JWT_SECRET):
⚠
NEXT_PUBLIC_JWT_SECRETlooks like a private secret but has a client-bundle prefix — it will be visible to anyone who opens your site's source. Are you sure you want to include it?
Do not send it without explicit confirmation.
Step 3 — Choose project name and confirm
3a. Infer a default name from the current folder name, slugified
(lowercase, hyphens, max 32 chars). Example: my-portfolio-site.
3b. Check availability by calling:
GET https://api.cybrix.cc/v1/slugs/<name>/available
Response: {"available": true, "slug": "my-portfolio-site"}
- If
available: true— use it as the default. - If
available: false— do NOT use it. Tell the user:The name
<name>is already taken. What would you like to call your project? It will be live at<your-name>.cbrx.cc. Then check availability of the new name too. Repeat until available.
3c. Present summary (only after confirming name is available):
Ready to deploy to Cybrix:
- Project name: <name> → live at
<name>.cbrx.cc- Build command: <detected>
- Output directory: <detected>
- Env vars: <count> included / none detected
Continue? (yes / change name / change build / change output)
If the user says change name, ask for a new name and re-check availability.
Use the confirmed answers to override defaults before proceeding.
Step 4 — Build
Run the build command in the project root. Stream output to the user.
If the build fails, do not retry. Show the last 40 lines and say:
Build failed. Fix the error above and try again.
Step 5 — Deploy
Run ${CLAUDE_PLUGIN_ROOT}/scripts/deploy.sh <project_name> <output_dir>.
The script:
- Creates a project via
POST /v1/projectswith{"name": "<project_name>"}, receives{ id, slug }. Skips ifCYBRIX_PROJECT_IDis already set. - Tars and gzips the output directory.
- POSTs the tarball to
https://api.cybrix.cc/v1/deploysas multipart form data with fieldsproject_id(UUID) andfile(the .tar.gz), optionallyenv_vars(JSON map). IncludesAuthorization: Bearer $CYBRIX_TOKEN. - Receives
{ id, project_id, status }in the response. - Polls
https://api.cybrix.cc/v1/deploys/<id>every 2 seconds until status isliveorfailed(max 5 minutes). - Prints the result as JSON on stdout including
deployed_urlandslug.
Step 6 — Report to the user
On success:
Deployed.
Live: https://<slug>.cbrx.cc Dashboard: https://app.cybrix.cc/dashboard/projects/<project_id>
On failure:
Deploy failed.
Reason: <error from API> Logs: https://app.cybrix.cc/deployments/<id>
Caching
After the first successful deploy, cache {project_id, slug} in
.cybrix/project.json. On su