nookins / docs

Configuration reference

Current YAML layout, fields, constraints, defaults and activation rules.

Documentation for Nookins 0.42.1-alpha.1 · Public alpha

Bundle layout

config/
  nookins.yaml
  owners.yaml
  channels.yaml
  retention.yaml
  agents.d/<agent_id>.yaml
  addons.d/<instance_id>.yaml
  automations.d/<automation_id>.yaml
  mcp.d/<server_id>.yaml
  hooks.d/<hook_id>.yaml

The four top-level files and at least one agent file are required. Other resource directories may be absent when unused. A filename is the stable resource ID. IDs use 1–64 ASCII letters, digits, _, or -, cannot begin with _ or -, and are unique ignoring ASCII case where portability requires it.

config.d overlays are not supported. Nookins rejects unknown fields, duplicate keys, anchors, aliases, merge keys, tags, multiple YAML documents, and non-.yaml resource fragments.

Bootstrap writes all generated singleton defaults. The schema permits omission only where the typed default or empty named collection is defined; loading does not silently rewrite an operator's file. The explicit migration emits a complete canonical schema-19 bundle from supported schema-8 through schema-18 input. Runtime startup accepts schema 19 only; earlier schemas require the protected migration path.

Installation intent

nookins.yaml contains the schema, listening address, default reasoning policy, bounded supervision concurrency, speech provider choices, intrinsic Sentinel posture, and governed skill-evolution controls:

schema_version: 19
listen: 127.0.0.1:8790

supervision:
  reasoning_workers: 2
  concurrent_turns_per_worker: 4
  concurrent_image_generations: 1

speech:
  transcription: disabled       # disabled | whisper | soniox
  text_to_speech: local          # disabled | local | soniox
  soniox:
    region: us                   # us | eu | jp

sentinel:
  enabled: false
  mode: summoned                # summoned | aware | watch | participate
  budget: standard              # low | standard | high
  retention: standard           # short | standard | extended

skill_evolution:
  enabled: true
  # Collection remains active, but provider work is inert without this runtime.
  curator_runtime: null
  qualification_runtimes: []
  budget:
    per_agent_daily_runs: 1
    global_daily_runs: 4
    episode_sample: 8
    max_episode_characters: 15000
    max_turns: 12
    daily_tokens: 200000

The three supervision fields are local-operator-only and restart-applied. reasoning_workers is 1–16, concurrent_turns_per_worker is 1–16, and concurrent_image_generations is 1–8. Including dedicated per-agent workers, configured reasoning capacity may not exceed 128 turns. One conversation lane still serializes its own turns. These values bound parallel work; they do not grant authority or make one conversation concurrent with itself.

Main listen: 127.0.0.1:8790 owns the bind address and port. It is editable in Settings → Overview and requires a restart. An explicit 0.0.0.0:8790 binds all IPv4 interfaces; [::]:8790 binds IPv6. A fixed nonzero port is required. Authentication remains enforced on every interface. Local health checks and transports connect to the corresponding loopback address for wildcard binds. The OCI image retains its container-interface bridge for loopback-configured listeners; an explicitly configured interface is honored.

An authored value wins. For existing files that omit listen, the established --listen, then NOOKINS_LISTEN, then 127.0.0.1:8790 fallback remains available. Exports materialize the effective address. A nondefault port requires the corresponding --url for control-API CLI commands. When changing ports, use the old running URL to apply the change and the new URL after restart.

The OpenRouter setup guide includes exact file locations, provider selection, a complete free-router example, agent inheritance, activation and troubleshooting. Main-file exports include every authored root section and comments; reasoning.openrouter: null makes the disabled policy visible without authorizing a provider. This additive surface remains schema 19 with database 26.

Owners and channels

owners.yaml is a direct owner-ID map:

owner:
  display_name: Owner
  identities:
    personal:
      - 15551234567@s.whatsapp.net

With one owner, Nookins infers the primary. With multiple owners, exactly one body must contain primary: true. The primary designation selects legacy/default delivery routes; it does not make that owner more powerful. Every configured owner is an equal fleet custodian when invoking the primary agent through an authenticated direct DM or an owner-bound local interactive session.

