Ctrl K
ComunidadGuías…

seamless.yaml.example

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Seamless configuration example: every key, at its default. Safe to commit --
# it holds no secrets.
#
# `make install` seeds ~/.config/seamless/seamless.yaml from this file when that
# config does not exist yet, and never overwrites it afterwards. So fill in your
# secrets THERE, not here: the installed copy is the live one, and the daemon and
# hooks read it from any directory.
#
# Every key also has a SEAMLESS_* environment override, and env wins over the
# file. Config file search order: $SEAMLESS_CONFIG, ~/.config/seamless/seamless.yaml,
# ./seamless.yaml (gitignored -- the pre-install layout's config).

# HTTP bind address. Change it here and re-run `make install`: this file is the
# single source of truth for the bind, and the Makefile reads the port back out
# of it rather than assuming 8081.
#
# An IPv4 address also listens, on the same port, on its interface's IPv6
# addresses that reach no further, where it has them: ::1 beside 127.0.0.1, a
# unique local address (fc00::/7, such as a tailnet's fd7a:...) beside a
# private or tailnet one, never a global IPv6 address beside a private IPv4
# one. So a dual-stack name is this daemon whichever address a client tries
# first. A wildcard already listens everywhere; doctor's bind row lists the pair.
# env: SEAMLESS_ADDR
addr: "127.0.0.1:8081"

# Data directory: SQLite database + markdown trees (memory/, notes/). A leading
# ~ is expanded to the home directory.
# env: SEAMLESS_DATA_DIR
data_dir: "~/.seamless"

# This install's part in a deployment. `server` (the default) runs the daemon
# here. `client` runs no daemon at all: serve refuses, there is no database and
# no corpus on this machine, and every surface -- the seam CLI, the hooks, the
# MCP registration -- dials server_url instead. A client REQUIRES server_url.
# env: SEAMLESS_ROLE
role: "server"

# The base URL clients dial, as a bare scheme://host[:port] with no path, query
# or fragment. Empty means "derive it from addr", which is what a loopback
# install wants. Set it when the bind address is not the address clients use: a
# wildcard bind, a LAN name, a TLS listener. Naming it also adds its host to the
# Host-header allowlist, which is what arms that guard on a wildcard bind.
# A wildcard host here is refused at load (0.0.0.0, ::, [::0], ...): that
# answers "where do I listen", not "where do I dial", so it belongs in addr
# instead.
# A client of a peer_auth daemon (see peer_auth below) names its socket here
# instead -- unix:///absolute/path.sock -- and needs no mcp.api_key: the daemon
# identifies the calling OS account by the uid the kernel reports.
# env: SEAMLESS_SERVER_URL
server_url: ""

# EXTRA Host-header values the daemon answers to, beyond the loopback names, a
# concrete bind host, and the host of server_url. Requests arriving under any
# other Host get 421 (the DNS-rebinding guard). Naming even one host turns the
# guard on for a wildcard bind, which otherwise has no allowlist to apply.
# env: SEAMLESS_ALLOWED_HOSTS (comma-separated, e.g. "studio.local,192.168.1.10")
allowed_hosts: []

tls:
  # PEM certificate chain and private key. Both set -> the daemon serves https
  # (TLS 1.2 floor) instead of http, and the console session cookie is marked
  # Secure. One without the other is refused at load: it cannot serve TLS. The
  # certificate's SANs must cover the host of server_url, which is what
  # `seamlessd doctor` checks. A leading ~ is expanded.
  # env: SEAMLESS_TLS_CERT_FILE / SEAMLESS_TLS_KEY_FILE
  cert_file: ""
  key_file: ""
  # CLIENT-side extra root CA: the certificate the `seam` CLI adds to the system
  # trust store when dialing an https server_url, for a private CA or a
  # self-signed server. It is the client half of the pair above -- setting
  # cert_file together with role: client is refused.
  # env: SEAMLESS_TLS_CA_FILE
  ca_file: ""

