# Bean & Bark: your first deploy

Bean & Bark's wholesale orders arrive by DM and spreadsheet, and the roastery's first tasting for retail buyers is **Thursday**. Nadia wants orders coming through an API before then. You have a fresh laptop, an AWS dev account, and one command surface. This is where you learn the thing the CLI is built around: a deploy isn't done when the code is up — it's done when the **deployment ledger** has recorded exactly what shipped.

**You'll learn**

- Scaffold a deploy-ready .NET service — solution, manifest, stages, tests — with one `kanject new`
- Prove your machine is ready with `kanject doctor`, **entirely offline**
- Deploy to a `dev` stage and follow the phase ladder to `record ledger`
- Predict every AWS resource name from two words: the service name and the stage

> **Monday, 9:15am — the ticket:** **Nadia** (founder): *"buyers at the tasting Thursday will ask how to order. can we have the Orders API on dev this week so Tunde can point the storefront at it? nothing fancy — it just has to exist and not fall over 🙏"*

## One command, a whole service

`kanject new` scaffolds from a template that ships **inside the CLI** — no feed, no license, nothing to authenticate. `webapi` gives you an ASP.NET Core REST API on AWS Lambda with a wired xUnit project, and `--yes` accepts the template prompts so it runs clean in one go.

```bash
# One command: a deploy-ready service
kanject new webapi --name BeanAndBark.Orders --yes
```