channels.yaml is a direct account-ID map:

personal:
  channel: whatsapp
  enabled: true
  sender_roles:
    "+15551234568": trusted

alerts:
  channel: telegram
  enabled: false
  sender_roles: {}

Telegram secret references are derived, never authored. Account alerts reads NOOKINS_TELEGRAM_ALERTS_TOKEN; hyphens become underscores.

Ordered reasoning fallback

Schema 19 uses explicit provider and native model fields. Targets are unique provider/model pairs, with one to five entries in effective attempt order. codex and openrouter are the supported providers; default selects Codex's native default. Omit an entry's reasoning_effort to use its provider default. The retired schema-19 target string is accepted only by explicit migration.

Main nookins.yaml owns the default chain and provider ceiling. For example, replace provider-slug with an authorized OpenRouter serving-provider slug:

reasoning:
  model_chain:
    - provider: codex
      model: default
      reasoning_effort: medium
    - provider: openrouter
      model: openrouter/free
  openrouter:
    allowed_providers: [provider-slug]
    data_collection: deny
    zero_data_retention: true
    max_run_cost_usd: "0"
    max_daily_cost_usd: "0"

Use allowed_providers: ["*"] to explicitly permit automatic selection across serving providers; the wildcard must appear alone. Privacy and spending ceilings still apply. Named provider lists retain their existing restrictive behavior, and an agent cannot replace a restricted main list with "*".

Omitting reasoning.openrouter disables OpenRouter. Its provider allowlist and privacy rules are ceilings for every agent. The daily USD ceiling applies across all agents on a UTC day and is enforced atomically with spend reservation. Zero USD is valid: only requests quoted at zero cost are allowed. This does not grant unlimited requests or waive provider rate limits. The host key is NOOKINS_OPENROUTER_API_KEY in secrets.env; it is never agent YAML or model input.

An agent's omitted or empty model_chain inherits reasoning.model_chain. A nonempty chain replaces the whole default; entries are never implicitly merged. An agent may supply an openrouter policy with narrower allowed providers, stricter privacy restrictions, or lower run/daily budgets. All fields of that optional policy are supplied together. Omitting it inherits the main policy for an OpenRouter chain. Agent daily limits coexist with the installation-wide cap. A Codex-only effective chain cannot declare an agent OpenRouter override.

Only structured availability, quota, authentication, rate-limit or compatibility failures advance the chain. Refusal, invalid input, cancellation, exhausted Nookins budgets, unknown failures and any failure after tool dispatch stop execution. Reservations survive failure/uncertainty. OpenRouter completion is stateless; images and raw Codex response capture cannot move to OpenRouter in this version. Shared provider cooldowns survive runs. After repairing authentication or credit, explicit configuration activation resets provider health without erasing spending.

openrouter/free is an explicit supported router model, not a fixed endpoint. Nookins checks fresh free-model metadata, retains the provider/privacy restrictions, and sends zero maximum prices. Before returning content or tool calls it requires a qualified returned model, authorized provider and confirmed zero usage cost. Unverifiable or incompatible routing fails closed. Other OpenRouter router IDs are not supported. Fixed-model requests retain endpoint-specific preflight and provider pinning; provider fallback remains disabled.

/model shows the effective chain and its source. /model list lists visible Codex choices and configured OpenRouter targets. /model codex MODEL_ID or /model openrouter AUTHOR/MODEL selects the conversation's first choice for subsequent turns and retains configured fallback entries without duplicates. The existing bare Codex model command remains available. /model auto removes the override and restores the configured chain, including inherited defaults. Owner/trusted authorization is required; OpenRouter selections must already be in the agent's effective configured chain. Commands cannot enable egress or raise budgets. Changes never retarget a running turn.

Protected schema-19 migration splits target strings into provider/model fields, keeps explicit agent chains, and constructs a main policy covering the union of previous provider permissions. Each agent retains its old restrictions. The main daily ceiling is the sum of predecessor agent daily caps, preserving previous aggregate authority without silently introducing paid fallback. Protected source bundles retain exact predecessor bytes for rollback. Core migration 26 preserves older spending records and adds global caps and zero-cost reservations.