mcp:
  # Static bearer key protecting /api/mcp and the console. On a true first run
  # (no config file anywhere) `seamlessd serve` generates one into
  # ~/.config/seamless/seamless.yaml; a file you author yourself needs one set
  # by hand:
  #   openssl rand -hex 32
  # An empty key is reported as a warning by `seamlessd doctor`.
  # env: SEAMLESS_MCP_API_KEY
  api_key: ""

budgets:
  # Hard token budget for the SessionStart briefing.
  # env: SEAMLESS_MAX_BRIEFING_TOKENS
  max_briefing_tokens: 1500
  # Default token budget for a recall response.
  # env: SEAMLESS_RECALL_BUDGET_TOKENS
  recall_budget_tokens: 1000
  # Per-field cap (in runes) on captured Interactions transport events (tool-call
  # args/result, hook prompt, session findings). 0 = unlimited: content is stored
  # in full and the tool-event retention prune is the growth control. Set a
  # positive value only as an opt-in guard against pathological payloads.
  # env: SEAMLESS_TOOL_EVENT_MAX_CHARS
  tool_event_max_chars: 0

# Briefing injection tunables: what the SessionStart <seam-briefing> auto-injects.
# Defaults reproduce the built-in behavior; every knob is also editable live in
# the console (Settings -> Briefing injection), which stores a runtime override
# in the database that WINS over this file and env until reset. Constraints and
# active-plan rollups are never dropped by these knobs (constraint_max_full only
# shapes how constraints render); a pinned stage is exempt too while its Status
# header marks a live gate.
briefing:
  # Top-ranked constraints rendered as full bullets in the briefing's
  # Constraints section; the rest collapse into one compact "+N more, equally
  # binding" line that still names every one. 0 = no tiering (all full).
  # env: SEAMLESS_BRIEFING_CONSTRAINT_MAX_FULL
  constraint_max_full: 4
  # Top-ranked conventions rendered as full bullets in the budget-competing
  # Conventions section; the rest stay behind the section's count line
  # ("recall kind=convention"). Starred conventions always render full.
  # 0 = no tiering, like constraint_max_full.
  # env: SEAMLESS_BRIEFING_CONVENTION_MAX_FULL
  convention_max_full: 4
  # Drop memory-index lines not updated within this many days. 0 = no filter.
  # env: SEAMLESS_BRIEFING_MEMORY_MAX_AGE_DAYS
  memory_max_age_days: 0
  # Cap on memory-index lines before budget packing. 0 = budget-only.
  # env: SEAMLESS_BRIEFING_MEMORY_MAX_ITEMS
  memory_max_items: 0
  # Recent findings injected. 0 hides the section.
  # env: SEAMLESS_BRIEFING_FINDINGS_COUNT
  findings_count: 3
  # Drop findings older than this many days. 0 = no filter.
  # env: SEAMLESS_BRIEFING_FINDINGS_MAX_AGE_DAYS
  findings_max_age_days: 0
  # Ready-task titles listed in the "Ready tasks" section. 0 hides the section.
  # env: SEAMLESS_BRIEFING_READY_TASKS_SHOWN
  ready_tasks_shown: 3
  # Days a captured, unapproved Claude Code plan earns an "awaiting approval"
  # line. 0 = no age cutoff.
  # env: SEAMLESS_BRIEFING_PENDING_PLAN_MAX_DAYS
  pending_plan_max_days: 7
  # Grace window (days since last update) a stage memory stays pinned when its
  # Status header is missing or not a live gate (open|in_progress|blocked).
  # 0 = pin forever.
  # env: SEAMLESS_BRIEFING_STAGE_UNKNOWN_MAX_AGE_DAYS
  stage_unknown_max_age_days: 7
  # Times budgets.max_briefing_tokens = the absolute truncation ceiling. 0 = 2.
  # env: SEAMLESS_BRIEFING_HARD_CAP_MULTIPLIER
  hard_cap_multiplier: 2
  # A child project inherits its shared parent's active memories.
  # env: SEAMLESS_BRIEFING_INCLUDE_PARENT_MEMORIES
  include_parent_memories: true
  # Recent findings from family-member projects. 0 hides the section.
  # env: SEAMLESS_BRIEFING_SIBLING_FINDINGS_COUNT
  sibling_findings_count: 2
  # Fold family members' memories in as a low-priority "Sibling memories"
  # section (their constraints/stages never cross over). Off by default.
  # env: SEAMLESS_BRIEFING_INCLUDE_SIBLING_MEMORIES
  include_sibling_memories: false
  # Utility's share of the memory-index sort key: (1-w)*recency + w*utility,
  # where utility is the time-decayed demand score from retrieval_stats.
  # 0 = pure recency (legacy). Constraints/stages/favorites stay pinned.
  # env: SEAMLESS_BRIEFING_UTILITY_WEIGHT
  utility_weight: 0.4
  # Gate for the briefing's utility re-ordering: auto = per project once the
  # gardener's readiness latch trips; on = everywhere now; off = never. The
  # bounded recall/prompt-recall utility boosts are always on.
  # env: SEAMLESS_BRIEFING_UTILITY_MODE
  utility_mode: auto

