Aller au contenu principal

ADR-045: stoa.yaml Declarative API Specification

Metadata

FieldValue
StatusAccepted
Date2026-02-16
AuthorChristophe ABOULICAM
DecidersSTOA Core Team
CategoryArchitecture / Developer Experience / GitOps
LinearCAB-1352
  • ADR-007: GitOps with ArgoCD
  • ADR-024: Gateway Unified Modes (edge-mcp, sidecar, proxy, shadow)
  • ADR-037: Deployment Modes -- Sovereign First Strategy
  • CAB-374: Deployment Lifecycle MEGA (parent ticket)
  • CAB-1353: Deployment Lifecycle API (PR #570)

Context

STOA Platform enables organizations to deploy APIs across multiple gateways (STOA, Kong, Gravitee, webMethods, Apigee) with a unified control plane. Today, API deployment requires multiple steps across different interfaces:

  1. Upload OpenAPI spec via Console UI
  2. Configure rate limits manually in Console
  3. Configure CORS via separate policy screen
  4. Deploy to gateway via "Deploy" button
  5. Repeat for each environment (dev, staging, production)

Problem Statement

No single source of truth: API configuration is scattered across Console UI forms, manual deployments, and undocumented settings.

No GitOps workflow: Developers cannot version-control their API configuration alongside their backend code.

No CI/CD integration: Automated pipelines cannot deploy APIs programmatically without complex API client scripts.

No MCP Copilot support: AI agents cannot propose or execute API deployments without a declarative format.

Inspiration

PlatformFormatPhilosophy
Vercelvercel.jsonConfig-as-code, zero-config defaults
KubernetesYAML manifestsDeclarative desired state
Terraform.tf filesInfrastructure-as-code
Kongkong.yaml (DB-less)Declarative gateway config

Decision

Introduce stoa.yaml -- Kubernetes-Inspired Declarative API Spec

apiVersion: stoa/v1alpha1
kind: APIDeployment
metadata:
name: customer-api
tenant: acme
spec:
source:
type: openapi
url: https://api.example.com/v1/openapi.json
gateways:
- name: stoa-edge
mode: edge-mcp
policies:
rateLimit:
requests: 1000
window: 1m
cors:
origins: ["https://app.acme.com"]
methods: ["GET", "POST"]
deployment:
strategy: rolling
rollback: automatic

Design Principles

  1. Single source of truth -- stoa.yaml lives in the same git repo as the API backend code
  2. Kubernetes-style -- Familiar apiVersion, kind, metadata, spec structure
  3. Vercel-inspired defaults -- Sensible zero-config defaults (rate limit = 1000/min, CORS = ["*"])
  4. Multi-entry-point -- Same spec consumed by CLI, Console UI, MCP Copilot
  5. Diff-driven -- Tools can compute what changed (like kubectl diff or terraform plan)
  6. Environment-aware -- Single spec, environment-specific overrides

Schema Structure

Top-Level Fields

apiVersion: stoa/v1alpha1        # Versioned API contract
kind: APIDeployment # Resource type
metadata: # Identity + ownership
name: string # API unique identifier
tenant: string # Multi-tenant namespace
labels: # Optional key-value tags
team: payments
spec: # Desired state
...

spec.source -- API Definition

spec:
source:
type: openapi | asyncapi | grpc | graphql
url: https://... # Remote URL (preferred)
file: ./openapi.yaml # Local file (relative to stoa.yaml)
inline: | # Inline YAML (for small APIs)
openapi: 3.0.0
...

Exactly one of url, file, or inline must be provided.

spec.gateways -- Deployment Targets

spec:
gateways:
- name: stoa-edge # Gateway instance name
mode: edge-mcp # Deployment mode (ADR-024)
environment: production
- name: kong-staging
mode: proxy
environment: staging

Zero-config default: if omitted, deploys to default gateway in stoa-edge mode.

spec.policies -- Traffic Policies

spec:
policies:
rateLimit:
requests: 1000
window: 1m
scope: tenant | api | consumer
cors:
origins: ["https://app.acme.com"]
methods: ["GET", "POST", "PUT", "DELETE"]
credentials: true
authentication:
type: oauth2 | api-key | mtls
issuer: https://auth.acme.com/realms/prod
scopes: ["read:customers", "write:customers"]

Zero-config defaults: no rateLimit = 1000 req/min, no cors = origins: ["*"], no authentication = type: api-key.

spec.deployment -- Deployment Strategy

spec:
deployment:
strategy: rolling | blue-green | canary
rollback: automatic | manual
healthCheck:
path: /health
interval: 10s
notifications:
webhook: https://slack.com/webhook/...
onFailure: true

Environment Overrides

spec:
policies:
rateLimit:
requests: 1000
window: 1m
environments:
dev:
policies:
rateLimit:
requests: 10000
production:
gateways:
- name: stoa-prod-1
- name: stoa-prod-2
policies:
rateLimit:
requests: 100

Merge logic: base spec + environment-specific overrides (deep merge).

Entry Points -- 3 Ways to Deploy

1. CLI (stoactl deploy)

stoactl deploy                        # Deploy to default environment
stoactl deploy --env production # Deploy to production
stoactl deploy --dry-run # Preview changes
stoactl deploy --watch # Stream deployment logs

2. Console UI (Form to YAML Generator)

  1. Fill out Console UI form
  2. Console generates stoa.yaml from form inputs
  3. User downloads stoa.yaml or commits directly to git
  4. Bidirectional sync: form edits update YAML, YAML edits update form

3. MCP Copilot (AI Agent)

AI agents can generate, validate, diff, and deploy via MCP tools:

  • generate_stoa_yaml -- parameters to YAML
  • validate_stoa_yaml -- validation result
  • diff_deployment -- current state diff
  • deploy_api -- execute deployment

Examples

Minimal REST API Proxy

apiVersion: stoa/v1alpha1
kind: APIDeployment
metadata:
name: weather-api
tenant: acme
spec:
source:
url: https://api.weather.com/v3/openapi.json

Deploys to default gateway with default rate limit (1000/min), CORS (*), and API key auth.

MCP Tool Registration

apiVersion: stoa/v1alpha1
kind: APIDeployment
metadata:
name: customer-lookup
tenant: acme
labels:
mcp-enabled: "true"
spec:
source:
type: openapi
url: https://crm.acme.com/api/openapi.json
gateways:
- name: stoa-edge
mode: edge-mcp
policies:
rateLimit:
requests: 500
window: 1m
scope: consumer
authentication:
type: oauth2
issuer: https://auth.acme.com/realms/prod
scopes: ["read:customers"]
mcp:
tools:
- name: lookup_customer_by_email
description: "Find customer record by email address"
endpoint: /customers/search
method: POST
parameters:
email:
type: string
required: true

Multi-Gateway Deployment

apiVersion: stoa/v1alpha1
kind: APIDeployment
metadata:
name: payment-api
tenant: acme
spec:
source:
url: https://api.acme.com/payments/openapi.json
gateways:
- name: stoa-edge
mode: edge-mcp
- name: kong-legacy
mode: proxy
- name: webmethods-mainframe
mode: sidecar
policies:
rateLimit:
requests: 100
window: 1m
authentication:
type: mtls
deployment:
strategy: blue-green
rollback: automatic
notifications:
webhook: https://hooks.slack.com/services/XXX
onFailure: true

Implementation Phases

Phase 1: Schema Definition + Validation (CAB-1352, 3 pts)

  • JSON Schema for stoa.yaml
  • Python Pydantic model (APIDeploymentSpec)
  • CLI command: stoactl validate stoa.yaml

Phase 2: CLI Entry Point (CAB-1358, 5 pts)

  • stoactl deploy command with --dry-run, --env, --watch flags
  • Environment variable expansion in stoa.yaml

Phase 3: Console UI Roundtrip (CAB-1359, 8 pts)

  • Console "Import stoa.yaml" / "Export stoa.yaml" buttons
  • Bidirectional sync: form edits update YAML, YAML edits update form

Phase 4: MCP Copilot Tools (CAB-1360, 5 pts)

  • 4 MCP tools: generate, validate, diff, deploy

Total: 21 points

Consequences

Positive

  • GitOps-native: API config lives in git alongside backend code
  • Developer experience: Single command (stoactl deploy) replaces multi-step UI
  • Multi-environment safety: Environment overrides prevent config drift
  • AI-assisted deployment: MCP Copilot can propose, validate, and deploy
  • Hybrid workflows: Developers use CLI, DevOps use Console UI, both stay in sync

Negative

  • Learning curve: New users must learn YAML syntax (mitigated by Console UI and MCP Copilot)
  • Schema evolution: Breaking changes require migration scripts (use v1alpha1 versioning)
  • Roundtrip sync: Bidirectional UI/YAML sync requires comment and formatting preservation

Risks

RiskProbabilityImpactMitigation
Developers stick to UIMediumMediumMake CLI the default in docs
Schema becomes kitchen sinkHighHighStrict review for new fields
Environment overrides too complexMediumMediumLimit to 2 levels deep

Alternatives Considered

JSON Format

Rejected: YAML is more human-readable for configuration, supports comments, and aligns with Kubernetes ecosystem.

HCL (HashiCorp Configuration Language)

Rejected: Less familiar than YAML, limited parser availability in Python/TypeScript.

Multiple Files (Pulumi-Style)

Rejected: No single source of truth, more files = more confusion for beginners.

No Declarative Format (REST API Only)

Rejected: No GitOps workflow, no version control, no AI Copilot support.

References