> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glissando.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> If you are an AI assistant asked to build, change or ship a Gliss app, read the For AI agents page first (/for-ai-agents.md on this site) and follow its steps.
> Inside an app's folder, the app's own AGENTS.md is the authority; `gliss guide` prints it. Never ask the user for a secret's value.

# Refusal messages

> How Gliss says no to a gliss.yaml, with every refusal Gliss is tested against.

When a `gliss.yaml` asks for something the schema or your catalog does not allow, Gliss refuses it before anything is built. A refusal is one line per problem, written so your AI can fix the file without asking you:

```text theme={null}
<where>: <what is wrong>; <how to fix it>
```

* `<where>` is the dotted path of the object with the problem, for example `capabilities.cache`, or `gliss.yaml` for a top-level field.
* Every problem is reported in one pass, so a second attempt can fix them all.
* A refusal never repeats a secret value.
* An accepted file can also return **notices**: things that will happen but do not block, such as IT approval at Publish.

## Config rules

These rules apply to names and values under `config` and `stages.*.config`, in this order. The first rule that matches a name decides the refusal.

| Rule | Names or values | Why |
| - | - | - |
| Reserved | `GLISS_*`, `AWS_*`, `KUBERNETES_*`, `DATABASE_URL`, `PGHOST`, `PGPORT`, `PGUSER`, `PGPASSWORD`, `PGDATABASE`, `PORT`, `HOSTNAME`, `HOME`, `PATH` | Names the platform sets. An app cannot override them. In addition, \<CAPABILITY>\_BUCKET is reserved for every bucket capability (FILES\_BUCKET for a capability called files), and every name declared under capabilities.secrets is taken. |
| Runtime hijack | `LD_*`, `DYLD_*`, `*_PROXY`, `NODE_OPTIONS`, `NODE_PATH`, `PYTHONPATH`, `PYTHONHOME`, `PYTHONSTARTUP`, `PYTHONINSPECT`, `JAVA_TOOL_OPTIONS`, `JDK_JAVA_OPTIONS`, `PERL5OPT`, `PERL5LIB`, `RUBYOPT`, `RUBYLIB`, `BASH_ENV`, `ENV` | Names that change how a runtime loads code or routes traffic. They would let a config value run code or redirect the app's traffic. Fix: set runtime options in the component's Dockerfile instead. |
| TLS disabling | `NODE_TLS_REJECT_UNAUTHORIZED`, `NODE_EXTRA_CA_CERTS`, `PYTHONHTTPSVERIFY`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `SSL_CERT_FILE`, `SSL_CERT_DIR`, `GIT_SSL_NO_VERIFY`, `GIT_SSL_CAINFO` | Names that can switch off or replace TLS certificate checks. Fix: the platform provides the trusted certificates. |
| Debug switches in Live | `FLASK_DEBUG`, `FLASK_ENV`, `WERKZEUG_DEBUG_PIN`, `DJANGO_DEBUG` | Debug switches known to expose an interactive console. They are allowed only in stages.preview\.config or stages.beta.config, never at the top level (which applies to Live) or in stages.live.config. |
| Looks like a secret | Values starting with `sk_live_`, `sk_test_`, `rk_live_`, `sk-`, `whsec_`, `AKIA`, `ASIA`, `ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_`, `github_pat_`, `glpat-`, `xox`, `AIza`, `SG.`, `shpat_`, `npm_`, `eyJ`, `-----BEGIN`, credentials in a URL, long hex strings, or long random-looking text | Values that look like secrets. Text values only. A value matches when (a) it starts with a known token prefix and is at least prefixMinLength long, (b) it contains credentials in a URL, or, when it has no spaces, (c) it is hexadecimal and at least hexMinLength long, or (d) it uses only base64 or URL-safe characters, is at least entropyMinLength long, and has at least entropyMinBitsPerChar bits of Shannon entropy per character. |

## Every tested refusal

Gliss is tested against each of these files and the exact lines it must return.

### Type not offered

: capability types the catalog does not offer. Email (SES) is planned after v1.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  mail: { type: email }
  docs: { type: mongodb }