# Optional features. These ship OFF: a fresh installation exposes none of them
# until you turn one on -- here, via env, or live in the console (Settings ->
# Features), which stores a runtime override in the database that WINS over this
# file and env until reset. Turning a feature off hides its console screens, its
# MCP tools, and its agent-facing mentions; it never deletes stored data, and
# re-enabling restores every surface. An installation that already holds research
# data keeps the feature on across the upgrade (a one-time seeded override).
features:
  # Research labs and trials: the Labs and Trials console screens, the trials
  # search scope, and the lab_open, trial_record, trial_query MCP tools.
  # env: SEAMLESS_FEATURES_RESEARCH
  research: false
  # Momentum surfaces woven into existing screens: the plan finish-line card
  # (and its agent-briefing emphasis), the activity calendar with capture
  # streaks, knowledge payoff moments, and project maturity stages.
  # env: SEAMLESS_FEATURES_MOMENTUM
  momentum: false
  # The arcade layer of the Now screen: the day tape, personal records, the
  # hot-streak pulse, and celebration moments. Every number is judged from
  # real recorded activity; off, the Now screen carries no trace of it.
  # env: SEAMLESS_FEATURES_GAMIFICATION
  gamification: false
  # Write screening: every agent write to a memory or note is screened for
  # secrets, injection markers, hidden Unicode, long encoded blobs and URLs
  # outside screening.url_allowlist. A suspicious write -- and any change to a
  # constraint, convention, protocol or stage by a session an agent guard
  # marked as having read outside text, and any supersede or delete of a
  # constraint -- is held out of briefings, recall and the gardener until you
  # release it on the console's Held screen; injected memories say whether you
  # confirmed them or an agent wrote them. Off, writes land exactly as before.
  # The Home welcome card offers it too. Tunables: the screening block below.
  # env: SEAMLESS_FEATURES_SCREENING
  screening: false

# The write screens behind features.screening (inert while it is off).
screening:
  # Hosts a URL in a screened write may point at without a verdict; each entry
  # covers its subdomains too. Unset or empty turns the URL screen off. A URL
  # to any other host holds the write for your review.
  # env: SEAMLESS_SCREENING_URL_ALLOWLIST (comma-separated)
  # url_allowlist: [example.com, docs.example.org]
  # The shortest unbroken base64 or hex run that counts as a long encoded blob
  # (hashes, ids and PEM certificate lines stay below the default).
  # env: SEAMLESS_SCREENING_BLOB_MIN_LENGTH
  blob_min_length: 200
  # How often the drift watch compares every memory and note file with the
  # hash Seamless last wrote for it. A file changed or planted outside Seamless
  # stays on disk untouched but is kept out of briefings and prompt recall
  # until you accept it on the Held screen. 0 turns the watch off.
  # env: SEAMLESS_SCREENING_DRIFT_INTERVAL
  drift_interval: 5m
  # A file whose existence turns lockdown on (another program can create it
  # during an incident; lockdown only ever narrows what agents receive, and
  # the console cannot lift a lockdown this file holds). Unset: the Held
  # screen's switch is the only way in. A leading ~ expands.
  # env: SEAMLESS_SCREENING_LOCKDOWN_FILE
  # lockdown_file: /path/to/lockdown.flag
  # The Unix socket of a metadata feed's consumer: every event's metadata --
  # kind, time, session, agent process, project (isolated ones as a keyed
  # hash), item kind, sizes and verdict codes, never arguments, results,
  # bodies or prompts -- is spooled and delivered there as NDJSON batches over
  # HTTP. Unset: no feed. A leading ~ expands.
  # env: SEAMLESS_SCREENING_FEED_SOCKET
  # feed_socket: /path/to/consumer/ingest.sock
  # Where the feed spools while its consumer is down (bounded: the oldest
  # events go first, and are counted). Unset: <data_dir>/feed.
  # env: SEAMLESS_SCREENING_FEED_SPOOL
  # feed_spool: /path/to/spool

