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.mdComing soon via CLI:
tawa chaac install plugin-scaffoldDetails
- Format
- Command
- Category
- configure
- Version
- 1.0.0
- Tokens
- ~1,000
- Updated
- 2026-03-01
pluginscaffoldgasseptoropenapizodvalidationinsureco