← Back to Skills

PlugIN Scaffold

Complete guide to building a production-ready Tawa PlugIN from scratch.

Commandconfigure

About

End-to-end walkthrough for building a Tawa PlugIN: scaffolding with tawa sample, catalog-info.yaml with routes, gas, and databases, the mandatory health endpoint, gas metering via Janus, Septor audit wiring, Zod input validation, and the OpenAPI spec that generates an auto-UI tile in the InsurEco portal. Includes a full checklist.

Skill Content

This is the raw markdown that gets installed as a Claude Code command.

# PlugIN Scaffold — Complete Guide

## What this skill covers
Building a complete Tawa PlugIN from scratch: catalog-info.yaml configuration, gas metering, Septor audit hooks, OpenAPI spec, and auto-generated UI.

## 1. Scaffold

```bash
cd ~/dev
tawa sample --api my-plugin
cd my-plugin
```

Generates an Express service skeleton with catalog-info.yaml, health endpoint, and tsconfig.

## 2. catalog-info.yaml

```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-plugin
  description: OFAC entity screening for insurance applications
  annotations:
    insureco.io/framework: express
    insureco.io/pod-tier: nano
    insureco.io/openapi: openapi.yaml    # enables auto-generated UI tile
spec:
  type: service
  lifecycle: production
  owner: my-org

  routes:
    - path: /api/my-plugin/screen
      methods: [POST]
      auth: required
      gas: 5          # tokens charged per successful call
    - path: /api/my-plugin/status
      methods: [GET]
      auth: required
      gas: 1

  databases:
    - type: mongodb

  internalDependencies:
    - service: septor   # SEPTOR_URL
```

## 3. Health Endpoint (mandatory)

```typescript
app.get('/health', (_req, res) => {
  res.json({ status: 'ok', service: 'my-plugin' })
})
```

Every service MUST have `GET /health`. Without it, the deploy health check fails immediately.

## 4. Gas Metering

Gas is charged automatically by Janus for routes declared with `gas:`. You do not write gas charging code — declare it in catalog-info.yaml and Janus handles it.

## 5. Septor Audit (mandatory for financial/compliance)

```typescript
import { Septor } from '@insureco/septor'

const septor = new Septor({
  apiUrl: process.env.SEPTOR_URL,
  namespace: process.env.SERVICE_NAME || 'my-plugin',
})

app.post('/api/my-plugin/screen', async (req, res) => {
  const { entityName, orgSlug } = req.body
  const userId = req.user?.bioId

  const result = await screenEntity(entityName)

  // Always fire-and-forget — never await Septor on the request path
  septor.emit(result.status === 'cleared' ? 'screen.cleared' : 'screen.flagged', {
    entityId: orgSlug,
    data: { entityName, result: result.status, confidence: result.confidence },
    metadata: { who: userId, why: 'OFAC screening requested' },
  }).catch((err) => logger.error({ err }, 'Septor emit failed'))

  res.json({ success: true, data: result })
})
```

## 6. Input Validation

Always validate at API boundaries:

```typescript
import { z } from 'zod'

const ScreenRequest = z.object({
  entityName: z.string().min(1).max(200),
  entityType: z.enum(['individual', 'business']),
  orgSlug: z.string().min(1),
})

app.post('/api/my-plugin/screen', async (req, res) => {
  const parsed = ScreenRequest.safeParse(req.body)
  if (!parsed.success) {
    return res.status(400).json({ success: false, error: parsed.error.message })
  }
  const { entityName, entityType, orgSlug } = parsed.data
  // ...
})
```

## 7. OpenAPI Spec (for Auto-Generated UI)

Create `openapi.yaml` in the repo root:

```yaml
openapi: 3.0.0
info:
  title: My Plugin
  version: 1.0.0
paths:
  /api/my-plugin/screen:
    post:
      summary: Screen an entity against OFAC watchlists
      operationId: screenEntity
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [entityName, entityType]
              properties:
                entityName:
                  type: string
                entityType:
                  type: string
                  enum: [individual, business]
      responses:
        '200':
          description: Screening result
```

On deploy, the platform reads this spec and auto-generates a tile UI.

## Complete PlugIN Checklist

```
Infrastructure:
  [ ] catalog-info.yaml with routes, gas, databases, internalDependencies
  [ ] GET /health returning { status: 'ok' }
  [ ] tawa preflight passes

Code Quality:
  [ ] Zod validation on all POST endpoints
  [ ] Septor emit on all financial/compliance operations (fire-and-forget)
  [ ] Structured logger (pino) — no console.log
  [ ] No hardcoded secrets — use process.env

Documentation:
  [ ] openapi.yaml for auto-generated UI
```

## Key Facts
- First deploy auto-provisions MongoDB, Redis, or Neo4j if declared in `databases:`
- Pod tier defaults to `nano` — start here, upgrade when you have load data
- Services are private by default — routes must be in `routes:` to be publicly accessible
- `gas: 0` means the route is free (good for status/health endpoints)
- PlugINs installed by org users appear as tiles in the InsurEco portal
- `insureco.io/openapi: openapi.yaml` annotation enables the auto-generated UI tile

## Common Mistakes
- Missing `/health` endpoint — deploy health check fails immediately
- Not declaring `internalDependencies` — `SEPTOR_URL` won't be injected
- Awaiting `septor.emit()` on the request path — always fire-and-forget
- Using `auth: optional` on financial routes — always `auth: required` for metered routes
- Forgetting `gas:` on routes — Janus won't meter the calls
- Omitting `openapi.yaml` reference in annotations — no auto-generated UI tile

Install

Copy the skill content and save it to:

~/.claude/commands/plugin-scaffold.md
Download .md

Coming soon via CLI:

tawa chaac install plugin-scaffold

Details

Format
Command
Category
configure
Version
1.0.0
Tokens
~1,000
Updated
2026-03-01
pluginscaffoldgasseptoropenapizodvalidationinsureco