# Keys and settings (names only, never values)

| Profile | Needs | Notes |
|---|---|---|
| client (library / CLI) | nothing | Set `SOV_JOBS_ALLOW_MEMORY_ONLY` only if you want an in-memory store for experiments. |
| native-v2 (run a server) | `SOV_HOME`, `SOV_JOBS_V2_DSN`, `SOV_JOBS_V2_SERVICE_ID`, `SOV_JOBS_V2_CAPABILITY_KEYS`, `SOV_JOBS_V2_ACTIVE_KEY_ID` (all five are required; see the rules below) | The server does not create or migrate the database. No Postgres, no server. |
| running the test suite | `SOV_JOBS_TEST_DSN` (a throwaway Postgres) | Without it many Postgres journeys error or fail; that is expected outside the private profile. |
| SOV-AGENT pay-per-token lane (later) | an OpenRouter key, optional | Prepaid with a hard cap. Never put a subscription login on a test box. |

## native-v2 setting rules (read from the code; any breach prints only `configuration_failed`, exit 1)

Known beta issue: the server does NOT say which setting is wrong. Check these in order.

| Setting | Rule |
|---|---|
| `SOV_HOME` | an absolute path, no spaces at the ends, no control characters |
| `SOV_JOBS_V2_DSN` | `postgresql+psycopg://` or `postgresql+psycopg2://` only, with a user name and a database name; the database must NOT be `postgres`, `template0` or `template1`; no spaces at the ends |
| `SOV_JOBS_V2_SERVICE_ID` | lowercase letter first, then lowercase letters, digits, `_` or `-`; up to 64 characters; no dots |
| `SOV_JOBS_V2_CAPABILITY_KEYS` | a JSON object with 1 to 4 entries `{"<key-id>": "<hex>"}`; key id: lowercase letter first, then lowercase letters, digits, `_` `.` `-`, up to 64; hex: lowercase hex digits only, even length, 64 to 8192 characters (32 to 4096 bytes); no duplicate keys, no BOM |
| `SOV_JOBS_V2_ACTIVE_KEY_ID` | must be one of the key ids in the keyring above |

Make a throwaway key (never reuse a real one): `python3 -c "import secrets; print(secrets.token_hex(32))"`.
### First run on an EMPTY database (corrected after the outsider test)

`migrate up` only upgrades an existing schema and `serve` only verifies one; on an empty database both fail with a bare `profile_mismatch`. The first-create path is `job native init` with a command card:

1. Create an empty database (any name except postgres/template0/template1) and set the five settings above. Run every command from a clean directory: a folder that contains its own `sov_jobs/` shadows the installed package.
2. Write a card, for example (the executable path in the card must be the file you hash: `sha256sum /usr/bin/echo`):

```json
{"schema_version": 1, "kind": "command-v1", "name": "hello", "program": "beta",
 "service_id": "<the same value as SOV_JOBS_V2_SERVICE_ID>", "origin": "http://127.0.0.1:18773",
 "host_id": "beta-host", "installation_id": "beta-install", "resource_id": "hello",
 "executable": "/usr/bin/echo", "executable_sha256": "<sha256 of that file>",
 "argv": ["{input.word}"], "working_root": "/tmp", "env_allow": [],
 "timeout_secs": 30, "output_limit_bytes": 65536, "tail_bytes": 2048,
 "input_schema": {"type": "object", "additionalProperties": false, "required": ["word"],
                  "properties": {"word": {"type": "string", "enum": ["hello", "world"]}}},
 "submit_principal": "beta-submitter", "worker_principal": "beta-worker",
 "submit_bearer_env": "SOV_BETA_SUBMIT", "worker_bearer_env": "SOV_BETA_WORKER"}
```

3. On Linux and macOS `--generate-missing` does not work (it writes only to the Windows credential store), so make the credentials yourself and export them: `SOV_JOBS_V2_CAPABILITY_KEYS` (the keyring above), `SOV_BETA_SUBMIT` and `SOV_BETA_WORKER` (long random strings, for example `python3 -c "import secrets; print(secrets.token_hex(32))"`).
4. `sov-jobs job native init hello.card.json --profile-out hello.profile.json` creates the schema and registers the card (the JSON it prints says `profile_created: true`).
5. `sov-jobs job native doctor hello.card.json` checks readiness and exits 5 while anything is not ready. Before the server is running it reports only `server_unreachable`.
6. Start the server in one terminal: `python -m sov_jobs.v2 serve --port 18773`. The health path is `/healthz` (not `/health`): `curl http://127.0.0.1:18773/healthz` answers `status: alive`.
7. Doctor stays at exit 5 after serve with a single remaining code, `reaper_unverified`. That is expected until you set `SOV_JOBS_ADMIN_BEARER` (a long random string, same idea as the other two) so doctor can verify the reaper. It does not stop jobs from running.
8. Start the worker in another terminal (with `SOV_BETA_WORKER` exported): `sov-jobs job command work hello.card.json`.
9. Submit a job (with `SOV_BETA_SUBMIT` exported). `--inputs` takes the PATH of a JSON file, not inline JSON: write `{"word":"hello"}` to `inputs.json`, then `sov-jobs job submit hello.profile.json --inputs inputs.json --wait --wait-seconds 60`. Success ends with exit 0, `domain_outcome: succeeded` and `stdout_tail` of `hello`.

Verified end to end on a clean Linux box by the outsider test on 2026-09-28 (empty Postgres 17, these exact steps, output `hello`). If a step does not match on your machine, that is a bug in this page: tell us.

Never paste a key into an issue, a chat or a report. Refer to it by name.
