# CoDuck — Complete Documentation This file is the entire CoDuck documentation, concatenated. AI agents can fetch this once at session start and have full fluency in CoDuck's CLI and platform for the rest of the conversation. Generated: 2026-06-17T21:47:44.947Z Source: https://coduck.ai/docs CLI: npm install -g @coduckai/cli --- ## Table of contents ### Start - [Getting Started](#start-getting-started) ### Projects - [Import an existing project](#projects-projects-create-existing) - [Deploying](#projects-projects-deploy) ### Domains - [Custom domains](#domains-domains-custom-domains) ### Database - [Database](#database-database-overview) ### Email - [Sending email](#email-email-sending) ### Payments - [Stripe payments](#payments-payments-stripe-connect) ### SDK - [SDK overview](#sdk-sdk-overview) - [Auth](#sdk-sdk-auth) - [Forms](#sdk-sdk-forms) - [Storage](#sdk-sdk-storage) - [Analytics](#sdk-sdk-analytics) ### CLI - [Install the CLI](#cli-cli-install) - [Authentication](#cli-cli-auth) - [Commands reference](#cli-cli-commands) ### Reference - [coduck.json reference](#reference-reference-coduck-json) - [Runtime architecture](#reference-reference-runtime) ### Changelog - [Changelog](#changelog-changelog) --- ## Getting Started Source: https://coduck.ai/docs/raw/getting-started.md # Getting Started CoDuck is an AI-powered platform where you describe what you want to build in plain English and get a live, deployed full-stack app. Every project comes with hosting, a dedicated Postgres database, custom domains, email sending, Stripe payments, file storage, and automatic backups — all configured for you. ## Start a project 1. Sign up at [coduck.ai/signup](https://coduck.ai/signup) (or sign in at [coduck.ai/login](https://coduck.ai/login)). 2. On the home page, you'll see a prompt textarea with a rotating placeholder showing examples like *"Generate an online store for selling handmade products."* 3. Type what you want to build and hit enter. CoDuck's AI scaffolds the codebase, generates the UI and backend, and deploys it. The project opens at `https://app.coduck.ai/project/` with a chat-based editor where you can keep iterating. > [!TIP] > Be specific in your first prompt — mention the kind of app, key pages, and any integrations (auth, payments, email). The clearer the prompt, the closer the first generation is to what you want. ## What you can build One project gives you one container, one Postgres database, and one `.coduck.app` subdomain. From there: | Capability | What you get | Docs | |---|---|---| | Bring your own code | Import an existing Next.js, React, or Node project | [/docs/projects/create-existing](/docs/projects/create-existing) | | Deploying | Zero-downtime redeploys with auto SSL | [/docs/projects/deploy](/docs/projects/deploy) | | Custom domains | Point your own domain with one-step DNS verification | [/docs/domains/custom-domains](/docs/domains/custom-domains) | | Database | A dedicated Postgres database per project | [/docs/database/overview](/docs/database/overview) | | Sending email | Transactional email from your own verified domain | [/docs/email/sending](/docs/email/sending) | | Stripe payments | Stripe Connect — payouts go directly to your account | [/docs/payments/stripe-connect](/docs/payments/stripe-connect) | ## Manage your project Open `https://app.coduck.ai/project/` and you'll see three things side by side: the chat, the code editor, and the Cloud panel. The Cloud panel has the following tabs: | Tab | What it controls | |---|---| | Settings | Project name, framework, general config | | Hosting | Sub-tabs: **Domains**, **Env**, **Deploys**, **API Key** | | Analytics | Built-in traffic and event analytics | | Data | Your project's Postgres database — schema, rows, queries | | Logs | Live deploy and runtime logs | | Forms | Form submissions captured from your site | | Email | Transactional email, verified domains, send history | | Payments | Stripe Connect status and payouts | | Activity | Audit trail of changes to the project | To make changes, either edit through the chat (ask the AI to add a feature, change a layout, wire up a new endpoint) or use the Cloud panel for infrastructure-level controls. > [!NOTE] > The database lives under the **Data** tab. Domains, environment variables, deploys, and your project's API key all live under **Hosting**. ## For developers and AI agents CoDuck also ships a CLI (`@coduckai/cli`) for bringing existing code into the platform, scripting deploys, and letting AI assistants like Claude Code drive CoDuck end-to-end. See [/docs/cli/install](/docs/cli/install) to get set up. ## For AI agents reading these docs If you're an AI assistant working with CoDuck on a user's behalf, fetch one of these at session start for full fluency across every feature: - [/llms.txt](/llms.txt) — index of every doc with one-line summaries - [/llms-full.txt](/llms-full.txt) — every doc concatenated into a single file --- ## Import an existing project Source: https://coduck.ai/docs/raw/projects/create-existing.md # Creating a project A project on CoDuck is one container plus one private Postgres database plus one subdomain on `.coduck.app`. There are two ways to create one: describe what you want to build (the usual path), or bring code you already have. ## Describe what you want Sign in at https://coduck.ai. The home page has a prompt textarea — its placeholder rotates through example ideas like "Generate an online store for selling handmade products." Type what you want to build in plain English and submit. CoDuck's AI scaffolds a Next.js + Postgres app, generates the code, provisions the database, and opens the project in the editor. From there, the chat on the left lets you keep iterating — ask for a checkout flow, a new page, a schema change, and CoDuck edits the code in place. > [!NOTE] > Be specific in your first prompt. "An online store for handmade products with Stripe checkout, product reviews, and a seller dashboard" gives CoDuck enough to scaffold something real; "a store" does not. ## Bring existing code If you already have a Next.js, Vite, Express, or Nest app on disk, CoDuck adopts the folder as a project, provisions a database, and deploys it under a `.coduck.app` subdomain — keeping your source layout and tooling. ```bash npm install -g @coduckai/cli coduck login cd my-app coduck create-existing # auto-detects runtime + build/start from package.json coduck push # upload source coduck deploy # build + run ``` `create-existing` writes a [`coduck.json`](/docs/reference/coduck-json) at the repo root describing how to build and run your app. It auto-detects most fields; edit them and re-`push`/`deploy` to change how the deploy behaves (the file is re-read on every deploy). Flags like `--dir ./web`, `--start`, `--build`, `--pre-start`, and `--push --deploy` let you do it in one shot. > [!NOTE] > One CoDuck project = one container. If your repo has a separate frontend and backend in subdirectories, either wrap both into a single start command (e.g. with `concurrently`), or create two CoDuck projects and wire them together with an env var pointing the frontend at the backend's public URL. ### Gotchas when importing an existing app These trip people up most often — check them before your first deploy: - **Bind `process.env.PORT`.** CoDuck assigns the port; an app hardcoding `3000` fails the health check. The `port` in `coduck.json` is informational only. - **The database starts empty.** CoDuck provisions Postgres but not your tables. Run migrations in a `preStart` command (e.g. `prisma migrate deploy`, `drizzle-kit push`) or you'll hit `relation "…" does not exist`. See [Database](/docs/database/overview). - **Some env keys are reserved** (`DATABASE_URL`, `NODE_ENV`, `CODUCK_*`, …). Setting them is rejected; `coduck env import` skips them and imports the rest. List them with `coduck env reserved`. - **Heavy builds may need a bigger instance.** If the build OOMs on the default `small` tier, deploy with `coduck deploy --size large`. - **Monorepo?** If your app lives in a subdirectory, set `dir` and use `@coduckai/cli` ≥ 0.1.11 so the root `coduck.json` is delivered to the server. ## Inside a project After it's created, a project lives at `https://app.coduck.ai/project/`. The list of all your projects is at https://app.coduck.ai/projects. The project view has three regions: - **Chat (left).** Where you talk to the AI agent — describe changes, ask questions about the code, request new features. - **Editor (center).** The generated source tree, openable and editable. - **Cloud panel (right, or switchable).** Everything operational. Tabs: **Settings**, **Hosting** (with sub-tabs for Domains, Env, Deploys, API Key), **Analytics**, **Data**, **Logs**, **Forms**, **Email**, **Payments**, **Activity**. You can drive almost everything from chat or the Cloud panel — set an env var, attach a custom domain, inspect logs, browse the database — without leaving the project. ## What comes with each project | Feature | Notes | |---|---| | Container | 1.5 GB RAM, 1.0 CPU, runs as non-root | | Postgres database | Per-project, auto-provisioned, connection injected as `DATABASE_URL` | | Subdomain | `.coduck.app` with HTTPS automatic | | Backups | Nightly automatic + on-demand | Custom domains, environment variables, deploy history, and the rest are managed from the Hosting tab of the Cloud panel. ## Next - [Deploying](/docs/projects/deploy) - [Custom domains](/docs/domains/custom-domains) --- ## Deploying Source: https://coduck.ai/docs/raw/projects/deploy.md # Deploying A deploy is when your project's code stops being a folder and becomes a live container with a public URL. CoDuck handles Docker image building, Postgres provisioning, migration runs, nginx + TLS, and starting the container. First deploy takes ~30–60 seconds; redeploys are ~10 seconds. ## Deploy from the dashboard Open `https://app.coduck.ai/project/`, switch to the **Cloud panel → Hosting tab → Deploys sub-tab**, and click **Deploy**. Watch progress in the **Logs** tab (tabbed alongside Hosting in the Cloud panel). > [!TIP] > Keep the Logs tab open in another window while deploying. Build and runtime errors stream there in real time and surface faster than the deploy status will. ## What happens during a deploy Every command comes from your [`coduck.json`](/docs/reference/coduck-json), which is **re-read on every deploy** — edit it, `coduck push`, `coduck deploy`, and the new commands run. Each phase streams to the Logs tab live: 1. **Install** — your `install` command, or `npm install --include=dev --no-audit --no-fund` by default. 2. **Prisma generate** — only if `prisma/schema.prisma` declares models. 3. **Build** — your `build` command, or `npm run build` by default. Set `"build": ""` to skip. 4. **Schema bootstrap / pre-start** — if you have a Prisma schema with models and **no** `preStart`, CoDuck runs `prisma db push`. If you set a `preStart` (e.g. `prisma migrate deploy`), that runs instead and you own schema setup. 5. **Start** — your `start` command, or `npm start` by default. nginx is then reconfigured to route traffic to the container. The base image is Node 20 by default; Node 22 and static-site runtimes are available via `runtime` in `coduck.json`. See the [`coduck.json` reference](/docs/reference/coduck-json) for the full schema and field semantics. ## Instance size (resources) Containers come in three tiers. Default is `small`; pick a bigger one with `coduck deploy --size medium|large` or `"instanceSize"` in `coduck.json`. | Size | Memory | CPU | Plan required | |---|---|---|---| | `small` | 1.5 GB | 1.0 | any paid plan | | `medium` | 2.5 GB | 2.0 | Pro or Studio | | `large` | 4 GB | 3.0 | Studio | The Node build-heap ceiling auto-scales with the tier, so heavy client builds that OOM on `small` (three.js, remotion, recharts) usually succeed on `large`. A request above your plan is clamped down, and the deploy tells you. Other limits: restart policy **on-failure** (2 retries), runs as non-root (**uid 1001**), working directory **/app**. ## Environment variables Manage env vars in **Cloud panel → Hosting → Env sub-tab**. You can set, view (masked by default), update, and delete values. Changes take effect on the **next deploy**. A set of keys are reserved — CoDuck injects them on every deploy and rejects them on write. They include `DATABASE_URL`, `DIRECT_URL`, `PORT`, `NODE_ENV`, and everything under `CODUCK_*` / `NEXT_PUBLIC_CODUCK_*`. The full, authoritative list is `coduck env reserved`; see the [`coduck.json` reference](/docs/reference/coduck-json#reserved-environment-variables). Stripe keys are reserved only once you connect Stripe. `coduck env import .env` skips reserved/invalid keys (with a warning) and imports the rest — a `.env` containing `DATABASE_URL` won't abort the whole import. ## Stop, restart, tear down In **Cloud panel → Hosting → Deploys sub-tab** you have three controls: - **Stop** — stops the container. The database, URL, and env vars are preserved. - **Restart** — restarts the container with the same config and a fresh runtime state. - **Destroy** — removes the container, deletes the project's Postgres database, and removes the nginx vhost. **Irreversible** — take a backup first if you need the data. ## Custom domains and TLS Once deployed, your project is reachable at `.coduck.app` over HTTPS — nginx and a Let's Encrypt certificate are set up automatically on the first deploy. To attach your own domain, see [Custom domains](/docs/domains/custom-domains). ## Reading logs Open **Cloud panel → Logs tab**, or from the CLI: `coduck logs --follow`, with `--since 30m` and `--grep ` to narrow down. `coduck logs-digest` summarizes errors/warnings. ## When a deploy fails A failed deploy reports the specific cause (not a generic crash): - **"out of memory …"** — the build or app exceeded the instance's RAM. Retry on a bigger tier: `coduck deploy --size large` (or `medium`). Moving asset compilation / codegen into the build step also helps. - **"the build failed …"** — fix the error shown in the logs and redeploy. A common cause is a build-time package living in `devDependencies` — make sure your install step includes dev deps (the default does). - **"responding on :3000 but health-checking : …"** — your app hardcodes a port. Listen on `process.env.PORT` instead. - **"the app crashed after starting …"** — the start command exited; check `coduck logs`. Usually a missing env var or a failed DB connection (remember the [database starts empty](/docs/database/overview) — run migrations in `preStart`). ## For developers There's also a CLI (`@coduckai/cli`) for deploying from your terminal or from CI. See [CLI commands](/docs/cli/commands). ## Next - [Custom domains](/docs/domains/custom-domains) — attach `example.com` to your project. - [Database overview](/docs/database/overview) — what `DATABASE_URL` points at, and how to work with it. --- ## Custom domains Source: https://coduck.ai/docs/raw/domains/custom-domains.md # Custom domains Every project gets a `.coduck.app` subdomain with HTTPS automatic. You can also attach one or more domains you own. CoDuck handles DNS verification, SSL via Let's Encrypt, and routing. ## The lifecycle ``` add → verify ownership → verify routing → active ``` 1. **Add** registers the domain against the project and returns the DNS records you need to set on your registrar. 2. **Verify ownership** confirms you control the domain by looking up a TXT record CoDuck issued on `_coduck.`. 3. **Verify routing** confirms traffic for the domain reaches CoDuck by checking the apex `A` record or subdomain `CNAME`. 4. **Active** means both checks passed, Let's Encrypt has issued a certificate, and the per-project nginx vhost is serving traffic. ## Add a domain 1. Open your project at `https://app.coduck.ai/project/`. 2. Switch to the **Cloud** panel, open the **Hosting** tab, then the **Domains** sub-tab. 3. Click **Add domain** and enter the domain you want to attach. 4. CoDuck displays the DNS records you need to set on your registrar (Namecheap, Cloudflare, GoDaddy, etc.). 5. Set the records, then click **Verify** once DNS has propagated. > [!NOTE] > DNS changes can take anywhere from seconds to an hour to propagate, depending on your registrar's TTL. If verification fails immediately, wait a minute and try again. ## DNS records you need to set The exact values are shown when you add the domain. They follow this shape: | Phase | Type | Name | Value | |---|---|---|---| | Ownership | TXT | `_coduck.` | `coduck-verify=` (shown when you add the domain) | | Routing (subdomain) | CNAME | `app` (or whatever subdomain) | `.coduck.app` | | Routing (apex) | A | `@` | CoDuck's published IP (shown when you add) | The TXT record stays in place — CoDuck re-checks it periodically to keep the domain associated with your account. ## Apex vs subdomain - **Apex** (`example.com`) needs an `A` record pointing at CoDuck's IP. Some registrars (Cloudflare, DNSimple) also support `ALIAS` or `ANAME` records that emulate CNAMEs at the apex — those work too. - **Subdomain** (`app.example.com`) can use a `CNAME` pointing at `.coduck.app`. This is preferred because IP changes on CoDuck's side propagate automatically without you touching DNS. ## SSL Let's Encrypt issues a certificate automatically after routing is verified. There's no extra step on your end, and renewals are handled automatically before expiry. ## Managing existing domains Under **Hosting → Domains** you can see every domain attached to the project, along with its current status — `pending`, `verifying`, `active`, or `failed`. From the same view you can: - Set one domain as **primary** (the canonical redirect target for the project). - **Remove** a domain, which detaches it and tears down the nginx vhost. - **Transfer** a domain to a different project you own. ## Common failures - **DNS not propagated** — verification ran before your registrar published the record. Check with `dig TXT _coduck.example.com` or `dig example.com`, then retry once the record is live. - **Wrong record type at apex** — `CNAME` at an apex will be rejected by most registrars; use `A` (or `ALIAS`/`ANAME` if supported) instead. - **Record at the wrong host** — the TXT goes on `_coduck.`, not the apex. Some registrar UIs auto-append the domain; double-check the final hostname. - **Let's Encrypt rate limit** — if both verifications pass but SSL doesn't issue, you've likely hit Let's Encrypt's per-domain issuance limit. Wait an hour and re-verify. ## For developers If you'd rather script domain operations than click through the dashboard, the CoDuck CLI exposes the same flow. See [CLI commands](/docs/cli/commands) for the full reference. ## Next - [Deploying](/docs/projects/deploy) — what triggers a re-build vs. a config reload after a domain change. - [Sending email](/docs/email/sending) — email uses domain verification too, but with DKIM CNAMEs only and a separate flow from custom domains. --- ## Database Source: https://coduck.ai/docs/raw/database/overview.md # Database Every CoDuck project comes with its own private Postgres database. It's provisioned automatically on first deploy, and the connection string is injected into your container as `DATABASE_URL` (pooled) and `DIRECT_URL` (direct, for migrations). > [!IMPORTANT] > The database is provisioned **empty** — CoDuck creates the database, not your > tables. You must create your schema yourself on deploy (see below). An app that > assumes tables already exist will fail with `relation "…" does not exist`. ## How it works On first deploy, CoDuck: 1. Creates a Postgres database dedicated to your project. 2. Provisions a dedicated database user with permissions scoped to that DB only. 3. Injects `DATABASE_URL` (PgBouncer at `127.0.0.1:6432` on the VPS loopback, transaction-pool mode with `max_prepared_statements=100`) and `DIRECT_URL` (postgres directly at `127.0.0.1:5433`, used only by Prisma's schema engine for `prisma db push` / `prisma migrate deploy`) — both reserved; you can't override them. See [Runtime architecture](/docs/reference/runtime) for the full container + networking model. Subsequent deploys reuse the same database. Your app reads `process.env.DATABASE_URL` like any normal Postgres host — Prisma, Drizzle, Kysely, `node-postgres`, `pg`, `knex`, and `sequelize` all work against it. The [CoDuck Auth SDK](/docs/auth/overview) (`@coduckai/sdk/auth`) uses the same database to store users. ## Schema and migrations How CoDuck applies your schema depends on whether you use Prisma and whether you've set a `preStart`: - **Prisma, no `preStart`** — if `prisma/schema.prisma` declares `model`s, CoDuck runs `prisma db push` on each deploy to sync the schema (additive; it preserves tables it doesn't know about). This is the zero-config path. - **You set a `preStart`** — CoDuck does **not** auto-push; your `preStart` owns schema setup. Use this for real migration files: `"preStart": "prisma migrate deploy"`. - **Not using Prisma** — set a `preStart` that runs your migration tool, e.g. `"preStart": "drizzle-kit push"` or `"preStart": "node scripts/migrate.js"`. `preStart` runs after install + build, before the app starts, on every deploy — see the [`coduck.json` reference](/docs/reference/coduck-json). For migrations, prefer `DIRECT_URL` over `DATABASE_URL` (Prisma's schema engine doesn't go through PgBouncer). ## Inspect your data Open your project at `https://app.coduck.ai/project/`, switch to the Cloud panel, then the **Data** tab. You'll see your schema, every table, and recent rows. The view is read-only — to write data, use your application code. ## Backups Backups run automatically every night (cluster-level physical backups via pgBackRest, managed by CoDuck). You can also create one on demand from the Cloud panel → **Data** tab, in the Backups section. On-demand backups use `pg_dump` so you get a portable SQL export of just your project's database. Restoring is destructive and asks for confirmation before overwriting your current DB. ## Why there's no raw connection URL > [!NOTE] > CoDuck intentionally does not expose a raw `postgres://...` URL for external connections. The cluster is multi-tenant Postgres behind PgBouncer — a raw URL would let arbitrary external SQL bypass your application's logic and logging, can't be scoped to read-only or per-table, and can't be revoked per-session. > > For analytical or one-off queries, add a temporary endpoint in your app that uses its own `DATABASE_URL` — you get auth, logging, and review for free. For pulling data out of the system, use backups. ## Connection pooling PgBouncer handles connection pooling on the server side, so a single project doesn't need a large client-side pool. Inside your app, set your client's `max_connections` to `10` or lower. Opening too many direct connections will get throttled. ## Limits | Resource | Limit | |---|---| | Storage | 1 GB per project DB (soft; raisable on paid plans) | | Connections | PgBouncer-pooled; keep app pool ≤ 10 | | Compute | Shared with the cluster; no separate DB CPU billing | ## Common failures - **`relation "…" does not exist` at runtime.** The database starts empty and your schema never got created. Make sure a Prisma schema with models is present (CoDuck runs `prisma db push`), or set a `preStart` that runs your migrations. - **Migration/`db push` fails on deploy.** Check the deploy logs. A destructive change (dropping a column with data) is blocked by `prisma db push` without `--accept-data-loss`; switch to a real migration (`preStart: "prisma migrate deploy"`). - **`bouncer config error` on every query.** PgBouncer can't authenticate connections to your DB. Almost always caused by running `DROP SCHEMA public CASCADE` (or equivalent) from your app, which destroys the `public.user_lookup()` function PgBouncer needs and the `USAGE` grant to `pgbouncer_auth`. Contact support to restore them. To migrate, drop and recreate your own tables instead of nuking `public`. - **Connection pool exhausted.** Symptom is `too many clients` or hung requests under load. Lower `max_connections` in your client pool — PgBouncer is already pooling on the server side, so your app should open very few direct connections. - **Schema mismatch at runtime.** Your code expects a column the DB doesn't have — a migration wasn't applied. CoDuck auto-runs `prisma generate` when your schema has models, so you don't need it in your build step; the usual cause is an uncommitted schema change. ## For developers If you'd rather work from a terminal, the CoDuck CLI exposes read-only schema/table inspection and backup management for your project. See [CLI commands](/docs/cli/commands). ## Next - [Deploying](/docs/projects/deploy) — when migrations run, deploy lifecycle. - [Sending email](/docs/email/sending) — the other auto-provisioned service. --- ## Sending email Source: https://coduck.ai/docs/raw/email/sending.md # Sending email CoDuck includes transactional email — verify a domain you own and send mail from it. CoDuck handles delivery, bounce processing, and a suppression list automatically. Backed by Amazon SES; you don't need an SES account. **Available on Pro and Studio plans only.** ## Why verified domains Modern email providers reject mail without proper SPF and DKIM — you can't just send from `from@yourdomain.com` without proving you own `yourdomain.com`. CoDuck generates the DKIM records via SES; you set them on your registrar; verification confirms it worked. ## Add a sending domain 1. Open your project at `https://app.coduck.ai/project/`. 2. Switch to the **Cloud** panel and open the **Email** tab. 3. Click **Add domain** and enter your domain (e.g. `example.com`). 4. CoDuck shows the DKIM CNAME records (typically 3) to set on your registrar. Add them. 5. Once DNS has propagated, click **Verify**. Sending becomes available immediately after verification passes. > [!NOTE] > This is separate from the custom-domain verification used to serve your project's website. Verifying a domain for email only authorizes it for sending mail. ## Send a message Three ways to send: the dashboard, the CLI, or your app's own code. **From the dashboard.** Open the **Email** tab and use the **Send test** composer. Set the From (must be a verified domain or the fallback `noreply@.coduck.app` — see [The coduck.app fallback address](#the-coduckapp-fallback-address)), To, subject, and body. **From your app's code.** Send with a server-side HTTP request to the email endpoint. `CODUCK_API_KEY` and `CODUCK_API_URL` are both injected into your project's environment at deploy time, so no configuration is needed. This is exactly what CoDuck's AI builder generates for you. > [!IMPORTANT] > Sending is **server-only**. Never expose `CODUCK_API_KEY` to the browser — call the endpoint from a route handler, server action, or API route, never from a client component. It also only works once the project is deployed: the key is not present in the in-editor preview. ```ts // e.g. app/api/invite/route.ts (Next.js App Router) export async function POST(req: Request) { const { to } = await req.json(); const res = await fetch(`${process.env.CODUCK_API_URL}/email/send`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.CODUCK_API_KEY}`, }, body: JSON.stringify({ from: `noreply@${process.env.NEXT_PUBLIC_CODUCK_PROJECT_SUBDOMAIN}.coduck.app`, to, // single recipient subject: "Welcome", // 1–998 chars html: "

