{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "OrdinaryConfig",
"description": "Config definition for an Ordinary Application",
"type": "object",
"properties": {
"lifecycle": {
"anyOf": [
{
"$ref": "#/$defs/TopLevelLifecycle"
},
{
"type": "null"
}
]
},
"domain": {
"description": "Domain name for the application to be run from the\ndeployment environment.",
"type": "string"
},
"cnames": {
"description": "additional domains with a CNAME or ALIAS records\npointing at the primary `OrdinaryConfig::domain`.\n\nadd a TXT record in the following format:\n`ordinary=your.config.domain`",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"canonical": {
"description": "specify which of the `domain` or `cnames` is\nthe \"canonical\" location.\n\nthis is useful for [indexing](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls)\nand situations where you want to display the primary\nURL as text on the page itself (i.e. pick one of `example.some.host`, `example.com`, and `www.example.com`).\n\ndefaults to `domain` if `cnames` is empty. defaults to first `cname` in list if `cnames`\nare not empty.",
"type": [
"string",
"null"
]
},
"redirects": {
"description": "configuration of internal redirects",
"anyOf": [
{
"$ref": "#/$defs/Redirects"
},
{
"type": "null"
}
]
},
"proxies": {
"description": "configuration of proxied services",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ProxyConfig"
}
},
"contacts": {
"description": "list of email addresses that can be used to contact\nthe application owner or administrators.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"hide_contacts": {
"description": "whether contacts should be hidden (defaults to `true`)",
"type": [
"boolean",
"null"
]
},
"version": {
"description": "Version of the site build.",
"type": "string"
},
"storage_size": {
"description": "Storage size in bytes (rounded up to nearest OS page size).",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0,
"default": 5000000
},
"default_timeout": {
"description": "Default request timeout.\n\nUnit (seconds).",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"csp": {
"description": "HTTP Content Security Policy configuration.\n\n<https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP>\n\n\"Base\" defaults to `default-src 'self';` and tacks on SHA-256 integrity\nhashes for all inlined scripts and styles (generated at build time) to\n`script-src 'self' sha256-b64` and `style-src 'self' sha256-b64`, respectively.\n\n`https:` is used when not running in `--insecure` mode.",
"anyOf": [
{
"$ref": "#/$defs/HttpCsp"
},
{
"type": "null"
}
]
},
"cors": {
"anyOf": [
{
"$ref": "#/$defs/HttpCors"
},
{
"type": "null"
}
]
},
"runtime": {
"description": "Specifies runtime mode for application on the host.\n\nIf none is specified, defaults to Shared (or host default).",
"anyOf": [
{
"$ref": "#/$defs/RuntimeMode"
},
{
"type": "null"
}
]
},
"hide_schema": {
"description": "When set to true, `{{ domain }}/.ordinary/schema`\nis not addressable.\n\nNote: this can break applications which depend on\nflags, and `action`/`template` query descriptors.",
"type": [
"boolean",
"null"
]
},
"client_rendering": {
"description": "Include template rendering code in the client WASM.",
"type": [
"boolean",
"null"
]
},
"obfuscation": {
"description": "Include E2EE handler code in the client WASM.",
"type": [
"boolean",
"null"
]
},
"client_events": {
"description": "Include E2EE handler code in the client WASM.",
"type": [
"boolean",
"null"
]
},
"port": {
"description": "Port to be used for standalone \"run\" instances.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"redirect_port": {
"description": "port used for redirecting from http when\nstandalone is running in secure mode.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"logging": {
"anyOf": [
{
"$ref": "#/$defs/LoggingConfig"
},
{
"type": "null"
}
]
},
"error": {
"description": "Configures error handling.\n\nNote: If not included just the error message will be\nsent back as text.",
"anyOf": [
{
"$ref": "#/$defs/ErrorConfig"
},
{
"type": "null"
}
]
},
"auth": {
"description": "Auth config for the Ordinary application.",
"anyOf": [
{
"$ref": "#/$defs/AuthConfig"
},
{
"type": "null"
}
]
},
"globals": {
"description": "Global constants that can be accessed from templates",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/Global"
}
},
"secrets": {
"description": "Secrets that can be used by actions or integrations.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/Secret"
}
},
"flags": {
"description": "Feature flags which can be referenced from templates\nto inform application behavior, and run experiments.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/Flag"
}
},
"content": {
"description": "Definitions for static content \"types\"/object structure\nthat can be used to inform template/page development (i.e.\none might define a \"post\" content definition, and then create\na template for their blog).",
"anyOf": [
{
"$ref": "#/$defs/Content"
},
{
"type": "null"
}
]
},
"models": {
"description": "Definitions for the models that will be stored in the Ordinary database.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ModelConfig"
}
},
"integrations": {
"description": "Definitions for the external APIs that will be integrated\ninto the Ordinary application.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/IntegrationConfig"
}
},
"actions": {
"description": "IO, access and language configuration for actions that\nare compiled to and executed as WebAssembly modules.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ActionConfig"
}
},
"assets": {
"description": "Specifies the asset directory and per-path configuration\ndetails for assets that require preprocessing (TypeScript, SCSS,\nJavaScript minification, etc.)",
"anyOf": [
{
"$ref": "#/$defs/AssetsConfig"
},
{
"type": "null"
}
]
},
"fragments": {
"description": "Configuration for the template fragments",
"anyOf": [
{
"$ref": "#/$defs/FragmentsConfig"
},
{
"type": "null"
}
]
},
"templates": {
"description": "Configuration for the templates/pages that the application\nwill render. Each template is compiled to a WebAssembly module\nwhich accepts runtime arguments for models/content/integrations, and can be\nexecuted on either the server or the client.\n\nWith the option to render on the client, only the result of the server\nquery needs to be sent, in a compact, optimized, format.\n\nCurrently, all rendering is happening server-side, and only HTML is being sent.\n\nIn an ideal/future state multiple modes will be supported, even up to a full\n'noscript' config.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateConfig"
}
}
},
"required": [
"domain",
"version"
],
"$defs": {
"TopLevelLifecycle": {
"type": "object",
"properties": {
"before_all": {
"description": "run before every lifecycle operation",
"type": [
"array",
"null"
],
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"build": {
"description": "configure build lifecycle hooks",
"anyOf": [
{
"$ref": "#/$defs/LifecycleBeforeAfterScripts"
},
{
"type": "null"
}
]
}
}
},
"LifecycleBeforeAfterScripts": {
"type": "object",
"properties": {
"before": {
"type": [
"array",
"null"
],
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"after": {
"type": [
"array",
"null"
],
"items": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"Redirects": {
"type": "object",
"properties": {
"host": {
"description": "host redirects",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/HostRedirect"
}
},
"route": {
"description": "route redirects",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/RouteRedirect"
}
}
}
},
"HostRedirect": {
"type": "object",
"properties": {
"from": {
"description": "from host name",
"type": "string"
},
"to": {
"description": "to host name",
"type": "string"
},
"method": {
"description": "redirect method",
"$ref": "#/$defs/RedirectMethod"
}
},
"required": [
"from",
"to",
"method"
]
},
"RedirectMethod": {
"oneOf": [
{
"description": "307 <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/307>",
"type": "string",
"const": "Temporary"
},
{
"description": "308 <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/308>",
"type": "string",
"const": "Permanent"
}
]
},
"RouteRedirect": {
"type": "object",
"properties": {
"condition": {
"description": "[axum route](https://docs.rs/axum/latest/axum/struct.Router.html#method.route) pattern",
"type": "string"
},
"rule": {
"description": "translation regexes map to [`Regex::replace_all`](https://docs.rs/regex/latest/regex/#example-replacement-with-named-capture-groups)\nwhere `rule.0` is body of `Regex::new()` and `rule.1` is second param of `re.replace_all()`.",
"type": "array",
"prefixItems": [
{
"type": "string"
},
{
"type": "string"
}
],
"minItems": 2,
"maxItems": 2
},
"method": {
"description": "redirect method",
"$ref": "#/$defs/RedirectMethod"
}
},
"required": [
"condition",
"rule",
"method"
]
},
"ProxyConfig": {
"description": "proxy configuration.\n\n**Note:** `path` should not be set at the same time as either `domain`\nor `port`. `path` takes precedence and the other sources will be ignored.\n\nif the `path`, `domain` and `port` are all unset, validation will fail.",
"type": "object",
"properties": {
"path": {
"description": "an `axum` [route](https://docs.rs/axum/latest/axum/routing/struct.Router.html#method.route)\nwith a `*path` wildcard that will be appended to the target.\n\ni.e. `\"/some/path/{*path}\"`\n\n**Note:** MUST end in a trailing `/{*path}`",
"type": [
"string",
"null"
]
},
"domain": {
"description": "custom domain with an ALIAS/CNAME pointing at the primary `OrdinaryConfig::domain`\nand a TXT record indicating ownership (i.e. `ordinary-proxy=your.config.domain`).\n\ni.e `example.com`\n\n**Note:** if a `path` is specified, the `domain` will be ignored",
"type": [
"string",
"null"
]
},
"port": {
"description": "preferred port that the proxy will listen on in a `--dedicated-ports`\nconfigured multi-tenant server OR a standalone app instance.\n\ni.e `8081`\n\n**Note:** if a `path` is specified, the `port` will be ignored,\nand `port` will not be tried without `domain` being present.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"target": {
"description": "url for resource to proxy.\n\ni.e. `https://example.com`",
"type": "string"
}
},
"required": [
"target"
]
},
"HttpCsp": {
"type": "object",
"properties": {
"default_src": {
"description": "defaults to `'self'` (`default-src ` does not need to be included)",
"type": [
"string",
"null"
]
},
"script_src": {
"description": "defaults to unset unless inline hashes are included,\nin which case the directive will start with `'self'` (`script-src ` does not need to be included).",
"type": [
"string",
"null"
]
},
"style_src": {
"description": "defaults to unset unless inline hashes are included,\nin which case the directive will start with `'self'` (`style-src ` does not need to be included).",
"type": [
"string",
"null"
]
},
"font_src": {
"description": "defaults to unset.\n\n(`font-src ` does not need to be included).",
"type": [
"string",
"null"
]
},
"img_src": {
"description": "defaults to unset.\n\n(`img-src ` does not need to be included).",
"type": [
"string",
"null"
]
},
"frame_src": {
"description": "defaults to unset.\n\n(`frame-src ` does not need to be included).",
"type": [
"string",
"null"
]
},
"include_inline_hashes": {
"description": "defaults to `true`.",
"type": [
"boolean",
"null"
]
}
}
},
"HttpCors": {
"type": "object",
"properties": {
"allow_credentials": {
"type": [
"boolean",
"null"
]
},
"allow_headers": {
"anyOf": [
{
"$ref": "#/$defs/HttpCorsAllowHeaders"
},
{
"type": "null"
}
]
},
"max_age": {
"description": "unit: Seconds",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"allow_methods": {
"anyOf": [
{
"$ref": "#/$defs/HttpCorsAllowMethods"
},
{
"type": "null"
}
]
},
"allow_origin": {
"anyOf": [
{
"$ref": "#/$defs/HttpCorsAllowOrigin"
},
{
"type": "null"
}
]
},
"expose_headers": {
"anyOf": [
{
"$ref": "#/$defs/HttpCorsExposeHeaders"
},
{
"type": "null"
}
]
},
"allow_private_network": {
"type": [
"boolean",
"null"
]
}
}
},
"HttpCorsAllowHeaders": {
"oneOf": [
{
"type": "string",
"enum": [
"Any"
]
},
{
"description": "<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers>",
"type": "object",
"properties": {
"Headers": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"Headers"
],
"additionalProperties": false
}
]
},
"HttpCorsAllowMethods": {
"oneOf": [
{
"type": "string",
"enum": [
"Any"
]
},
{
"description": "<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods>",
"type": "object",
"properties": {
"Methods": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"Methods"
],
"additionalProperties": false
}
]
},
"HttpCorsAllowOrigin": {
"oneOf": [
{
"type": "string",
"enum": [
"Any"
]
},
{
"description": "<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin>",
"type": "object",
"properties": {
"Origins": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"Origins"
],
"additionalProperties": false
}
]
},
"HttpCorsExposeHeaders": {
"oneOf": [
{
"type": "string",
"enum": [
"Any"
]
},
{
"description": "<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers>",
"type": "object",
"properties": {
"Headers": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"Headers"
],
"additionalProperties": false
}
]
},
"RuntimeMode": {
"oneOf": [
{
"description": "Application will run on the shared multithreaded\ntokio runtime.",
"type": "string",
"const": "Shared"
},
{
"description": "Application will run on a separate thread with its\nown single-threaded tokio runtime.",
"type": "string",
"const": "SingleThreaded"
},
{
"description": "Application will run on a separate thread with its\nown multithreaded tokio runtime.",
"type": "string",
"const": "MultiThreaded"
}
]
},
"LoggingConfig": {
"type": "object",
"properties": {
"client": {
"anyOf": [
{
"$ref": "#/$defs/ClientLoggingConfig"
},
{
"type": "null"
}
]
},
"server": {
"anyOf": [
{
"$ref": "#/$defs/ServerLoggingConfig"
},
{
"type": "null"
}
]
}
}
},
"ClientLoggingConfig": {
"type": "object",
"properties": {
"min_delay": {
"description": "bottom end of the delayed delivery range (seconds)",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"max_delay": {
"description": "top end of delayed delivery range (seconds)",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0
},
"max_buffer": {
"description": "max number of events to be buffered on the client\nprior to flush.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"max_batch": {
"description": "sets the max number of events in a given request.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
}
}
},
"ServerLoggingConfig": {
"type": "object",
"properties": {
"ips": {
"type": [
"boolean",
"null"
]
},
"headers": {
"type": [
"boolean",
"null"
]
},
"credentials": {
"anyOf": [
{
"$ref": "#/$defs/RedactedHashAlg"
},
{
"type": "null"
}
]
},
"timing": {
"type": [
"boolean",
"null"
]
},
"sizes": {
"type": [
"boolean",
"null"
]
}
}
},
"RedactedHashAlg": {
"type": "string",
"enum": [
"Blake2",
"Blake3"
]
},
"ErrorConfig": {
"type": "object",
"properties": {
"template": {
"description": "Refers to the error template by name.\n\nNote: if set, will override the `asset` field",
"type": [
"string",
"null"
]
},
"asset": {
"description": "Refers to the asset by path.\n\nNote: if `template` is set it will override this field",
"type": [
"string",
"null"
]
}
}
},
"AuthConfig": {
"description": "Auth configuration.",
"type": "object",
"properties": {
"password": {
"description": "Configuration for passwords.",
"$ref": "#/$defs/PasswordConfig"
},
"mfa": {
"description": "MFA configuration for auth.",
"$ref": "#/$defs/MfaConfig"
},
"refresh_token": {
"description": "Configuration for refresh tokens.",
"$ref": "#/$defs/RefreshTokenConfig"
},
"access_token": {
"description": "Configuration for access tokens.",
"$ref": "#/$defs/AccessTokenConfig"
},
"cookies_enabled": {
"description": "Determines whether cookies should be used\nfor browser based template navigation, and form submissions\n\nNote: when set to `false`, `protected` templates, and actions triggered\nby form submission will fail. Form based registration and login will\nnecessarily be disabled, also.\n\n!! Important: when set to `true`, tokens retrieved for `js` and `noscript` flavors\n!! do not support client signatures, because http-only cookies cannot be signed by\n!! the client.",
"type": "boolean"
},
"client_hash": {
"description": "what algorithm is used to hash passwords and MFA codes",
"$ref": "#/$defs/ClientPasswordHash"
},
"invite": {
"description": "invite config",
"anyOf": [
{
"$ref": "#/$defs/InviteConfig"
},
{
"type": "null"
}
]
}
},
"required": [
"password",
"mfa",
"refresh_token",
"access_token",
"cookies_enabled",
"client_hash"
]
},
"PasswordConfig": {
"description": "Configuration for passwords.",
"type": "object",
"properties": {
"protocol": {
"$ref": "#/$defs/PasswordProtocol"
}
},
"required": [
"protocol"
]
},
"PasswordProtocol": {
"description": "Configuration for password protocol.",
"oneOf": [
{
"description": "When WASM is enabled, this will run the client portions\nbrowser-side. When JavaScript-only, passwords will be hashed\nand then sent to the server where the client portion will\nbe done on behalf of the user. In noscript mode, the password\nis sent only protected by TLS, hashed and then the client operations\nare done server side.\n\nIf a user later decides to enable JavaScript or WASM, they'll be\nable to opt in to the no-plain-password-sent modes without interruption.",
"type": "string",
"const": "Opaque"
}
]
},
"MfaConfig": {
"type": "object",
"properties": {
"totp": {
"$ref": "#/$defs/TotpConfig"
}
},
"required": [
"totp"
]
},
"TotpConfig": {
"type": "object",
"properties": {
"template": {
"description": "Used for response after registration form is submitted.\nWill just return a QR code SVG if not set.",
"type": [
"string",
"null"
]
},
"algorithm": {
"$ref": "#/$defs/TotpAlgorithm"
}
},
"required": [
"algorithm"
]
},
"TotpAlgorithm": {
"oneOf": [
{
"description": "only allowing SHA1 for now\nbecause many of the major MFA authenticator\napps don't support SHA256 or SHA512, and fail silently.",
"type": "string",
"const": "Sha1"
}
]
},
"RefreshTokenConfig": {
"description": "Configuration for refresh tokens.",
"type": "object",
"properties": {
"algorithm": {
"description": "Algorithm used for verifying the token.",
"$ref": "#/$defs/TokenAlgorithm"
},
"lifetime": {
"description": "How long a token should be valid for (seconds).",
"type": "integer",
"format": "uint32",
"minimum": 0
},
"rotation": {
"description": "how frequently the key should be rotated",
"type": "integer",
"format": "uint32",
"minimum": 0
}
},
"required": [
"algorithm",
"lifetime",
"rotation"
]
},
"TokenAlgorithm": {
"type": "string",
"enum": [
"HmacBlake2b256"
]
},
"AccessTokenConfig": {
"description": "Configuration for access tokens.",
"type": "object",
"properties": {
"algorithm": {
"description": "Algorithm used for verifying the token.",
"$ref": "#/$defs/TokenAlgorithm"
},
"lifetime": {
"description": "How long a token should be valid for (seconds).",
"type": "integer",
"format": "uint32",
"minimum": 0
},
"rotation": {
"description": "how frequently the key should be rotated",
"type": "integer",
"format": "uint32",
"minimum": 0
},
"claims": {
"description": "Token claims structuring.\n\nNote: `idx` starts at 1 to create space for system claims (id, domain, and account).",
"type": "array",
"items": {
"$ref": "#/$defs/Field"
}
}
},
"required": [
"algorithm",
"lifetime",
"rotation",
"claims"
]
},
"Field": {
"description": "Fields are used to describe properties on a model,\nIO for actions/integrations and content definitions.",
"type": "object",
"properties": {
"idx": {
"description": "The index used for compact, vector based serialization.\nThe index is preserved even if the property name changes.\nEach field must be assigned an index value from 0-255, with\nno gaps (i.e. cannot go from 2 to 4 without also having a field\nwith an index of 3).",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "this is the name that is used when accessing a property in a template\nand will be the name that is used in the JSON serialized format.",
"type": "string"
},
"kind": {
"description": "the \"kind\" represents the type of the value that will\nbe stored or sent.",
"$ref": "#/$defs/Kind"
},
"indexed": {
"description": "Used for models and content definitions. Determines whether\nyou can access an object/item by this property. All items\nhave are automatically stored ID that can be used for look\nup if no properties are indexed.\n\nNote: `indexed` fields also have a uniqueness constraint.\n\n!! Important: only `String`, `Uuid` and `Url` kinds can be indexed",
"type": [
"boolean",
"null"
]
},
"queryable": {
"description": "Specifies whether field can be queried via Gte/Gt, Lte/Lt, Eq and `BeginsWith`.\n\n!! Important: only `Bool`, `String`, `Url`, `Uuid`, `Timestamp`, `U8/16/32/64`, `I8/16/32/64`,\n!! and `F32/64` kinds can be made queryable.",
"type": [
"boolean",
"null"
]
},
"searchable": {
"description": "Specifies whether the field can be searched by any value of the same kind.",
"type": [
"boolean",
"null"
]
},
"mapping": {
"description": "Used for integrations, so that a property on the external\nresource's response can be renamed.",
"type": [
"string",
"null"
]
},
"doc": {
"description": "Optional message to hint to the user\nin an editor.",
"type": [
"string",
"null"
]
},
"compressed": {
"description": "Compresses the value of the field using the Zstd compression algorithm (level 4).\n\nNote: at some point in the future, compression levels and compression algorithm\nselection will be configurable.",
"type": [
"boolean",
"null"
]
},
"encrypted": {
"description": "Encrypts the field using `XChaCha20Poly1305`.\n\nNote: The storage encryption key is stored on the same\nserver as the data it's encrypting; anyone with full access\nto the server. This is mostly useful for protecting\nsensitive data in backups (assuming the key isn't stored with the\nbackup), and can potentially limit exposure in partial-access context\nstemming from a misconfiguration or a mistake in a multi-tenant scenario\n(i.e. for whatever reason tenant A's database contents is moved to tenant B's\ndirectory, without the keys also being shifted over).\n\n!! Important: this is not currently intended for storing *any* meaningfully\n!! sensitive data (e.g. SSNs, credit card numbers, passwords, encryption keys, etc.)\n!! use (or don't) at your own risk.",
"type": [
"boolean",
"null"
]
}
},
"required": [
"idx",
"name",
"kind"
]
},
"Kind": {
"description": "Defines the types recognized by this framework.",
"oneOf": [
{
"description": "In the case of one-to-many the deletion of the\nparent reference causes a cascading delete (i.e if you have a user, chat, message\nrelationship structure, deletion of the user or the chat automatically\ndeletes messages owned by either).\n\nReferences must always be bidirectionally defined.",
"type": "object",
"properties": {
"Ref": {
"type": "object",
"properties": {
"model": {
"description": "name of model you're referencing.",
"type": "string"
},
"field": {
"description": "name of the field on the model.",
"type": "string"
},
"many": {
"description": "whether you have many of the referenced model.",
"type": [
"boolean",
"null"
]
}
},
"required": [
"model",
"field"
]
}
},
"required": [
"Ref"
],
"additionalProperties": false
},
{
"description": "null/void/undefined/None",
"type": "string",
"const": "Void"
},
{
"description": "boolean/true/false",
"type": "string",
"const": "Bool"
},
{
"description": "integers between",
"type": "string",
"const": "I8"
},
{
"description": "integers between",
"type": "string",
"const": "U8"
},
{
"description": "integers between",
"type": "string",
"const": "I16"
},
{
"description": "integers between",
"type": "string",
"const": "U16"
},
{
"description": "integers between",
"type": "string",
"const": "I32"
},
{
"description": "integers between",
"type": "string",
"const": "U32"
},
{
"description": "integers between",
"type": "string",
"const": "I64"
},
{
"description": "integers between",
"type": "string",
"const": "U64"
},
{
"description": "floats between",
"type": "string",
"const": "F32"
},
{
"description": "floats between",
"type": "string",
"const": "F64"
},
{
"description": "universal unique identifiers. 16 bytes\nor 36 characters.",
"type": "string",
"const": "Uuid"
},
{
"description": "must be a valid URL",
"type": "string",
"const": "Url"
},
{
"description": "unix timestamp",
"type": "object",
"properties": {
"Timestamp": {
"type": "object",
"properties": {
"unit": {
"$ref": "#/$defs/TimeUnit"
}
},
"required": [
"unit"
]
}
},
"required": [
"Timestamp"
],
"additionalProperties": false
},
{
"description": "array of bytes.",
"type": "string",
"const": "Blob"
},
{
"description": "text/string/characters.",
"type": "string",
"const": "String"
},
{
"description": "JSON formatted string.",
"type": "string",
"const": "Json"
},
{
"description": "Markdown formatted string",
"type": "string",
"const": "Markdown"
},
{
"description": "array of values.",
"type": "object",
"properties": {
"List": {
"type": "object",
"properties": {
"kind": {
"description": "the type for the values stored in the list.",
"$ref": "#/$defs/Kind"
}
},
"required": [
"kind"
]
}
},
"required": [
"List"
],
"additionalProperties": false
},
{
"description": "Select from a list of options. Stored as its u8 index value.",
"type": "object",
"properties": {
"Enum": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"opts": {
"type": "array",
"items": {
"$ref": "#/$defs/EnumOpt"
}
}
},
"required": [
"name",
"opts"
]
}
},
"required": [
"Enum"
],
"additionalProperties": false
},
{
"description": "complex kind with its own name and subfields.",
"type": "object",
"properties": {
"Object": {
"type": "object",
"properties": {
"name": {
"description": "the object definition's name.",
"type": "string"
},
"fields": {
"description": "nested subfields.",
"type": "array",
"items": {
"$ref": "#/$defs/Field"
}
}
},
"required": [
"name",
"fields"
]
}
},
"required": [
"Object"
],
"additionalProperties": false
}
]
},
"TimeUnit": {
"type": "string",
"enum": [
"Seconds"
]
},
"EnumOpt": {
"type": "object",
"properties": {
"idx": {
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"type": "string"
}
},
"required": [
"idx",
"name"
]
},
"ClientPasswordHash": {
"description": "Configuration for client password hashing.\n\nWhen JavaScript or WASM modes are enabled, passwords are\nhashed with the application name, and account, before transit\n(or in the case of WASM prior to the Opaque client operations\nif Opaque is selected for the `PasswordProtocol`).",
"oneOf": [
{
"description": "Limited by what browsers can support, SHA-256 is\na good option for a client-side hash to enable\nslightly better password protection when in\njavascript-only mode, without the WASM for Opaque.",
"type": "string",
"const": "Sha256"
}
]
},
"InviteConfig": {
"type": "object",
"properties": {
"mode": {
"$ref": "#/$defs/InviteMode"
},
"lifetime": {
"description": "How long a token is valid for.\n\nDefaults to 7 days.",
"type": "integer",
"format": "uint32",
"minimum": 0
},
"clean_interval": {
"description": "On what interval to clean up expired token ids.\n\nDefaults to 30 - 90 seconds.",
"type": "array",
"prefixItems": [
{
"type": "integer",
"format": "uint32",
"minimum": 0
},
{
"type": "integer",
"format": "uint32",
"minimum": 0
}
],
"minItems": 2,
"maxItems": 2
},
"claims": {
"description": "Values that can be used internally in the API server to\nset default permissions for new accounts.\n\nTODO: in the future, these can also be set in order to pre-validate\nTODO: or constrain `app` account registrations. This will require the\nTODO: use of an `InviteCreate` action trigger (for validating and setting\nTODO: the invite token claims), and a pre-registration `InviteValidate`,\nTODO: as well as passing the invite token claims to the `Registration` trigger\nTODO: (allowing for the validated invite claims to be used in the account\nTODO: claims setting operation).\n\nNote: `idx` starts at 1 to create space for system claims (id, domain, and account).",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/Field"
}
}
},
"required": [
"mode",
"lifetime",
"clean_interval"
]
},
"InviteMode": {
"oneOf": [
{
"description": "only the root user can invite",
"type": "string",
"const": "Root"
},
{
"description": "only a site admin can invite",
"type": "string",
"const": "Admin"
},
{
"description": "anyone who has been invited can invite anyone else",
"type": "string",
"const": "Viral"
}
]
},
"Global": {
"description": "Global constant definitions, for use in [`ordinary_template::Template`]s.",
"type": "object",
"properties": {
"name": {
"description": "Name of the global constant.",
"type": "string"
},
"kind": {
"description": "Type definition for the global variable.",
"$ref": "#/$defs/Kind"
},
"value": {
"description": "JSON value of the global constant."
}
},
"required": [
"name",
"kind",
"value"
]
},
"Secret": {
"description": "Mechanism for exposing secrets to `Integration`s.",
"type": "object",
"properties": {
"name": {
"description": "Name of the secret.",
"type": "string"
},
"source": {
"description": "Where to retrieve the secret from.",
"$ref": "#/$defs/SecretSource"
},
"visibility": {
"description": "Where the secret is to be used.",
"$ref": "#/$defs/SecretVisibility"
}
},
"required": [
"name",
"source",
"visibility"
]
},
"SecretSource": {
"description": "Where Ordinary will look for the secret.",
"oneOf": [
{
"description": "Name of the host provided environment variable/secret.\n\n`Env` secrets are available to every tenant for a given\nAPI server (when running in \"multi\" mode).\n\nThis is useful in scenarios where a provider running the API\nserver would like to expose convenient integrations with 3rd parties, for\nwhich it only wants to maintain a single set of credentials.\n\n`Env` secrets are also useful when you're running a standalone\napplication and do not need a more complicated secrets management\nparadigm.",
"type": "string",
"const": "Env"
},
{
"description": "Name of a stored secret.\n\n`Stored` secrets are application scoped, and can be set\nthrough an API server on which the application runs.\n\n`Stored` secrets live in their own database and are only\naccessible to components with permissions.",
"type": "string",
"const": "Stored"
}
]
},
"SecretVisibility": {
"description": "Where the secret is accessible from.",
"oneOf": [
{
"description": "`Integrations` level visibility runs the risk of unintentionally\nsending a secret to the incorrect endpoint, but does not expose\nsecrets to any user defined modules.",
"type": "string",
"const": "Integrations"
}
]
},
"Flag": {
"description": "Feature flag definition.\n\nNote: Flags use cookies for non-logged in users,\nand use fields set on their token after they're logged in\nso that users have the ability to configure their preference\nif they have one.",
"type": "object",
"properties": {
"idx": {
"description": "Unique index for the feature flag.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the feature flag.",
"type": "string"
},
"options": {
"description": "Options for this flag. Percentage must\ntotal 100.",
"type": "array",
"items": {
"$ref": "#/$defs/FlagOption"
}
}
},
"required": [
"idx",
"name",
"options"
]
},
"FlagOption": {
"description": "Option for the feature flag.",
"type": "object",
"properties": {
"idx": {
"description": "Unique index for this flag option.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of this feature flag option.",
"type": "string"
},
"percentage": {
"description": "Percentage of users that will have this\nflag turned on.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
}
},
"required": [
"idx",
"name",
"percentage"
]
},
"Content": {
"description": "Content is used for static values that should be updated\nindependent of document structure, stylings or behavior.\n\nContent is stored in a denormalized format for indexed fields\nto optimize for maximum read efficiency.",
"type": "object",
"properties": {
"file_path": {
"description": "Specifies the path to the JSON file which contains\nthe content objects.",
"type": "string"
},
"definitions": {
"description": "Definitions/structure of the content objects in the\n`content.json`.",
"type": "array",
"items": {
"$ref": "#/$defs/ContentDefinition"
}
},
"update": {
"anyOf": [
{
"$ref": "#/$defs/ContentUpdateConfig"
},
{
"type": "null"
}
]
}
},
"required": [
"file_path",
"definitions"
]
},
"ContentDefinition": {
"type": "object",
"properties": {
"idx": {
"description": "Unique index for the content definition.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the content definition.",
"type": "string"
},
"fields": {
"description": "Fields for the content definition.\n\nNote: only fields with the `String` and `Uuid` kinds can\nbe indexed (for now).",
"type": "array",
"items": {
"$ref": "#/$defs/Field"
}
},
"lifecycle": {
"description": "configure scripts for responding to object lifecycle events.",
"anyOf": [
{
"$ref": "#/$defs/ContentObjectLifecycle"
},
{
"type": "null"
}
]
}
},
"required": [
"idx",
"name",
"fields"
]
},
"ContentObjectLifecycle": {
"type": "object",
"properties": {
"before_all": {
"description": "run script before all commands in the set",
"type": [
"array",
"null"
],
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"on_add": {
"description": "hook for running scripts before/after an object\nis added via CLI or studio.\n\n`after` receives the stringified JSON object as the\nfirst stdarg.",
"anyOf": [
{
"$ref": "#/$defs/LifecycleBeforeAfterScripts"
},
{
"type": "null"
}
]
},
"on_edit": {
"description": "hook for running scripts before/after an object\nis edited via CLI or studio.\n\n`after` receives the stringified JSON object as the\nfirst stdarg.",
"anyOf": [
{
"$ref": "#/$defs/LifecycleBeforeAfterScripts"
},
{
"type": "null"
}
]
},
"on_delete": {
"description": "hook for running scripts before/after an object\nis deleted via CLI or studio.\n\n`after` receives the stringified JSON object as the\nfirst stdarg.",
"anyOf": [
{
"$ref": "#/$defs/LifecycleBeforeAfterScripts"
},
{
"type": "null"
}
]
}
}
},
"ContentUpdateConfig": {
"type": "object",
"properties": {
"lifecycle": {
"anyOf": [
{
"$ref": "#/$defs/LifecycleBeforeAfterScripts"
},
{
"type": "null"
}
]
}
}
},
"ModelConfig": {
"description": "Defines a model in the Ordinary Database.",
"type": "object",
"properties": {
"idx": {
"description": "Index of the model. Used for its kind\nfor storage keys. There can be no gaps in\nindex values.\n\nNote: the first (0) index is always skipped as it\nis reserved for the item UUID.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the model.",
"type": "string"
},
"fields": {
"description": "Fields on the model.",
"type": "array",
"items": {
"$ref": "#/$defs/Field"
}
},
"uuid": {
"description": "Every model is generated with a UUID key. If this value\nis blank, it will default to V4. If you'd like records to\nbe ordered by time, V7 is recommended.",
"anyOf": [
{
"$ref": "#/$defs/UuidVersion"
},
{
"type": "null"
}
]
}
},
"required": [
"idx",
"name",
"fields"
]
},
"UuidVersion": {
"type": "string",
"enum": [
"V4",
"V7"
]
},
"IntegrationConfig": {
"description": "The mechanism for reverse proxying and calling\nout to external APIs.",
"type": "object",
"properties": {
"idx": {
"description": "Unique index for integration",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the integration.",
"type": "string"
},
"protocol": {
"description": "Protocol used for communicating with the external service.",
"$ref": "#/$defs/IntegrationProtocol"
},
"endpoint": {
"description": "Endpoint that the external service lives at.",
"type": "string"
},
"send": {
"description": "Definition for the parameters of the receiving service.\n\n(Automatically translated to the service's protocol/encoding format).",
"$ref": "#/$defs/Kind"
},
"recv": {
"description": "Definition for the returned value of the external service.\n\n(Automatically translated from the service's protocol/encoding format).",
"$ref": "#/$defs/Kind"
},
"secrets": {
"description": "Names of secrets to include",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"timeout": {
"description": "Max duration for the template.\n\nUnit: seconds",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
}
},
"required": [
"idx",
"name",
"protocol",
"endpoint",
"send",
"recv"
]
},
"IntegrationProtocol": {
"description": "The protocol for the integration.",
"oneOf": [
{
"description": "For integrating an external HTTP API.",
"type": "object",
"properties": {
"Http": {
"type": "object",
"properties": {
"method": {
"description": "HTTP method.",
"type": "string"
},
"headers": {
"description": "static http headers",
"type": "array",
"items": {
"type": "array",
"prefixItems": [
{
"type": "string"
},
{
"type": "string"
}
],
"minItems": 2,
"maxItems": 2
}
},
"send_encoding": {
"description": "how to encode the value passed from the action",
"$ref": "#/$defs/IntegrationProtocolHttpEncoding"
},
"recv_encoding": {
"description": "how to decode the value passed back to the action",
"$ref": "#/$defs/IntegrationProtocolHttpEncoding"
}
},
"required": [
"method",
"headers",
"send_encoding",
"recv_encoding"
]
}
},
"required": [
"Http"
],
"additionalProperties": false
}
]
},
"IntegrationProtocolHttpEncoding": {
"type": "string",
"enum": [
"Json",
"Text",
"None"
]
},
"ActionConfig": {
"description": "Configuration parameters for Ordinary Actions.",
"type": "object",
"properties": {
"ffi": {
"description": "Foreign function interface config",
"$ref": "#/$defs/ActionFfi"
},
"idx": {
"description": "Unique index value for action.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Action name. Must be unique.",
"type": "string"
},
"lang": {
"description": "The source language the action is written in.",
"$ref": "#/$defs/ActionLang"
},
"dir_path": {
"description": "Relative path to the source directory for the action.",
"type": [
"string",
"null"
]
},
"protected": {
"description": "What to check the token fields against. If blank\naction is public.",
"anyOf": [
{
"$ref": "#/$defs/Check"
},
{
"type": "null"
}
]
},
"transactional": {
"description": "whether the storage interactions\nare executed under a single transaction.",
"type": [
"boolean",
"null"
]
},
"access": {
"description": "Which Ordinary Application resources the action has access to.",
"type": "array",
"items": {
"$ref": "#/$defs/ActionAccessPermission"
}
},
"accepts": {
"description": "Input definition for the action.",
"$ref": "#/$defs/Kind"
},
"returns": {
"description": "Output definition for the action.",
"$ref": "#/$defs/Kind"
},
"triggered_by": {
"description": "How this action is called (i.e. side effect from DB/Auth,\nhttp API call, browser form submission, etc.)",
"type": "array",
"items": {
"$ref": "#/$defs/ActionTrigger"
}
},
"timeout": {
"description": "Max duration for the action.\n\nUnit: seconds",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"cors": {
"anyOf": [
{
"$ref": "#/$defs/HttpCors"
},
{
"type": "null"
}
]
},
"wasm_opt": {
"anyOf": [
{
"$ref": "#/$defs/WasmOpt"
},
{
"type": "null"
}
]
},
"privileged": {
"description": "Whether the action should have bindings for API server interaction.\n\nCan only be set on applications which have been explicitly\nallow-listed by the API server administrator via their domain.",
"type": [
"boolean",
"null"
]
},
"variables": {
"description": "List of build time environment variables.\n\nformat in template: `{{ YOUR_VAR }}`",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"required": [
"ffi",
"idx",
"name",
"lang",
"access",
"accepts",
"returns",
"triggered_by"
]
},
"ActionFfi": {
"type": "object",
"properties": {
"version": {
"$ref": "#/$defs/ActionFfiVersion"
},
"serialization": {
"$ref": "#/$defs/ActionFfiSerialization"
}
},
"required": [
"version",
"serialization"
]
},
"ActionFfiVersion": {
"type": "string",
"enum": [
"V1"
]
},
"ActionFfiSerialization": {
"description": "Input/output serialization for module and host functions.",
"type": "string",
"enum": [
"FlexBufferVector"
]
},
"ActionLang": {
"description": "The language in which the action is written.\n\nMany more languages will be supported in the future.",
"oneOf": [
{
"description": "Action is written in the Rust programming language.\n\nUses zero-copy `FlexBuffer` vectors for serialization format.",
"type": "string",
"const": "Rust"
},
{
"description": "Action is written in JavaScript.\n\n`QuickJS` runtime embedded in a Rust WASM which itself uses\n`FlexBuffer` vectors for FFI serialization, but then translates\nto JSON when communicating across the `QuickJS` runtime barrier.",
"type": "string",
"const": "JavaScript"
}
]
},
"Check": {
"type": "object"
},
"ActionAccessPermission": {
"description": "Defines access permissions that an Ordinary\nAction can configure.",
"oneOf": [
{
"description": "Provides the action access to a given model.",
"type": "object",
"properties": {
"Model": {
"type": "object",
"properties": {
"name": {
"description": "Name of the model.",
"type": "string"
},
"ops": {
"description": "List of allowed operations that the action\ncan take on the model.",
"type": "array",
"items": {
"$ref": "#/$defs/ActionAccessModelOps"
}
}
},
"required": [
"name",
"ops"
]
}
},
"required": [
"Model"
],
"additionalProperties": false
},
{
"description": "Provides the action access to a given content def.",
"type": "object",
"properties": {
"Content": {
"type": "object",
"properties": {
"name": {
"description": "Content definition name.",
"type": "string"
}
},
"required": [
"name"
]
}
},
"required": [
"Content"
],
"additionalProperties": false
},
{
"description": "Provides the action access to a given integration.",
"type": "object",
"properties": {
"Integration": {
"type": "object",
"properties": {
"name": {
"description": "Name of the integration the action can access.",
"type": "string"
}
},
"required": [
"name"
]
}
},
"required": [
"Integration"
],
"additionalProperties": false
},
{
"description": "Provides the action access to another action.",
"type": "object",
"properties": {
"Action": {
"type": "object",
"properties": {
"name": {
"description": "Name of the other action.",
"type": "string"
}
},
"required": [
"name"
]
}
},
"required": [
"Action"
],
"additionalProperties": false
},
{
"description": "Provides the action access to Ordinary Auth.",
"type": "object",
"properties": {
"Auth": {
"type": "object",
"properties": {
"ops": {
"description": "Which operations the action is allowed to take.",
"type": "array",
"items": {
"$ref": "#/$defs/ActionAccessAuthOps"
}
}
},
"required": [
"ops"
]
}
},
"required": [
"Auth"
],
"additionalProperties": false
}
]
},
"ActionAccessModelOps": {
"description": "Model operations that an action is allowed to make.",
"oneOf": [
{
"description": "Can create an item for this model.",
"type": "string",
"const": "Insert"
},
{
"description": "Can get an item for this model by UUID or Index.",
"type": "string",
"const": "Get"
},
{
"description": "Can query an item for this model by queryable field.",
"type": "string",
"const": "Query"
},
{
"description": "Can search an item for this model by searchable field.",
"type": "string",
"const": "Search"
},
{
"description": "Can update an item for this model.",
"type": "string",
"const": "Update"
},
{
"description": "Can delete an item for this model.",
"type": "string",
"const": "Delete"
}
]
},
"ActionAccessAuthOps": {
"description": "Auth operations that an action can make.",
"oneOf": [
{
"description": "Allows the action the ability to set\na user's access token fields/claims.",
"type": "string",
"const": "SetTokenFields"
}
]
},
"ActionTrigger": {
"description": "Action trigger options.",
"oneOf": [
{
"description": "HTTP request with the Ordinary data format",
"type": "string",
"const": "Ordinary"
},
{
"description": "JSON formatted API call",
"type": "object",
"properties": {
"Json": {
"type": "object",
"properties": {
"route": {
"description": "API rout",
"type": "string"
},
"method": {
"description": "HTTP method",
"$ref": "#/$defs/HttpMethod"
}
},
"required": [
"route",
"method"
]
}
},
"required": [
"Json"
],
"additionalProperties": false
},
{
"description": "Web Form Submission",
"type": "object",
"properties": {
"Form": {
"type": "object",
"properties": {
"route": {
"description": "endpoint the form should point at",
"type": "string"
},
"method": {
"description": "method for the form to use",
"$ref": "#/$defs/HttpMethod"
},
"redirect": {
"description": "redirect for after submission.\n\nnote: it is allowed to use return values in the route\nvia {return} or {`return.some_field_name`}",
"type": "string"
}
},
"required": [
"route",
"method",
"redirect"
]
}
},
"required": [
"Form"
],
"additionalProperties": false
},
{
"description": "Login event from Auth",
"type": "string",
"const": "Login"
},
{
"description": "Registration event from Auth",
"type": "string",
"const": "Registration"
},
{
"description": "For when content updates",
"type": "object",
"properties": {
"Content": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
]
}
},
"required": [
"Content"
],
"additionalProperties": false
}
]
},
"HttpMethod": {
"type": "string",
"enum": [
"PUT",
"POST",
"GET",
"DELETE"
]
},
"WasmOpt": {
"description": "Corresponds to <https://docs.rs/wasm-opt/latest/wasm_opt/struct.OptimizationOptions.html#impl-OptimizationOptions>.\n\nCertain levels may not work with `wasmtime`/`cranelift`.",
"type": "string",
"enum": [
"Size",
"SizeAggressive",
"Level0",
"Level1",
"Level2",
"Level3",
"Level4"
]
},
"AssetsConfig": {
"type": "object",
"properties": {
"dir_path": {
"description": "Relative path to assets directory.",
"type": [
"string",
"null"
]
},
"base_route": {
"description": "Note: must start with a `/` and cannot end with a `/` unless\n`/` is the entire route.",
"type": "string",
"default": "/assets"
},
"append_index_html": {
"description": "whether to add `index.html` to the base route\nand the end of routes having a trailing slash\nor missing an extension.\n\ni.e. `https://example.com/static`, `https://example.com/static/`,\n`https://example.com/static/{*path}/` and `https://example.com/static/{*path}`\n(with no extension) would return the contents of\n`/static/index.html` or `/static/{*path}/index.html`.\n\nprimarily useful when serving a generated static site.\n\nNote: cannot be used with `append_index_ext`",
"type": [
"boolean",
"null"
]
},
"skip_base_route_index_html": {
"description": "whether the `{base_route}` and `{base_route}/` routes should\nskip returning the `index.html` at the root of the static dir.\n\nuseful when the static dir `base_route` is set to `/` but you'd\nlike to have a template use the `/` route.",
"type": [
"boolean",
"null"
]
},
"append_html_ext": {
"description": "whether to append `.html` to routes that do not\ninclude an extension.\n\ni.e. `https://example.com/static/about` would return the contents of\n`/static/about.html`.\n\nprimarily useful when serving a generated static site.\n\nNote: cannot be used with `append_index_html`",
"type": [
"boolean",
"null"
]
},
"preserve_exif": {
"description": "will not strip the exif data from images.\n\nImportant: only use if you want all metadata (including\nlocation data) on your photos.",
"type": [
"boolean",
"null"
]
},
"html_csp": {
"description": "content security policy for HTML assets",
"anyOf": [
{
"$ref": "#/$defs/HttpCsp"
},
{
"type": "null"
}
]
},
"http": {
"description": "HTTP cache configuration.\n\nTODO: provide a way to pattern match files for which to apply\nTODO: specific cache controls.",
"anyOf": [
{
"$ref": "#/$defs/HttpCache"
},
{
"type": "null"
}
]
},
"precompression": {
"description": "Which encodings to use when precompressing assets.\n\nServing priority is dictated by specified order.",
"anyOf": [
{
"$ref": "#/$defs/CompressionAlgorithms"
},
{
"type": "null"
}
]
},
"internal_precompression": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/CompressionAlgorithm"
},
"maxItems": 4,
"writeOnly": true
},
"minify_css": {
"description": "Determines whether CSS files should be minified,\nprior to write.",
"type": [
"boolean",
"null"
]
},
"minify_js": {
"description": "Determines whether JS files should be minified,\nprior to write.",
"type": [
"boolean",
"null"
]
},
"minify_html": {
"description": "Determines whether HTML files should be minified,\nprior to write.",
"type": [
"boolean",
"null"
]
},
"internal_cache_control_header_value": {
"type": [
"string",
"null"
]
}
}
},
"HttpCache": {
"type": "object",
"properties": {
"cache_control": {
"anyOf": [
{
"$ref": "#/$defs/HttpCacheControl"
},
{
"type": "null"
}
]
},
"expires": {
"description": "Corresponds to the HTTP Expires header.\n<https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Expires>\n\nUnit: seconds.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"etag": {
"anyOf": [
{
"$ref": "#/$defs/HttpEtag"
},
{
"type": "null"
}
]
}
}
},
"HttpCacheControl": {
"description": "HTTP Cache-Control.\nSee: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control>",
"type": "object",
"properties": {
"max_age": {
"type": [
"integer",
"null"
],
"format": "uint",
"minimum": 0
},
"s_maxage": {
"type": [
"integer",
"null"
],
"format": "uint",
"minimum": 0
},
"no_cache": {
"type": [
"boolean",
"null"
]
},
"no_store": {
"type": [
"boolean",
"null"
]
},
"no_transform": {
"type": [
"boolean",
"null"
]
},
"must_revalidate": {
"type": [
"boolean",
"null"
]
},
"proxy_revalidate": {
"type": [
"boolean",
"null"
]
},
"private": {
"type": [
"boolean",
"null"
]
},
"public": {
"type": [
"boolean",
"null"
]
},
"immutable": {
"type": [
"boolean",
"null"
]
},
"stale_while_revalidate": {
"type": [
"boolean",
"null"
]
},
"stale_if_error": {
"type": [
"boolean",
"null"
]
}
}
},
"HttpEtag": {
"description": "HTTP Cache-Control.\nSee: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control>",
"type": "object",
"properties": {
"alg": {
"anyOf": [
{
"$ref": "#/$defs/HttpEtagAlgorithm"
},
{
"type": "null"
}
]
}
}
},
"HttpEtagAlgorithm": {
"description": "Hashing algorithm options for generating etags.\n\n`AHash` is the default if none is selected.",
"oneOf": [
{
"type": "string",
"enum": [
"AHash",
"Rustc",
"Blake3"
]
},
{
"type": "object",
"properties": {
"XXH3": {
"$ref": "#/$defs/XXH3Variation"
}
},
"required": [
"XXH3"
],
"additionalProperties": false
}
]
},
"XXH3Variation": {
"type": "string",
"enum": [
"Bit64",
"Bit128"
]
},
"CompressionAlgorithms": {
"type": "array",
"items": {
"$ref": "#/$defs/CompressionAlgorithm"
}
},
"CompressionAlgorithm": {
"description": "Compression algorithms",
"oneOf": [
{
"type": "string",
"enum": [
"All",
"Gzip",
"Brotli",
"Deflate"
]
},
{
"type": "object",
"properties": {
"Zstd": {
"type": "object",
"properties": {
"level": {
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
}
},
"required": [
"level"
]
}
},
"required": [
"Zstd"
],
"additionalProperties": false
}
]
},
"FragmentsConfig": {
"type": "object",
"properties": {
"dir_path": {
"description": "Relative path to fragments directory.",
"type": "string"
}
},
"required": [
"dir_path"
]
},
"TemplateConfig": {
"description": "Template configuration for Ordinary Applications.",
"type": "object",
"properties": {
"ffi": {
"description": "Foreign function interface config",
"$ref": "#/$defs/TemplateFfi"
},
"idx": {
"description": "Unique index for template.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Template's name.",
"type": "string"
},
"mime": {
"description": "Used as the `content-type` header for HTTP responses.\nValidated against file extension (if `path` is present).",
"type": "string"
},
"minify": {
"description": "Specifies whether the content in the file should\nbe \"minified\"/have whitespace removed.",
"type": [
"boolean",
"null"
]
},
"path": {
"description": "Relative path to the template file",
"type": [
"string",
"null"
]
},
"route": {
"description": "The route used in the HTTP server to serve this template.\nCan use segments to bind to properties on models or content",
"type": "string"
},
"protected": {
"description": "What to check the token fields against. If left blank, route is considered public.",
"anyOf": [
{
"$ref": "#/$defs/Check"
},
{
"type": "null"
}
]
},
"cache": {
"description": "Used to specify the cache policy for this template.",
"anyOf": [
{
"$ref": "#/$defs/TemplateCache"
},
{
"type": "null"
}
]
},
"csp": {
"description": "HTTP Content Security Policy configuration.\n\n<https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP>\n\n\"Base\" defaults to `default-src 'self';` and tacks on SHA-256 integrity\nhashes for all inlined scripts and styles (generated at build time) to\n`script-src 'self' sha256-b64` and `style-src 'self' sha256-b64`, respectively.\n\n`https:` is used when not running in `--insecure` mode.",
"anyOf": [
{
"$ref": "#/$defs/HttpCsp"
},
{
"type": "null"
}
]
},
"cors": {
"anyOf": [
{
"$ref": "#/$defs/HttpCors"
},
{
"type": "null"
}
]
},
"timeout": {
"description": "Max duration for the template.\n\nUnit: seconds",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"fields": {
"description": "Used for template-specific variables that don't need to be shared\nbeyond the scope of the given template, and don't warrant a content\nobject.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateField"
}
},
"globals": {
"description": "List of global variables to be included with the compiled template\nbinary. Globals are excluded by default and have to be explicitly\nlisted in the globals to be accessed from the template.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"flags": {
"description": "List of flags to be referenced by the template.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateFlagRef"
}
},
"params": {
"description": "List of params to be referenced by the template.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateParamRef"
}
},
"models": {
"description": "List of models and what fields the template needs from the models.\nThis is effectively a query definition.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateRef"
}
},
"content": {
"description": "List of content definitions and the content definition fields\nthat this template will use.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateRef"
}
},
"actions": {
"description": "Specifies which actions this template triggers, and adds\nthis template's route pattern to the action's list of valid\norigins.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"wasm_opt": {
"anyOf": [
{
"$ref": "#/$defs/WasmOpt"
},
{
"type": "null"
}
]
},
"variables": {
"description": "List of build time environment variables.\n\nformat in template: `{{ YOUR_VAR }}`",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"required": [
"ffi",
"idx",
"name",
"mime",
"route"
]
},
"TemplateFfi": {
"type": "object",
"properties": {
"version": {
"$ref": "#/$defs/TemplateFfiVersion"
},
"serialization": {
"$ref": "#/$defs/TemplateFfiSerialization"
}
},
"required": [
"version",
"serialization"
]
},
"TemplateFfiVersion": {
"type": "string",
"enum": [
"V1"
]
},
"TemplateFfiSerialization": {
"description": "Input serialization format. Output is always an array of bytes.",
"type": "string",
"enum": [
"FlexBufferVector"
]
},
"TemplateCache": {
"type": "object",
"properties": {
"stored": {
"anyOf": [
{
"$ref": "#/$defs/StoredCache"
},
{
"type": "null"
}
]
},
"http": {
"anyOf": [
{
"$ref": "#/$defs/HttpCache"
},
{
"type": "null"
}
]
}
}
},
"StoredCache": {
"description": "Render caching policy.\n\nIMPORTANT: Very experimental, may not work as described. `policy: Permanent` is currently\nthe most likely to behave correctly.",
"type": "object",
"properties": {
"policy": {
"$ref": "#/$defs/StoredCachePolicy"
},
"compression": {
"description": "Which compression formats should be stored",
"anyOf": [
{
"$ref": "#/$defs/CompressionAlgorithms"
},
{
"type": "null"
}
]
},
"internal_compression": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/CompressionAlgorithm"
},
"maxItems": 4,
"writeOnly": true
},
"max_ttl": {
"description": "Upper limit on total time a cached item can be stored\n\nUnit: seconds",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"hit_ttl": {
"description": "Compared with time since last hit.\n\nUnit: seconds",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"max_size": {
"description": "Upper limit on the cumulative size of all cached responses\nfor a given template.\n\nUnit: bytes",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"max_count": {
"description": "Upper limit on the number of cached responses stored at\na given time, for a given template.",
"type": [
"integer",
"null"
],
"format": "uint",
"minimum": 0
},
"frequency_window": {
"description": "How long an LFU \"hit\" tick is valid for\n\nUnit: seconds",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0
},
"clean_interval": {
"description": "Rate at which the cache is cleaned based on other rules.\n\nOption<(min, max)>",
"type": [
"array",
"null"
],
"prefixItems": [
{
"type": "integer",
"format": "uint64",
"minimum": 0
},
{
"type": "integer",
"format": "uint64",
"minimum": 0
}
],
"minItems": 2,
"maxItems": 2
},
"evict_on_dependency_change": {
"description": "Whether a cached item should also track the of models and content\nwhich it depends on, and evict when they are modified.",
"type": [
"boolean",
"null"
]
}
},
"required": [
"policy"
]
},
"StoredCachePolicy": {
"oneOf": [
{
"description": "No eviction on clean or write. Constrained only by overall storage limit or `max_size`\n(if set). Will only be evicted if dependencies change and `evict_on_dependency_change`\nis set.",
"type": "string",
"const": "Permanent"
},
{
"description": "Prioritize the most Frequently accessed, Recently accessed and smallest Sized items\n\n(`frequency_equality_threshold` (hit count), `recency_equality_threshold` (seconds))",
"type": "object",
"properties": {
"FRs": {
"type": "array",
"prefixItems": [
{
"type": "integer",
"format": "uint64",
"minimum": 0
},
{
"type": "integer",
"format": "uint64",
"minimum": 0
}
],
"minItems": 2,
"maxItems": 2
}
},
"required": [
"FRs"
],
"additionalProperties": false
}
]
},
"TemplateField": {
"description": "Field used within the scope of a single template.",
"type": "object",
"properties": {
"name": {
"description": "Field name",
"type": "string"
},
"kind": {
"description": "Specifies the type of the value.",
"$ref": "#/$defs/Kind"
},
"value": {
"description": "JSON value for template field."
}
},
"required": [
"name",
"kind",
"value"
]
},
"TemplateFlagRef": {
"description": "Server flags can only be used in templates whose cache is\nset to \"Never\".",
"type": "object",
"properties": {
"idx": {
"description": "Specifies the index position for the referenced\ndata. Index must be unique across flags, data models and\ncontent definitions.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the flag.",
"type": "string"
}
},
"required": [
"idx",
"name"
]
},
"TemplateParamRef": {
"type": "object",
"properties": {
"idx": {
"description": "Specifies the index position for the referenced\ndata. Index must be unique across flags, data models, params, and\ncontent definitions.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the param.",
"type": "string"
}
},
"required": [
"idx",
"name"
]
},
"TemplateRef": {
"description": "How templates reference Content Definitions\nand Data Models, and the specific fields it\nwants to include.",
"type": "object",
"properties": {
"idx": {
"description": "Specifies the index position for the referenced\ndata. Index must be unique across flags, params, data models and\ncontent definitions.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of the model or content definition.",
"type": "string"
},
"fields": {
"description": "Which fields to include.",
"type": "array",
"items": {
"$ref": "#/$defs/TemplateRefField"
}
},
"all": {
"description": "for requesting multiple top-level items\n\nnote: only supported for content. models use relationships.",
"type": [
"string",
"null"
]
}
},
"required": [
"idx",
"name",
"fields"
]
},
"TemplateRefField": {
"description": "Declaration for which fields from a content definition\nor data model to include.",
"type": "object",
"properties": {
"idx": {
"description": "Specifies index for reference field. Index\nmust be unique across fields for a given reference.",
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255
},
"name": {
"description": "Name of field to be included.",
"type": "string"
},
"bind": {
"description": "Option to bind this field's value to a route\nsegment, querystring parameter or token field.",
"anyOf": [
{
"$ref": "#/$defs/TemplateRefFieldBind"
},
{
"type": "null"
}
]
},
"fields": {
"description": "List of any nested subfields to include for this field.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/TemplateRefField"
}
}
},
"required": [
"idx",
"name"
]
},
"TemplateRefFieldBind": {
"description": "Binding options for template field refs.",
"oneOf": [
{
"description": "Bind this property to a token field.",
"type": "object",
"properties": {
"Token": {
"type": "object",
"properties": {
"field": {
"description": "Name of the field that this property should bind to.\n(i.e \"account\" if you'd like to have an \"/account\" route\nthat interprets the request based on the logged-in user's\ntoken claims/fields).",
"type": "string"
},
"expression": {
"description": "Include if the parent field is queryable.",
"anyOf": [
{
"$ref": "#/$defs/QueryExpression"
},
{
"type": "null"
}
]
}
},
"required": [
"field"
]
}
},
"required": [
"Token"
],
"additionalProperties": false
},
{
"description": "Bind this property to a route segment (i.e /{something})",
"type": "object",
"properties": {
"Segment": {
"type": "object",
"properties": {
"name": {
"description": "Specifies the name of the route segment that this property is binding to.",
"type": "string"
},
"expression": {
"description": "Include if the parent field is queryable.",
"anyOf": [
{
"$ref": "#/$defs/QueryExpression"
},
{
"type": "null"
}
]
}
},
"required": [
"name"
]
}
},
"required": [
"Segment"
],
"additionalProperties": false
}
]
},
"QueryExpression": {
"description": "Query expression to be used in template bindings.",
"type": "string",
"enum": [
"Gte",
"Gt",
"Lte",
"Lt",
"Eq",
"BeginsWith"
]
}
}
}