See setup and testing.

Agents

agents.d/main.yaml owns the agent’s complete intent, including routes and outgoing links:

display_name: Nookin
instructions: >-
  Serve with discretion, initiative, warmth, and exact respect for authority.

model_chain:
  - provider: codex
    model: default
    reasoning_effort: medium

features:
  administration: {}
  schedules:
    audience: owner_only
  sentinel:
    audience: owner_only

web:
  access: restricted             # disabled | public | restricted
  domains: [docs.example.com]

workspace:
  capabilities: [files, downloads, execution]
  allowed_network_domains: [api.example.com]

interaction:
  busy_input: append             # append | queue
  acknowledgement_reaction: true
  background_result: brief       # brief | digest | full | silent

routes:
  owner_dm:
    account: personal
    conversation: "+15551234567"
    activation:
      mode: all_messages
    access:
      roles: [owner]
      senders: []
  family:
    account: personal
    conversation: family-group-id
    activation:
      mode: addressed
      hotwords:
        - literal: computer
        - regex: '(?i)hey\s+nookin'
    access:
      roles: [owner, trusted, member]
      senders: []

links:
  research:
    capabilities: [ask, history]

addons: [weather]

With one agent, Nookins infers the primary. With multiple agents, exactly one agent body must contain primary: true. Downloads and execution imply files. A route activation section has a mode of commands, addressed, or all_messages. addressed retains commands, native mentions, and replies, always matches the bound agent's current display name, and may contain up to 16 reviewed literal or regex hotword matchers. Literal matching uses NFKC, Unicode lowercase, collapsed whitespace, and Unicode-aware word boundaries. Regex matchers use Rust regex syntax against the original text and are compiled with bounded, non-backtracking automata before configuration can be staged. Explicit matchers are limited to 2,048 bytes total; each literal is at most 64 characters and each regex at most 256. Voice notes follow the same activation rules as text: an addressed route requires a native mention, a reply to Nookins, or a command/hotword in the original caption. A bound unaddressed recording may be transcribed for ambient context when transcription is enabled, but does not start an agent turn. Transcript text is never searched for activation hotwords. With transcription disabled, an unaddressed recording is retained without agent work. Queued transcription retries recheck the original request against routing. A non-empty allowed_network_domains list grants networked execution and therefore requires execution. web.access: restricted requires at least one domain; other access modes require an empty list.

Nookins-owned features are compiled into the product and configured directly on the agent. addons contains only instance IDs for installed extension packages; both package: ward and package: nookins are reserved and rejected for third-party add-ons.

The strict feature keys are:

FeatureConfiguration
administrationEmpty object; primary agent only and always custodian-only.
browseraudience, plus optional paired isolation: nookins_managed_profile and exact profile.
forgeaudience.
google_calendaraudience, state, connection_id, calendar_ids, access, and writes_require_approval.
hostaudience.
houseaudience.
mcpaudience.
media_recoveryaudience.
schedulesaudience.
sentinelaudience; the managed installation policy remains in top-level sentinel.
social_mediaaudience, required anonymous-media acknowledgement, and optional paired authenticated-YouTube acknowledgement/profile.

Every audience is owner_only by default or agent_callers. Use nookins feature catalog and nookins feature inspect FEATURE for the compiled catalog. The primary agent can propose durable typed changes with Nookins.feature.propose; built-in features deliberately have no enable-once lease.

Instructions remain in agent YAML. Existing persona.md, tools.md, and heartbeat.md context files retain their separate roles.

Resource files

Each file contains one body; its ID comes from the filename.

addons.d/weather.yaml:

package: acme_weather
addon: weather
audience: owner_only
settings: {}

automations.d/investigate_failure.yaml keeps the typed when, run, optional delivery, and limits contract.

mcp.d/catalog.yaml keeps enabled, required, and an HTTP or stdio transport. Nookins owns the MCP timeout:

enabled: true
required: false
transport: http
url: https://mcp.example.com
bearer_token_env: NOOKINS_CATALOG_TOKEN

hooks.d/audit.yaml keeps module identity and events. Nookins owns the fuel budget:

