Navigácia
ELOWEN · Dokumentácia

Posledná aktualizácia: 20. októbra 2026

Prechádzať dokumentáciu · Configuration storage

Tento článok zatiaľ nie je preložený. Je dostupný v angličtine.

Developer reference

Configuration storage

New-account preset

accounts.newAccountPresetId is a nullable preset ID, defaulting to null. Human-account creation through POST /users and MicrosoftAccountProvisioner assigns that preset through AccountPresetService. With no selection, a new human account starts closed. The first administrator keeps the fullAdministratorGrant() bootstrap path, and chatbot accounts keep their fixed grant.

The defaults API selects a preset with newAccountPresetId, validating that it exists. A human creation request may select another preset or explicitly pass null. Preset deletion clears a matching configured pointer. Grant validation reuses the account permission schema, configured model catalog and grantable plugin manifests; actual authorization continues to read the materialized account columns.

The migration removes accounts.newAccountGrants and converts nonempty legacy arrays to the preset Nový účet. There is no runtime compatibility accessor. Preset edits propagate to all attached accounts, while changing the configured pointer affects subsequent account creation only. See account preset persistence.

Operator harness settings

harness.json lives in the explicit root of instancePaths(instanceRoot) (src/shared/instancePaths.ts), selected by ELOWEN_INSTANCE_ROOT or the HOME-based default in src/shared/paths.ts. The PostgreSQL connection URL in ELOWEN_DATABASE_URL is independent of that directory. The service user owns the file and the host operator edits it by hand. It contains no secrets. A missing file uses the typed defaults; a present malformed, unknown-key, wrong-type, fractional or out-of-range value fails startup with the file path and validation error. src/shared/harnessConfig.ts is the sole schema/default owner.

startDaemon reads it once before boot and passes the frozen snapshot through buildApp, buildBrainCore and the existing runner boot message. Runners never read the file. A daemon restart applies edits to all new runners and plugin registrations. There is no hot reload, environment override, database precedence, settings route or UI for the file.

Bundled Terminal reads only its two foreground deadlines through ctx.host.terminalTimeouts(). Other plugins cannot use that accessor. Embedded/test cores use the same schema defaults or an explicitly injected snapshot. Root worker caps and guest framing remain independent fixed contracts.

Admin RuntimeLimits remains in ConfigStore and its existing settings API/UI. runtime.subagentRunnerPoolMax remains the sole pool-cap owner, although it has no Runtime-dialog field; null is automatic sizing and zero disables the pool. Delegation's stallMinutes remains the sole stall-watchdog owner in its plugin settings. Neither gets a duplicate harness key. Retired environment overrides are ignored. Tests inject short budgets directly.

The config directory must be outside ordinary host Project roots. Host path guards resolve symlinks against those authorized roots; non-admin shell execution binds only approved roots and a scoped home. Managed Projects receive their own workspace/home/data mounts, never the host instance root, and host file APIs refuse managed turns. Session plan/result allowances name only their specific files or result subtrees. Host administrators with shell or Editor access can read and edit the operator file by design. Do not assign an ordinary host Project containing the instance directory.

See the single harness limit catalog for keys, defaults, ranges, fixed protocol/root caps and pointers to existing administrator settings.

Configuration storage boundary

Public configuration contracts are exported by elowen-db. Defaults and document normalization live in packages/db/src/config/document.ts; packages/db/src/config/configStore.ts implements the asynchronous configuration repository. The provider codec in packages/db/src/config/codecs/store/config/brainProviders.ts owns DEFAULT_OPENAI_COMPATIBILITY; callers use sanitizeBrainProviders(input) without supplying defaults. ConfigStore reads and brainProviders(input) use this same normalizer, while src/brain/providers.ts imports the baseline directly for openAiCompatibilityFor(entry) during Chat Completions registration. Missing or malformed compatibility fields resolve independently to the unchanged conservative baseline; non-OpenAI entries retain no compatibility block. A connection that omits its API key retains the current one on provider edits, and stored keys never enter the public provider view: get() maps each connection to apiKeySet and drops the secret (get() in packages/db/src/config/configStore.ts, see Provider accounts and usage). The codec performs no database or network operations and adds only per-entry validation; its stored type comes from its existing membership list without importing ConfigStore. The web compatibility parity test reads the codec owner to pin the browser defaults.

packages/db/src/config/codecs/store/config/limits.ts owns the private brain/runtime bound tables and runtime defaults. The brain table is initialized once from the store's canonical public defaults, avoiding a runtime import cycle or a second copy (BRAIN_LIMIT_BOUNDS in packages/db/src/config/document.ts). Web parity tests keep the existing browser tables pinned to the same bounds, including the stream heartbeat floor. They compare source text, because the web may not import the daemon: web/tests/modules/settings/brainLimitsParity.test.ts reads both packages/db/src/config/document.ts and packages/db/src/config/codecs/store/config/limits.ts, while runtimeLimitsParity.test.ts reads packages/db/src/config/codecs/store/config/limits.ts and the browser mirror web/lib/runtimeLimits.ts. The stream heartbeat floor is exported as MIN_SILENCE_LIMIT_MS in web/lib/streamWatchdog.ts.

