Building a Tawa PlugIN
tawa init my-plugin # full scaffold tawa sample --api my-plugin # alternative ```
Ruleprovision
About
tawa init my-plugin # full scaffold tawa sample --api my-plugin # alternative ```
Skill Content
This is the raw markdown that gets installed as a Claude Code rule.
# Building a Tawa PlugIN
## Scaffold
```bash
tawa init my-plugin # full scaffold
tawa sample --api my-plugin # alternative
```
## Checklist
```
Infrastructure:
[ ] catalog-info.yaml with routes, gas, databases, internalDependencies
[ ] GET /health returning { status: 'ok', service: 'my-plugin' }
[ ] tawa preflight passes
Code:
[ ] Zod validation on all POST endpoints
[ ] Septor emit on all financial/compliance operations (fire-and-forget)
[ ] No console.log — use pino logger
[ ] No hardcoded secrets — use process.env
[ ] RelayClient.fromEnv() not new RelayClient({ jwtSecret })
[ ] verifyTokenJWKS() not verifyToken(token, secret)
Testing:
[ ] 80%+ test coverage
[ ] Tests verify Septor events emitted correctly
Documentation:
[ ] openapi.yaml for auto-generated UI tile
[ ] CHANGELOG.md (builder publishes on deploy automatically)
```
## Minimal catalog-info.yaml
```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-plugin
description: What your plugin does
annotations:
insureco.io/framework: express
insureco.io/pod-tier: nano
insureco.io/openapi: openapi.yaml # enables auto-generated UI tile
insureco.io/catalog-version: "0.5.0"
spec:
type: service
lifecycle: production
owner: my-org
routes:
- path: /api/my-plugin/action
methods: [POST]
auth: required
gas: 5 # tokens charged per successful call
databases:
- type: mongodb
internalDependencies:
- service: septor
port: 3000
tests:
smoke:
- path: /health
expect: 200
```
## Required: Health Endpoint
```typescript
app.get('/health', (_req, res) => {
res.json({ status: 'ok', service: 'my-plugin' })
})
```
## Required: Input Validation
```typescript
import { z } from 'zod'
const RequestSchema = z.object({
entityName: z.string().min(1).max(200),
orgSlug: z.string().min(1),
})
app.post('/api/my-plugin/action', async (req, res) => {
const parsed = RequestSchema.safeParse(req.body)
if (!parsed.success) {
return res.status(400).json({ success: false, error: parsed.error.message })
}
const { entityName, orgSlug } = parsed.data
// ...
})
```
## Required: Septor Audit
```typescript
septor.emit('action.completed', {
entityId: orgSlug,
data: { entityName, result: result.status },
metadata: { who: req.user?.bioId, why: 'Plugin action requested' },
}).catch((err) => logger.error({ err }, 'Septor emit failed'))
```
## Gas Is Automatic
Gas is charged by Janus for routes declared with `gas`. You write no gas code. The platform handles it.
## Key Facts
- `gas: 0` = free route (good for status/health checks)
- `auth: required` always on financial/compliance routes
- Missing `/health` = deploy health check fails immediately
- Missing `internalDependencies` = env vars like `SEPTOR_URL` not injected
- `CHANGELOG.md` in your repo is published automatically on every deploy
Install
Copy the skill content and save it to:
~/.claude/rules/tawa-plugin-scaffold.mdComing soon via CLI:
tawa chaac install tawa-plugin-scaffoldDetails
- Format
- Rule
- Category
- provision
- Version
- 1.0.51515
- Tokens
- ~738
- Updated
- 2026-06-24
platformpluginscaffoldgasseptoropenapivalidation