enabled: true
module: audit.wasm
sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
events: [management.proposal.review_requested]

Retention

retention.yaml is the complete human data-lifetime policy:

operational_history: 30d
audit: 90d
effect_payloads: 7d
social_media:
  sources: 30d
  derived: 90d

Durations here are whole days from 1d through 3650d. Nookins derives internal tombstone, count, and cleanup mechanics without broadening these lifetimes.

Harness defaults and explanation

Derived values are read-only. Inspect them through authenticated config status or the harness explanation namespace:

nookins config status
nookins config explain /harness/scheduler/tick
nookins config explain /harness/workspace/quotas/workspace_size

Changing a harness default requires a new schema and migration decision; it is never smuggled into hidden legacy state.

Validate, migrate, export, and activate

nookins config validate
nookins config diff
nookins config apply --dry-run
nookins config apply

# Breaking schema-8 through schema-18 to schema-19 migration:
nookins config migrate
nookins service stop
nookins config migrate --write
nookins service start

# Restore the exact digest-fenced predecessor.
nookins service stop
nookins config migrate --restore <migration-id>

# Atomically materialize an exact schema-19 source bundle in a new directory.
nookins config export --view exact --directory ./nookins-config-export

Migration composes legacy overlays, writes canonical resource files, reports every non-default tuning replaced by the current harness defaults, and blocks ambiguous web policy, noncanonical Telegram secret references, and resource-ID collisions. --write requires Nookins to be stopped and protects the exact predecessor and forward bundles before activation.

Agent interaction policy

interaction.private_records_without_confirmation is an optional boolean, false when omitted. It permits only the authenticated requester's creator-only Records mutations described in operations.

interaction.quiet_hours is optional and contains an IANA timezone plus 1–32 windows. Each window has nonempty days (mon through sun), exact HH:MM start and end, and allowed_roles drawn from owner, trusted, member. An empty role list silences the agent in that window; blocked is invalid. Distinct start/end times are required. Overnight windows use the starting day; multiple active windows intersect roles. These restrictions never grant access.

features.google_calendar.personal_calendar_ids is an optional set, empty when omitted. It must be a subset of the selected calendar_ids on a read-write feature. writes_require_approval remains true for all actions outside that standing scope. The connecting user must explicitly grant the conditional calendar.acls.readonly OAuth scope before Nookins can verify personal-calendar eligibility. Missing verification retains confirmation.

interaction.custom_roles is an optional agent-local map from arbitrary role names to account maps of sender-identity sets. quiet_hours.windows.allowed_roles accepts these names alongside the built-in roles. Custom roles grant reply-policy membership only, never global tool authority. The settings catalogue exposes the entire membership map atomically; unknown accounts/roles and built-in-name collisions are invalid.

Role-aware interaction guidance

Each agent may configure interaction.role_prompts as an ordered list. It defaults to empty and uses the existing owner, trusted, member, and blocked security roles or names declared in that agent's interaction.custom_roles.

interaction:
  custom_roles:
    family:
      personal: ["+15551234567"]
  role_prompts:
    - role: trusted
      prompt: "Use concise answers and offer relevant available tools."
    - role: family
      prompt: "Use a warm, familiar tone."

Role guidance supplements the base persona in groups and DMs. All matching entries apply in order; later entries override earlier conflicting style preferences. Entries are private system instructions configured through the existing owner configuration proposal workflow. They never grant permissions or bypass approval, route admission, quiet hours, or blocked status. Custom roles retain their existing agent/account-scoped identity matching and have no security authority.

Each prompt must contain non-whitespace text and be at most 4 KiB of UTF-8. The serialized list, including role labels, is bounded to 16 KiB. Unknown custom-role references are rejected. Removing a custom role requires removing its prompt references in the same validated configuration change. Existing configuration requires no migration.

Only the authenticated current caller selects guidance; other participants and quoted or forwarded messages do not. Background, scheduled and delegated work do not inherit these interactive prompts. Caller identity and selected guidance are part of the prompt fingerprint, so role changes and alternating group callers cannot reuse another caller's prompt configuration.

On this page