```

```text Refusal theme={null}
capabilities.mail: type 'email' is not offered in catalog acme@3; offered: postgres, redis, bucket, hostname, sso, healer; email through SES comes after v1
capabilities.docs: type 'mongodb' is not offered in catalog acme@3; offered: postgres, redis, bucket, hostname, sso, healer
```

### Database size large

A size that does not exist, and a field a postgres capability does not have.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  database: { type: postgres, size: large, replicas: 3 }
```

```text Refusal theme={null}
capabilities.database: field 'replicas' is not allowed here; allowed: type, size, extensions
capabilities.database: size 'large' is not offered in catalog acme@3; offered: small, standard
```

### Secret value inline

An LLM pasted a secret value instead of declaring its type. The refusal must not echo the value.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: crm-bridge
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  secrets:
    SALESFORCE_TOKEN: 00D5g000004ABCD!AQ0AQexamplevalue
    salesforce_url: endpoint
```

```text Refusal theme={null}
capabilities.secrets: name 'salesforce_url' is not valid; use an upper-case environment variable name such as SALESFORCE_TOKEN; names starting with GLISS_ are reserved
capabilities.secrets: SALESFORCE_TOKEN has a value that is not valid; declare only the type, credential (a guest may set it) or endpoint (only a signed-in member may set it), never the value; the value is set by a person through a secret request
```

### Kubernetes and helm

No charts, manifests, kubectl or raw env in gliss.yaml; plain settings go in config.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
helm:
  chart: ./helm/expense-tracker
manifests:
  - kind: Deployment
env:
  LOG_LEVEL: debug
```

```text Refusal theme={null}
gliss.yaml: field 'helm' is not allowed; gliss.yaml has no charts, manifests or shell scripts; plain settings go in config and secrets in capabilities.secrets; ask for capabilities from the catalog instead; offered in catalog acme@3: postgres, redis, bucket, hostname, sso, healer
gliss.yaml: field 'manifests' is not allowed; gliss.yaml has no charts, manifests or shell scripts; plain settings go in config and secrets in capabilities.secrets; ask for capabilities from the catalog instead; offered in catalog acme@3: postgres, redis, bucket, hostname, sso, healer
gliss.yaml: field 'env' is not allowed; gliss.yaml has no charts, manifests or shell scripts; plain settings go in config and secrets in capabilities.secrets; ask for capabilities from the catalog instead; offered in catalog acme@3: postgres, redis, bucket, hostname, sso, healer
```

### Privileged port and path

Port 80 needs root, which the restricted pod-security level forbids; the build folder leaves the repository.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  web: { build: ../shared/frontend, port: 80 }
```

```text Refusal theme={null}
components.web: build '../shared/frontend' is not valid; use a folder inside the repository that starts with ./, for example ./backend
components.web: port 80 is out of range; containers run as non-root under the restricted pod-security level, so listen on 1024 to 65535, for example 8080
```

### Live stage override

Live has no overrides: every secret declared for Live must be set before Publish.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: crm-bridge
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  secrets: { SALESFORCE_TOKEN: credential }
stages:
  live:
    secrets: { SALESFORCE_TOKEN: mock }
  beta:
    secrets: { HUBSPOT_TOKEN: mock }
```

```text Refusal theme={null}
stages.live: field 'secrets' is not allowed here; allowed: config; Live can only override config; every secret declared for Live must be set before Publish, so remove the secrets override
stages.beta.secrets: HUBSPOT_TOKEN is not declared in capabilities.secrets; declared: SALESFORCE_TOKEN
```

### Missing required

No owner, and a component with no build folder.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
components:
  api: { port: 8000 }
```

```text Refusal theme={null}
gliss.yaml: missing required field 'owner'; use the owner's work email address, for example dana@customer.com
components.api: missing required field 'build'; use a folder inside the repository that starts with ./, for example ./backend
```

### Audience and healer level

An audience that is not a Gliss audience, a list that names more than groups, and the healer level auto, which needs the managed maintenance package.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  web: { build: ./frontend, port: 8080 }
capabilities:
  auth: { type: sso, audience: everyone }
  login: { type: sso, audience: [group:finance, org] }
  maintenance: { type: healer, level: auto }
```