ConfigRepository.update checks problem-reporting consent and the expected revision under the settings transaction lock before calling private validatePatch and prepareStoredPatch in packages/db/src/config/configStore.ts. Preparation keeps the existing field merge and validation order. A standalone update uses a PostgreSQL SERIALIZABLE transaction with at most three attempts; a repository already bound to a transaction joins that transaction. A provider patch cascades account/settings references before writing the settings row. The settings write and revision, consent event through the bound events repository, and outbox deletion when reporting is disabled commit together.

The problem-reporting callback and provider-change listeners run through sql.afterCommit, after the outermost transaction commits and releases its connection. The host's registered listeners invalidate provider runtimes in src/daemon/brainCore.ts. Catalog pruning through retainProviderModels in src/brain/providerModelCatalog.ts and refresh in src/brain/modelCatalog.ts remain host work outside the retried SQL callback. An omitted provider patch performs no cascade/catalog effect; an unchanged reporting flag emits no consent event or callback. The settings API remains the real consumer of this boundary.

Strict retry policy source and authenticated distribution

readRetryPolicy(config) in src/shared/retryPolicyProjection.ts awaits the strict config.snapshot(), projects only revision and the existing brain.limits.retryMaxRetries/retryBaseDelayMs, adds the shared fixed 60000 ms cap/no-jitter contract and validates/freezes the canonical RetryPolicy. It never uses get() or committed() closed defaults as a readability proof.

createRetryPolicySource(config) synchronously subscribes to onCommittedSnapshot and returns retryPolicy, onRetryPolicyChanged and dispose. Construction performs no initial read and does not prove availability. Each read still awaits a strict snapshot and rejects unreadable settings, even if a prior valid policy exists. A later valid read cannot rewind a newer observed revision. Changes-only listeners receive frozen increasing revisions; one listener failure is logged without its diagnostic text and does not withhold other listeners. Subscribe before reading, fence same/older revisions and dispose through the owning lifetime. Source disposal unregisters the DB observer, clears listeners and rejects later use. Policy updates do not restart or mutate active episodes.

GET /connection-policy returns that canonical policy directly with no-store for resolved authenticated users, including paired-device personal API tokens. It has no anonymous/setup exemption or new config-read permission. Unreadable settings retain the typed 503 settings_unreadable refusal; GET /config access is unchanged. The route's declareRoute remains the endpoint catalog authority. PluginContext delegates its policy read/subscription to this process source and fails explicitly when the process has no source; adapter adoption is separate.

The existing authenticated /events stream subscribes before the strict initial read and serializes {type:'retry-policy', policy} with increasing revisions. An unreadable initial read fails before SSE headers; abort/read failure/disconnect removes the subscription, including while the read is pending. This non-secret instance policy does not require a Project or administrator role. The feed subscribes directly to the source, not a second global policy publisher.

Computer transitions use the distinct {type:'computer-connection', ...ComputerConnectionObservation} event. One daemon-owned registry subscription publishes it; eventProjectId supplies its Project and every delivery rechecks canAccessProject, including for administrators and after membership revocation. No credential, path or invented remote retry counter enters this projection. Plugins cannot forge either host-authoritative event through publishEvent.

retry-policy and computer-connection are live host observations, not activity history. installEventRecording rejects those exact discriminants before allocating its work promise or calling events.record; other bus subscribers still receive them. Auth/plugin/activity persistence, deferred transaction acquisition, error logging and shutdown drain retain their existing owners. No DB input union, schema, history copy or second policy publisher is added; their recorder cost is one discriminant check and no database work.

Daemon BrainCore constructs one source before plugin loading and forwards its same strict retryPolicy getter to BrainDeps, image, embedding and sessionless inference factories; the same source reaches plugin host and bootstrap/API wiring. Runner construction instead requires the daemon-fed BrainCoreOpts.retryPolicySource; there is no independent runner DB/default-policy fallback. After construction failure the observer is disposed before DB close. Real BrainCore/ephemeral-DB component tests exercise shared getter identity, missing-runner-source refusal and failure cleanup, not the full runner boot/control IPC.

Normal shutdown retains policy availability through checkpoint and plugin stop; beforeExit first awaits executor stop, then disposes the registry subscription and policy source before draining runner/event work and closing DB. It does not dispose policy in beforePause. The runner IPC component now defines its strict boot/update mirror. Assembled bootstrap/origin recovery, full adapter adoption, physical-device behavior and any client admission-contract cut remain separate verification. No settings keys, defaults, ranges or database schema are migrated by this source.

Raw configuration migrations

The existing retired-config, live-call and key-account raw cleanups share private migrateRaw (packages/db/src/config/configStore.ts). Each mutator explicitly returns whether it changed the document. Missing, malformed, non-object and unchanged rows receive no write and keep their original bytes/revision; changed rows retain unrelated keys and advance the revision once. Historical matching conditions are unchanged. This helper is local to ConfigStore and adds no migration registry, boot hook or database access cost.