# Commit records, for an integrity monitor that matches a branch's commits to
# the tracked work that made them. Off by default, and off is exactly the
# previous behavior: no argument offered, nothing recorded, no snapshot.
commits:
  # Offer the commits argument on session_end and tasks_update: the full ids
  # (40 or 64 hex digits) of the commits a session made or a task's work
  # produced, recorded on that session or task.
  # env: SEAMLESS_COMMITS_RECORD
  record: false
  # Where the daemon writes the commit-matching snapshot: one JSON document,
  # replaced whole on every write (mode 0640; its directory must exist),
  # listing the sessions live in the window -- name, repository, start and
  # end -- and the tasks a commit in it may name, each with the commits it
  # recorded. Identifiers and times only. Unset: no snapshot. A leading ~
  # expands.
  # env: SEAMLESS_COMMITS_SNAPSHOT_FILE
  # snapshot_file: /path/to/outbox/snapshot-seamless_feed.json
  # How often the snapshot is rewritten (it is also written at start).
  # env: SEAMLESS_COMMITS_SNAPSHOT_INTERVAL
  snapshot_interval: 5m
  # How far back the snapshot reaches: a commit seen earlier is outside it.
  # env: SEAMLESS_COMMITS_SNAPSHOT_WINDOW
  snapshot_window: 720h

# The observability console. Presentation only: nothing here changes what your
# agents receive -- briefings, MCP tools, hooks, recall, and the gardener are
# identical at every level.
console:
  # How much of the console you see. One of: basic | standard | advanced.
  #   basic    -- the essentials: what your agents remember (Memories, Notes),
  #               what they did (Sessions), cleanup suggestions (Gardener).
  #   standard -- adds following the work: Now, Projects, Plans, Tasks.
  #   advanced -- every screen and knob, including Interactions and Retrieval.
  # Hidden screens stay reachable by URL (they show a banner), so no link ever
  # breaks. The console's own picker (Settings -> Experience, or the welcome
  # card on Home) stores a choice in the database that WINS over this file and
  # env until reset. A fresh installation starts at basic; an installation that
  # already recorded sessions before levels existed starts at advanced (a
  # one-time seeded choice), so an upgrade never hides a screen you were using.
  # env: SEAMLESS_CONSOLE_LEVEL
  level: basic

# Console search (the full page's fused semantic+lexical retrieval). Agent
# recall is not affected by these knobs.
search:
  # Minimum cosine similarity a semantic-only hit needs to appear in results;
  # hits the keyword leg also matched are exempt. Without a floor the cosine leg
  # is pure nearest-neighbor, so any query -- including nonsense -- fills the
  # page to its limit. 0 disables the floor. Useful values depend on the
  # embedding model; 0.3 suits OpenAI text-embedding-3-*.
  # env: SEAMLESS_SEARCH_SEMANTIC_FLOOR
  semantic_floor: 0.3