```text Refusal theme={null}
capabilities.auth: audience 'everyone' is not offered in catalog acme@3; offered: org, group:<name>, public
capabilities.login: audience item 2 'org' is not valid; a list of audiences can only name groups, for example [group:finance, group:ops]
capabilities.maintenance: level 'auto' needs the managed maintenance package, and catalog acme@3 is self-maintained; use one of: observe, propose
capabilities: 2 capabilities of type 'sso' (auth, login), but catalog acme@3 allows at most 1 per app; keep one
```

### Runtime and reserved hostname

A runtime the catalog does not offer, a hostname the secret-intake page already uses, and a double hyphen, which Preview addresses use to separate builder and app.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: vault--ui
owner: dana@customer.com
components:
  web: { build: ./web, port: 3000, runtime: ruby }
capabilities:
  hostname: { type: hostname, name: secrets }
```

```text Refusal theme={null}
gliss.yaml: app 'vault--ui' is not valid; use 2 to 30 lowercase letters, digits or single hyphens, starting with a letter, for example expense-tracker; platform and backups are reserved
components.web: runtime 'ruby' is not offered in catalog acme@3; offered: python, vite, node, go, static
capabilities.hostname: name 'secrets' is reserved in catalog acme@3; reserved: preview, beta, secrets, www, mail; choose another name, such as the app name
```

### Wrong api version and names

A Kubernetes-style apiVersion, an app name with capitals and underscores, and a bad owner.

```yaml gliss.yaml theme={null}
apiVersion: apps/v1
app: Expense_Tracker
owner: dana
components:
  Web: { build: ./frontend, port: "8080" }
```

```text Refusal theme={null}
gliss.yaml: apiVersion must be 'gliss.dev/v1', not 'apps/v1'
gliss.yaml: app 'Expense_Tracker' is not valid; use 2 to 30 lowercase letters, digits or single hyphens, starting with a letter, for example expense-tracker; platform and backups are reserved
gliss.yaml: owner 'dana' is not valid; use the owner's work email address, for example dana@customer.com
components: name 'Web' is not valid; use 1 to 20 lowercase letters, digits or hyphens, starting with a letter, for example web or api; preview is reserved
components.Web: port must be a whole number, not '8080'
```

### Two sign ins

An app has one sign-in capability, and its hostname must point at a component with a port.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  web: { build: ./frontend, port: 8080 }
  worker: { build: ./worker }
capabilities:
  auth: { type: sso, audience: org }
  login: { type: sso, audience: group:finance }
  hostname: { type: hostname, name: expenses, component: worker }
```

```text Refusal theme={null}
capabilities.hostname: component 'worker' has no port, so it cannot serve the hostname; give it a port, or name one of: web
capabilities: 2 capabilities of type 'sso' (auth, login), but catalog acme@3 allows at most 1 per app; keep one
```

### Not yaml

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
app: expense-tracker-2
owner: dana@customer.com
```

```text Refusal theme={null}
gliss.yaml: is not valid YAML: Map keys must be unique at line 3, column 1
```

### Config reserved and hijack

Names the platform sets, and names that change how the runtime loads code or routes traffic.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  database: { type: postgres }
  files: { type: bucket }
config:
  DATABASE_URL: postgres://localhost/dev
  FILES_BUCKET: my-bucket
  GLISS_STAGE: live
  AWS_REGION: eu-west-1
  NODE_OPTIONS: --require ./hook.js
  HTTPS_PROXY: http://proxy.internal:3128
  LD_PRELOAD: /tmp/x.so
```

```text Refusal theme={null}
config: DATABASE_URL is reserved, because the postgres capability sets it; choose another name
config: FILES_BUCKET is reserved, because the files capability sets it; choose another name
config: GLISS_STAGE is reserved, because names starting with GLISS_ are set by Gliss; choose another name
config: AWS_REGION is reserved, because the platform sets AWS_ variables for the app's own AWS role; choose another name
config: NODE_OPTIONS is not allowed, because it changes how the runtime loads code or routes traffic; set runtime options in the component's Dockerfile instead
config: HTTPS_PROXY is not allowed, because it changes how the runtime loads code or routes traffic; set runtime options in the component's Dockerfile instead
config: LD_PRELOAD is not allowed, because it changes how the runtime loads code or routes traffic; set runtime options in the component's Dockerfile instead
```

