The single source of truth for how the builder deploys your service. Every deploy reads this file. The builder generates Dockerfiles, provisions databases, configures OAuth, registers routes, and sets up DNS — all from this one file.
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-svc
description: My API service
annotations:
insureco.io/framework: express
spec:
type: service
lifecycle: production
owner: my-org # your org slug from Bio-ID JWT
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-svc
annotations:
insureco.io/framework: express # REQUIRED
insureco.io/catalog-version: "0.5.0" # required for spec.tests
insureco.io/pod-tier: nano # nano|small|medium|large|xlarge
insureco.io/health-endpoint: /health # REQUIRED (0.2.0+)
spec:
type: service
lifecycle: production
owner: my-org # REQUIRED (0.2.0+), not 'unknown'
routes:
- path: /api/my-svc/data
methods: [GET, POST]
auth: required
gas: 1
databases:
- type: mongodb # → MONGODB_URI
- type: redis # → REDIS_URL
internalDependencies:
- service: septor
- service: iec-wallet
port: 3000
auth:
mode: sso # sso | service-only | none (default)
passport: # enrich JWT (catalog 0.6.0+)
branding: true # bundle vault branding into JWT
scopes: [raterspot:rate] # resolve from Koko, bundle into permissions
headless: true # enable embed API (optional)
allowedOrigins: # browser SDK CORS (optional)
- https://myapp.tawa.pro
tests:
smoke:
- path: /health
expect: 200
| Framework | Start Command | Port | Health |
|---|---|---|---|
express | node dist/index.js | 3000 | /health |
nextjs | npm start | 3000 | /api/health |
hono | node dist/index.js | 3000 | /health |
fastify | node dist/index.js | 3000 | /health |
worker | node dist/worker.js | 3000 | none |
static | nginx | 80 | /health |
Key rule: internalDependencies for everything without Bio-ID scopes. dependencies only when the target service defines explicit scope grants.
# Use internalDependencies for platform services and most internal services:
spec:
internalDependencies:
- service: septor # → SEPTOR_URL
- service: iec-wallet # → IEC_WALLET_URL
- service: iec-queue # → IEC_QUEUE_URL
# Use dependencies ONLY for services with Bio-ID scope grants:
spec:
dependencies:
- service: raterspot
transport: direct # injects RATERSPOT_URL
scopes: [raterspot:rate] # REQUIRED, cannot be empty
Common mistake: using dependencies with scopes: [] — that is a validation error.
Common mistake: using transport: gateway expecting a URL env var — gateway does not inject a URL.
routes:
- path: /api/my-svc/screen
methods: [POST]
auth: required # required | none | service | public
gas: 5 # tokens per successful call. 0=free. omit=default(1)
Auth values:
required — valid user JWT required (Janus verifies)service — service-to-service JWT requiredpublic — no auth, but still meterednone — no auth, not metered (use for health checks)spec:
queues:
- name: process-claim
endpoint: /internal/jobs/process-claim
concurrency: 5
retries: 3
retryDelayMs: 5000
timeoutMs: 30000
schedules:
- name: nightly-sync
cron: "0 2 * * *"
endpoint: /internal/cron/nightly-sync
timezone: America/Denver
timeoutMs: 60000
Queue and schedule endpoints must NOT be listed under routes:. They are internal-only.
spec:
auth:
mode: sso # "sso" = user-facing OAuth (BIO_CLIENT_ID + BIO_CLIENT_SECRET injected)
# "service-only" = client credentials only (no user login)
# "none" = no OAuth client provisioned (default)
BIO_ID_URL is auto-injected as a core platform env var — no internalDependencies declaration needed. NEVER use internalDependencies: bio-id to enable OAuth.
The builder registers the redirect URI automatically:
https://{service}.sandbox.tawa.pro/api/auth/callbackhttps://{service}.tawa.pro/api/auth/callbackEnrich the JWT with org branding and permissions at mint time.
spec:
auth:
mode: sso
passport:
branding: true # bundle vault branding into JWT
scopes: [raterspot:rate] # resolve from Koko, bundle into permissions
headless: true # enable embed API (optional)
allowedOrigins: # browser SDK CORS (optional)
- https://myapp.tawa.pro
branding: true — Bio-ID fetches vault.ins.bio/v1/branding/{orgSlug} at mint time, bundles into JWTscopes — resolved from Koko, bundled into passport.permissions.scopesheadless: true — enables /api/embed/* endpoints for this client (signup, login, magic-link, verify, refresh)allowedOrigins — CORS whitelist for browser SDK callers (headless mode only)annotations:
insureco.io/framework: express # REQUIRED
insureco.io/catalog-version: "0.5.0" # required for spec.tests
insureco.io/node-version: "20" # default: 20
insureco.io/pod-tier: nano # default: nano
insureco.io/health-endpoint: /health # REQUIRED (0.2.0+)
insureco.io/port: "3000" # default: 3000
insureco.io/build-command: npm run build
insureco.io/start-command: node dist/index.js
insureco.io/output-dir: dist
insureco.io/openapi: openapi.yaml # enables auto-generated UI tile
If your service lives in a subdirectory (e.g., web/ inside a monorepo), add .iec.yaml at the repo root:
# .iec.yaml (repo root)
appPath: web
The builder then reads catalog-info.yaml from web/catalog-info.yaml, uses web/ as the build context, and resolves Dockerfile and Helm chart paths relative to web/. No other changes needed.
See the .iec.yaml — Builder Config reference for all supported fields.
metadata.name becomes your hostname: my-svc.tawa.insureco.ionano — start here, upgrade when you have data.iec.yaml at the repo root with appPath: <subdir> — see .iec.yaml referenceLast updated: March 6, 2026