llm:
  # Primary provider for chat (session digests) and embeddings. OpenAI is the
  # default and the first-class provider. One of: openai | ollama | anthropic.
  # env: SEAMLESS_LLM_PROVIDER
  provider: "openai"

  openai:
    # env: SEAMLESS_OPENAI_API_KEY
    api_key: ""
    base_url: "https://api.openai.com/v1"
    # Any OpenAI chat model; set to your preferred one.
    chat_model: "gpt-4o"
    embedding_model: "text-embedding-3-large"
    # Native dimensionality of the embedding model. 0 = auto-detect from the
    # first embedding response (the store records dims per row regardless).
    # Maximum: 65536.
    embedding_dims: 3072

  ollama:
    base_url: "http://127.0.0.1:11434"
    chat_model: "llama3.3:latest"
    embedding_model: "qwen3-embedding:8b"
    # 0 = auto-detect; maximum 65536.
    embedding_dims: 0

  anthropic:
    # env: SEAMLESS_ANTHROPIC_API_KEY
    api_key: ""
    # env: SEAMLESS_ANTHROPIC_BASE_URL
    base_url: "https://api.anthropic.com"
    chat_model: "claude-sonnet-5"
    # Anthropic has no embeddings API; when provider=anthropic, embeddings fall
    # back to OpenAI if its api_key is set, else Ollama.

gardener:
  # The gardener only ever proposes; it never mutates memory without an explicit
  # apply. Safe to leave enabled.
  # env: SEAMLESS_GARDENER_ENABLED
  enabled: true
  # Minutes between full gardener passes (dedup, staleness, stale-stage,
  # dead-weight, digest, stale-plan, memory-wanted).
  # env: SEAMLESS_GARDENER_INTERVAL_MINUTES
  interval_minutes: 60
  # Cosine similarity at/above which two active memories are proposed for a merge.
  # env: SEAMLESS_GARDENER_DEDUP_THRESHOLD
  dedup_threshold: 0.88
  # Days of no activity (no update, injection, or read) before a memory is
  # proposed for archiving. Constraints and stages are never archived this way
  # (dead stages are handled by stale_stage_days below).
  # env: SEAMLESS_GARDENER_STALENESS_DAYS
  staleness_days: 90
  # Trailing window of completed sessions rolled into a monthly digest proposal.
  # env: SEAMLESS_GARDENER_DIGEST_DAYS
  digest_days: 30
  # Age (days) beyond which transport-level Interactions events (tool.call,
  # hook.prompt) are pruned. 0 disables the prune. Domain events are never pruned.
  # env: SEAMLESS_TOOL_EVENT_RETENTION_DAYS
  tool_event_retention_days: 30
  # Days after which a captured Claude Code plan still in draft/presented is
  # proposed for abandonment (retag plan-status:abandoned). 0 disables the pass.
  # env: SEAMLESS_GARDENER_STALE_PLAN_DAYS
  stale_plan_days: 14
  # Days without an update after which a stage memory that is not a live gate
  # (Status done, missing, or unrecognized) is proposed for archiving.
  # 0 disables the pass.
  # env: SEAMLESS_GARDENER_STALE_STAGE_DAYS
  stale_stage_days: 14
  # Minutes of no activity before an active session counts as dead: the reaper
  # expires it and the console stops showing it as live. One knob drives both,
  # so they never drift. Must comfortably exceed a long single agent turn.
  # Must be positive; omit the key to retain the built-in 45m default.
  # env: SEAMLESS_GARDENER_SESSION_IDLE_MINUTES
  session_idle_minutes: 45

capture:
  # Destination ports the capture_url tool may dial, enforced in the dialer on
  # the initial URL and on every redirect hop (alongside the private-IP and
  # scheme guards). This is an SSRF control: widen it only for a host you trust,
  # and never to reach anything on your own network. An empty list or a missing
  # key means the 80/443 default -- it does NOT mean "any port". Ports outside
  # 1-65535 are rejected at startup.
  # env: SEAMLESS_CAPTURE_ALLOWED_PORTS (comma-separated, e.g. "80,443,8080")
  allowed_ports: [80, 443]

