Deployment
ayjnt deploy is git-safety rails around wrangler deploy. It regenerates, enforces that your working tree is in sync with origin, and hands off.
The happy path
# In a clean, pushed tree:
bun run deploy
# Equivalent:
bunx ayjnt deployUnder the hood, this:
- Runs preflight: git clean, in sync with origin, no uncommitted lockfile changes. If any fail, abort.
- Re-runs the build without writing a new migration — if a migration would be staged, abort with “pending migration detected, commit it first.”
-
Spawns
bunx wrangler deploy --config .ayjnt/dist/wrangler.jsoncwith stdio inherited. - Exits with wrangler's exit code.
Preflight in detail
The preflight runs three git checks and one migration check. You'll see an error message for the first one that fails; fix it, retry.
1. Working tree must be clean
$ ayjnt deploy
uncommitted changes detected:
M agents/chat/agent.ts
?? agents/new-thing/agent.ts
commit or stash before deploying. Use --force to bypass.
Any output from git status --porcelain is a failure.
git stash, git commit, or delete the
untracked file. Then retry.
2. Must be in sync with origin
$ ayjnt deploy
3 unpushed commit(s) on main. Push before deploying. Use --force to bypass.
# or
2 unpulled commit(s) from origin/main. Pull before deploying. Use --force to bypass.
ayjnt checks git rev-list --count origin/<branch>..HEAD
(unpushed) and HEAD..origin/<branch> (unpulled).
Both must be zero.
3. No pending migration
$ ayjnt deploy
pending migration detected — not yet committed to .ayjnt/migrations.json.
Run `ayjnt build` to stage it, then `git add .ayjnt/migrations.json && git commit && git push` before deploying.
If your file tree has changed in ways that would produce a new
migration entry but migrations.json hasn't been
updated (or has been updated but not committed), deploy refuses.
The fix is literally what the message says: run ayjnt build, commit the lockfile, push, retry.
What --force does
ayjnt deploy --force skips all preflight. Useful in
emergencies (“Slack is down, production is broken, I need to
ship a one-line fix NOW”). Never skip the check by default —
the whole point of the lockfile+git machinery is to make diverging
migrations impossible, and --force is the trapdoor
that lets you out in rare circumstances.
Resist the urge to use --force “just to get
the deploy out.” If you're racing another developer
and bypass the check, you can overwrite their migration state
in wrangler's perception of the world. The cleanup is
painful.
Legitimate --force cases: solo incident response,
a first-ever deploy from a repo without a remote yet, hotfix
from a machine that can't git push for some
reason.
First-time deploy
Wrangler will prompt for authentication the first time:
$ bun run deploy
✓ git clean
✓ 1 agent(s), 1 staged migrations
⎔ wrangler: authenticating...
(browser tab opens to Cloudflare OAuth; complete login)
✓ deployed https://my-app.your-account.workers.dev
After the first login, credentials are cached in ~/.config/.wrangler/. Subsequent deploys don't
prompt.
Forwarding flags to wrangler
Any flag ayjnt doesn't recognize is forwarded to wrangler untouched. Common ones:
# Deploy to staging
ayjnt deploy --env staging
# Deploy with dry-run (wrangler checks your config, doesn't upload)
ayjnt deploy --dry-run
# Deploy but don't create a rollback
ayjnt deploy --keep-vars
# Deploy with a specific compatibility date override
ayjnt deploy --compatibility-date 2025-01-01 --force is the only flag ayjnt consumes itself;
everything else passes through.
Environments
Wrangler supports multiple environments in one config. ayjnt generates the top-level wrangler fields (main, DO bindings, migrations, assets) but doesn't currently manage per-env overrides. If you need environment-specific config, you have two options:
-
Add a
wrangler.extras.jsoncfile (or similar) in your project and merge at deploy time with a pre-deploy script. Future ayjnt versions may support aframework.config.tswith per-env sections. - Use wrangler environment variables + secrets via CLI rather than config overrides. This is often enough — the DO bindings are the same across envs anyway.
Secrets
Secrets (API keys, bearer tokens, etc.) should never live in the
generated wrangler.jsonc. Use wrangler's secrets
machinery:
# Set a secret (prompts for value)
bunx wrangler secret put OPENAI_API_KEY
# List secrets (names only, values never shown)
bunx wrangler secret list
# Delete
bunx wrangler secret delete OPENAI_API_KEY
Secrets are available as env bindings at runtime: this.env.OPENAI_API_KEY. Declare them in your Env type so TypeScript knows about them:
import type { GeneratedEnv } from "@ayjnt/env";
type Env = GeneratedEnv & {
OPENAI_API_KEY: string;
};Rollback
Wrangler supports rollbacks to any previous deployed version:
bunx wrangler deployments list
bunx wrangler rollback <deployment-id>Rollback does NOT undo DO migrations. If the previous version had fewer DO classes (or different ones), those bindings still exist at Cloudflare. Rollback is for worker code + config; the DO schema is monotonic.
If you're about to delete an agent and aren't sure
you'll need to rollback, keep the class and just stop
exposing it (remove its URL route via a middleware or an onRequest that returns 404). You can delete the
class safely later, once the new version has baked.
CI/CD
For continuous deploy from CI, you'll want to:
-
Provide a
CLOUDFLARE_API_TOKENsecret to your CI. Wrangler picks it up automatically. -
In CI, your checkout is always “clean,” but it's
also not in sync with origin/main in the same way a developer
checkout is.
ayjnt deploywill handle this fine as long as CI checks out the same commit asorigin/main; if your CI runs from a merge commit that doesn't match origin, use--forcein CI specifically (the whole point of the check is to catch developer-side races, which don't apply in CI).
# sketch — adjust to your CI
# - Set CLOUDFLARE_API_TOKEN as a repo secret
# - Run on push to main
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun test
- run: bun run build
- run: bun run deploy --force
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Running bun test and bun run build as
separate steps before deploy makes the CI output easier to read
when something breaks.
Observability
ayjnt generates observability: { enabled: true }
into wrangler config is not yet default — we may add it. For now,
to turn on Cloudflare's Workers observability, add it
manually via your own wrangler config overrides, or enable it from
the dashboard after first deploy. The Cloudflare dashboard shows
request counts, latency, DO storage usage, and logs for each
agent.