When it finishes you have a working tree, not a stub: the solution and csproj, `kanject-cli/manifest.json` (the service's identity), a stage file each for `dev`, `stage`, and `prod` under `kanject-cli/stages/`, and the test project. `new` also runs `kanject init` for you, so the project is already wired — you never run `init` on a fresh scaffold.

## Prove the machine is ready

Before anything touches AWS — a guess. `doctor` is about to run its checks: how many of them need AWS credentials? Run it and count.

```bash
kanject doctor
```

```text
Kanject Doctor ─────────────────────────────────────────────────

     Check                                           Status   Code   Fix
  ✓  dotnet 10.0.100                                 ok       -      -
  ✓  global tools on PATH (~/.dotnet/tools)          ok       -      -
  ✓  kanject-cli/manifest.json (schema v2)           ok       -      -
  ✓  Amazon.Lambda.Tools installed                   ok       -      -
  ✓  kanject-cli/manifest.lock.json in sync (no deps) ok      -      -

  ok     5
  warn   0
  error  0
```

The answer is **none**. Without `--env`, every check is offline: the .NET SDK version, the global-tools PATH, the manifest, the Lambda tooling, the lockfile. Doctor only calls AWS when you hand it a stage (`kanject doctor --env dev`) — so "is my laptop set up right?" and "is my AWS config right?" are separate questions you can ask separately. The exit code is honest too: warnings keep it `0`; only a red `✗` row makes it `1`.

## Point dev at your AWS

Open `kanject-cli/stages/dev.json`. A stage is the whole answer to "where does this deploy?" — region, profile, stack, artifact bucket:

```json
{
  "schemaVersion": 1,
  "region": "eu-west-1",
  "profile": "default",
  "stack": "dev-beanandbark-orders",
  "artifactBucket": "dev-beanandbark-orders-artifacts",
  "env": {
    "ASPNETCORE_ENVIRONMENT": "Development"
  }
}
```

Look at the names. You never chose them — they're **derived**: service `beanandbark-orders` + stage `dev` gives stack `dev-beanandbark-orders`, bucket `dev-beanandbark-orders-artifacts`, and (when you add parameters later) the SSM path `/beanandbark-orders/dev/`. Two words predict every resource name, on every stage, forever.

> **The bucket must exist first:** Kanject deliberately does not provision your artifact bucket — that's an account-level decision it won't make for you. Create `dev-beanandbark-orders-artifacts` (console, CLI, or your team's IaC) before the first deploy, or the deploy fails at the artifact phase.

## Ship it

```bash
kanject aws deploy --env dev
```

```text
Deploy  stage dev ──────────────────────────────────────────────

  ✓  sync deps                  (no deps)
  ✓  resolve env                1 key
  ✓  prepare artifacts
  ✓  write deploy config
  ✓  render provider artifacts
  ✓  deploy Lambda              ok
  ✓  promote revision           1 → live
  ✓  record ledger              s3 snapshot written

  ✓  Deployed dev-beanandbark-orders (Lambda) @ revision 1
```

Read the ladder from the bottom up, because the last two phases are the point. `promote revision` published a **Lambda version** and flipped the `live` alias to it — that number `1` is real, and it's about to matter. `record ledger` wrote a JSON snapshot to `s3://dev-beanandbark-orders-artifacts/_ledger/versions/1.json`: what shipped, who shipped it, the git commit, and content hashes of the lockfile and every resolved config value. And the ordering is a guarantee: **if any phase fails, no ledger entry is written.** The ledger never claims a deploy that didn't finish.

> **No AWS account handy? Rehearse it:** Run `kanject aws deploy --env dev --dry-run` instead. It renders every deploy input and reports *"Dry run — rendered the deploy inputs; no AWS changes were made"*, telling you which boundary it stopped before. Everything in this chapter except the live deploy itself is free and offline.

## Read it back

Don't take the terminal's word for it — ask the ledger:

```text
Deployments  stage dev ─────────────────────────────────────────

  Revision   Target   Deployed           Commit    Status     By
  1          Lambda   2026-07-07 09:26   9f3c1ab   ✓ stable   you@beanandbark
```

One revision, target Lambda, your commit, status `stable`, your name on it. Every deploy from now on — including Thursday's panicked ones — appends a row here. When something goes wrong, this table is where the recovery starts.

> **🎯 Shipped, with a receipt:** Monday, 11 minutes in: the Orders API is live on `dev`, Tunde has a URL, and Nadia has her answer three days early. But the thing you actually built is the **paper trail** — revision 1 is in the ledger with its commit and content hashes. Getting code onto a Lambda is easy. Being able to say *exactly what's running and prove it* is the job.

**Recap**

- `kanject new webapi --name <Name> --yes` scaffolds a deploy-ready service — solution, manifest, three stage files, tests — and runs `init` for you.
- `kanject doctor` without `--env` is **fully offline**; only errors (not warnings) flip its exit code to 1.
- Stage + service name derive every resource: `dev-beanandbark-orders`, `…-artifacts`, `/beanandbark-orders/dev/`. The artifact bucket is the one thing you create yourself.
- A deploy ends by publishing a Lambda version, flipping the `live` alias, and writing an append-only ledger snapshot — and a failed deploy writes **no** ledger entry.

**Before Thursday — check yourself**

**1. Your deploy fails at the `deploy Lambda` phase. What does the deployment ledger show afterwards?**

- Nothing new — no entry is written ✓ — the ledger is written by the final phase, after promotion succeeds. A failed deploy leaves no ledger entry, so the ledger never lists a deploy that didn't complete.
- A new entry marked `broken` — `broken` is a *manual* annotation you apply later with `kanject aws deployments mark` — the CLI never writes a failed deploy into the ledger at all.
- A partial entry missing the revision id — the ledger is append-only and written once, whole, at the end. There are no partial entries to clean up.

**2. Which of these belong in git?**

- `manifest.json`, `stages/*.json`, and `manifest.lock.json` ✓ — these three are the service's contract — identity, targets, and pinned dependencies. Committing them is what makes a teammate's clone (and CI) reproduce your deploy.
- Those three plus `aws-lambda-tools.dev.json` — the `aws-lambda-tools.<stage>.json` files are regenerated on every deploy and git-ignored — hand-edits to them are overwritten, so there's nothing durable to commit.
- Everything under `kanject-cli/` and `.kanject/` — `.kanject/` is derived cache and local feed — delete-and-resync territory. `init` adds it to `.gitignore` for exactly this reason.

**Try it yourself**

**1. Rehearse the deploy for free**

Run the dev deploy as a dry run and find the line telling you where it stopped. Which phases ran, and which boundary was never crossed?

_Hint:_ Add one flag to the deploy command. The output ends with a dim "Stopped before …" line.

_Solution:_ A dry run renders the deploy inputs — sync, env resolution, artifacts, config — then stops before anything AWS-mutating. You get the full plan and the "no AWS changes were made" confirmation without an account, a bucket, or a bill.

```bash
kanject aws deploy --env dev --dry-run
```

**2. Predict the names**

Nadia spins up a roastery-operations service (`beanandbark-roastery`) with a `qa` stage. Without scaffolding anything: what are its CloudFormation stack, artifact bucket, and SSM parameter path?

_Hint:_ Stack and bucket lead with the stage; the SSM path leads with the service. Same pattern as dev.

_Solution:_ Stack `qa-beanandbark-roastery`, bucket `qa-beanandbark-roastery-artifacts`, SSM path `/beanandbark-roastery/qa/`. Two words really do predict everything — which is why teammates can find each other's resources without asking.

> **Where next?:** The API exists — but its config is all plain values, and Thursday's demo needs a real database connection string that can't live in a JSON file in git. [The secret nobody wrote down](https://www.kanject.com/docs/cli-bean-bark-secrets/) wires secrets in properly and reproduces a prod-shaped bug on your laptop with `--pull-env`. And when a deploy eventually goes wrong (one will), the ledger you met today is what makes the rollback drill calm instead of frantic.

---
_Source: https://www.kanject.com/docs/cli-bean-bark-first-deploy/ · Kanject Docs_
