Skip to content

Flank Seeds

Flank Seeds

Git-driven definitions for adapters and workflows. Synced to the sirloin database on every server startup.

How Seed Sync Works

On startup (server/entry.ts), flank reads all JSON files from seeds/adapters/ and seeds/workflows/ and upserts them via gRPC:

  1. New entity (not in DB) → created
  2. Existing, seed version higher than DB → updated
  3. Existing, seed version ≤ DB → skipped (DB is newer, e.g. edited via UI)

Sync is idempotent — safe to restart repeatedly.

Making Changes

  1. Edit the seed JSON file
  2. Bump seedVersion (required — equal/lower versions are skipped)
  3. Restart the flank server (or Docker container)
  4. Verify with pnpm validate:seeds before committing

Validation

Terminal window
pnpm validate:seeds

Checks all seed files for:

  • Required fields present
  • Valid connection/auth types
  • Graph structure (no cycles, exactly one trigger, no disconnected nodes)

Adapter Seed Format

See adapters/adapter.schema.json for the full JSON Schema.

{
"id": "my-adapter", // Unique slug (lowercase, hyphens)
"name": "My Adapter", // Display name
"description": "What it does", // Optional
"category": "inference", // inference | storage | ml | ai | legacy
"seedVersion": 1, // Bump to push updates to DB
"connection": {
"type": "rest", // rest | grpc | s3
"baseUrl": "https://api.example.com"
},
"auth": {
"type": "bearer", // bearer | api-key-header | basic | aws-sig-v4 | none
"headerName": "Authorization",
"valueTemplate": "Bearer {{secret:my-api-key}}"
},
"operations": [
{
"id": "generate",
"name": "Generate Something",
"description": "Calls the /generate endpoint",
"inputSchema": {
"type": "object",
"properties": {
"prompt": { "type": "string" }
},
"required": ["prompt"]
},
"request": {
"method": "POST",
"path": "/generate",
"bodyTemplate": {
"prompt": "{{input.prompt}}"
}
},
"outputMapping": {
"taskId": "$.data.id" // JSONPath-like extraction from response
},
"polling": { // Optional: for async operations
"request": { "method": "GET", "path": "/tasks/{{taskId}}" },
"statusField": "$.status",
"completedValues": ["completed"],
"failedValues": ["failed"],
"outputMapping": { "result": "$.data.output" },
"intervalMs": 2000,
"maxAttempts": 120
},
"retry": { // Optional
"maxRetries": 2,
"backoffMs": 1000,
"retryableStatusCodes": [429, 502, 503]
}
}
]
}

Secret References

Adapter auth and templates can reference secrets with {{secret:name}}. Secrets are stored encrypted in sirloin and managed via the flank UI (/secrets) or pnpm provision:secrets.

For local dev, secrets fall back to env vars: {{secret:wavespeed-api-key}}FLANK_SECRET_WAVESPEED_API_KEY.

Workflow Seed Format

{
"name": "my-workflow", // Unique name (used as lookup key)
"description": "What it does",
"seedVersion": 1, // Bump to push updates
"inputSchema": { // JSON Schema for workflow inputs
"type": "object",
"properties": {
"user_id": { "type": "string" }
},
"required": ["user_id"]
},
"graph": {
"nodes": [
{
"id": "trigger",
"type": "core:trigger", // Format: "nodeType:kind"
"position": { "x": 0, "y": 200 },
"config": {}
},
{
"id": "load-data",
"type": "data:character",
"position": { "x": 250, "y": 200 },
"config": {
"characterId": "{{trigger.character_id}}"
}
},
{
"id": "generate",
"type": "adapter:wavespeed-seedream-v4:generate-image",
"position": { "x": 500, "y": 200 },
"config": {
"prompt": "{{nodes.load-data.description}}"
}
}
],
"edges": [
{ "id": "e1", "source": "trigger", "target": "load-data" },
{ "id": "e2", "source": "load-data", "target": "generate" }
]
}
}

Node Type Format

Node types in seeds use compound strings: "type:kind".

FormatTypeKindExample
core:triggercoretriggerWorkflow entry point
core:templatecoretemplateString template
core:conditioncoreconditionIf/else branch
core:fallbackcorefallbackPrimary + fallback adapter
data:characterdatacharacterLoad character from sirloin
adapter:wavespeed-seedream-v4:generate-imageadapterwavespeed-seedream-v4:generate-imageCall adapter operation

Template Syntax

Templates use {{...}} placeholders:

  • {{trigger.field}} — workflow input field
  • {{nodes.nodeId.outputKey}} — output from a previous node
  • {{secret:name}} — resolved secret value