Thanks for signing up.

", text: "Thanks for signing up.", // html and/or text — at least one replyTo: "support@example.com", // optional }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); // err.error is a stable code (see Error responses); err.message is human-readable. return Response.json({ error: err.error ?? "send_failed" }, { status: res.status }); } const { messageId } = await res.json(); // 202 → { ok: true, messageId } return Response.json({ messageId }); } ``` > [!NOTE] > There is no `@coduckai/sdk` email helper today — send via the HTTP endpoint above. A typed SDK wrapper may land later, but the endpoint is the supported, stable interface and won't change underneath you. ### The coduck.app fallback address If you haven't verified a custom sending domain yet, paid projects can send from `noreply@.coduck.app`. `` is your project's **deployment subdomain** — the exact value in your project's URL (`https://.coduck.app`). It is injected into your app as `NEXT_PUBLIC_CODUCK_PROJECT_SUBDOMAIN`, so build the address from that rather than hardcoding it: ```ts const from = `noreply@${process.env.NEXT_PUBLIC_CODUCK_PROJECT_SUBDOMAIN}.coduck.app`; ``` The local part (`noreply`, `hello`, …) is yours to choose; only the domain is validated, and it must match your project's own subdomain exactly — you cannot send from another project's subdomain. Once you verify your own domain, prefer sending from it for better deliverability and branding. ### Error responses Non-2xx responses carry a stable `error` code plus a human-readable `message`. The ones to handle: | Status | `error` | Meaning | |---|---|---| | 402 | `email_requires_paid_plan` | Project owner is on Free — email is Pro/Studio only. Don't retry. | | 402 | `quota_exceeded` | Monthly quota reached. | | 403 | `from_not_allowed` | `from` isn't a verified domain or your `coduck.app` subdomain. | | 413 | `body_too_large` | HTML + text exceed 256 KB combined. | | 422 | `recipient_suppressed` | Recipient previously bounced or complained. Don't auto-retry. | | 429 | `rate_limited` / `new_account_cooldown` | Back off and retry; body includes `retryAfterSec` where applicable. | | 503 | `project_paused` | Sending is paused (SES policy). Resume from the **Email** tab. | **From the CLI.** For terminal or CI use, see [`coduck email send`](/docs/cli/commands). ## Suppressions Hard bounces, spam complaints, and unsubscribes are automatically added to your project's per-project suppression list. Subsequent sends to those addresses fail with a 422 without ever hitting SES — this protects your sender reputation. Manage the list from **Cloud panel → Email tab → Suppressions**. > [!NOTE] > Removing a suppressed address and re-sending to them is the fastest way to wreck your sender reputation — only do it when you've confirmed the suppression was a mistake. ## Pausing High bounce or complaint rates auto-pause your project's sending. Resume from the **Email** tab once you've fixed the underlying issue (bad list, broken signup form, abuse from a compromised account). ## Quotas | Plan | Included / month | Overage | |---|---|---| | Free | not available | — | | Pro ($20/mo) | 5,000 | not available yet | | Studio ($200/mo) | 50,000 | not available yet | Quotas reset on the 1st of each month UTC. Sends past the monthly quota return a 402 error (`quota_exceeded`). Sends from a free-tier project return a 402 (`email_requires_paid_plan`). ## Limits | Limit | Value | |---|---| | Rate | 50/min, 500/hour per project | | Recipients per send | 1 | | Body size | 256 KB combined HTML + text | | Attachments | not supported yet | | Use case | transactional only — not for newsletters or marketing | For bulk marketing or newsletters, use a dedicated provider. CoDuck email is for receipts, password resets, alerts, and similar transactional traffic. ## Common failures - **Verification fails after setting records** — DNS hasn't propagated. Wait 5-10 minutes and retry. - **"From address not allowed"** — the From domain isn't verified yet. Check status in the **Email** tab. - **Mail lands in spam** — also add a DMARC record to your domain. CoDuck only handles SPF and DKIM; DMARC is on you. ## For developers A command-line interface is available for sending mail and managing suppressions from a terminal or CI environment. See [/docs/cli/commands](/docs/cli/commands). ## Next - [Deploy your project](/docs/projects/deploy) - [Custom domains](/docs/domains/custom-domains) --- ## Stripe payments Source: https://coduck.ai/docs/raw/payments/stripe-connect.md # Stripe payments CoDuck integrates with [Stripe Connect](https://stripe.com/connect) so your apps can accept payments. Money goes directly to your Stripe account — CoDuck never touches it. **Available on Pro and Studio plans.** This is BYOS ("Bring Your Own Stripe"): sign in once, your app gets credentials, Stripe handles the rest. CoDuck takes 0% of your revenue. ## Connect your Stripe account 1. Open your project at `https://app.coduck.ai/project/`. 2. Switch to the **Cloud** panel and select the **Payments** tab. 3. Click **Connect Stripe**. A Stripe OAuth window opens. 4. Sign in to Stripe (or create an account) and approve the connection. 5. CoDuck stores the connection. On the next deploy, the keys are injected into your container. ## What gets injected When a Stripe account is connected, the next deploy injects these env vars into your container: | Variable | Description | |---|---| | `STRIPE_SECRET_KEY` | Restricted API key for the connected account (auto-rotated on refresh) | | `STRIPE_PUBLISHABLE_KEY` | Public key for the connected account | | `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Same publishable key, prefixed for client-side access in Next.js | These are reserved — you can't override them in the **Env** sub-tab. ## Charging in your code Two patterns work — pick whichever you prefer. ### CoDuck SDK (recommended) The SDK reads `STRIPE_SECRET_KEY` from the environment automatically: ```ts import { stripe } from "@coduckai/sdk/payments"; const session = await stripe.checkout.sessions.create({ line_items: [{ price: "price_xxx", quantity: 1 }], mode: "payment", success_url: "https://example.com/success", cancel_url: "https://example.com/cancel", }); ``` ### Raw `stripe` npm package Works the same way — instantiate with `process.env.STRIPE_SECRET_KEY`. Useful if you want full TypeScript types for every Stripe API surface. ```ts import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); const session = await stripe.checkout.sessions.create({ line_items: [{ price: "price_xxx", quantity: 1 }], mode: "payment", success_url: "https://example.com/success", cancel_url: "https://example.com/cancel", }); ``` ## Webhooks to your app Stripe webhooks (`charge.succeeded`, `customer.subscription.updated`, etc.) go directly to your app via the webhook endpoint you configure in your Stripe dashboard. 1. In the Stripe dashboard, add an endpoint pointing at your deployed app — e.g. `https://.coduck.app/api/stripe/webhook`. 2. Copy the signing secret Stripe gives you. 3. In CoDuck, set it under **Cloud panel → Hosting → Env** as `STRIPE_WEBHOOK_SECRET`. (This one is user-set, not auto-injected.) A minimal Next.js Route Handler that verifies the signature with the raw body and the `stripe-signature` header: ```ts // app/api/stripe/webhook/route.ts import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); export async function POST(req: Request) { const sig = req.headers.get("stripe-signature")!; const body = await req.text(); // raw body required for verification const event = stripe.webhooks.constructEvent( body, sig, process.env.STRIPE_WEBHOOK_SECRET!, ); switch (event.type) { case "charge.succeeded": // ... break; } return new Response("ok"); } ``` Note: CoDuck handles connection-level events (e.g. account deauthorized) on its own platform webhook — you don't have to set those up. ## Analytics View stats, transactions, a revenue chart, and connection health under **Cloud panel → Payments**. ## Status The connection has three states, shown in the **Payments** tab: - `connected` — working normally. - `needs_reconnect` — Stripe returned 401 on a recent call. Re-run the OAuth flow to refresh the token. - `revoked` — you or Stripe deauthorized the connection. Reconnect to restore. ## Disconnect In **Cloud panel → Payments**, click **Disconnect Stripe**. This revokes CoDuck's OAuth access and removes the injected env vars on the next deploy. Your Stripe account itself is untouched. ## Notes > [!NOTE] > CoDuck takes 0% of your revenue. The Stripe connection is included in your Pro or Studio plan. - One Stripe account per CoDuck project. Different projects can connect to different accounts. - Refunds, subscriptions, and disputes are handled through the standard Stripe API using the injected key. CoDuck doesn't proxy them. ## Common failures - **"Connect Stripe" returns `upgrade_required`** — you're on the Free plan. Upgrade to Pro or Studio first, then retry the OAuth flow. - **"No Stripe account connected"** when calling the SDK at runtime — you connected but haven't redeployed yet, so the env vars aren't in the container. Redeploy to pick them up. - **Status flipped to `needs_reconnect`** — Stripe returned 401 on a recent call. Re-run the OAuth flow from the Payments tab. ## For developers A CLI is also available for connecting Stripe, checking status, and viewing payments from the terminal — see [CLI commands](/docs/cli/commands). ## Next - [Deploying a project](/docs/projects/deploy) — when injected env vars take effect - [Database overview](/docs/database/overview) — the other half of a typical paid app --- ## SDK overview Source: https://coduck.ai/docs/raw/sdk/overview.md # SDK overview `@coduck/sdk` is the official SDK for apps deployed on CoDuck. It gives your project **auth, forms, file storage, and analytics** backed by the platform — no separate backend to stand up. When CoDuck generates and deploys your project, the SDK is already installed and configured. You just import the module you need. ## Install In a CoDuck-deployed project the SDK and its env vars are wired up automatically. To add it to a project by hand: ```bash npm install @coduck/sdk ``` ## How it configures itself At deploy time CoDuck injects these env vars, and the SDK reads them automatically — you rarely construct a client yourself: | Variable | Scope | Purpose | |---|---|---| | `CODUCK_API_KEY` | server only | Authenticates server-side calls (forms read, storage, analytics, project meta) | | `CODUCK_AUTH_KEY` | server only | Tenant key for auth. **Only injected on deploy** — auth does not work in the in-editor preview | | `CODUCK_PROJECT_ID` / `NEXT_PUBLIC_CODUCK_PROJECT_ID` | server / browser | Identifies your project | | `CODUCK_API_URL` / `NEXT_PUBLIC_CODUCK_API_URL` | server / browser | SDK API base (defaults to `https://api.coduck.ai/sdk`) | > **Server vs. browser.** Most SDK calls require `CODUCK_API_KEY`, so they must run on the server (route handlers, server actions, server components). The only browser-safe call is `forms.submit()`. Auth additionally needs `CODUCK_AUTH_KEY`, which means **sign-in only works once the project is deployed**, not in the live preview. ## The client ```ts import { coduck } from '@coduck/sdk'; const meta = await coduck.meta(); // → { projectId, projectName, deploymentSubdomain, deploymentStatus, apiKeyPrefix } ``` Or construct one explicitly (tests, scripts, non-Next runtimes): ```ts import { CoDuck } from '@coduck/sdk'; const c = new CoDuck({ apiKey: process.env.CODUCK_API_KEY, apiUrl: 'https://api.coduck.ai/sdk', projectId: process.env.CODUCK_PROJECT_ID, }); ``` ## Modules | Import | What it does | |---|---| | [`@coduck/sdk/server`](/docs/sdk/auth#server-helpers) | Cookie-based auth for Next.js server code | | [`@coduck/sdk/client`](/docs/sdk/auth#client-hook) | `useUser()` React hook | | [`@coduck/sdk/auth`](/docs/sdk/auth#low-level-client) | Low-level auth client (tokens) | | [`@coduck/sdk/forms`](/docs/sdk/forms) | Capture + read form submissions | | [`@coduck/sdk/storage`](/docs/sdk/storage) | Per-project file storage | | [`@coduck/sdk/analytics`](/docs/sdk/analytics) | Custom event tracking | ## Errors SDK calls throw `CoDuckError` (with a `code`) on failure — e.g. `NO_API_KEY`, `NO_AUTH_KEY`, `NO_PROJECT_ID`, or `HTTP_`: ```ts import { CoDuckError } from '@coduck/sdk'; try { await coduck.meta(); } catch (err) { if (err instanceof CoDuckError) console.error(err.code, err.message); } ``` --- ## Auth Source: https://coduck.ai/docs/raw/sdk/auth.md # Auth Email/password authentication for your deployed app. Sessions are stored in an httpOnly cookie and managed by server helpers; the client gets a small `useUser()` hook. > **Deploy required.** Auth needs `CODUCK_AUTH_KEY`, which CoDuck only injects when the project is **deployed**. Sign-in will not work in the in-editor preview — deploy first. ## Server helpers `@coduck/sdk/server` wraps the cookie + session handling for Next.js (App Router). Use it in route handlers and server actions. ```ts import { getSession, signInWithPassword, signUpWithPassword, signOut, } from '@coduck/sdk/server'; ``` | Function | Returns | Notes | |---|---|---| | `getSession()` | `{ user } \| null` | Reads + verifies the session cookie | | `signUpWithPassword(email, password, name?)` | `AuthSession` | Creates the user **and** sets the cookie | | `signInWithPassword(email, password)` | `AuthSession` | Logs in **and** sets the cookie | | `signOut()` | `void` | Clears the cookie + invalidates the refresh token | ### Sign in (server action) ```ts 'use server'; import { signInWithPassword } from '@coduck/sdk/server'; import { redirect } from 'next/navigation'; export async function login(formData: FormData) { await signInWithPassword( String(formData.get('email')), String(formData.get('password')), ); redirect('/dashboard'); } ``` ### Protect a server component ```tsx import { getSession } from '@coduck/sdk/server'; import { redirect } from 'next/navigation'; export default async function Dashboard() { const session = await getSession(); if (!session) redirect('/login'); return