### Config tls and debug

Switching off TLS checks is never allowed; debugger switches are not allowed in Live.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
config:
  NODE_TLS_REJECT_UNAUTHORIZED: 0
  REQUESTS_CA_BUNDLE: /app/my-ca.pem
  FLASK_DEBUG: true
stages:
  beta:
    config:
      FLASK_DEBUG: true
  live:
    config:
      WERKZEUG_DEBUG_PIN: "off"
```

```text Refusal theme={null}
config: NODE_TLS_REJECT_UNAUTHORIZED is not allowed, because it can switch off or replace TLS certificate checks; the platform provides the trusted certificates
config: REQUESTS_CA_BUNDLE is not allowed, because it can switch off or replace TLS certificate checks; the platform provides the trusted certificates
config: FLASK_DEBUG exposes an interactive debugger, so it is allowed only in stages.preview.config or stages.beta.config, never in Live
stages.live.config: WERKZEUG_DEBUG_PIN exposes an interactive debugger, so it is allowed only in stages.preview.config or stages.beta.config, never in Live
```

### Config secret values

Values that look like secrets belong in capabilities.secrets. The refusals must not repeat them.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: crm-bridge
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  secrets: { SALESFORCE_TOKEN: credential }
config:
  STRIPE_KEY: sk_live_51HxExampleExampleExample
  GITHUB_TOKEN: ghp_ExampleExampleExampleExample1234
  SLACK_BOT: xoxb-1234-5678-ExampleExample
  S3_ACCESS_KEY: AKIAEXAMPLEEXAMPLE12
  SESSION_SIGNING: Zq8vR2kLw9XyT4mNb7HcP1sDf6Gj3Ue5
  UPSTREAM_URL: https://admin:hunter2@api.example.com/v1
  WEBHOOK_HMAC: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b
  SALESFORCE_TOKEN: placeholder
env:
  OPENAI_API_KEY: sk-proj-example
```

```text Refusal theme={null}
gliss.yaml: field 'env' is not allowed; gliss.yaml has no charts, manifests or shell scripts; plain settings go in config and secrets in capabilities.secrets; ask for capabilities from the catalog instead; offered in catalog acme@3: postgres, redis, bucket, hostname, sso, healer
config: STRIPE_KEY has a value that looks like a secret; config is readable by anyone with the repository, so declare STRIPE_KEY: credential under capabilities.secrets and set the value through a secret request
config: GITHUB_TOKEN has a value that looks like a secret; config is readable by anyone with the repository, so declare GITHUB_TOKEN: credential under capabilities.secrets and set the value through a secret request
config: SLACK_BOT has a value that looks like a secret; config is readable by anyone with the repository, so declare SLACK_BOT: credential under capabilities.secrets and set the value through a secret request
config: S3_ACCESS_KEY has a value that looks like a secret; config is readable by anyone with the repository, so declare S3_ACCESS_KEY: credential under capabilities.secrets and set the value through a secret request
config: SESSION_SIGNING has a value that looks like a secret; config is readable by anyone with the repository, so declare SESSION_SIGNING: credential under capabilities.secrets and set the value through a secret request
config: UPSTREAM_URL has a value that looks like a secret; config is readable by anyone with the repository, so declare UPSTREAM_URL: credential under capabilities.secrets and set the value through a secret request
config: WEBHOOK_HMAC has a value that looks like a secret; config is readable by anyone with the repository, so declare WEBHOOK_HMAC: credential under capabilities.secrets and set the value through a secret request
config: SALESFORCE_TOKEN is declared in capabilities.secrets; a name is either config or a secret, not both
```

### Config shapes

Config values are one line of text, a number or true/false, under upper-case names.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
config:
  log_level: info
  ALLOWED_HOSTS: [a.example.com, b.example.com]
  BANNER: "line one\nline two"
