Scaffold Service
How to scaffold a new service on Tawa with `tawa sample`.
Commandplatform
About
How to scaffold a new service on Tawa with `tawa sample`.
Skill Content
This is the raw markdown that gets installed as a Claude Code command.
# Scaffold Service
## What this skill covers
How to scaffold a new service on Tawa with `tawa sample`.
> Checked against tawa CLI **0.26.4** (`tawa sample --help`). The old `--api` and `--nextjs`
> templates are gone: the CLI rejects them with `error: unknown option`. If your CLI
> disagrees with this page, trust `tawa sample --help`.
## Templates
| Template | Command | What you get |
|----------|---------|-------------|
| **Eco App** (recommended for web apps) | `tawa sample my-app --eco-app` | Full-stack Next.js 15 app with Bio-ID login (`spec.auth: mode: sso`), MongoDB, Septor audit, Relay email, object storage, cron + queue examples, and an admin dashboard. Uses pnpm, dev port 4140 |
| Static Site | `tawa sample my-site --static` | Static HTML/CSS with Tailwind 4 design tokens, served by nginx. No auth, no database |
There is no API-only template today. For a backend service, start from `--eco-app` and delete
what you don't need, or write a minimal catalog by hand (example below).
## Options
| Flag | What it does |
|------|-------------|
| `--with-storage` | Adds object storage: declares `spec.storage` (`name: default`, `tier: s3-sm`) in the catalog, adds `@insureco/storage@^0.2.0` to `package.json`, raises `catalog-version` to at least `0.3.0`, and writes a `src/storage.ts` example. A catalog that already declares storage (Eco App) is left as it is |
| `--git` | **Creates a NEW private repo** at `git.insureco.io/insureco/<name>`, sets the HTTPS `origin`, adds the auto-deploy webhook, pushes `main`, and protects it (PR-only). From tawa **0.27.0** it also protects release tags (`v*`, `*-v*`): only you and, for an org repository, the org's `Owners` team can create them. See the agile-workflow convention, Release Management. Known gap: when the repo is created under your personal account, the output still prints `insureco/<name>` ([tawa-cli#129](https://git.insureco.io/insureco/tawa-cli/issues/129)); check the real owner in Forgejo before running `tawa git protect` on it |
| `--skip-git` | Skips `git init` entirely |
| `--skip-install` | Skips dependency install. Eco App and Static never auto-install anyway; run `pnpm install` yourself |
### `--with-storage`
Eco App **already includes storage** (`spec.storage` in the catalog, `@insureco/storage@^0.2.0`
in `package.json`), so `--with-storage` adds nothing to it. On `--static` it adds the catalog
entry, the dependency and the `catalog-version` bump.
**On tawa 0.26.3 or earlier** (`tawa --version`) the flag was broken. It added
`tawa-storage@^0.1.0` instead of `@insureco/storage`, and on `--eco-app` it appended a
duplicate `storage:` key. If an older CLI scaffolded your project, fix it by hand:
- `package.json` should depend on **`@insureco/storage` at `^0.2.0`** or later. Remove
`tawa-storage`. Versions below 0.2.0 read credentials from `process.env`, and those go stale
in a deployed pod.
- `catalog-info.yaml` needs exactly one `storage:` key under `spec`, and
`insureco.io/catalog-version: "0.3.0"` or higher, or `spec.storage` is ignored.
See the object-storage convention for how credentials work.
### `--git`: only when the repo doesn't exist yet
`--git` always tries to **create** the repo. If the Forgejo repo already exists (you made it
with `repo_create`, the web UI, or a teammate did), don't pass `--git`. Scaffold locally and
point it at the existing repo:
```bash
tawa sample my-app --eco-app --skip-git
cd my-app
git init -b main
git remote add origin https://git.insureco.io/insureco/my-app.git # HTTPS, never SSH
git add . && git commit -m "chore: scaffold from tawa sample --eco-app"
git push -u origin main # only if the remote is empty; otherwise branch + PR
tawa link # registers the service with the builder
```
`--git` sets `origin` to the repo's HTTPS URL. If a push fails, run `tawa login`: it stores the
git credential. **On tawa 0.26.3 or earlier**, `--git` set an SSH remote instead. Switch it with
`git remote set-url origin https://git.insureco.io/insureco/<name>.git`.
## Step-by-Step
### 1. Scaffold
```bash
tawa sample my-app --eco-app # or --static for a marketing site
cd my-app
pnpm install
tawa dev --offline # writes .env.local, starts local mocks
pnpm dev # http://localhost:4140
```
### 2. Review catalog-info.yaml
Eco App ships a complete catalog. Trim it to what you use. A minimal hand-written catalog
looks like this:
```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-new-service
description: What this service does
annotations:
insureco.io/framework: express # or nextjs, hono, fastify, worker, static
insureco.io/catalog-version: "0.6.0"
insureco.io/pod-tier: nano # start here; upgrade when you have load data
insureco.io/health-endpoint: /health # REQUIRED (nextjs default is /api/health)
spec:
type: service
lifecycle: production
owner: my-org # your org slug
# ── Add a database ──────────────────────────────────────────────────────────
databases:
- type: mongodb # → MONGODB_URI injected automatically
# ── Enable user login (Bio-ID OAuth) ────────────────────────────────────────
# WITHOUT this, BIO_CLIENT_ID and BIO_CLIENT_SECRET are never injected.
# Listing bio-id as a dependency is NOT the same thing. Don't do that.
auth:
mode: sso # → BIO_CLIENT_ID + BIO_CLIENT_SECRET injected on every deploy
# ── Expose public API routes ────────────────────────────────────────────────
routes:
- path: /api/my-service/data
methods: [GET, POST]
auth: required
gas: 1
# ── Call NON-platform services ──────────────────────────────────────────────
# Platform services (septor, iec-queue, iec-wallet, koko, janus, bio-id) are
# auto-injected: SEPTOR_URL etc. and their NetworkPolicy labels come for free.
# Do NOT declare them. internalDependencies is deprecated.
dependencies:
- service: relay # → RELAY_URL (+ client credentials for the SDK)
scopes: [relay:send]
- service: docman # → DOCMAN_URL (transport defaults to janus)
```
The health handler must return the running commit: `{ status: 'ok', service: 'my-new-service', commit: process.env.GIT_COMMIT ?? null }`. The builder injects `GIT_COMMIT` on every deploy and warns after deploy if `/health` does not report it. Keep the handler cheap (no DB call, no auth).
### 3. Run preflight
```bash
tawa preflight # catches config issues before you waste a build
```
### 4. Deploy
```bash
tawa link # first time only, if not created with --git
tawa deploy --prod --watch
```
## Next.js with Auth Checklist
Eco App has this wired already. If you are adding login to a different Next.js app:
- [ ] `spec.auth: mode: sso` is in catalog-info.yaml
- [ ] Callback is at `/api/auth/callback` (exactly, no variations)
- [ ] `BioAuth.fromEnv()` reads from env vars, not hardcoded values
- [ ] Every API route passes `request` to `getAuthUser(request)`, not `getAuthUser()`
- [ ] Only the root/dashboard page auto-redirects on 401; sub-pages show a login button
- [ ] Run `/tawa-auth` for the full Next.js auth implementation pattern
## Key Facts
- `metadata.name` becomes your hostname: `my-svc.tawa.pro` (prod)
- You do NOT need a Dockerfile; the builder generates one
- `spec.auth` is the ONLY way to get `BIO_CLIENT_ID` injected. A bio-id dependency won't do it
- `BIO_ID_URL`, `SEPTOR_URL`, `IEC_QUEUE_URL`, `IEC_WALLET_URL` are always injected; never declare them
- See tawa.insureco.io/reference/catalog-info for the full catalog reference
Install
Copy the skill content and save it to:
~/.claude/commands/tawa-scaffold.mdComing soon via CLI:
tawa chaac install tawa-scaffoldDetails
- Format
- Command
- Category
- platform
- Version
- 1.0.1097
- Tokens
- ~1,914
- Updated
- 2026-10-13
platform