mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-23 03:27:43 +09:00
451 lines
17 KiB
JSON
451 lines
17 KiB
JSON
{
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://serverkit.ai/serverkit-yaml.schema.json",
|
|
"title": "ServerKit Manifest",
|
|
"description": "Declarative deployment manifest (serverkit.yaml / serverkit.json). A version:1 file describes a whole deployment — services, databases, disks, backups, env vars, cron and domains — that ServerKit parses, diffs against live state, and applies. camelCase is canonical; snake_case aliases are accepted everywhere.",
|
|
"type": "object",
|
|
"required": ["version"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"version": {
|
|
"description": "Manifest schema version. 1 selects the declarative multi-service spec. Files without version:1 are legacy flat manifests and keep their v0 behavior.",
|
|
"const": 1
|
|
},
|
|
"server": {
|
|
"description": "Optional default fleet target (agent server name or id). A service may override it. Omit for the local panel host.",
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"project": {
|
|
"description": "Optional project name this manifest maps to. Defaults to the repository name.",
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"envVars": {
|
|
"description": "Manifest-level (project-scoped) environment variables, applied to every service as a shared variable group.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/envVar" }
|
|
},
|
|
"services": {
|
|
"description": "The services that make up this deployment.",
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": { "$ref": "#/definitions/service" }
|
|
},
|
|
"domains": {
|
|
"description": "Public domains routed to services.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/domain" }
|
|
},
|
|
"capabilities": {
|
|
"description": "Closed Recipe capability set. Every configure/verify step must declare its type:kind capability here so older panels can refuse unsupported Recipes before doing work.",
|
|
"type": "array",
|
|
"uniqueItems": true,
|
|
"items": { "type": "string", "pattern": "^[a-z]+:[a-z0-9._-]+$" }
|
|
},
|
|
"requires": {
|
|
"description": "Recipe host requirements used by preflight and provider placement.",
|
|
"$ref": "#/definitions/recipeRequirements"
|
|
},
|
|
"configure": {
|
|
"description": "Ordered, idempotent Recipe steps run after the declared services are applied.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/recipeStep" }
|
|
},
|
|
"verify": {
|
|
"description": "Outcome checks. A successful process is not a successful Recipe until these checks pass.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/recipeVerifyStep" }
|
|
}
|
|
},
|
|
"definitions": {
|
|
"service": {
|
|
"type": "object",
|
|
"required": ["name", "type"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"name": {
|
|
"description": "Unique service name within the manifest. Becomes the Application (or ManagedDatabase) name.",
|
|
"type": "string",
|
|
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
|
|
"minLength": 1,
|
|
"maxLength": 63
|
|
},
|
|
"type": {
|
|
"description": "Service kind. web/worker/static/docker create Applications; postgres/mysql/mariadb/redis create ManagedDatabases.",
|
|
"enum": ["web", "worker", "static", "docker", "postgres", "mysql", "mariadb", "redis"]
|
|
},
|
|
"runtime": {
|
|
"description": "Build runtime hint for app services (e.g. python, node, static, docker). Ignored for db types.",
|
|
"type": "string"
|
|
},
|
|
"buildCommand": {
|
|
"description": "Command used to build the app (buildpack override).",
|
|
"type": "string"
|
|
},
|
|
"startCommand": {
|
|
"description": "Command used to start the app (buildpack override).",
|
|
"type": "string"
|
|
},
|
|
"dockerfilePath": {
|
|
"description": "Build the service from this Dockerfile in the repository (repo root = build context) — the monorepo path: one repo, several services, each built from its own Dockerfile. Relative path only; mutually exclusive with `image` and `containers`.",
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"port": {
|
|
"description": "Container port the app listens on.",
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"maximum": 65535
|
|
},
|
|
"ports": {
|
|
"description": "Typed L4 port publishes (appliance tier). Each entry is a raw tcp/udp publish — the escape hatch for services that speak more than HTTP (media/UDP, VoIP, brokers). The legacy scalar `port` keeps its HTTP/nginx behavior and is separate.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/portDecl" }
|
|
},
|
|
"image": {
|
|
"description": "Container image reference for a BYO-image app (appliance tier) — a first-class peer of the buildpack path.",
|
|
"type": "string"
|
|
},
|
|
"registry": {
|
|
"description": "Named private registry to authenticate against before pulling `image`. A registry that is unknown or has no stored credential is a plan-time blocker.",
|
|
"type": "string"
|
|
},
|
|
"hostRequirements": {
|
|
"description": "Elevated host requirements (appliance tier) — privileged/capAdd/sysctls/devices/kernelModules. Always listed in plain words in the plan; never applied silently.",
|
|
"$ref": "#/definitions/hostRequirements"
|
|
},
|
|
"bootstrap": {
|
|
"description": "First-boot bootstrap (appliance tier). Runs once via `docker compose run --rm` after volumes exist and before the first up — for appliances that generate a config tree once.",
|
|
"$ref": "#/definitions/bootstrap"
|
|
},
|
|
"containers": {
|
|
"description": "A multi-container UNIT (appliance tier): one service rendered as one compose project with a private network and health-gated dependsOn. Mutually exclusive with the buildpack keys. Keyed by container name.",
|
|
"type": "object",
|
|
"minProperties": 1,
|
|
"additionalProperties": { "$ref": "#/definitions/container" }
|
|
},
|
|
"healthCheckPath": {
|
|
"description": "HTTP path used for health checks and the zero-downtime restart gate.",
|
|
"type": "string"
|
|
},
|
|
"autoDeploy": {
|
|
"description": "When true, a push that changes this service's declared config auto-applies. Otherwise the manifest flips to pending and waits for an explicit apply.",
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"version": {
|
|
"description": "Engine version for db services (e.g. \"16\" for postgres).",
|
|
"type": ["string", "number"]
|
|
},
|
|
"server": {
|
|
"description": "Per-service fleet target override.",
|
|
"type": "string"
|
|
},
|
|
"cpu": {
|
|
"description": "CPU limit (cores).",
|
|
"type": ["number", "string"]
|
|
},
|
|
"memory": {
|
|
"description": "Memory limit (e.g. 512MB, 1GB).",
|
|
"type": ["string", "number"]
|
|
},
|
|
"envVars": {
|
|
"description": "Per-service environment variables.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/envVar" }
|
|
},
|
|
"disks": {
|
|
"description": "Persistent volumes for app services.",
|
|
"type": "array",
|
|
"items": { "$ref": "#/definitions/disk" }
|
|
},
|
|
"disk": {
|
|
"description": "Single primary disk for db services.",
|
|
"$ref": "#/definitions/disk"
|
|
},
|
|
"cron": {
|
|
"description": "Scheduled job(s) run in the service context.",
|
|
"oneOf": [
|
|
{ "$ref": "#/definitions/cron" },
|
|
{ "type": "array", "items": { "$ref": "#/definitions/cron" } }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"bootstrap": {
|
|
"type": "object",
|
|
"required": ["command"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"command": {
|
|
"description": "Command run once inside a throwaway service container.",
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"timeoutSeconds": {
|
|
"description": "Max seconds the bootstrap may run before it is killed and the step fails.",
|
|
"type": "integer",
|
|
"minimum": 1
|
|
}
|
|
}
|
|
},
|
|
"container": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"image": { "description": "Container image reference.", "type": "string" },
|
|
"registry": { "description": "Named private registry to authenticate against before pulling.", "type": "string" },
|
|
"ports": { "type": "array", "items": { "$ref": "#/definitions/portDecl" } },
|
|
"disks": { "type": "array", "items": { "$ref": "#/definitions/disk" } },
|
|
"envVars": { "type": "array", "items": { "$ref": "#/definitions/envVar" } },
|
|
"bootstrap": { "$ref": "#/definitions/bootstrap" },
|
|
"hostRequirements": { "$ref": "#/definitions/hostRequirements" },
|
|
"healthCheck": { "$ref": "#/definitions/healthCheck" },
|
|
"dependsOn": { "type": "array", "items": { "$ref": "#/definitions/dependsOn" } }
|
|
}
|
|
},
|
|
"healthCheck": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"cmd": { "description": "Shell command probe (rendered as CMD-SHELL).", "type": "string" },
|
|
"httpPath": { "description": "HTTP path probed with wget on localhost.", "type": "string" },
|
|
"interval": { "description": "e.g. 30s.", "type": "string" },
|
|
"timeout": { "description": "e.g. 5s.", "type": "string" },
|
|
"retries": { "type": "integer", "minimum": 1 }
|
|
}
|
|
},
|
|
"dependsOn": {
|
|
"type": "object",
|
|
"required": ["service"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"service": { "description": "Sibling container name in the same unit.", "type": "string" },
|
|
"condition": { "description": "healthy waits for the dependency's healthcheck; started only waits for it to start.", "enum": ["healthy", "started"] }
|
|
}
|
|
},
|
|
"hostRequirements": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"privileged": { "description": "Run privileged (big hammer — always shown in the plan, never silent).", "type": "boolean" },
|
|
"capAdd": { "description": "Linux capabilities to add.", "type": "array", "items": { "type": "string" } },
|
|
"sysctls": { "description": "sysctl key/value pairs.", "type": "object" },
|
|
"devices": { "description": "Host devices to expose.", "type": "array", "items": { "type": "string" } },
|
|
"kernelModules": { "description": "Kernel modules the workload needs (advisory /proc/modules check only).", "type": "array", "items": { "type": "string" } }
|
|
}
|
|
},
|
|
"portDecl": {
|
|
"type": "object",
|
|
"required": ["port"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"port": {
|
|
"description": "Host port to publish.",
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"maximum": 65535
|
|
},
|
|
"containerPort": {
|
|
"description": "Container port to publish to. Defaults to the host port.",
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"maximum": 65535
|
|
},
|
|
"protocol": {
|
|
"description": "L4 protocol. Defaults to tcp.",
|
|
"enum": ["tcp", "udp"]
|
|
},
|
|
"expose": {
|
|
"description": "public = bind 0.0.0.0 and open the firewall; local = bind 127.0.0.1 only. Defaults to public.",
|
|
"enum": ["public", "local"]
|
|
}
|
|
}
|
|
},
|
|
"envVar": {
|
|
"type": "object",
|
|
"required": ["key"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"key": {
|
|
"description": "Environment variable name.",
|
|
"type": "string",
|
|
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
},
|
|
"value": {
|
|
"description": "Literal value. Use only for non-secrets — secrets never live in the manifest.",
|
|
"type": ["string", "number", "boolean"]
|
|
},
|
|
"fromSecret": {
|
|
"description": "Reference to a vault secret by name; resolved at injection time, never stored in the env row.",
|
|
"type": "string"
|
|
},
|
|
"fromService": {
|
|
"description": "Reference to a sibling service's connection property.",
|
|
"type": "object",
|
|
"required": ["name", "property"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"property": {
|
|
"type": "string",
|
|
"enum": [
|
|
"connectionString", "host", "port", "database", "username", "password", "url"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"fromServer": {
|
|
"description": "Reference to the service's own target server identity (appliance tier). publicIp is what a NAT'd media/WebRTC bridge must advertise to clients.",
|
|
"type": "object",
|
|
"required": ["property"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"property": { "type": "string", "enum": ["publicIp", "hostname"] }
|
|
}
|
|
},
|
|
"generate": {
|
|
"description": "When true, ServerKit generates a random value on first apply, stores it as a vault secret, and rewires this entry as a fromSecret reference.",
|
|
"type": "boolean"
|
|
}
|
|
}
|
|
},
|
|
"disk": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"name": {
|
|
"description": "Volume name.",
|
|
"type": "string"
|
|
},
|
|
"mountPath": {
|
|
"description": "Mount path inside the container.",
|
|
"type": "string"
|
|
},
|
|
"size": {
|
|
"description": "Requested size (e.g. 10GB). Recorded; not enforced by the local driver.",
|
|
"type": ["string", "number"]
|
|
},
|
|
"backup": { "$ref": "#/definitions/backup" }
|
|
}
|
|
},
|
|
"backup": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"schedule": {
|
|
"description": "Backup frequency.",
|
|
"type": "string",
|
|
"enum": ["hourly", "daily", "weekly", "monthly"]
|
|
},
|
|
"retain": {
|
|
"description": "Number of backups to keep.",
|
|
"type": "integer",
|
|
"minimum": 1
|
|
}
|
|
}
|
|
},
|
|
"cron": {
|
|
"type": "object",
|
|
"required": ["schedule", "command"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"schedule": {
|
|
"description": "Cron expression (5-field) or shorthand (daily, hourly...).",
|
|
"type": "string"
|
|
},
|
|
"command": {
|
|
"description": "Command to run.",
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"domain": {
|
|
"type": "object",
|
|
"required": ["host"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"host": {
|
|
"description": "Fully-qualified hostname (e.g. api.example.com).",
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"service": {
|
|
"description": "Name of the service this domain routes to.",
|
|
"type": "string"
|
|
},
|
|
"ssl": {
|
|
"description": "Certificate mode. auto = best-effort issuance (never blocks); off = HTTP only.",
|
|
"oneOf": [
|
|
{ "type": "string", "enum": ["auto", "off", "on"] },
|
|
{ "type": "boolean" }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"recipeRequirements": {
|
|
"type": "object",
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"cpuCores": { "type": "integer", "minimum": 1 },
|
|
"memoryMB": { "type": "integer", "minimum": 1 },
|
|
"diskGB": { "type": "integer", "minimum": 1 },
|
|
"igpu": { "enum": ["required", "preferred", "none"] },
|
|
"gpu": { "enum": ["required", "preferred", "none"] }
|
|
}
|
|
},
|
|
"recipeStep": {
|
|
"type": "object",
|
|
"required": ["id", "type", "kind"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
|
|
"type": { "enum": ["probe", "derive", "apply", "handoff", "verify"] },
|
|
"kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
|
|
"title": { "type": "string", "minLength": 1 },
|
|
"description": { "type": "string" },
|
|
"dependsOn": {
|
|
"type": "array",
|
|
"uniqueItems": true,
|
|
"items": { "type": "string" }
|
|
},
|
|
"params": { "type": "object" },
|
|
"ttlSeconds": { "type": "integer", "minimum": 1 },
|
|
"input": { "$ref": "#/definitions/recipeInput" }
|
|
}
|
|
},
|
|
"recipeVerifyStep": {
|
|
"type": "object",
|
|
"required": ["id", "kind"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
|
|
"type": { "const": "verify" },
|
|
"kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
|
|
"title": { "type": "string", "minLength": 1 },
|
|
"description": { "type": "string" },
|
|
"dependsOn": {
|
|
"type": "array",
|
|
"uniqueItems": true,
|
|
"items": { "type": "string" }
|
|
},
|
|
"params": { "type": "object" }
|
|
}
|
|
},
|
|
"recipeInput": {
|
|
"type": "object",
|
|
"required": ["key", "label"],
|
|
"additionalProperties": true,
|
|
"properties": {
|
|
"key": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
"label": { "type": "string", "minLength": 1 },
|
|
"help": { "type": "string" },
|
|
"url": { "type": "string" },
|
|
"secret": { "type": "boolean" }
|
|
}
|
|
}
|
|
}
|
|
}
|