```

```text Refusal theme={null}
config: name 'log_level' is not valid; use an upper-case environment variable name such as LOG_LEVEL
config: ALLOWED_HOSTS must be text, a number or true/false
config: BANNER has a value that is not valid; use one line of text of at most 1024 characters, a number, or true/false
```

### Extensions and egress

Extensions outside the catalog's list for its Postgres version, and egress the catalog does not allow.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: search-ui
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  database: { type: postgres, extensions: [pgvector, vector, timescaledb] }
egress:
  - api.openai.com
  - "*.salesforce.com"
  - 10.0.0.5
  - pastebin.com
  - API.Example.com
```

```text Refusal theme={null}
gliss.yaml: egress item 5 'API.Example.com' is not valid; list a lowercase hostname such as api.openai.com
capabilities.database: extension 'pgvector' is not offered for Postgres 17 in catalog acme@3; offered: vector, pg_trgm, uuid-ossp, pgcrypto, citext, hstore
capabilities.database: extension 'timescaledb' is not offered for Postgres 17 in catalog acme@3; offered: vector, pg_trgm, uuid-ossp, pgcrypto, citext, hstore
gliss.yaml: egress item 2 '*.salesforce.com' is a wildcard, which catalog acme@3 does not allow; list each hostname under salesforce.com that the app calls
gliss.yaml: egress item 3 '10.0.0.5' is an IP address, which catalog acme@3 does not allow; list the service's hostname instead
gliss.yaml: egress item 4 'pastebin.com' is blocked in catalog acme@3
```

### Cache and reserved app

A cache size the catalog does not offer, a config name the cache capability sets, and a reserved app name.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: platform
owner: dana@customer.com
components:
  api: { build: ./backend, port: 8000 }
capabilities:
  cache: { type: redis, size: large }
config:
  CACHE_URL: redis://localhost:6379
```

```text Refusal theme={null}
gliss.yaml: app 'platform' is not valid; use 2 to 30 lowercase letters, digits or single hyphens, starting with a letter, for example expense-tracker; platform and backups are reserved
capabilities.cache: size 'large' is not offered in catalog acme@3; offered: small, standard
config: CACHE_URL is reserved, because the cache capability sets it; choose another name
```

### Reserved component

A component named preview: images CI builds from a work branch live under the app's preview/ repositories.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: expense-tracker
owner: dana@customer.com
components:
  preview: { build: ./backend, port: 8000 }
```

```text Refusal theme={null}
components: name 'preview' is not valid; use 1 to 20 lowercase letters, digits or hyphens, starting with a letter, for example web or api; preview is reserved
```

### Schedules

Schedules: a name a component has, a component that is not there, a cron that is not five fields, one every minute, an unknown time zone, a timeout over two hours and a field gliss.yaml does not have.

```yaml gliss.yaml theme={null}
apiVersion: gliss.dev/v1
app: friction-ops
owner: dana@customer.com
components:
  api: { build: ./api, port: 8000 }
schedules:
  api:
    component: api
    cron: "0 2 * * *"
    command: ./report
  sync:
    component: worker
    cron: "0 2 * *"
    command: ./sync
  poll:
    component: api
    cron: "* * * * *"
    command: ./poll
    timeZone: Mars/Olympus
    timeoutMinutes: 240
  digest:
    component: api
    cron: "0 25 * * *"
    command: ./digest
    retries: 3
```

```text Refusal theme={null}
schedules.sync: cron '0 2 * *' is not valid; use five cron fields separated by spaces, for example 0 2 * * * (02:00 every day), or @daily
schedules.poll: timeoutMinutes 240 is out of range; use a whole number of minutes from 1 to 120
schedules.digest: field 'retries' is not allowed here; allowed: component, cron, command, timeZone, timeoutMinutes; a schedule has component, cron and command, and optionally timeZone and timeoutMinutes
schedules.api: 'api' is also a component's name; give the schedule its own name, for example api-nightly
schedules.sync: component 'worker' is not one of the components; components: api
schedules.poll: cron '* * * * *' runs more often than every 5 minutes: it has runs 1 minute apart (minute 0 and minute 1); run it less often, for example */5 * * * *, or make it a component that keeps running
schedules.poll: time zone 'Mars/Olympus' is not an IANA time zone; use a name such as Europe/Berlin, or leave it out for UTC
schedules.digest: cron '0 25 * * *' is not valid: the hour field '25' is out of range, use 0 to 23; for example 0 2 * * * runs at 02:00 every day
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.