Source: schemas/manifest.schema.json
Generated automatically from the published contract sources.
Source path: schemas/manifest.schema.json.
Schema fields
| JSON pointer | Description and constraints |
|---|---|
| "" (root) | The executable form of the admission manifest contract (MAN). The two state one contract and change together; a test in the platform's suite holds them to each other. $schema: https://json\-schema\.org/draft/2020\-12/schema version: 2026-10-07.1 title: Turn Zero Cloud — run-layer manifest, revision 1 Type: object Additional properties: false Required fields: ["manifest_version","services","health","region","egress","audience","packages"] |
| / |
MAN-01: the contract revision this manifest declares against. Required value: 1 |
| / |
MAN-03: the provisioned services consumed, by the catalog's names; empty is a statement. Type: array uniqueItems: true |
| / |
|
| / |
Type: object Additional properties: false Required fields: ["kind"] |
| / |
MAN-03: `database`, `object_storage`, `accounts`, and `push` are the kinds the platform provisions or admits at submission as their services state. The `push` kind admits the configuration and the routes and provisions nothing at submission. The `email` kind is admitted ahead of its service and provisions nothing at this version. Allowed values: ["database","object_storage","email","accounts","push"] |
| / |
Type: object Additional properties: false Required fields: ["kind"] minProperties: 2 |
| / |
PSH-L0-01: the push kind naming its providers, as `configure_push` takes them: `apns` without Apple's gateway, and `fcm`. Each submission records them on the push configuration of each environment the application has, effective at once, and `create_environment` records them on development's. A rule `configure_push` refuses by name is refused `manifest_invalid` at the member's path, the violation naming that rule's error. A credential name not stored yet is accepted, as the `realm` member states. A provider the entry names is the manifest's: `configure_push` refuses a change to it `manifest_owned_field`. Apple's gateway differs by environment and stays `configure_push`'s: `sandbox` in development and `production` in production until it names another. Required value: push |
| / |
$ref: #/$defs/pushApns |
| / |
$ref: #/$defs/pushFcm |
| / |
Type: object Additional properties: false Required fields: ["kind","domain"] |
| / |
MAN-03: admitted ahead of its service; a custom domain provisions nothing at this version. Required value: custom_domain |
| / |
$ref: #/$defs/hostname |
| / |
Type: object Additional properties: false Required fields: ["kind","schedules"] |
| / |
Required value: schedule |
| / |
MAN-03: the schedule service's declarations (SVC-L0-15; the schedule service PRD's declaration statement) — one to twenty-five at the shape's cap, the plan's `schedule-count-limit` refused at submit by name. Each is a name (a lowercase letter first, then lowercase letters, digits, or underscores, with no hyphen, unique within the application), a five-field cron expression, and an absolute path. The cron expression is evaluated in UTC at the minute grain (minute, hour, day of month, month, day of week; `*`, an integer, a range, a list, or a step per field). Names, macros, and a sixth field are refused here, and a field out of range or an expression with no due time inside a year is refused at submit. The path is an absolute path on the application's own server (MAN-05's form) that is under neither reserved path prefix, `/__account/` nor `/__router/`. The handler at that path is an ordinary route the platform invokes through the serving router with the router's mark and the invocation headers Node.js Runtime states. The platform's harness answers 404 on that path to any request the serving router did not mark as a scheduled run, so the handler receives scheduled runs alone. The handler's one rule is idempotency per `x-turnzero-cloud-schedule-due`, a check of `x-turnzero-cloud-invocation` its own option, and it completes inside the schedule kind's window (HST-L0-02). Type: array Minimum items: 1 Maximum items: 25 uniqueItems: true |
| / |
Type: object Additional properties: false Required fields: ["name","cron","path"] |
| / |
$ref: #/$defs/slug |
| / |
$ref: #/$defs/cron |
| / |
Type: string Pattern: ^/[^\s]*$ |
| / |
Type: object Additional properties: false Required fields: ["kind"] |
| / |
MAN-03: the issue-tracking service (SVC-L0-19). With no `space`, the application's first submission creates one space of its own for both environments, its deployments reaching it at `report`; an application provisioned with a space per environment before that keeps its two spaces (ITS-L0-01). The receipt's `issue_tracking` list names each space the application's calls reach. Required value: issue_tracking |
| / |
MAN-03: optional. A space of the account's own the application's calls reach in place of a space of its own: one space identifier for both environments, or `{ development, production }` naming one for each. The platform binds it and creates nothing, and the gateway serves the binding while the account holds the `blueprint` profile. An application's own space, this application's or another's, is never named here and is refused `space_not_owned`, as is a space the account does not hold. An application holding its own space, or a space per environment from before, is refused `invalid_request`, since it binds no chosen space (ITS-L0-01). |
| / |
$ref: #/$defs/space_id |
| / |
Type: object Additional properties: false Required fields: ["development","production"] |
| / |
$ref: #/$defs/space_id |
| / |
$ref: #/$defs/space_id |
| / |
MAN-03: optional, named with `space` alone: `report`, the default, files reports and reads them; `contribute` adds the working calls (update, comment, relate, and the signal and ask calls). Any other value, `owner` among them, is refused `level_invalid`: a deployment never settles, configures, or exports a space. A `level` with no `space` is refused `invalid_request` naming `space` (ITS-L0-01). Type: string |
| / |
MAN-05: the health endpoint as an absolute HTTP path on the application's own address, conventionally `/health`. At each deploy and promote, the health gate sends GET to this path every three seconds for up to 180 seconds, and passes only on status 200 exactly (PLD-L0-63). Under an invited or workforce audience the serving router gates this path from outside, while the health gate reaches the compute directly; the `audience` description says what each means (ADM-L0-05). Each probe waits at most ten seconds for its answer to begin. Twenty consecutive probes answering one 4xx status other than 408, 425, and 429, then a control probe the application's own process answers, end the gate early, about a minute in. A 5xx answer or a transport error (a failed or timed-out connection) never counts toward that run. Serve the route from the moment the server listens, answering 503 until ready, so a route mounted late is never ended early. Answer no 4xx while starting. A server that waits to listen until its database migrations finish reads as starting, and the gate keeps waiting within its bound. A failed gate records its evidence in the version row's `outcome.gate`, read through `read_status` and `list_versions`. It holds the probe answers as a ledger, the last answer with its body's first 512 bytes where the application answered, what answered, the compute's state, and the console's last forty lines (PLD-L0-59). Type: string Pattern: ^/[^\s]*$ |
| / |
MAN-07: one of the day-one regions (CQ-174); meaning is PLD-L0-22's. At the beta the platform serves `usa` alone: `europe`, `uk`, and `global` are admitted by the schema ahead of their availability and a submission naming one is refused `region_unavailable` before anything is provisioned (PLD-L0-22). Allowed values: ["usa","europe","uk","global"] |
| / |
MAN-09: the network allowlist contains exact lowercase DNS hostnames and host families with one leading wildcard label (`*.example.com`, admitting names below it at any depth but not the apex). An empty list declares no tunneled external destinations. In enforce mode, an undeclared destination is refused as `egress_undeclared`. In observe mode, the destination is not refused for being undeclared, and the same name is recorded with the attempted host and the remedy — for a hostname the exact manifest edit (add the host to `egress` and redeploy). An address literal cannot be declared, so its remedy names the hostname the address serves (ADM-L0-03; EGW-L0-14; EGW-L0-17). Applications begin in observe mode; an operator can select enforce mode. Refusals return 403 and the `X-Egress-Refusal` header where the client's route exposes the proxy response. CONNECT tunnels are restricted to port 443; the proxy carries their bytes without checking for TLS or HTTP, and an entry contains no scheme or port (EGW-L0-12). Platform endpoints—the declared services' endpoints and the platform origin—are reached outside the proxy and need no list entry (EGW-L0-15). Names resolving to, and address literals in, private, loopback, link-local (including metadata), reserved, or platform ranges are refused by address class in both modes (EGW-L0-13). The list names the outside hosts the application's own process dials, apart from the platform endpoints and the declared upstreams, which the gateway dials. An upstream called on a stored key is declared in the `upstreams` member or through `declare_upstream`, not in this list (EGW-L0-02). Type: array uniqueItems: true |
| / |
$ref: #/$defs/egressEntry |
| / |
MAN-10: exactly one boundary — public; invited (ADM-L0-05: the serving router gates on the realm's session, and the application's end-user realms are invitation-only from the declaration, whatever creation mode their configuration holds); or workforce (one company's identity-provider tenant, named by the `tenant` member). Under invited and workforce, the optional `session_free_paths` member lists the paths the router lets through with no session (ADM-L0-05). Under those two, the serving router gates every path, the `health` path among them: a request from outside carrying no valid session is sent to sign-in or answered 401, unless a `session_free_paths` prefix covers it (ADM-L0-05). The health gate at each deploy and promote probes the application's compute directly, never through the router, so a deploy needs nothing listed (PLD-L0-63). |
| / |
Type: object Additional properties: false Required fields: ["kind"] |
| / |
Required value: public |
| / |
Type: object Additional properties: false Required fields: ["kind"] |
| / |
Required value: invited |
| / |
$ref: #/$defs/sessionFreePaths |
| / |
Type: object Additional properties: false Required fields: ["kind","tenant"] |
| / |
Required value: workforce |
| / |
The company's identity-provider tenant identifier on the provider's terms — Microsoft Entra ID's tenant id, a GUID, at this revision (ACS-L0-09). The audience gates who reaches the application and signs nobody in, so declare `entra` in the `realm` member's `sign_in_methods` and name this tenant in its `entra` member, in the same manifest. Type: string Minimum length: 1 |
| / |
$ref: #/$defs/sessionFreePaths |
| / |
MAN-13: the library entries this application holds, each as its name and the version held. At each submission, copy each row of the project's folder manifest, the manifest.json in its system/ folder, into this list as its `name` and `version`. That copy is the derivation the rule asks for; what it forbids is a list kept by hand beside the folder manifest (SPM-L0-50's rows; SPM-L0-49). Admission reads it to refuse a deploy declaring a version whose compatibility window has closed (API-L0-16), a refusal not active during the beta while the first-customer-release date is unset. An application holding no entry declares [] rather than omitting the member — MAN-02 requires the member, and an omission would read as a fact not yet known where the truth is that there are none. Type: array uniqueItems: true |
| / |
Type: object Additional properties: false Required fields: ["name","version"] |
| / |
The entry's stable name, as the catalog answers it: a package's folder name, `ui/<vocabulary>`, `fonts/<family>`, or `library/prd` for the library's own requirements entry. Type: string Minimum length: 1 |
| / |
FTR-L0-62's front-matter version, as held. Null on a font family, which carries no specification of its own and so no version of its own, and on the library's own requirements entry, whose two documents declare none. Type: ["string","null"] |
| / |
MAN-14: optional. Binds a stored secret to a setting the application's process reads, as an object from the setting's name to `{"secret": "<stored name>"}`, at most fifty entries. Omit the member where the process reads no stored value as a setting. This member is not `declare_upstream`'s `settings`, which name the settings an upstream's client reads while the stored key stays at the gateway. A setting name is an upper-case letter, then upper-case letters, digits, or underscores, ending in a letter or a digit. A name the platform reserves is refused `manifest_invalid` at its own path. The platform reserves any name under `TURNZERO_`, `APP_`, `HOSTING_`, `EGRESS_`, `ROUTER_`, `NODE_`, or `NPM_`, and `PORT`, `PATH`, `HOME`, `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY`. Store the value with `store_secret` naming the application and each environment; one name may hold a development value and a production value. A name at the account scope or at another application's scope is refused `setting_scope_refused`, and a platform-minted name `platform_minted_name`. A name an upstream of the application names is refused `setting_is_upstream_key`, because the gateway applies that key and never gives it to the application. The rule choosing between a setting and `declare_upstream` stands whole, with its example, on read_documentation's page /cloud/getting-started/store-a-secret/. A realm's route credential is refused `name_bound_to_realm`, and a push provider's credential `name_bound_to_push`. Each deploy and promote reads the value at that environment's scope and injects it as the setting, refused `setting_secret_missing` where the name is not stored there. A rotated value takes effect at the environment's next deploy or promote, and at a `restart_application` where the running copy already carries the binding. Type: object maxProperties: 50 Additional properties: false |
| / |
$ref: #/$defs/binding |
| / |
EGW-L0-02: optional. The keyed upstreams the application calls through the egress gateway, as an object from the upstream's name to its declaration, at most fifty entries. A declaration takes what `declare_upstream` takes but `application`, because the manifest binds each upstream to the application it is submitted for. An upstream's name is unique within the account. Each submission records every entry as `declare_upstream` records a declaration, effective at once, and each deploy and promote injects its `settings`. A rule `declare_upstream` refuses by name is refused `manifest_invalid` at the entry's path, the violation naming that rule's error. A name another application of the account holds is refused naming that application (`binding_refused`). The other errors are `reserved_upstream_name`, `refused_platform_host`, `platform_minted_name`, `upstream_key_is_bound`, `name_bound_to_realm`, `name_bound_to_push`, and `invalid_request`. A credential name not stored yet is accepted. The answer's `upstreams` rows say where it is stored, and the gateway refuses the upstream's calls `credential_not_in_custody` until `store_secret` stores it. A deploy or promote whose environment reads no stored key for an upstream names it in its row's `outcome.credentials_missing`. An upstream the member names is the manifest's: `declare_upstream` refuses a change to it, and `undeclare_upstream` its end, `manifest_owned_field`. Removing an entry leaves the upstream declared, and `declare_upstream` then revises it; no submission ends an upstream. To end one, remove its entry, submit the manifest, promote the version that no longer calls it, and call `undeclare_upstream`. Omit the member to declare every upstream through `declare_upstream`. Type: object maxProperties: 50 Additional properties: false |
| / |
$ref: #/$defs/upstream |
| / |
ACS-L0-07: optional. The sign-in configuration of the application's end-user realms, as `configure_realm` takes it: `sign_in_methods`, `creation`, and the `entra` and `apple` members. Those two name a stored credential and never hold a value (ACS-L0-09). The services list must include the `accounts` kind. Each submission records the member on each end-user realm the application has, effective at once, and `create_environment` records it on the development realm it creates. A rule `configure_realm` refuses by name is refused `manifest_invalid` at the member's path, the violation naming that rule's error. The limits, the session days, the invitation days, and the native clients differ by environment and stay `configure_realm`'s. A credential name not stored yet at an environment's scope is accepted. The answer's `detail` names each credential not stored yet, with the environment where one alone lacks it. A sign-in method reads it once `store_secret` stores it and the manifest is submitted again, which moves it into the realm's vault. A deploy or promote names a credential its environment cannot read in its row's `outcome.provider_credentials_missing`. A field the member names is the manifest's: `configure_realm` refuses a change to it `manifest_owned_field`. Removing a field leaves its configuration standing, and `configure_realm` then changes it. Type: object Additional properties: false minProperties: 1 |
| / |
$ref: #/$defs/realmSignInMethods |
| / |
ACS-L0-07: who may create an account by signing in, `open` or `invited`. An invited audience refuses `open` by name. Allowed values: ["open","invited"] |
| / |
$ref: #/$defs/realmEntra |
| / |
$ref: #/$defs/realmApple |
| / |
ACS-L0-07: the enabled sign-in methods, replacing each realm's set: any of `google`, `github`, `passkey`, `email`, `entra`, and `apple`. Naming `entra` or `apple` needs that route's configuration, in this member or through `configure_realm`. Type: array uniqueItems: true |
| / |
Allowed values: ["google","github","passkey","email","entra","apple"] |
| / |
ACS-L0-09: the work-account route's configuration: the Entra tenant id in its GUID form, the registration's client id, and the client secret's stored name, never a value. The registration lists the platform's callback as a web redirect URI, exactly as `configure_realm` and `read_realm` answer it in `callbacks.entra`: one address for the estate, the same for both environments, and never the application's hostname. Type: object Additional properties: false Required fields: ["tenant","client_id","client_secret_name"] |
| / |
Type: string Pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ |
| / |
Type: string Pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,254}$ |
| / |
Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
ACS-L0-09: Sign in with Apple's configuration: the Services ID, the team and key identifiers, and the signing key's stored name, never a value. The Services ID lists the host of `callbacks.apple` as a domain and that address, exactly, as its return URL, as `configure_realm` and `read_realm` answer it. Type: object Additional properties: false Required fields: ["services_id","team_id","key_id","key_secret_name"] |
| / |
Type: string Pattern: ^[A-Za-z0-9][A-Za-z0-9.-]{0,254}$ |
| / |
Type: string Pattern: ^[A-Z0-9]{10}$ |
| / |
Type: string Pattern: ^[A-Z0-9]{10}$ |
| / |
Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
PSH-L0-01: Apple's push provider: the team and key identifiers, the app's bundle identifier, and the signing key's stored name. The gateway is not here, because it differs by environment. Type: object Additional properties: false Required fields: ["team_id","key_id","bundle_id","key_secret_name"] |
| / |
Type: string Pattern: ^[A-Z0-9]{10}$ |
| / |
Type: string Pattern: ^[A-Z0-9]{10}$ |
| / |
Type: string Pattern: ^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+$ Maximum length: 255 |
| / |
Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
PSH-L0-01: Google's push provider: the Firebase project identifier and the service-account file's stored name. Type: object Additional properties: false Required fields: ["project_id","service_account_secret_name"] |
| / |
Type: string Pattern: ^[a-z][a-z0-9-]{4,29}$ |
| / |
Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
A name in this form: a lowercase letter first, then lowercase letters, digits, or underscores, with no hyphen. The pattern is `^[a-z][a-z0-9_]*$`, so `hourly_heartbeat` passes and `hourly-heartbeat` is refused. Type: string Pattern: ^[a-z][a-z0-9_]*$ |
| / |
Type: string Pattern: ^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$ |
| / |
MAN-09: one egress entry — an exact lowercase hostname, or a host family: one leading wildcard label and then a hostname of at least two labels, so `*.example.com` stands and `*.com`, `*.*.example.com`, and a wildcard anywhere but first do not. Type: string Pattern: ^(?=.{1,253}$)(\*\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$ |
| / |
MAN-03: five cron fields in UTC, digits, `*`, `,`, `/`, `-` per field; ranges and the day-of-month/day-of-week rule are the admitting act's. Type: string Pattern: ^([0-9*,/-]+)\s+([0-9*,/-]+)\s+([0-9*,/-]+)\s+([0-9*,/-]+)\s+([0-9*,/-]+)$ |
| / |
MAN-14: one binding, the stored name whose value the setting holds, in the custody name shape (SCRT-L0-01). Type: object Additional properties: false Required fields: ["secret"] |
| / |
Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
MAN-10; ADM-L0-05: the paths the serving router lets through with no end-user session under the invited or workforce audience, so a provider's callback, such as a payment webhook, reaches the application. At most ten path prefixes, each starting with /, never / alone, holding no whitespace and no .., and outside /__account/ and /__router/, the platform's own prefixes. A prefix covers the path equal to it and every path below it. The application checks each such caller itself, for example by the provider's signature. The public audience lists none. Type: array Maximum items: 10 |
| / |
Type: string Pattern: ^/(?!__account(/|$))(?!__router(/|$))(?!.*\.\.)[^\s]+$ |
| / |
EGW-L0-01: one upstream's declaration, the members `declare_upstream` takes but `name`, which is the entry's key, and `application`. Type: object Additional properties: false Required fields: ["base_url","credential_name","auth_header"] |
| / |
The upstream's base URL, public `https://` alone. A platform host is refused `refused_platform_host`. Type: string Minimum length: 9 Maximum length: 2000 |
| / |
The stored name of the upstream's key, stored with `store_secret` at an environment's scope of the application or at the account scope. Type: string Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ |
| / |
The HTTP header the gateway sends the key in, such as `Authorization` or `x-api-key`. Type: string Pattern: ^[a-zA-Z0-9-]{1,64}$ |
| / |
The header value around the key, `{value}` standing for the key, such as `Bearer {value}`. `{value}` alone where absent. Type: string Minimum length: 7 Maximum length: 200 |
| / |
How token counts are read from the upstream's answers: `gemini` or `anthropic`. Absent for an upstream that reports none. Allowed values: ["gemini","anthropic"] |
| / |
$ref: #/$defs/upstreamSettings |
| / |
EGW-L0-01: the settings an unchanged client of the upstream reads in a deployed container. `base_url` receives the gateway's address for the upstream, ending with `base_path` where given, and `key` receives an egress key the gateway swaps for the stored key. Absent, no setting is injected for the upstream. Type: object Additional properties: false Required fields: ["base_url"] |
| / |
Type: string Pattern: ^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$ |
| / |
Type: string Pattern: ^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$ |
| / |
Type: string Pattern: ^(/[A-Za-z0-9._~%-]+)+$ Maximum length: 200 |
| / |
A space's identifier at the issue-tracking service, a lower-case UUID. Type: string Pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ |
Complete source
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"version": "2026-10-07.1",
"title": "Turn Zero Cloud — run-layer manifest, revision 1",
"description": "The executable form of the admission manifest contract (MAN). The two state one contract and change together; a test in the platform's suite holds them to each other.",
"type": "object",
"additionalProperties": false,
"required": [
"manifest_version",
"services",
"health",
"region",
"egress",
"audience",
"packages"
],
"properties": {
"manifest_version": {
"description": "MAN-01: the contract revision this manifest declares against.",
"const": 1
},
"services": {
"description": "MAN-03: the provisioned services consumed, by the catalog's names; empty is a statement.",
"type": "array",
"uniqueItems": true,
"items": {
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"database",
"object_storage",
"email",
"accounts",
"push"
],
"description": "MAN-03: `database`, `object_storage`, `accounts`, and `push` are the kinds the platform provisions or admits at submission as their services state. The `push` kind admits the configuration and the routes and provisions nothing at submission. The `email` kind is admitted ahead of its service and provisions nothing at this version."
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"minProperties": 2,
"properties": {
"kind": {
"const": "push",
"description": "PSH-L0-01: the push kind naming its providers, as `configure_push` takes them: `apns` without Apple's gateway, and `fcm`. Each submission records them on the push configuration of each environment the application has, effective at once, and `create_environment` records them on development's. A rule `configure_push` refuses by name is refused `manifest_invalid` at the member's path, the violation naming that rule's error.\n\nA credential name not stored yet is accepted, as the `realm` member states. A provider the entry names is the manifest's: `configure_push` refuses a change to it `manifest_owned_field`. Apple's gateway differs by environment and stays `configure_push`'s: `sandbox` in development and `production` in production until it names another."
},
"apns": {
"$ref": "#/$defs/pushApns"
},
"fcm": {
"$ref": "#/$defs/pushFcm"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"domain"
],
"properties": {
"kind": {
"const": "custom_domain",
"description": "MAN-03: admitted ahead of its service; a custom domain provisions nothing at this version."
},
"domain": {
"$ref": "#/$defs/hostname"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"schedules"
],
"properties": {
"kind": {
"const": "schedule"
},
"schedules": {
"description": "MAN-03: the schedule service's declarations (SVC-L0-15; the schedule service PRD's declaration statement) — one to twenty-five at the shape's cap, the plan's `schedule-count-limit` refused at submit by name. Each is a name (a lowercase letter first, then lowercase letters, digits, or underscores, with no hyphen, unique within the application), a five-field cron expression, and an absolute path. The cron expression is evaluated in UTC at the minute grain (minute, hour, day of month, month, day of week; `*`, an integer, a range, a list, or a step per field). Names, macros, and a sixth field are refused here, and a field out of range or an expression with no due time inside a year is refused at submit. The path is an absolute path on the application's own server (MAN-05's form) that is under neither reserved path prefix, `/__account/` nor `/__router/`. The handler at that path is an ordinary route the platform invokes through the serving router with the router's mark and the invocation headers Node.js Runtime states. The platform's harness answers 404 on that path to any request the serving router did not mark as a scheduled run, so the handler receives scheduled runs alone. The handler's one rule is idempotency per `x-turnzero-cloud-schedule-due`, a check of `x-turnzero-cloud-invocation` its own option, and it completes inside the schedule kind's window (HST-L0-02).",
"type": "array",
"minItems": 1,
"maxItems": 25,
"uniqueItems": true,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"cron",
"path"
],
"properties": {
"name": {
"$ref": "#/$defs/slug"
},
"cron": {
"$ref": "#/$defs/cron"
},
"path": {
"type": "string",
"pattern": "^/[^\\s]*$"
}
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"const": "issue_tracking",
"description": "MAN-03: the issue-tracking service (SVC-L0-19). With no `space`, the application's first submission creates one space of its own for both environments, its deployments reaching it at `report`; an application provisioned with a space per environment before that keeps its two spaces (ITS-L0-01). The receipt's `issue_tracking` list names each space the application's calls reach."
},
"space": {
"description": "MAN-03: optional. A space of the account's own the application's calls reach in place of a space of its own: one space identifier for both environments, or `{ development, production }` naming one for each. The platform binds it and creates nothing, and the gateway serves the binding while the account holds the `blueprint` profile. An application's own space, this application's or another's, is never named here and is refused `space_not_owned`, as is a space the account does not hold. An application holding its own space, or a space per environment from before, is refused `invalid_request`, since it binds no chosen space (ITS-L0-01).",
"oneOf": [
{
"$ref": "#/$defs/space_id"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"development",
"production"
],
"properties": {
"development": {
"$ref": "#/$defs/space_id"
},
"production": {
"$ref": "#/$defs/space_id"
}
}
}
]
},
"level": {
"type": "string",
"description": "MAN-03: optional, named with `space` alone: `report`, the default, files reports and reads them; `contribute` adds the working calls (update, comment, relate, and the signal and ask calls). Any other value, `owner` among them, is refused `level_invalid`: a deployment never settles, configures, or exports a space. A `level` with no `space` is refused `invalid_request` naming `space` (ITS-L0-01)."
}
}
}
]
}
},
"health": {
"description": "MAN-05: the health endpoint as an absolute HTTP path on the application's own address, conventionally `/health`. At each deploy and promote, the health gate sends GET to this path every three seconds for up to 180 seconds, and passes only on status 200 exactly (PLD-L0-63). Under an invited or workforce audience the serving router gates this path from outside, while the health gate reaches the compute directly; the `audience` description says what each means (ADM-L0-05). Each probe waits at most ten seconds for its answer to begin. Twenty consecutive probes answering one 4xx status other than 408, 425, and 429, then a control probe the application's own process answers, end the gate early, about a minute in. A 5xx answer or a transport error (a failed or timed-out connection) never counts toward that run. Serve the route from the moment the server listens, answering 503 until ready, so a route mounted late is never ended early. Answer no 4xx while starting. A server that waits to listen until its database migrations finish reads as starting, and the gate keeps waiting within its bound.\n\nA failed gate records its evidence in the version row's `outcome.gate`, read through `read_status` and `list_versions`. It holds the probe answers as a ledger, the last answer with its body's first 512 bytes where the application answered, what answered, the compute's state, and the console's last forty lines (PLD-L0-59).",
"type": "string",
"pattern": "^/[^\\s]*$"
},
"region": {
"description": "MAN-07: one of the day-one regions (CQ-174); meaning is PLD-L0-22's. At the beta the platform serves `usa` alone: `europe`, `uk`, and `global` are admitted by the schema ahead of their availability and a submission naming one is refused `region_unavailable` before anything is provisioned (PLD-L0-22).",
"enum": [
"usa",
"europe",
"uk",
"global"
]
},
"egress": {
"description": "MAN-09: the network allowlist contains exact lowercase DNS hostnames and host families with one leading wildcard label (`*.example.com`, admitting names below it at any depth but not the apex). An empty list declares no tunneled external destinations. In enforce mode, an undeclared destination is refused as `egress_undeclared`. In observe mode, the destination is not refused for being undeclared, and the same name is recorded with the attempted host and the remedy — for a hostname the exact manifest edit (add the host to `egress` and redeploy). An address literal cannot be declared, so its remedy names the hostname the address serves (ADM-L0-03; EGW-L0-14; EGW-L0-17). Applications begin in observe mode; an operator can select enforce mode. Refusals return 403 and the `X-Egress-Refusal` header where the client's route exposes the proxy response. CONNECT tunnels are restricted to port 443; the proxy carries their bytes without checking for TLS or HTTP, and an entry contains no scheme or port (EGW-L0-12). Platform endpoints—the declared services' endpoints and the platform origin—are reached outside the proxy and need no list entry (EGW-L0-15). Names resolving to, and address literals in, private, loopback, link-local (including metadata), reserved, or platform ranges are refused by address class in both modes (EGW-L0-13). The list names the outside hosts the application's own process dials, apart from the platform endpoints and the declared upstreams, which the gateway dials. An upstream called on a stored key is declared in the `upstreams` member or through `declare_upstream`, not in this list (EGW-L0-02).",
"type": "array",
"uniqueItems": true,
"items": {
"$ref": "#/$defs/egressEntry"
}
},
"audience": {
"description": "MAN-10: exactly one boundary — public; invited (ADM-L0-05: the serving router gates on the realm's session, and the application's end-user realms are invitation-only from the declaration, whatever creation mode their configuration holds); or workforce (one company's identity-provider tenant, named by the `tenant` member). Under invited and workforce, the optional `session_free_paths` member lists the paths the router lets through with no session (ADM-L0-05). Under those two, the serving router gates every path, the `health` path among them: a request from outside carrying no valid session is sent to sign-in or answered 401, unless a `session_free_paths` prefix covers it (ADM-L0-05). The health gate at each deploy and promote probes the application's compute directly, never through the router, so a deploy needs nothing listed (PLD-L0-63).",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"const": "public"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind"
],
"properties": {
"kind": {
"const": "invited"
},
"session_free_paths": {
"$ref": "#/$defs/sessionFreePaths"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"tenant"
],
"properties": {
"kind": {
"const": "workforce"
},
"tenant": {
"type": "string",
"minLength": 1,
"description": "The company's identity-provider tenant identifier on the provider's terms — Microsoft Entra ID's tenant id, a GUID, at this revision (ACS-L0-09). The audience gates who reaches the application and signs nobody in, so declare `entra` in the `realm` member's `sign_in_methods` and name this tenant in its `entra` member, in the same manifest."
},
"session_free_paths": {
"$ref": "#/$defs/sessionFreePaths"
}
}
}
]
},
"packages": {
"description": "MAN-13: the library entries this application holds, each as its name and the version held. At each submission, copy each row of the project's folder manifest, the manifest.json in its system/ folder, into this list as its `name` and `version`. That copy is the derivation the rule asks for; what it forbids is a list kept by hand beside the folder manifest (SPM-L0-50's rows; SPM-L0-49). Admission reads it to refuse a deploy declaring a version whose compatibility window has closed (API-L0-16), a refusal not active during the beta while the first-customer-release date is unset. An application holding no entry declares [] rather than omitting the member — MAN-02 requires the member, and an omission would read as a fact not yet known where the truth is that there are none.",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version"
],
"properties": {
"name": {
"description": "The entry's stable name, as the catalog answers it: a package's folder name, `ui/<vocabulary>`, `fonts/<family>`, or `library/prd` for the library's own requirements entry.",
"type": "string",
"minLength": 1
},
"version": {
"description": "FTR-L0-62's front-matter version, as held. Null on a font family, which carries no specification of its own and so no version of its own, and on the library's own requirements entry, whose two documents declare none.",
"type": [
"string",
"null"
]
}
}
}
},
"settings": {
"description": "MAN-14: optional. Binds a stored secret to a setting the application's process reads, as an object from the setting's name to `{\"secret\": \"<stored name>\"}`, at most fifty entries. Omit the member where the process reads no stored value as a setting. This member is not `declare_upstream`'s `settings`, which name the settings an upstream's client reads while the stored key stays at the gateway.\n\nA setting name is an upper-case letter, then upper-case letters, digits, or underscores, ending in a letter or a digit. A name the platform reserves is refused `manifest_invalid` at its own path. The platform reserves any name under `TURNZERO_`, `APP_`, `HOSTING_`, `EGRESS_`, `ROUTER_`, `NODE_`, or `NPM_`, and `PORT`, `PATH`, `HOME`, `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY`.\n\nStore the value with `store_secret` naming the application and each environment; one name may hold a development value and a production value. A name at the account scope or at another application's scope is refused `setting_scope_refused`, and a platform-minted name `platform_minted_name`. A name an upstream of the application names is refused `setting_is_upstream_key`, because the gateway applies that key and never gives it to the application. The rule choosing between a setting and `declare_upstream` stands whole, with its example, on read_documentation's page /cloud/getting-started/store-a-secret/. A realm's route credential is refused `name_bound_to_realm`, and a push provider's credential `name_bound_to_push`.\n\nEach deploy and promote reads the value at that environment's scope and injects it as the setting, refused `setting_secret_missing` where the name is not stored there. A rotated value takes effect at the environment's next deploy or promote, and at a `restart_application` where the running copy already carries the binding.",
"type": "object",
"maxProperties": 50,
"patternProperties": {
"^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$": {
"$ref": "#/$defs/binding"
}
},
"additionalProperties": false
},
"upstreams": {
"description": "EGW-L0-02: optional. The keyed upstreams the application calls through the egress gateway, as an object from the upstream's name to its declaration, at most fifty entries. A declaration takes what `declare_upstream` takes but `application`, because the manifest binds each upstream to the application it is submitted for.\n\nAn upstream's name is unique within the account. Each submission records every entry as `declare_upstream` records a declaration, effective at once, and each deploy and promote injects its `settings`. A rule `declare_upstream` refuses by name is refused `manifest_invalid` at the entry's path, the violation naming that rule's error. A name another application of the account holds is refused naming that application (`binding_refused`). The other errors are `reserved_upstream_name`, `refused_platform_host`, `platform_minted_name`, `upstream_key_is_bound`, `name_bound_to_realm`, `name_bound_to_push`, and `invalid_request`.\n\nA credential name not stored yet is accepted. The answer's `upstreams` rows say where it is stored, and the gateway refuses the upstream's calls `credential_not_in_custody` until `store_secret` stores it. A deploy or promote whose environment reads no stored key for an upstream names it in its row's `outcome.credentials_missing`.\n\nAn upstream the member names is the manifest's: `declare_upstream` refuses a change to it, and `undeclare_upstream` its end, `manifest_owned_field`. Removing an entry leaves the upstream declared, and `declare_upstream` then revises it; no submission ends an upstream. To end one, remove its entry, submit the manifest, promote the version that no longer calls it, and call `undeclare_upstream`. Omit the member to declare every upstream through `declare_upstream`.",
"type": "object",
"maxProperties": 50,
"patternProperties": {
"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$": {
"$ref": "#/$defs/upstream"
}
},
"additionalProperties": false
},
"realm": {
"description": "ACS-L0-07: optional. The sign-in configuration of the application's end-user realms, as `configure_realm` takes it: `sign_in_methods`, `creation`, and the `entra` and `apple` members. Those two name a stored credential and never hold a value (ACS-L0-09). The services list must include the `accounts` kind.\n\nEach submission records the member on each end-user realm the application has, effective at once, and `create_environment` records it on the development realm it creates. A rule `configure_realm` refuses by name is refused `manifest_invalid` at the member's path, the violation naming that rule's error. The limits, the session days, the invitation days, and the native clients differ by environment and stay `configure_realm`'s.\n\nA credential name not stored yet at an environment's scope is accepted. The answer's `detail` names each credential not stored yet, with the environment where one alone lacks it. A sign-in method reads it once `store_secret` stores it and the manifest is submitted again, which moves it into the realm's vault. A deploy or promote names a credential its environment cannot read in its row's `outcome.provider_credentials_missing`.\n\nA field the member names is the manifest's: `configure_realm` refuses a change to it `manifest_owned_field`. Removing a field leaves its configuration standing, and `configure_realm` then changes it.",
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"sign_in_methods": {
"$ref": "#/$defs/realmSignInMethods"
},
"creation": {
"description": "ACS-L0-07: who may create an account by signing in, `open` or `invited`. An invited audience refuses `open` by name.",
"enum": [
"open",
"invited"
]
},
"entra": {
"$ref": "#/$defs/realmEntra"
},
"apple": {
"$ref": "#/$defs/realmApple"
}
}
}
},
"$defs": {
"realmSignInMethods": {
"description": "ACS-L0-07: the enabled sign-in methods, replacing each realm's set: any of `google`, `github`, `passkey`, `email`, `entra`, and `apple`. Naming `entra` or `apple` needs that route's configuration, in this member or through `configure_realm`.",
"type": "array",
"uniqueItems": true,
"items": {
"enum": [
"google",
"github",
"passkey",
"email",
"entra",
"apple"
]
}
},
"realmEntra": {
"description": "ACS-L0-09: the work-account route's configuration: the Entra tenant id in its GUID form, the registration's client id, and the client secret's stored name, never a value. The registration lists the platform's callback as a web redirect URI, exactly as `configure_realm` and `read_realm` answer it in `callbacks.entra`: one address for the estate, the same for both environments, and never the application's hostname.",
"type": "object",
"additionalProperties": false,
"required": [
"tenant",
"client_id",
"client_secret_name"
],
"properties": {
"tenant": {
"type": "string",
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
},
"client_id": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,254}$"
},
"client_secret_name": {
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"
}
}
},
"realmApple": {
"description": "ACS-L0-09: Sign in with Apple's configuration: the Services ID, the team and key identifiers, and the signing key's stored name, never a value. The Services ID lists the host of `callbacks.apple` as a domain and that address, exactly, as its return URL, as `configure_realm` and `read_realm` answer it.",
"type": "object",
"additionalProperties": false,
"required": [
"services_id",
"team_id",
"key_id",
"key_secret_name"
],
"properties": {
"services_id": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9.-]{0,254}$"
},
"team_id": {
"type": "string",
"pattern": "^[A-Z0-9]{10}$"
},
"key_id": {
"type": "string",
"pattern": "^[A-Z0-9]{10}$"
},
"key_secret_name": {
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"
}
}
},
"pushApns": {
"description": "PSH-L0-01: Apple's push provider: the team and key identifiers, the app's bundle identifier, and the signing key's stored name. The gateway is not here, because it differs by environment.",
"type": "object",
"additionalProperties": false,
"required": [
"team_id",
"key_id",
"bundle_id",
"key_secret_name"
],
"properties": {
"team_id": {
"type": "string",
"pattern": "^[A-Z0-9]{10}$"
},
"key_id": {
"type": "string",
"pattern": "^[A-Z0-9]{10}$"
},
"bundle_id": {
"type": "string",
"pattern": "^[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)+$",
"maxLength": 255
},
"key_secret_name": {
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"
}
}
},
"pushFcm": {
"description": "PSH-L0-01: Google's push provider: the Firebase project identifier and the service-account file's stored name.",
"type": "object",
"additionalProperties": false,
"required": [
"project_id",
"service_account_secret_name"
],
"properties": {
"project_id": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]{4,29}$"
},
"service_account_secret_name": {
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"
}
}
},
"slug": {
"description": "A name in this form: a lowercase letter first, then lowercase letters, digits, or underscores, with no hyphen. The pattern is `^[a-z][a-z0-9_]*$`, so `hourly_heartbeat` passes and `hourly-heartbeat` is refused.",
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
},
"hostname": {
"type": "string",
"pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"
},
"egressEntry": {
"description": "MAN-09: one egress entry — an exact lowercase hostname, or a host family: one leading wildcard label and then a hostname of at least two labels, so `*.example.com` stands and `*.com`, `*.*.example.com`, and a wildcard anywhere but first do not.",
"type": "string",
"pattern": "^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"
},
"cron": {
"description": "MAN-03: five cron fields in UTC, digits, `*`, `,`, `/`, `-` per field; ranges and the day-of-month/day-of-week rule are the admitting act's.",
"type": "string",
"pattern": "^([0-9*,/-]+)\\s+([0-9*,/-]+)\\s+([0-9*,/-]+)\\s+([0-9*,/-]+)\\s+([0-9*,/-]+)$"
},
"binding": {
"description": "MAN-14: one binding, the stored name whose value the setting holds, in the custody name shape (SCRT-L0-01).",
"type": "object",
"additionalProperties": false,
"required": [
"secret"
],
"properties": {
"secret": {
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"
}
}
},
"sessionFreePaths": {
"description": "MAN-10; ADM-L0-05: the paths the serving router lets through with no end-user session under the invited or workforce audience, so a provider's callback, such as a payment webhook, reaches the application. At most ten path prefixes, each starting with /, never / alone, holding no whitespace and no .., and outside /__account/ and /__router/, the platform's own prefixes. A prefix covers the path equal to it and every path below it. The application checks each such caller itself, for example by the provider's signature. The public audience lists none.",
"type": "array",
"maxItems": 10,
"items": {
"type": "string",
"pattern": "^/(?!__account(/|$))(?!__router(/|$))(?!.*\\.\\.)[^\\s]+$"
}
},
"upstream": {
"description": "EGW-L0-01: one upstream's declaration, the members `declare_upstream` takes but `name`, which is the entry's key, and `application`.",
"type": "object",
"additionalProperties": false,
"required": [
"base_url",
"credential_name",
"auth_header"
],
"properties": {
"base_url": {
"description": "The upstream's base URL, public `https://` alone. A platform host is refused `refused_platform_host`.",
"type": "string",
"minLength": 9,
"maxLength": 2000
},
"credential_name": {
"description": "The stored name of the upstream's key, stored with `store_secret` at an environment's scope of the application or at the account scope.",
"type": "string",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"
},
"auth_header": {
"description": "The HTTP header the gateway sends the key in, such as `Authorization` or `x-api-key`.",
"type": "string",
"pattern": "^[a-zA-Z0-9-]{1,64}$"
},
"auth_format": {
"description": "The header value around the key, `{value}` standing for the key, such as `Bearer {value}`. `{value}` alone where absent.",
"type": "string",
"minLength": 7,
"maxLength": 200
},
"token_shape": {
"description": "How token counts are read from the upstream's answers: `gemini` or `anthropic`. Absent for an upstream that reports none.",
"enum": [
"gemini",
"anthropic"
]
},
"settings": {
"$ref": "#/$defs/upstreamSettings"
}
}
},
"upstreamSettings": {
"description": "EGW-L0-01: the settings an unchanged client of the upstream reads in a deployed container. `base_url` receives the gateway's address for the upstream, ending with `base_path` where given, and `key` receives an egress key the gateway swaps for the stored key. Absent, no setting is injected for the upstream.",
"type": "object",
"additionalProperties": false,
"required": [
"base_url"
],
"properties": {
"base_url": {
"type": "string",
"pattern": "^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$"
},
"key": {
"type": "string",
"pattern": "^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$"
},
"base_path": {
"type": "string",
"pattern": "^(/[A-Za-z0-9._~%-]+)+$",
"maxLength": 200
}
}
},
"space_id": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"description": "A space's identifier at the issue-tracking service, a lower-case UUID."
}
}
}