plan_capture:
  # Capture Claude Code plan-mode activity via the PostToolUse/SubagentStop/
  # PermissionRequest hooks: plan-file saves upsert cc-plan notes, planning
  # subagents cache as cc-agent notes, approval flips the plan note's status.
  # env: SEAMLESS_PLAN_CAPTURE_ENABLED
  enabled: true
  # Create an "Implement plan: ..." tracking task when a plan is approved.
  # env: SEAMLESS_PLAN_CAPTURE_AUTO_TASK
  auto_task: true
  # On a session's first captured plan iteration, return related prior plans and
  # memories to the planning agent as additionalContext.
  # env: SEAMLESS_PLAN_CAPTURE_INJECT_RELATED
  inject_related: true

# Update check and automatic updates. The daemon asks GitHub's releases API
# whether a newer Seamless release exists -- one anonymous request that carries
# no identifiers -- and tells you when one does. auto, max_defer and min_age
# decide whether and when it also installs one by itself, which only an install
# the curl or PowerShell installer made ever does -- and a peer_auth system
# daemon whose account owns its binary directory (docs: guides/peer-auth,
# Updates). Homebrew, builds from source, client machines and layouts the
# daemon cannot vouch for are only told.
# Set auto, max_defer and min_age here, not in the daemon's environment: any
# SEAMLESS_* variable besides SEAMLESS_CONFIG there makes the install
# notify-only, so their env variables below can turn automatic updates off but
# never on.
update:
  # Unset (the default) means on for release builds and off for builds from
  # source. false means zero update traffic: no request to GitHub at all, and so
  # no automatic update either. The key is left commented out because seamlessd
  # releases before v0.7.0 reject config keys they do not know, so setting it
  # pins this file to v0.7.0 or newer.
  # env: SEAMLESS_UPDATE_CHECK
  # check: false
  # Time between checks, as a Go duration (6h, 90m, 1h30m) from 1h to 720h. A
  # bare number is refused rather than guessed at: write 6h, not 6.
  # env: SEAMLESS_UPDATE_CHECK_INTERVAL
  check_interval: 6h
  # Whether the daemon installs a newer release by itself. Unset (the default)
  # means on. false keeps the check and its notices and leaves installing to you
  # (seamlessd update); a false here or in the environment is final, and the
  # console cannot turn it back on. Commented out for the same reason as check.
  # env: SEAMLESS_UPDATE_AUTO
  # auto: false
  # How long a pending automatic update waits for a quiet daemon -- no live
  # agent session, no request in flight -- before it takes the next lull in
  # requests instead. A Go duration from 1m to 720h.
  # env: SEAMLESS_UPDATE_MAX_DEFER
  max_defer: 24h
  # How long a release must have been out before an automatic update takes it,
  # so a bad one can be pulled first. A Go duration from 0 (no wait) to 720h.
  # Installing by hand (seamlessd update) does not wait.
  # env: SEAMLESS_UPDATE_MIN_AGE
  min_age: 24h