Welcome, {session.user.email}

; } ``` ### The `/api/me` route The client hook reads the session from your own app via `GET /api/me`. Expose it with the server helper so the browser never touches the cookie directly: ```ts // app/api/me/route.ts import { getSession } from '@coduck/sdk/server'; export async function GET() { const session = await getSession(); return Response.json(session ?? { user: null }); } ``` ## Client hook `@coduck/sdk/client` gives you the current user in a client component. It fetches `/api/me` (above). ```tsx 'use client'; import { useUser } from '@coduck/sdk/client'; export function UserBadge() { const { user, loading, refresh } = useUser(); // useUser('/api/me') by default if (loading) return null; if (!user) return Sign in; return {user.email}; } ``` `useUser()` returns `{ user: AuthUser | null, loading: boolean, refresh: () => Promise }`. ## Low-level client For full control over tokens (custom flows, non-cookie clients), `@coduck/sdk/auth` exposes the raw client: ```ts import { auth } from '@coduck/sdk/auth'; const session = await auth.login({ email, password }); // → { user, accessToken, refreshToken, expiresIn } const user = await auth.me(session.accessToken); const renewed = await auth.refresh(session.refreshToken); await auth.logout(session.accessToken); ``` | Method | Returns | |---|---| | `signup({ email, password, name? })` | `AuthSession` | | `login({ email, password })` | `AuthSession` | | `refresh(refreshToken)` | `AuthSession` | | `verify(accessToken)` | `AuthUser` | | `me(accessToken)` | `AuthUser` | | `logout(accessToken)` | `void` | ## Types ```ts interface AuthUser { id: string; email: string; name?: string | null; createdAt: string; } interface AuthSession { user: AuthUser; accessToken: string; refreshToken: string; expiresIn: number; // seconds until the access token expires } ``` --- ## Forms Source: https://coduck.ai/docs/raw/sdk/forms.md # Forms Capture submissions from any form (contact, waitlist, feedback) without writing a backend. Submitting is browser-safe; reading requires the API key, so it runs on the server. ```ts import { forms } from '@coduck/sdk/forms'; ``` ## Submit (browser-safe) `submit()` is public — call it from a client component. It only needs `NEXT_PUBLIC_CODUCK_PROJECT_ID` (injected at deploy). ```tsx 'use client'; import { forms } from '@coduck/sdk/forms'; async function onSubmit(e: React.FormEvent) { e.preventDefault(); const data = new FormData(e.currentTarget); await forms.submit('contact', { name: data.get('name'), email: data.get('email'), message: data.get('message'), }); } ``` You can also import the bound helper directly: ```ts import { submit } from '@coduck/sdk/forms'; await submit('waitlist', { email }); ``` The `formName` is just a label you choose — submissions are grouped by it. ## Read submissions (server only) `list()` and `markRead()` require `CODUCK_API_KEY`, so call them from a route handler or server component. ```ts import { forms } from '@coduck/sdk/forms'; const recent = await forms.list({ formName: 'contact', limit: 50 }); await forms.markRead(recent[0].id); ``` | Method | Scope | Returns | |---|---|---| | `submit(formName, fields)` | browser-safe | `void` | | `list({ formName?, limit? })` | server | `FormSubmission[]` | | `markRead(id)` | server | `FormSubmission` | Submissions also show up in your project's **Forms** tab in the cloud panel. ## Type ```ts interface FormSubmission { id: string; projectId: string; formName: string; fields: Record; submitterIp: string | null; userAgent: string | null; read: boolean; createdAt: string; } ``` --- ## Storage Source: https://coduck.ai/docs/raw/sdk/storage.md # Storage Per-project file storage for uploads, generated assets, exports — anything your app needs to keep. All methods require `CODUCK_API_KEY`, so use storage from the server. ```ts import { storage } from '@coduck/sdk/storage'; ``` ## Upload `file` can be a `Blob`, `Buffer`, or `Uint8Array`. Max **50 MB** per file. Names may contain letters, digits, and `+ . _ -`. ```ts // e.g. inside a route handler const form = await request.formData(); const file = form.get('file') as File; const saved = await storage.upload(file.name, file); // → { name, size, modifiedAt } ``` ## List, link, delete ```ts const files = await storage.list(); // StoredFile[] const href = storage.url('logo.png'); // direct fetch URL (needs API key header) await storage.delete('logo.png'); ``` | Method | Returns | |---|---| | `list()` | `StoredFile[]` | | `upload(name, file)` | `StoredFile` | | `delete(name)` | `void` | | `url(name)` | `string` | > `url(name)` returns a direct link, but fetching it requires the API key in the `Authorization` header — so serve files through your own route rather than exposing the raw URL to the browser. ## Type ```ts interface StoredFile { name: string; size: number; // bytes modifiedAt: string; } ``` --- ## Analytics Source: https://coduck.ai/docs/raw/sdk/analytics.md # Analytics Track custom events — signups, purchases, feature usage — alongside the pageview data CoDuck already collects for your project. ```ts import { analytics } from '@coduck/sdk/analytics'; await analytics.track('signup_completed', { plan: 'pro' }); ``` Or the bound helper: ```ts import { track } from '@coduck/sdk/analytics'; await track('checkout_started', { cartValue: 49.0 }); ``` | Method | Returns | |---|---| | `track(name, properties?)` | `void` | `properties` is an optional `Record` of metadata for the event. > Requires `CODUCK_API_KEY`, so call `track()` from the server (route handler or server action). **Pageviews are recorded automatically** — reach for `track()` only for the custom events that matter to your product. --- ## Install the CLI Source: https://coduck.ai/docs/raw/cli/install.md # Install the CLI The CoDuck CLI ships on npm as **`@coduckai/cli`**. It's the same binary used by humans in their terminal and by AI agents calling into CoDuck from their host. ## Requirements - **Node.js 20** or newer (we use modern fetch + native `Set` semantics) - A POSIX shell (`bash` / `zsh` / `fish`) or PowerShell - macOS, Linux, or Windows ## Global install (recommended) ```bash npm install -g @coduckai/cli ``` Verify: ```bash coduck --version ``` You should see a JSON line with the current version and the API base URL. If the `coduck` command isn't found, your `npm` global bin isn't on `$PATH` — see Troubleshooting below. ## After installing Three commands to get going: ```bash coduck login # browser opens, click Approve coduck create-existing # in your project folder — creates a coduck.json coduck chat # opens an interactive chat with the AI agent ``` That's it — you're now operating a CoDuck project from your terminal. ## One-off / no global install ```bash npx @coduckai/cli@latest ``` Useful in CI or on someone else's machine. Slower per invocation because npm has to resolve the package each time. ## Updating ```bash npm install -g @coduckai/cli@latest ``` The CLI **does not auto-update**. If you want to know what changed between versions, see the changelog on the [npm page](https://www.npmjs.com/package/@coduckai/cli). ## Uninstall ```bash npm uninstall -g @coduckai/cli rm -rf ~/.coduck ``` The second command removes your stored credentials and any `coduck.json`-cached state. (Your remote CoDuck account is untouched. To delete that, sign in to [coduck.ai](https://coduck.ai) and use account settings.) ## Output modes The CLI behaves differently depending on whether its stdout is a terminal: - **Interactive (TTY)** — pretty tables, colors, spinners, prompts - **Piped (non-TTY)** — pure JSON, no colors, no prompts, exit codes carry the meaning Force machine mode at any time with `--json`. Force "fail instead of prompting" with `--no-input`. Both can be combined for use inside agent loops. ```bash coduck projects --json | jq '.[].id' coduck deploy --no-input --json ``` ## Troubleshooting ### `coduck: command not found` Your npm global bin directory isn't on `$PATH`. Find it with: ```bash npm config get prefix ``` The binary lives in `/bin`. Add that to your `$PATH` in your shell config (`~/.zshrc`, `~/.bashrc`, etc.). ### `Cannot find module ...node_modules\coduck-cli\...` on Windows You have a stale shim from a previous attempted install of a different package name. Run these in Command Prompt: ```cmd npm uninstall -g coduck-cli npm uninstall -g @coduckai/cli del "%APPDATA%\npm\coduck.cmd" del "%APPDATA%\npm\coduck.ps1" del "%APPDATA%\npm\coduck" npm install -g @coduckai/cli@latest ``` The `del` commands may say "could not find" for some files — that's fine, keep going. Last command installs fresh. ### `EACCES: permission denied` during install You're using a system Node where `npm install -g` needs sudo. Don't `sudo npm install` — instead, switch to a user-level Node manager: - macOS / Linux: [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm) - Windows: [nvm-windows](https://github.com/coreybutler/nvm-windows) Then re-run `npm install -g @coduckai/cli` without sudo. ### `EBADENGINE` or "unsupported engine" Your Node version is below 20. Upgrade Node. ## What's installed The package contains: - `bin/coduck.js` — the entry point (calls into compiled JS) - `dist/` — compiled TypeScript output (~30 KB) - Runtime deps: `commander`, `chalk`, `@inquirer/prompts`, `cli-table3`, `ora`, `form-data` No native modules, no postinstall scripts, no telemetry. ## Next You're ready to [sign in](/docs/cli/auth). --- ## Authentication Source: https://coduck.ai/docs/raw/cli/auth.md # Authentication The CLI uses **browser-based device authorization**. You never type your CoDuck password into your terminal. Approval happens on a CoDuck web page that you're already signed in to. ## Signing in ```bash coduck login ``` What happens, step by step: 1. CLI prints a short confirmation code like `AB7K-9XQM` and opens your browser to `https://app.coduck.ai/cli?code=AB7K-9XQM`. 2. If you're not signed in to the web app, you're sent to `/login` first, then bounced back. 3. The page shows the device the CLI is asking for ("your hostname / your OS") and an Approve / Deny button. 4. You click **Approve**. 5. Within ~5 seconds, the CLI receives the token and writes it to `~/.coduck/credentials.json` (file mode `0600`). That's it. From this point on, every CoDuck command works without prompting. > [!NOTE] > The CLI has no `--password` flag and never reads your password from stdin or an env var. If you're seeing a doc anywhere that mentions one, it's outdated. ## Token lifetime | What | How long | |---|---| | Initial token | 90 days | | Sliding extension on each use | +30 days, capped at 180 from issue | | Hard maximum lifetime | 180 days from creation | | Idle expiry | 90 days of no use | In practice: if you use the CLI even occasionally, you'll never get logged out. If you stop using it for 90 days, your next call will 401 and you'll need to `coduck login` again. ## Managing tokens Every device gets its own token. You can have many — one per laptop, one for CI, etc. ### List your tokens ```bash coduck token list ``` Shows each token with its device name, last-used timestamp, last IP, and expiration. ### Revoke a single token ```bash coduck token revoke ``` Takes effect within ~30 seconds (the API caches the revocation list for performance). ### Revoke every token on your account ```bash coduck logout --all ``` Use this if you think a token might have been exposed. Your web session is untouched. ### Sign out from just this machine ```bash coduck logout ``` Revokes only the current device's token and clears `~/.coduck/credentials.json`. Other devices keep working. ## Where your credentials live ``` ~/.coduck/ └── credentials.json (mode 0600) ``` The file contains your token, the API URL, your user ID, and your email. **Don't share it.** Anyone with this file has full account access until the token expires or you revoke it. If you accidentally print or share it, run `coduck logout --all` immediately and `coduck login` to issue a fresh token. ## Inspecting the current token ```bash coduck token status # plan + credits, no token printed coduck whoami # who you're signed in as coduck token show --confirm # actually print the token (refuses without --confirm) ``` The `--confirm` requirement on `show` is intentional: by default it refuses to leak the token to stdout, where it could end up in shell history or someone else's screen. ## For CI / GitHub Actions `npm login` doesn't work in CI. The token-based flow is what you want anyway: 1. Run `coduck login` once on your local machine. 2. `cat ~/.coduck/credentials.json` — copy the `token` field. 3. Add it to your CI secrets as `CODUCK_TOKEN`. 4. In CI: ```yaml - run: npm install -g @coduckai/cli - run: | mkdir -p ~/.coduck cat > ~/.coduck/credentials.json < ## Commands reference Source: https://coduck.ai/docs/raw/cli/commands.md # Commands reference Every CoDuck CLI command, grouped by what it operates on. All commands honor `--json` (machine-readable output), `--no-input` (fail instead of prompting), and real exit codes (0 ok, 2 usage, 3 auth, 4 not found, 5 conflict, 6 network, 7 server — full table at the bottom). For installation, see [Install the CLI](/docs/cli/install). For sign-in, see [Authentication](/docs/cli/auth). ## Chat with the agent Send a prompt to a project's AI agent, stream the response. Designed for AI agents calling CoDuck programmatically — JSON-first, NDJSON streaming, no interactive prompts. ```bash coduck chat "add a login page" # streams response, exits coduck chat "add a login page" --json # NDJSON output for agents coduck chat "add a login page" --project # override coduck.json coduck chat --history # print recent messages coduck chat --history --json # same, as JSON ``` `coduck ask "..."` is an alias for `coduck chat "..."`. ### NDJSON event types (what an agent sees with `--json`) | event | what it means | |---|---| | `chunk` (type=`ttft`) | First token arrived; includes time-to-first-token in ms | | `chunk` (type=`text` or `response`) | Streaming response text | | `chunk` (type=`tool_call`) | Agent called a tool: `tool`, `path`, args | | `chunk` (type=`code`) | Wrote/edited a file: `path`, `content` | | `chunk` (type=`preview_url`) | Sandbox boot, ephemeral preview URL | | `chunk` (type=`usage`) | Tokens used in this round | | `chunk` (type=`complete`) | Generation finished server-side; cost, file list | | `complete` | Final summary (mirror of last chunk) | | `done` | CLI-side wrap-up event with totals | | `paywall` | Insufficient credits; includes plan + upgrade hint | | `error` | Failure; includes `message` | ## Projects ```bash coduck projects # list your projects coduck project # show one project's details coduck create --name # create a new AI-scaffolded project coduck create-existing # import the current directory as a project coduck rename # rename a project coduck delete --yes # delete a project (irreversible) coduck pause # pause (stops billing, keeps state) coduck resume # resume a paused project ``` ## Files ```bash coduck push # upload local files (honors .gitignore + .coduckignore) coduck push --dry-run # show file count, total size, biggest dirs — no upload coduck push --force # push even if the server has newer files coduck pull # download project files locally coduck files ls # list remote files coduck files cat # print a remote file ``` `push` uploads the source dir from your [`coduck.json`](/docs/reference/coduck-json) and delivers the config file itself (even from a subdirectory). Transient network errors retry automatically; `deploy` warns if your last push failed or local files changed since. ## Deploy ```bash coduck deploy # build + ship the project coduck deploy --size large # deploy on a bigger instance (small|medium|large) coduck stop # stop the running container coduck restart # restart with no config change coduck status # current deployment status coduck teardown --yes # destroy container + DB + vhost coduck logs --follow # tail container logs coduck logs --since 30m --grep error # filter by age + pattern coduck logs-digest # summarized error/warn digest ``` `deploy` uses the commands + `instanceSize` from your [`coduck.json`](/docs/reference/coduck-json); `--size` overrides the tier for one deploy. A failed deploy reports the specific cause (out of memory, build failed, wrong port, crashed after start). ## Generation (AI agent) ```bash coduck generate "add a login page" --wait # send a prompt to the agent coduck jobs status # check a running generation coduck jobs messages # conversation history ``` ## Environment variables ```bash coduck env list # list env vars (values masked) coduck env list --reveal # show full values coduck env get coduck env set coduck env unset coduck env import .env # bulk import (skips reserved/invalid keys, imports the rest) coduck env reserved # list the keys CoDuck manages (can't be set) ``` A set of keys are reserved (CoDuck injects them and rejects them on write): `DATABASE_URL`, `DIRECT_URL`, `PORT`, `NODE_ENV`, and everything under `CODUCK_*` / `NEXT_PUBLIC_CODUCK_*`. `coduck env reserved` prints the authoritative list; see the [`coduck.json` reference](/docs/reference/coduck-json#reserved-environment-variables). `coduck env import` skips reserved/invalid keys with a warning instead of aborting. ## Custom domains ```bash coduck domains list coduck domains add example.com coduck domains verify example.com coduck domains remove example.com coduck domains transfer example.com ``` ## Database ```bash coduck db schema # full schema as JSON coduck db tables # list tables coduck db tables --table users # read rows from a table coduck db users # auth users (if using @coduckai/sdk/auth) ``` Read-only by design. No raw connection URL is exposed. ## Backups ```bash coduck backups list coduck backups create coduck backups download --out backup.sql.gz coduck backups restore --yes coduck backups delete ``` ## Email ```bash coduck email status coduck email domains list coduck email domains add example.com coduck email domains dns # reprint DNS records coduck email domains verify coduck email send --from noreply@example.com --to user@example.com \ --subject "Welcome" --text "Thanks." --html "