# Peer-user authentication: a Unix socket whose callers the daemon identifies by
# the uid the kernel reports (never by anything the client sends), and the
# projects locked behind the owner's approval. Off by default, and off is
# exactly the behavior of every earlier release. On:
#   - the TCP bearer key is the owner account: it reaches every project that is
#     not locked, and so does the owner on the socket;
#   - a locked project is closed to every owner request -- its name shows, its
#     content does not, writes are refused -- unless the request rides a socket
#     connection whose process holds a lease the owner approved; the TCP bearer
#     key never holds one;
#   - an account under restricted reaches the open projects that are not locked
#     and reads -- never writes -- the global scope; any other uid on the socket
#     is refused (403);
#   - the console is the owner's alone, and shows a locked project by name and
#     counts only until it is unlocked in that browser;
#   - the daemon reads no agent files (transcripts, plan files): seam hook reads
#     them and sends the findings, token totals, model, plan and subagent
#     captures, from clients whose server_url is the socket.
# serve refuses to start unless the boundary would hold on disk too: the daemon
# runs as its own account (not the owner, not root), data_dir is that account's
# and 0700, the socket's directory and every directory above the data, the
# config file and the seamlessd binary are writable by nobody but root and the
# daemon's account, every named project exists, and every locked project is
# confidential or sealed. `seamlessd doctor`, run as the daemon's account,
# reports each of those. See the peer_auth guide in the docs for the deployment
# and the cutover from an existing install.
# The block is new after v0.7.4 and releases before it refuse config keys they
# do not know, so it is left commented out here.
# peer_auth:
#   # env: SEAMLESS_PEER_AUTH_ENABLED
#   enabled: false
#   # The OS account (user name or numeric uid) the TCP bearer key stands for.
#   # env: SEAMLESS_PEER_AUTH_OWNER
#   owner: ""
#   socket:
#     # Absolute path. Its directory may be searchable by the accounts that
#     # connect, and writable by nobody but the daemon's account or root. Not
#     # inside data_dir, which is 0700.
#     # env: SEAMLESS_PEER_AUTH_SOCKET_PATH
#     path: ""
#     # Octal permission bits. Connecting needs write permission on the socket;
#     # the kernel still names every peer, so 0666 lets every local account
#     # knock and refuses the unconfigured ones. Empty means 0666.
#     # env: SEAMLESS_PEER_AUTH_SOCKET_MODE
#     mode: "0666"
#     # Optional group (name or gid) to hand the socket to, with a mode such as
#     # 0660 to let only that group's members connect at all.
#     # env: SEAMLESS_PEER_AUTH_SOCKET_GROUP
#     group: ""
#   # Force-locked projects: locks only this file can remove. The console can
#   # set and remove locks of its own with a passkey, but never one listed here.
#   # Each locked project is closed to every owner request -- its name shows,
#   # its content does not, writes are refused -- unless the request rides a
#   # socket connection whose process holds a lease the owner approved; the TCP
#   # bearer key never holds one. The lock is not the sealed isolation state: a
#   # locked project is normally confidential. File only (no env). The earlier
#   # name, locked, still loads.
#   force_locked: []
#   # Caps every lease the owner approves on a locked project, "until this
#   # session ends" included. At most 24h; unset means 8h. File only (no env).
#   max_lease: 8h
#   # The WebAuthn relying party of the owner's approver passkeys (seamlessd
#   # approvers). Unset: on a TLS install whose server_url names a host, rp_id
#   # is that host and the origin its https address; otherwise rp_id localhost
#   # and the console's own address on localhost. rp_id is a host name, never
#   # an IP; each origin is https, or http on localhost, on rp_id or a
#   # subdomain. Changing rp_id (or the server_url it came from) later strands
#   # every passkey enrolled under the old one. File only (no env).
#   passkeys:
#     rp_id: ""
#     origins: []
#   # Restricted local accounts (user names or numeric uids): a second OS
#   # account whose agents work beside the owner's. It connects over the socket
#   # only (the kernel names it; it never holds the bearer key), reads and
#   # writes every open project that is not locked, and reads the global scope.
#   # It cannot create projects, files no access requests and holds no leases.
#   # Its writes are labeled with its name, and a constraint, convention,
#   # protocol or stage it writes is a proposal until you promote it. Refused
#   # while peer_auth is disabled, and for the owner. File only (no env).
#   restricted: []
#   # users is retired: a non-empty users map is refused at load, even while the
#   # block is disabled. List those projects under locked and remove users (the
#   # peer_auth guide has the steps).

# Project isolation beyond each project's own state (open, confidential or
# sealed, set on the console's project Overview or with seam project
# isolation). Off by default, and off is exactly the previous behavior.
#   requests: a read refused at a confidential project's fence files an access
#   request instead of only refusing: the owner may grant that one session
#   (under peer_auth, that one agent process tree) read-only access to that one
#   project for a limited time. A session in an open project that holds a grant
#   may write only into its own project for the rest of its life. Nothing that
#   involves a sealed project is ever offered, and writes are never granted.
#   Without peer_auth the approval is advisory: seam approve and the console's
#   Approve commit it with the bearer key, so any process holding the key can
#   approve. Under peer_auth it takes the same proofs as a locked project's
#   lease.
# The block is new after v0.7.4 and releases before it refuse config keys they
# do not know, so it is left commented out here.
# isolation:
#   # env: SEAMLESS_ISOLATION_REQUESTS
#   requests: false