Thanks.

" coduck email usage # quota coduck email messages # recent messages coduck email suppressions list coduck email suppressions remove coduck email unpause # resume after auto-pause ``` ## Storage ```bash coduck storage list coduck storage upload coduck storage get --out local.bin coduck storage remove ``` ## Stripe (BYOS payments) ```bash coduck stripe connect # print OAuth URL coduck stripe status coduck stripe disconnect --yes coduck stripe payments stats coduck stripe payments transactions coduck stripe payments revenue --days 30 coduck stripe payments health ``` ## Activity, analytics, forms ```bash coduck activity --limit 100 coduck analytics --days 30 coduck forms list coduck forms read coduck forms delete ``` ## Account + tokens ```bash coduck whoami coduck token status # plan, credits coduck token list # CLI tokens on your account coduck token revoke coduck token show --confirm # print the JWT (refuses without --confirm) ``` ## Discovery ```bash coduck --help # human-friendly help coduck --help --json # machine-readable spec (use this to build an MCP wrapper) coduck version coduck doctor # diagnose auth + config + connectivity ``` ## Global flags | Flag | What it does | |---|---| | `--json` | Force JSON output regardless of TTY | | `--no-input` | Fail with exit 2 instead of prompting | | `--quiet` | Suppress non-essential output | | `--project ` | Override the `coduck.json` projectId for this command | ## Exit codes | Code | Meaning | |---|---| | 0 | Success | | 1 | Generic error | | 2 | Usage error (bad flag, missing input under `--no-input`) | | 3 | Authentication error | | 4 | Not found | | 5 | Conflict (e.g. payment required / state conflict) | | 6 | Network error | | 7 | Server error (5xx) | ## Next - [Install the CLI](/docs/cli/install) - [Authentication](/docs/cli/auth) --- ## coduck.json reference Source: https://coduck.ai/docs/raw/reference/coduck-json.md # coduck.json reference `coduck.json` is the source of truth for how CoDuck builds and runs your project. `coduck create-existing` writes it; you can edit it by hand. It is **re-read on every deploy** — change a command, `coduck push`, `coduck deploy`, and the new command runs. (Earlier behavior baked config at create-time; that is no longer the case.) ```jsonc { "projectId": "ff0c35e6-…", // set by create-existing; don't change "name": "my-app", "dir": "./", // source root to push + run (see "Monorepos") "runtime": "node20", // node20 | node22 | static "install": "npm ci", // optional — default: npm install --include=dev "build": "npm run build", // optional — default: npm run build "preStart": "npm run migrate", // optional — runs after build, before start "start": "node server.js", // optional — default: npm start "port": 3000, // informational; bind process.env.PORT (see below) "instanceSize": "small" // small | medium | large } ``` ## How each field drives the deploy A deploy runs these steps in order, in your container: 1. **install** — your `install`, else `npm install --include=dev --no-audit --no-fund`. (Dev deps are installed by default because build tools like Tailwind/PostCSS live there. If you override with `npm ci`, note `NODE_ENV=production` is set, so add `--include=dev` yourself if your build needs dev deps.) 2. **prisma generate** — only if `prisma/schema.prisma` exists *and* declares `model`s. Skipped otherwise. 3. **build** — your `build`, else `npm run build`. 4. **schema bootstrap** — if `prisma/schema.prisma` declares models *and* you have **no** `preStart`, CoDuck runs `prisma db push`. If you set `preStart`, CoDuck does **not** auto-push — you own schema setup (see [Database](/docs/database/overview)). 5. **preStart** — your `preStart` (e.g. `prisma migrate deploy`, `drizzle-kit push`, `node scripts/migrate.js`). The right place for migrations. 6. **start** — your `start`, else `npm start`. **Skip a step** by setting it to `""` (empty string). E.g. a static/no-build app: `"build": ""`. ## Ports — bind `process.env.PORT` CoDuck assigns the port and injects it as `PORT`. **Your app must listen on `process.env.PORT`**, not a hardcoded value. The `port` field in `coduck.json` is informational only — if your app hardcodes `3000` but CoDuck health-checks the assigned port, the deploy fails with "responding on :3000 but health-checking : — bind process.env.PORT". ## Instance size `instanceSize` (or `coduck deploy --size`) selects container resources. The CLI flag wins; otherwise `coduck.json` is used; default `small`. | Size | Memory | CPU | Plan required | |---|---|---|---| | `small` | 1.5 GB | 1.0 | any paid plan | | `medium` | 2.5 GB | 2.0 | Pro or Studio | | `large` | 4 GB | 3.0 | Studio | A request above your plan is clamped down (and the deploy tells you). The Node build-heap ceiling auto-scales to ~80% of the tier, so heavy client builds (three.js, remotion, recharts) that OOM on `small` usually succeed on `large`. ## Monorepos / subdirectory source `dir` can point at a subdirectory (e.g. `"./web"`) while `coduck.json` stays at the repo root. The CLI uploads the repo-root `coduck.json` (and root `.gitignore`/`.coduckignore`) alongside the subtree so the server still sees your config. **This requires `@coduckai/cli` ≥ 0.1.11** — older CLIs pushing a subdir silently omitted `coduck.json`, so declared commands were ignored. Run `coduck --version` and upgrade if needed. One CoDuck project = one container. For a separate frontend + backend, wrap them in a single `start` (e.g. `concurrently`) or create two projects. ## Reserved environment variables CoDuck injects these into every container and **rejects them on write** (setting them has no effect — platform values win). The live, authoritative list is `coduck env reserved`. | Key / pattern | What it is | |---|---| | `DATABASE_URL` | Pooled Postgres connection string (see [Database](/docs/database/overview)). | | `DIRECT_URL` | Direct (non-pooled) Postgres URL for migrations/`prisma db push`. | | `PORT` | The port to listen on. Bind `process.env.PORT`. | | `NODE_ENV` | Set to `production` in the container. | | `HOME` | Container home dir. | | `CODUCK_*` | All CoDuck-managed keys (`CODUCK_API_KEY`, `CODUCK_AUTH_KEY`, `CODUCK_PROJECT_ID`, `CODUCK_API_URL`, `CODUCK_STORAGE_DIR`, …). | | `NEXT_PUBLIC_CODUCK_*` | Browser-exposed CoDuck values (project id, auth/api URLs, subdomain). | Stripe keys (`STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`) are injected **only when you connect Stripe** — until then you may set them yourself. `coduck env import .env` skips reserved/invalid keys with a warning and imports the rest, so a `.env` containing `DATABASE_URL` won't abort the whole import. ## Next - [Deploying](/docs/projects/deploy) — the deploy lifecycle + failure messages. - [Import an existing project](/docs/projects/create-existing) - [Database](/docs/database/overview) — the managed Postgres starts empty. --- ## Runtime architecture Source: https://coduck.ai/docs/raw/reference/runtime.md # Runtime architecture This page describes the runtime environment a deployed project actually runs in: the container, the network, the database connection, the filesystem. Use it when you're reasoning about what `process.env.DATABASE_URL` connects to, what `localhost` means inside your container, or what sits between your app and Postgres. ## Container model Each deployed project runs in its **own Docker container on CoDuck's VPS**. One project, one container. The base image is Node (Node 20 by default; pick another via `runtime` in [`coduck.json`](/docs/reference/coduck-json)). Your code is bind-mounted at `/app` as the working directory, and the container runs as a non-root user (uid `1001`, the image's `coduck` user). Container resource caps (CPU, memory) come from the instance size — see [Deploying → Instance size](/docs/projects/deploy#instance-size-resources) for the table. ## Networking Containers run with Docker's `--network=host`. The consequences are worth spelling out, because they're different from the bridged-network mental model most "sandboxed hosting" platforms use: - **`localhost` inside your container is the VPS's loopback.** A socket to `127.0.0.1:6432` from your app code is the same kernel socket as a socket to `127.0.0.1:6432` from the host process. There is no NAT, no `docker-proxy`, no userspace TCP shim. - **No `-p host:container` mapping.** Your app binds the `PORT` it was assigned directly on the host. nginx upstreams to that port for `.coduck.app` traffic (and any [custom domain](/docs/domains/custom-domains)). - **External egress** (calls to public services — your own external API, Stripe, OpenAI, etc.) goes out the host's interface normally. Nothing intercepts it. When something in this environment looks like "a local proxy" — it isn't. The only thing between your app and `127.0.0.1:` is the kernel's loopback interface. ## Database connectivity Two database env vars are injected on every deploy. Both are reserved (you can't override them via `coduck env set` — see [Environment variables](/docs/projects/deploy#environment-variables)): | Env var | Points at | What it's for | |---|---|---| | `DATABASE_URL` | `postgres://…@127.0.0.1:6432/?pgbouncer=true&connection_limit=1` | **Runtime queries — all stacks.** The host:port is **PgBouncer**, running on the VPS in transaction-pool mode with server-side prepared statements enabled (`max_prepared_statements = 100`). Prisma reads `?pgbouncer=true` to disable its client-side prepared-statement caching; other clients (`node-postgres`, `pg`, `drizzle-orm`, `knex`, `sequelize`) just send prepared statements normally — PgBouncer caches them per backend. `connection_limit=1` keeps Prisma clients to one server connection per request. | | `DIRECT_URL` | `postgres://…@127.0.0.1:5433/` | **Schema operations only.** `prisma db push` and `prisma migrate deploy` need the schema engine to talk directly to Postgres (their connection pattern doesn't match what the pool expects), so CoDuck routes those to a direct pooler-bypass URL. `127.0.0.1:5433` is the project's Postgres cluster bound directly to loopback. You should not use this URL for runtime queries — use `DATABASE_URL`. | > [!WARNING] > **Don't `DROP SCHEMA public CASCADE` on your project DB.** PgBouncer authenticates > incoming connections by calling a `public.user_lookup()` function in your DB; if > you drop and recreate `public`, that function (and the schema-level `USAGE` grant > to `pgbouncer_auth`) goes with it. `DATABASE_URL` will start returning > `bouncer config error` on every query until support restores them. Use > `prisma migrate` / `drizzle-kit drop` etc. instead, which target your own tables > without nuking `public`. Both `6432` and `5433` are loopback-only — neither is reachable from outside the VPS. That's why there's [no raw external connection URL](/docs/database/overview#why-theres-no-raw-connection-url). PgBouncer's transaction-pool mode means a single client connection to `:6432` is served by any of N backend Postgres connections, transparently and per-transaction. That's why your **client-side pool should stay small** (≤ 10) — opening many client connections doesn't help when PgBouncer is already pooling on the server side, and oversized client pools waste PgBouncer slots. See [Connection pooling](/docs/database/overview#connection-pooling). ## Filesystem - **Working directory:** `/app`, bind-mounted from CoDuck's project store on the host. Reads and writes both work — your build can write `node_modules`, `.next/`, etc. and they persist across redeploys of the same project. - **User:** `uid 1001` (`coduck`, non-root). `HOME` is set to `/tmp` so npm's cache/lockfile machinery has somewhere to write without trying to touch `/root`. - **Restart policy:** `on-failure` with 2 retries. If your app crashes 3 times in a row, the container stays stopped and the deploy reports it — redeploy to restart. ## Common questions this page answers - **What does `process.env.DATABASE_URL` connect to?** → PgBouncer on `127.0.0.1:6432` (transaction-pool mode), on CoDuck's VPS loopback. Works for all stacks — Prisma, drizzle-orm, node-postgres, knex, sequelize. - **Why is `localhost:5433` in my env (`DIRECT_URL`)?** → That's the pooler-bypass connection used by Prisma's schema engine at deploy time. It's not the runtime path. - **Is `localhost` inside my container the same as `localhost` on the host?** → Yes — `--network=host`. - **Is there a proxy between my app and Postgres?** → PgBouncer (a connection pooler, not a proxy in the security sense) sits on the host on `:6432`. The network hop between your container and PgBouncer is a direct loopback socket — no shim or proxy in addition to PgBouncer itself. ## Next - [Deploying](/docs/projects/deploy) — instance sizes, deploy lifecycle, failure modes. - [Database overview](/docs/database/overview) — schema, migrations, backups. - [`coduck.json` reference](/docs/reference/coduck-json) — the per-project deploy config. --- ## Changelog Source: https://coduck.ai/docs/raw/changelog.md # Changelog What's new in CoDuck, newest first. **Platform** changes (hosting, deploy, the API, the web app) are live on `coduck.ai` / `api.coduck.ai` as soon as they land. **CLI** changes ship in an `@coduckai/cli` release — run `npm i -g @coduckai/cli@latest` to get them, and `coduck --version` to check what you have. **SDK** changes ship in an `@coduck/auth` release — run `npm i @coduck/auth@latest` in your project to upgrade. > **Agents:** if a deploy behaves differently than you expect, scan the Platform > section first — behavior may have changed. The machine-readable copy of this > page is at [`/docs/raw/changelog.md`](https://coduck.ai/docs/raw/changelog.md), > and it's indexed in [`/llms.txt`](https://coduck.ai/llms.txt). ## Platform — 2026-05-26 - **Settings → Billing shows your plan + credit burn rate.** The top of the billing panel now displays "Your plan: Pro / Plus / Studio" (or "Free — not subscribed"), and the credits line shows "$5.00 / $20.00 this month" so you can see how much of your monthly allowance is left. Top-up balance is shown separately because it doesn't have a monthly cap and never expires. - **The pricing page now knows what plan you're on.** When you're signed in and subscribed, your plan card shows a "Current" badge and its button is disabled with "Current plan" text. The other paid cards relabel as "Upgrade to X" or "Downgrade to X" so the action you're about to take is obvious before you click. - **Pricing page layout cleanup.** Pro / Plus / Studio now sit in their own three-column row as the self-serve options, with Enterprise broken out as a slim "Contact sales" row beneath. Each of the three main plan cards has more room to breathe, and Enterprise no longer looks like a fourth equal-weight option. - **Switching plans now actually works for existing subscribers.** Previously, clicking any plan card on the pricing page or in Settings → Billing while you were already subscribed silently failed (the request came back as "Already subscribed"). The buttons now route you to the Stripe customer portal — the right place to switch up or down, change your card, or cancel — and the click goes through immediately. - **Settings → Billing buttons now say "Upgrade ↑" or "Downgrade ↓"** depending on whether the plan you're clicking is above or below your current one. Previously they all said "Upgrade" regardless, so a Studio user looking at the Plus card was told to "upgrade" to a cheaper plan. - **Settings → Billing no longer shows a "Free" plan card.** CoDuck's hosting is paid-only — a free account can't deploy a project at all — so listing Free as a "plan" next to Pro/Plus/Studio was misleading. The billing page now shows just the three real plans you can subscribe to. If you're not subscribed, no plan card is highlighted as your current one (the Credits remaining panel above still shows your true balance). - **New Plus plan ($100/mo or $1,000/yr).** A middle tier between Pro and Studio for builders shipping more than they thought. Plus gets $100 of AI credits per month, Lake hosting (1 GB database), and a 15,000-emails- per-month send limit — slotting cleanly between Pro (Pond / 100 MB / 5k emails) and Studio (Ocean / 5 GB / 50k emails). Annual billing saves $200/year (2 months free). - **Pricing cards now list what you actually get.** Every plan card on the pricing page and in Settings → Billing now shows credits, hosting tier name, database size, monthly email-send cap, and key features — instead of a one-line tagline. The numbers are the same values the platform enforces, so what you see on the card is what you get. - **Settings → Billing "Top up credits" section refreshed.** Clearer heading and description so it's obvious that top-up credits stack on top of your monthly subscription and are spent first. Each amount button now shows the credit value ("$20 credits") rather than the generic "one-time" caption. Plus subscribers can top up too, not just Pro and Studio. - **Billing page in Settings now lets you upgrade to Studio.** The Studio plan ($200/mo — same product as Pro, 10× the runway) is now visible alongside Free and Pro under Settings → Billing. Clicking **Upgrade →** on either Pro or Studio goes straight to a Stripe Checkout for that plan. - **Add Credits buttons in Settings → Billing fixed.** The top-up grid now shows **$5 / $20 / $50 / $100** and each one works — previously the buttons offered amounts CoDuck didn't accept, so clicks failed silently. Studio subscribers can now top up too (it used to be Pro-only). - **Out-of-credits modal: top-up buttons fixed.** When you run out of credits mid-build, the four quick-top-up buttons inside the paywall modal ($5 / $20 / $50 / $100) now correctly open Stripe Checkout — they were previously hitting a route that didn't exist and 404'ing. The "Subscribe to CoDuck Pro" button next to them was unaffected. - **`DATABASE_URL` now works for every Postgres client, not just Prisma.** Before today, apps using `node-postgres`, `pg`, `drizzle-orm`, `knex`, or `sequelize` would hit `bouncer config error` when trying to query through `DATABASE_URL` (the pooled path at `127.0.0.1:6432`) and had to fall back to `DIRECT_URL` — which has no server-side pool, so bursty traffic could exhaust it. PgBouncer is now configured with server-side prepared statements enabled, so prepared-statement-using clients work on the pooled path alongside Prisma. **Use `DATABASE_URL` for runtime queries regardless of stack;** `DIRECT_URL` is only for `prisma db push` / `prisma migrate deploy`. - **New runtime docs page:** [Runtime architecture](/docs/reference/runtime) documents the container model, `--network=host` networking, exactly what host:port `DATABASE_URL` and `DIRECT_URL` point at, and a `[!WARNING]` about a footgun — **don't run `DROP SCHEMA public CASCADE` on your project DB.** That command destroys the `public.user_lookup()` function PgBouncer needs for connection auth (and the `USAGE` grant on the schema). If you've already done it and you're seeing `bouncer config error`, contact support to restore it. ## Platform — 2026-05-25 - **`coduck.json` is honored on every deploy.** Your `install`, `build`, `preStart`, and `start` commands are re-read from `coduck.json` on each deploy and run as declared — previously the container always ran a hardcoded `npm install` → `npm run build` → `npm start`. Notes: - `preStart` runs after build, before start — the right place for DB migrations (e.g. `prisma migrate deploy`). Declaring it also disables CoDuck's automatic `prisma db push`, so you own schema setup. - Set any command to `""` to skip that step (e.g. `build: ""` for a no-build app). - Monorepo note: this only takes effect once your CLI delivers `coduck.json` to the server — see the CLI 0.1.11 entry. Older CLIs pushing a subdirectory did not upload the repo-root `coduck.json`. - **Specific deploy-failure messages.** A failed deploy now tells you the cause instead of one generic "crashed before becoming healthy": - out of memory → "retry on a bigger instance: `coduck deploy --size large`" - build failed → fix the error and redeploy - wrong port → "bind the port CoDuck assigns — use `process.env.PORT`" - crashed after start → check `coduck logs` - **Faster reinstalls for imported apps.** Projects with no `@coduck/*` dependency now reuse the cached `node_modules` across deploys instead of a full reinstall every time (the cache-bust only runs for projects that use the CoDuck SDK). - **Instance sizes.** `coduck deploy --size small|medium|large` (or `instanceSize` in `coduck.json`) selects more build/runtime RAM + CPU; medium/large require a Pro/Studio plan. The Node build-heap ceiling now uses the container's full RAM, fixing out-of-memory builds for heavy client bundles (three.js, remotion, recharts, …). - **Reserved env keys.** The set of keys CoDuck manages (`DATABASE_URL`, `PORT`, `NODE_ENV`, `CODUCK_*`, …) is complete and rejected clearly on write. List them with `coduck env reserved`. The managed Postgres starts empty — reach it at `DATABASE_URL`/`DIRECT_URL` and bootstrap your schema in `build` or `preStart`. ## CLI — Unreleased (0.1.11) Merged; ships on the next `@coduckai/cli` publish. - **Monorepo `coduck.json` delivery.** A subdirectory push (`dir: "./web"`) now uploads the repo-root `coduck.json` to the server, so the platform's deploy-config honoring (above) actually applies to monorepo layouts. Repo-root `.gitignore`/`.coduckignore` are honored for subdir pushes too. - **`coduck env import` no longer aborts on the first reserved key** — it skips reserved/invalid keys with a warning and imports the rest. New `coduck env reserved` lists managed keys. - **`push` honors `.gitignore`** (plus sensible defaults: `node_modules`, `.venv`, build output, caches). `push --dry-run` prints a size summary + biggest dirs instead of a full file manifest. - **Resilient push.** `push` auto-retries transient network errors; `deploy` warns when your last push failed or local files changed since it. - **Logs filters.** `coduck logs --since <30m|1h|…>` and `--grep `. - **`coduck --version`** reads the installed package version correctly (no longer under-reports). ## CLI 0.1.10 - `coduck deploy --size` and `coduck.json` `instanceSize` for instance-tier selection. ## CLI 0.1.9 - Chunked `push` for large projects (no more 413s) and `.coduckignore` support. ## CLI 0.1.8 - `coduck db import ` — upload a SQL dump to the project database. ## CLI 0.1.7 - Real `coduck push` and `create-existing --push --deploy`; fixed `coduck logs`. - Browser-based device login (`coduck login`) — the CLI never sees your password. ---