Skip to content

configuration reference

KeepPeek documentation edited this page Oct 18, 2026 · 4 revisions

Configuration reference

KeepPeek stores settings in one application configuration file, named config.toml by default. The existing companion secrets.toml holds reusable private strings. Layouts, notification rules, credential metadata, and camera templates belong in the application configuration, not in separate settings files. Media files and recording catalogs are data, not additional settings stores.

Live-wall display settings belong to each saved dashboard in [peek_layouts]. Shape, fit, gap, corner radius, streaming mode, stream ceiling, and wake-lock intent are stored on the server and included in configuration and layout exchange. Browser permission and active media/wake-lock handles remain runtime state.

This chapter describes the supported on-disk sections and their serialized Rust types. It is not a list of every API response or runtime struct. Use visual configuration for normal administration and configuration exchange for validated export and import.

Location and editing

Environment Default application configuration
macOS ~/Library/Application Support/keeppeek/config.toml
Linux ${XDG_CONFIG_HOME:-$HOME/.config}/keeppeek/config.toml
Windows %APPDATA%\keeppeek\config.toml
Docker /config/keeppeek/config.toml

--config <path>, --config=<path>, or -c <path> selects another file. Its companion secrets file is always named secrets.toml in the same directory. These flags do not create a second settings store.

Default paths belong to the account running the process. A Windows service can resolve a different %APPDATA% from an interactive launch; the macOS package runs a per-user launch agent. Confirm the service identity and effective paths before transferring an installation. See Upgrades and migrations.

Use the UI and typed configuration operations while KeepPeek runs. Stop the server before manual edits so a concurrent settings update cannot overwrite them. Server writers validate candidates, preserve unrelated sections and secret references, and replace the file atomically under the shared configuration-update lock. Some changes apply live; listener or storage changes can require the restart or migration reported by the configuration plan.

On Unix, KeepPeek creates its default private directory with mode 0700 and private files with mode 0600. Restrict the directory to the service account on every platform. Secret values are plaintext at rest, not encrypted by TOML.

In the tables below, optional means omit the field when unset: TOML has no null. Integer types refer to the Rust model; values must also fit TOML's integer representation. Vec<T> is an array, and a map is a table keyed by the indicated identity. Byte limits and character limits are different. Root fields must appear before a table header or they belong to that table.

Section index

TOML section Owning type Ownership
Root fields Config Operator settings
[access] AccessConfig Administrator security policy
[direct_card] DirectCardConfig Exact browser-origin allowlist
[storage] StorageToml Recording paths, retention, and safety
[battery_wake] BatteryWakeConfig Reolink battery-camera wake service
[logging] LoggingConfig Service log destination
[operational_events] OperationalEventsConfig Health-event timing
[operational_events.cameras."<camera-id-or-ip>"] OperationalEventOverride Per-camera timing overrides
[privacy.default] PrivacySchedule Inherited server-enforced recurring privacy
[privacy.cameras."<camera-id-or-ip>"] PrivacySchedule Per-camera server-enforced recurring privacy
[event_forwarder.mqtt] MqttForwarderConfig inside EventForwarderConfig MQTT configuration; server-owned revision
[camera_defaults] CameraCredentialDefaults Shared camera defaults
[<namespace>.<camera-key>] CameraConfig Camera settings
[<namespace>.<camera-key>.events] EventConfig Native camera event selection and filters
[access_credentials] PersistedAccessCatalog Server-managed credential records
[peek_layouts] StoredRegistry Server-managed layouts and per-user selection
[configuration_templates] StoredTemplateDocument Server-managed camera templates
[notifications] NotificationConfiguration Server-managed rule drafts and active rules
[runtime_profiles] ProfileRegistry Server-managed profile definitions and selection
[storage_migration] StorageMigration Server-managed pending storage move

homekit is a reserved root section, not an implemented HomeKit configuration schema. Do not use it as a camera namespace. Every other non-reserved root table is interpreted as a camera namespace; cameras is the conventional name, not the only allowed one. A typo in a section name can therefore be interpreted as camera configuration. Some structs ignore unknown fields, so successful TOML parsing alone does not prove a field has an effect.

[runtime_profiles] is server-managed internal data. Its registry is bounded to 32 profiles, and each profile can target at most 127 cameras with sparse runtime-safe overrides. Do not edit this section manually. The partial implementation supports Administrator management and activation through WebRTC StateStore, including bounded definition reads and staged bounded writes. Privacy, recording, notifications, and camera availability reach their current runtime owners. Delayed profile commands cannot replace newer pending runtime status; an already running camera stop is not preempted. An active profile's saved target that is no longer configured remains degraded through startup, completion and live removal; re-adding the camera replaces that observation. Activating Base clears missing-target observations from the previously active profile. Events-only camera edits also remain in effect after a profile disables the camera and Base enables it again. Profile targets use the server-managed profile_identity described below. An Administrator can retry a pending or degraded selection with Reapply active policy in Settings. Complete privacy leak, asynchronous convergence, capacity and performance qualification remain open under #202.

Sources: configuration, cameras, and the subsystem sources linked below. Config.source is an in-memory copy of the original TOML table, marked serde(skip); there is no writable source field in the file.

Passwords and secret references

Secrets is a flat BTreeMap<String, String>. Its keys must match [A-Z_][A-Z0-9_]*. Nested tables, arrays, numbers, and booleans are not secret values. Enter real values directly on the server; do not put them in source control, screenshots, logs, or shell arguments.

Example companion secrets file, with placeholders that must be replaced locally:

CAMERA_USERNAME = "replace-with-camera-user"
CAMERA_PASSWORD = "replace-with-camera-password"
FRONT_CAMERA_PASSWORD = "replace-with-front-camera-password"
MQTT_PASSWORD = "replace-with-broker-password"

The application configuration refers to those keys:

host = "0.0.0.0"
port = 8081

[camera_defaults]
username = "{secret:CAMERA_USERNAME}"
password = "{secret:CAMERA_PASSWORD}"

[cameras.front_door]
ip = "192.0.2.10"
password = "{secret:FRONT_CAMERA_PASSWORD}"
main_rtsp_url = "rtsp://{secret:CAMERA_USERNAME|url}:{secret:FRONT_CAMERA_PASSWORD|url}@192.0.2.10:554/stream1"

[cameras.back_door]
ip = "192.0.2.11"

[event_forwarder.mqtt]
username = "keeppeek"
password = "{secret:MQTT_PASSWORD}"
  • {secret:KEY} substitutes the string directly. It can occupy the entire value or part of it.
  • {secret:KEY|url} percent-encodes a URL component. Use it for a password, username, or query value embedded in a URL, not for an entire URL. Bytes other than RFC 3986 unreserved characters are encoded, including @, :, /, spaces, and non-ASCII UTF-8 bytes.
  • KEEPPEEK_SECRET_<KEY> in the server process environment overrides the same key in the file. A literal value does not consult either source.
  • A missing referenced key, malformed reference, unsupported modifier, or invalid secrets file fails loading or the corresponding settings operation. Diagnostics identify the key without including its value.
  • Resolution substitutes strings, not TOML syntax. It does not convert a quoted numeric secret into an integer or expand table keys. Inserted secret values are not recursively expanded.
  • Reusing a key gives several cameras the same credential. A per-camera key overrides the shared default. Rotate an ordinary secret in the companion file and restart KeepPeek to reconnect with the new value.

Reference boundaries

Setting Supported resolution
Root settings, camera defaults, camera entries, and ordinary settings sections String values and strings inside arrays/tables are resolved before typed deserialization. The resolved string must still satisfy the field's type and validation.
[event_forwarder.mqtt].password Direct or referenced string. The MQTT settings writer stores a supplied password in the companion file under MQTT_PASSWORD.
Notification active.actions[].destination and draft.actions[].destination The notification loader explicitly resolves destination references. Non-browser destinations saved through the rule store use managed KEEPPEEK_NOTIFICATION_... secret keys.
Camera-template username_secret_reference and password_secret_reference Complete reference tokens only, not inline credentials or interpolated surrounding text. Values are resolved when applying camera configuration.
[access_credentials], [peek_layouts], other notification fields, and other template fields Not blanket-expanded as secret templates. These sections have dedicated loaders.
[storage_migration] Concrete paths written by the server, not a secret-template interface.

Configuration responses and unrelated settings updates preserve existing reference tokens instead of returning or persisting resolved credentials. Inline camera credentials remain supported but are not automatically moved to the secret file. Intentionally replacing a camera credential in the editor can replace its reference with an inline value. Ordinary layout exchange must not include credentials, verifiers, or unrelated user state. Full recovery archives have a different security contract; see backup and restore.

The first-run access key is generated as KEEPPEEK_ACCESS_KEY in the companion file and referenced from the root access_key field. Named credentials created later retain only verifiers in the application configuration and return their raw keys once. See authentication and the secret lifecycle guide.

Root settings

Type: Config. Nested section fields are listed in the section index.

Field Type Default Meaning
host String "0.0.0.0" HTTP listener bind address.
port u16 8081 HTTP listener port.
access_key AccessKey Unset in the model; generated on startup Compatibility initial Administrator key: a UUID string, a reference resolving to a UUID, or integer 0 as the unset marker. 0 is not a switch that disables authentication.

Access policy

Type: AccessConfig, section [access].

Field Type Default Validation and meaning
local_networks Vec<IpNet> Networks below At most 64 CIDRs. Matches become trusted-local Administrators without sign-in. Setting the array replaces the defaults.
trusted_proxies Vec<IpNet> [] At most 64 CIDRs, checked separately from local_networks. Only these peers can supply the supported forwarded-client header contract.
require_secure_remote bool true Require the declared secure boundary for remote traffic.
failed_authentication_limit u32 5 Nonzero failed-attempt limit per address/window.
failed_authentication_window_secs u64 60 Nonzero authentication-rate window, in seconds.
session_idle_timeout_secs u64 1800 Nonzero; cannot exceed the absolute timeout.
session_absolute_timeout_secs u64 86400 Nonzero maximum session lifetime.
max_sessions_per_principal u32 64 Nonzero concurrent-session limit per identity.
max_sessions_per_address u32 128 Nonzero concurrent-session limit per address.

Default local networks are 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1/128, fc00::/7, and fe80::/10. A container bridge or VPN within those ranges is trusted unless the operator narrows the list. Forwarding headers and CORS do not replace authentication. Review authentication and access control before exposing the listener.

Direct card origins

Type: DirectCardConfig, section [direct_card].

Field Type Default Validation and meaning
allowed_origins Vec<String> [] Unique canonical exact HTTP(S) origins, such as "https://ha.example". No credentials, path, query, fragment, wildcard, or trailing slash.

External authentication configuration

Type: access::external::Config, optional section [external_auth]. Configured OIDC providers use browser authorization-code login with PKCE. Configured identity proxies use exact assertions from their immediate trusted transport peer. Both methods use revocable browser sessions and the existing server-side camera grants. Omitting the section preserves existing local and bearer behavior.

Field Type Default Validation
allowed_origins Vec<String> Required 1–16 unique canonical exact HTTPS origins, without credentials, path, query, fragment, wildcard, or trailing slash.
providers Vec<Provider> Required 1–4 providers with unique IDs.
bearer_enabled bool false Mixed mode requires an explicit transition deadline.
bearer_transition_until_ms Optional i64 Absent Positive Unix milliseconds; present if and only if bearer_enabled is true.

Each [[external_auth.providers]] has id, name, mappings, and a method table. IDs contain 1–64 ASCII letters, digits, underscores, or hyphens. Names contain 1–64 bytes without control characters and cannot be whitespace-only. At least one mapping is required per provider; there are at most 128 across all providers. Unknown fields are rejected.

Each [[external_auth.providers.mappings]] contains claim (1–64 bytes), value (1–256 bytes), and role (administrator or user). Claim names and values cannot be whitespace-only or contain control characters. User mappings require an explicit camera_access table using the existing all_cameras, camera_ids, and group_ids fields and limits, with at most 128 camera and group IDs combined per external identity policy. Set all_cameras and camera_ids explicitly; group_ids defaults to an empty list. With all_cameras = false, empty camera and group lists grant no cameras. Administrator mappings must omit camera_access because Administrators are unrestricted. Unmapped identities and identities matching multiple rules are denied. Mapping claims must be strings or lists of at most 128 strings, each at most 256 bytes; the claims object is limited to 16 KiB serialized JSON.

For [external_auth.providers.method] with kind = "oidc":

Field Type Default Validation
issuer String Required HTTPS URL without user-info, query, or fragment.
client_id String Required 1–256 bytes, nonblank, no control characters.
client_secret Optional String Absent Complete {secret:KEY} or {secret:KEY|url} reference; resolved value is nonblank, at most 4,096 bytes, without control characters. Inline secrets are rejected.
redirect_uri String Required Allowed HTTPS origin with exactly /auth/callback, no query or fragment.
scopes Vec<String> ["openid"] At most 16 unique OAuth scope tokens of 1–64 bytes; must include openid.
display_name_claim String "name" 1–64 bytes, nonblank, no control characters.
endpoint_origins Vec<String> [] At most 16 unique canonical HTTPS origins for additional provider endpoints.
private_networks Vec<IpNet> [] At most 64 explicitly permitted private issuer networks.
logout_uri Optional String Absent HTTPS URL without user-info, query, or fragment.

All external-authentication URLs are limited to 2,048 bytes. A configured URL does not authorize arbitrary discovery redirects or other network destinations.

For [external_auth.providers.method] with kind = "proxy":

Field Type Default Validation
trusted_peers Vec<IpNet> Required 1–64 immediate transport-peer CIDRs, independent of forwarded-client classification.
subject_header String Required Exact identity assertion header.
role_header String Required Exact role assertion header; values still require explicit mapping.
name_header Optional String Absent Optional display metadata header.
secret_header Optional String Absent Present if and only if shared_secret is present.
shared_secret Optional String Absent Same reference and resolved-value rules as client_secret.

Header names are distinct ignoring ASCII case, at most 64 ASCII letters, digits, or hyphens, start with X-, and cannot start with X-Forwarded-. Secret references remain unchanged in config.toml; only the runtime copy resolves them. Existing configuration validation rejects invalid candidates before the atomic writer runs.

Trusted peer networks from different identity proxy providers must not overlap. KeepPeek checks the immediate peer after normalizing IPv4-mapped IPv6 addresses; configure IPv4 CIDRs for those peers.

Durable external identities

[external_identities] has version = 1 and at most 1,024 records. Each record contains id (non-nil UUID), provider_id, subject_fingerprint (64 lowercase hexadecimal characters), display_name, role, camera_access, positive revision, enabled, and nonnegative created_at_ms. Provider and display names are nonblank, at most 64 bytes, and contain no control characters. Subject fingerprints and IDs are unique. Administrator camera access is unrestricted.

The optional admission_policy_fingerprint is a 64-character lowercase hexadecimal digest of the resolved provider policy at successful authentication. KeepPeek updates it after ordinary or replacement-Administrator authentication. An absent or stale digest cannot count as a retained Administrator path; the identity must authenticate under the current policy first. Legacy records remain readable and still trigger lockout protection. Raw subjects, claims, and tokens are not stored.

Camera defaults and camera entries

Source: camera types and the camera loader in configuration.

Type: CameraCredentialDefaults, section [camera_defaults].

Field Type Default or inheritance
username String Empty string; accepts secret references.
password String Empty string; accepts secret references.
backend Optional CameraBackend No shared override; camera model defaults to "auto".
transport Optional CameraTransport No shared override; camera model defaults to "tcp".
record_generic_motion_events Optional bool No shared override; camera model defaults to false.
recording_mode Optional CameraRecordingMode No shared override; camera model defaults to "event-boost".
event_recording_duration_secs Optional u64 No shared override; camera model defaults to 60 seconds.
event_pre_recording_duration_secs Optional u64 No shared override; camera model defaults to 0; range 0..30 seconds.
event_recording_stream Optional EventRecordingStream No shared override; camera model defaults to "main"; "main" or "sub".

Username and password fall back to their shared defaults when the per-camera resolved value is empty, including when the field is omitted. The other shared defaults apply only when the per-camera field is absent. Ports and RTSP URLs are not fields of CameraCredentialDefaults.

Type: CameraConfig, section [<namespace>.<camera-key>], usually [cameras.front_door].

Each camera table also contains the server-managed profile_identity string. It is a unique, canonical, 36-character UUID v4 assigned at startup or when the camera is created. It has no operator-supplied default and does not accept secret references. The configuration owner preserves it across ordinary camera edits; changing the IP in the same saved entry keeps profile references. Deleting the camera and adding another at the same IP assigns a new identity. Saved profile targets for the deleted identity remain unavailable until an Administrator explicitly removes or retargets them. Other camera controls and media source IDs continue to use their existing IP identities.

Camera ip supports references such as {secret:CAMERA_IP}; the resolved value must be an IP address. Identity migration, the profile identity map and camera definitions use the same loaded disk secrets.toml snapshot. The raw reference stays in config.toml after migration instead of being replaced by the resolved address. Editing the secrets file does not change an already loaded configuration snapshot; the next configuration load uses the updated address and preserves the camera's saved profile_identity.

On upgrade, KeepPeek assigns identities and converts existing IP-based profile references in one atomic configuration write before media starts. A current, unambiguous target becomes its saved identity. An absent target becomes an unavailable missing-ip:<address> reference that cannot bind to a later device at that address. Changed profile and registry revisions advance once, so stale clients must reload. Invalid or duplicate UUIDs and ambiguous active targets fail validation without writing the configuration. Keep the identity metadata when exporting or restoring the full configuration; do not edit it manually.

A migrated [runtime_profiles] section carries server-managed identity_schema = 1. Keep it with the profile definitions. Earlier KeepPeek binaries reject this section rather than starting with UUID targets that they cannot apply; restoring an older binary requires restoring a compatible pre-migration configuration as well. Unknown identity schema versions fail validation.

Field Type Default Meaning
ip IpAddr Required Camera IP address, not a DNS hostname.
name Optional String Camera table key The loader replaces this value with the table key. Use display_name to change the UI label without changing storage identity.
display_name Optional String Falls back to name Human-readable camera label.
manufacturer Optional String No override Nonempty trimmed value takes precedence over discovery.
username String Shared default or empty Camera username; literal or secret reference.
password String Shared default or empty Camera password; literal or secret reference.
onvif_port Optional u16 8000 when unset ONVIF service port. Typed configuration operations require 1..65535.
http_port Optional u16 80 when unset Direct camera HTTP control port; typed operations require 1..65535.
main_rtsp_url Optional String Discover stream Explicit main-stream RTSP URL takes precedence over ONVIF discovery. Blank values are not explicit URLs.
sub_rtsp_url Optional String Discover stream Explicit sub-stream RTSP URL. Use URL-escaped references in credential components.
uid Optional String None Reolink P2P UID for direct BCUDP discovery.
backend CameraBackend Shared default or "auto" "auto", "retina", or "reo-proto".
transport CameraTransport Shared default or "tcp" "tcp" or "udp".
record_generic_motion_events bool Shared default or false Opt in to supported generic motion events.
recording_mode CameraRecordingMode Shared default or "event-boost" "off", "sub", "main", "both", "event-boost", or "event-only".
event_recording_duration_secs u64 Shared default or 60 Event-triggered recording duration; typed operations and TOML loading for event-only or pre-roll-enabled event-boost require 1..3600 seconds.
event_pre_recording_duration_secs u64 Shared default or 0 Range 0..30 seconds; 0 disables pre-roll allocation.
event_recording_stream EventRecordingStream Shared default or "main" "main" or "sub"; selects the event-only recording stream. EventBoost always boosts to main.
events EventConfig Defaults below Camera-native event policy, independent of video backend; configured through the file.

These are stored configuration types, not promises that every camera supports every backend, transport, or recording capability. Discovery results such as CameraCapabilities, CameraPorts, MediaProfile, VideoConfig, and AudioConfig are not additional editable camera tables.

Native camera events

Type: EventConfig, section [<namespace>.<camera-key>.events], for example [cameras.front_door.events]. Source: camera event policy. These settings are not part of CameraCredentialDefaults and are not exposed by the fixed protobuf camera-settings form. Ordinary camera API edits preserve the saved event table.

Field Type Default Values and limits
mode EventMode "auto" "auto", "vendor", "onvif-pullpoint", "rtsp-metadata", or "disabled".
metadata_stream MetadataMode "auto" "auto", "enabled", or "disabled". Enabling metadata does not override vendor or disabled event mode.
event_service_url Optional String None; discover service HTTP(S) URL of at most 4,096 UTF-8 bytes using the exact configured camera IP. No hostname alias, wildcard/multicast address, port zero, credentials, query, fragment, backslash, whitespace, or control character.
source_tokens Vec<String> [] At most 32 exact opaque tokens, each at most 256 UTF-8 bytes. Empty or control-character entries are rejected.
include_topics Vec<String> [] Expanded namespace root plus slash-separated local path. Up to 32 filters combined with exclusions, each at most 1,024 UTF-8 bytes.
exclude_topics Vec<String> [] Same syntax and shared count/byte limits as inclusions; exclusions take precedence.
snapshots bool true Allow optional camera snapshots; a missing or failed image does not discard the event.

An empty inclusion list permits recognized topics. An example filter is {http://www.onvif.org/ver10/topics}VideoSource/MotionAlarm; unstable document prefixes such as tns1:VideoSource/MotionAlarm are rejected. Source tokens retain their exact text. Filters require nonempty namespace and path segments without whitespace, embedded prefixes, or control characters.

The loader resolves supported {secret:KEY} and {secret:KEY|url} references in event strings before validation. Preserve those references in config.toml and keep reusable private values in secrets.toml; URL escaping does not relax endpoint restrictions. Direct file edits require the existing configuration activation/restart path; there is no automatic file watcher. Generic motion recording still requires record_generic_motion_events = true, independently of event mode. The native event operator guide describes transport selection, health evidence, and verification.

Recording storage

Type: StorageToml, section [storage]. Optional paths are resolved by the runtime when absent; inspect the effective settings instead of assuming an omitted path is the current directory.

Both recording directories default to recordings/ under the default OS KeepPeek configuration directory, even when --config selects a configuration file elsewhere. An omitted catalog path becomes recordings.db under the effective long-term directory; the thumbnail directory defaults to .event-thumbnails under that same directory. See StorageConfig::from_toml.

Field Type Default Meaning
medium_term_path Optional String Runtime-selected Medium-term recording directory.
long_term_path Optional String Runtime-selected Long-term recording directory.
recording_catalog_path Optional String Runtime-selected Recording catalog path.
event_thumbnail_path Optional String Runtime-selected Event-thumbnail directory.
event_thumbnail_max_mb u64 1024 Thumbnail storage budget, in MiB.
short_term_secs u64 120 Short-term retention, in seconds.
medium_term_secs u64 1800 Medium-term retention, in seconds.
flush_interval_secs u64 60 Recording flush interval, in seconds.
write_buffer_bytes usize 8192 Recording write-buffer size, in bytes.
long_term_max_gb u64 1024 Long-term storage budget, in GiB.
minimum_free_gb u64 10 Minimum-free-space safety threshold, in GiB.
maximum_used_percent Optional u8 No percentage threshold When set, 1..99; filesystem-used percentage threshold.
warning_free_gb u64 20 Warning-free-space threshold, in GiB.
critical_free_gb u64 10 Critical-free-space threshold, in GiB.
cleanup_hysteresis_gb u64 5 Cleanup recovery margin, in GiB.
pre_recording_stream_max_bytes u64 67108864 (64 MiB) Positive encoded-byte limit per candidate stream; must not exceed the global limit.
pre_recording_global_max_bytes u64 268435456 (256 MiB) Positive encoded-byte limit across all candidate streams, including pending replay; must fit the platform address space and TOML signed 64-bit integer range.

The effective critical threshold is the greater of critical_free_gb and minimum_free_gb. If warning_free_gb is zero while the effective critical threshold is nonzero, the warning threshold is derived as critical plus hysteresis. Otherwise, warning must be at least critical. Effective media paths come from named owners, and catalog paths come from the metadata binding. Use named-volume move controls for stored objects and the confirmed metadata restart workflow for the catalog. The general storage editor displays these paths read-only.

These values are policy defaults, not a capacity guarantee. Storage budgets and free-space thresholds must suit the actual disks and camera bitrates. A configuration ZIP preserves the target's existing storage paths on apply; it does not move a recording archive. See storage migration and recording archive recovery.

event_thumbnail_max_mb applies to catalog-referenced image files in the configured thumbnail directory. Unindexed images and temporary files are excluded from this quota and are not removed at startup. Missing image files retain their catalog references so that restoring an unavailable thumbnail directory does not require rebuilding event metadata.

Recording retention rules

Optional [storage.retention] rules set independent recording lifetimes in days. Without this section, recordings keep the existing capacity-based cleanup behavior.

[storage.retention.default]
continuous_days = 1.0
motion_days = 7.0

[storage.retention.default.events]
person = 30.0

[storage.retention.cameras."192.0.2.10"]
continuous_days = 0.0

default supplies global rules. Each cameras entry overrides rules for the exact catalog camera identity, which is the camera IP address for RTSP recordings. Omitted fields inherit defaults; explicit zero disables that rule. Event entries use exact canonical event kind strings. Motion rules match canonical motion events. Stream-specific events apply to their matching logical recording stream; events without a stream apply to all recording streams for that camera.

Durations must be finite, nonnegative, and representable as whole milliseconds within a signed 64-bit lifetime. Settings allow at most 127 camera overrides and 16 effective rules per camera. Event selectors contain 1–128 ASCII letters, digits, underscores, hyphens, or periods. Unknown fields are rejected. Numeric durations do not accept secret references.

The longest matching rule sets a whole-file deadline from the recording end. Files can therefore retain unrelated footage beside a matching event. Protection blocks automatic deletion. Shorter settings, removed rules, or corrected events never shorten an already committed deadline. Files whose event evidence exceeds the bounded evaluation limit remain excluded from automatic cleanup until their evidence can be evaluated successfully.

Apply file edits through the existing configuration activation/restart workflow; there is no file watcher or separate retention settings file. Activation persists a bounded catalog sweep and fences new cleanup admissions until evaluation finishes. Finalized recordings and event changes then receive automatic reevaluation. Expiry removes eligible whole files even when storage has spare capacity. Interrupted cleanup resumes through the existing storage owner.

To diagnose activation, inspect structured log events retention_started and retention_activation_ready, correlated by run_id. The first reports whether retention is enabled and the settings request was accepted; the second reports whether initial evaluation encountered quarantined files. Per-file evaluation warnings identify the recording and cause. Expiry logs identify removed recordings and whole-file bytes. These answer whether activation finished, which evidence needs repair, and whether expiry made progress. The worker logs no serialized configuration or event payloads.

Named storage volumes

[storage.named_volumes] stores volume and placement definitions in config.toml. A fresh installation creates four private roots beneath storage/ beside the configuration: media for active and archived recordings, exports, images for thumbnails, and metadata for the catalog and export history. Global placement rules use these roots without fallback. The initial media and image caps use long_term_max_gb and event_thumbnail_max_mb; after initialization, edit each volume's byte cap directly.

Saved volume and placement changes take effect after restart and affect future placement. Existing objects keep their catalog owner. Create custom root directories before enabling them. A missing bound root stays unavailable; startup does not recreate it or redirect writes.

The optional section has two arrays: volumes (at most 32) and placement (at most 256). Both default to empty. Ordinary settings updates that omit named_volumes preserve the section. An explicit empty section requests removal, subject to ownership and metadata checks. Unknown fields in this section and its entries are rejected.

Administrator runtime settings carry these definitions in the optional protobuf named_volumes field. Updates that include it require the current configuration revision. String fields, including IDs, roots, selectors, and candidate IDs, support existing secret references. Validation uses resolved values; saved settings and responses retain the references.

Administrators can edit volumes under Settings → Storage → Named storage volumes. Byte limits use exact whole-byte values. Refreshing status or a failed save preserves unsaved inputs. Removing a saved volume definition requires confirmation. Root probes inspect existing configured roots without creating directories; a successful probe does not establish write permission. Individual move preview, confirmation, status, and cancellation appear only when a volume runtime is available. The same runtime controls let administrators stop new writes to a volume or clear that operator drain, after confirmation. Already admitted writes finish normally. Clearing an operator drain does not override a draining or read-only saved configuration. Operator drain survives restart and does not itself move existing media.

The move controls preview up to 16 objects from the loaded page and show exact file and byte totals before confirmation. Each confirmed object has a durable move job. Stop dispatch after the current request or cancel admitted work under Move jobs. Refresh jobs to inspect progress; refresh the object page after completed batches. A lost confirmation reply is checked against the same job identity before further work is admitted.

Removing a bound definition requires stopped writes, zero owned and reserved bytes, and completed cleanup receipts. An offline enabled runtime must be stopped through operator drain, or disabled and restarted before removing an unbound definition. Remove archive-policy references and restart before removing their destination; already captured archive work must finish first. Removal keeps the catalog's immutable binding and completed history and does not delete files. The removal check inspects up to 1,024 pending archive policies; finish a larger backlog before retrying removal.

[storage.metadata] is a server-managed binding, not an ordinary placement draft. It contains volume_id, catalog_file, history_file, catalog_id, generation, filesystem, and root_identity. The filenames use one shared canonical UUID with catalog-<uuid>.db and exports-<uuid>.json. The catalog ID is a UUID, generation is 1 through i64::MAX, and both filesystem identity strings contain 1–256 bytes without control characters. The volume must exist, have the metadata role, and be enabled; a separate recording_catalog_path override is incompatible with this binding. Startup verifies the saved directory and catalog authority. It does not create an empty replacement catalog if the selected owner is unavailable.

Ordinary settings cannot install, clear, disable, or relocate the metadata owner. Unrelated updates preserve its existing secret references. Use the administrator metadata preview and confirmation controls to schedule relocation; do not construct or edit this binding manually. The internal [storage.metadata_pending] restart record contains a 32-byte source-configuration digest and the target binding. It retains the previous effective paths until a stopped transfer verifies both the catalog snapshot and copied export history, then atomically commits the new binding. Failed or interrupted transfers retain their source files and pending state for retry. Pending handoffs reject effective storage-setting changes while allowing unrelated settings saves. Startup rechecks available space, the byte cap, and the larger minimum/critical free-space reserve before fencing the source. Managed export history must already exist, fit within 8 MiB, and contain valid, uniquely owned jobs; missing or invalid history is preserved and reported unavailable.

In Settings, add and save a disabled volume with only the Metadata role, then use Preview metadata move to check its root and required space. Confirm metadata move saves the plan without interrupting recording. The service restart copies the latest catalog and export history before workers resume. Cancel a pending plan before restarting if needed. Returning to an earlier metadata volume uses the same workflow and a new catalog generation; never point the service at a retained old catalog. An interrupted startup leaves the plan available for retry after its reported storage problem is corrected.

Each [[storage.named_volumes.volumes]] entry has these fields:

Field Type Default Meaning
id String Required 1–64 lowercase ASCII letters, digits, _ or -; legacy- is reserved.
root Path string Required Absolute UTF-8 directory path, at most 4096 bytes and 64 components; no traversal, control characters, duplicate or nested roots. Windows requires a local drive path and rejects device names and reserved characters.
roles Role array Required 1–5 distinct values: active, archive, export, thumbnail, metadata. Only one bound metadata owner is active; other metadata-capable volumes can be relocation targets.
state State enabled enabled admits new writes; read_only and draining retain reads but stop new writes; disabled does not open the root.
priority u16 0 Smaller values rank first for priority placement.
capacity_bytes Optional u64 No cap Positive owned-data byte cap.
minimum_free_bytes u64 0 Minimum filesystem free space to retain.
critical_free_bytes u64 0 Placement retains the larger of minimum and critical free space.
warning_free_bytes u64 0 Must be at least both minimum and critical free space.
sources, groups String arrays Empty At most 256 unique exact selectors each, 1–256 bytes without control or surrounding whitespace. Empty lists allow all sources. When either list is populated, matching either a source or a group permits placement. Metadata volumes cannot restrict sources or groups.

All byte thresholds must fit a signed TOML 64-bit integer. A root can use the existing {secret:KEY} or {secret:KEY|url} resolver; unchanged root references are preserved during settings updates. Paths are redacted from the volume model's debug output. Lexical validation does not establish filesystem identity, writability, or protection against path replacement.

On Windows, owned storage requires local NTFS volumes with persistent ACLs and directory metadata flushing. ReFS, FAT, and network shares are not qualified for owned writes or deletion; they remain unavailable rather than bypassing these checks. Keep the configuration directory on NTFS when using the automatically created default volumes. An unavailable bound root is never recreated.

Each [[storage.named_volumes.placement]] entry contains:

Field Type Default Meaning
role Role Required One of the roles above.
source, group Optional string Absent Select either a source or a group, using the same string limits as volume allowlists. Absence selects the role default.
candidates Volume ID array Required 1–8 distinct configured volumes supporting the role.
strategy Strategy priority priority ranks smaller priorities first; free_space ranks usable free bytes after reserve and cap limits. Ties use volume ID order.
allow_fallback Boolean false False limits selection to the first candidate. True permits ranking all eligible candidates in the explicit pool.

Source rules take precedence over group rules, which take precedence over role defaults. A failed override never falls through to a broader rule. Duplicate selectors for a role are invalid. Metadata placement requires one global candidate and forbids fallback. Selection is a pure proposal; it does not reserve capacity, authorize writes, or alter existing object locations.

Source: named-volume model. Windows path validation follows the Windows filename rules.

Fourth-tier local archive

Optional [storage.archive] configures one verified local replica destination. Omitting it disables this tier. It is separate from named-volume archive placement, which relocates the authoritative recording. A verified fourth-tier copy retires its primary only through an enabled eviction policy or an Administrator's confirmed per-job action. Local recording, live viewing and the first three tiers do not wait for archive availability.

[storage.archive]
target_id = "local-archive"
root = "{secret:ARCHIVE_ROOT}"
enabled = false
automatic = false
source_ids = []
minimum_age_secs = 0
evict_after_archive = false
maximum_object_bytes = 4294967296
maximum_pending_count = 256
maximum_pending_bytes = 68719476736
operation_timeout_secs = 120
maximum_attempts = 3
Field Type Default Validation and meaning
target_id String Required Stable ID with 1–64 lowercase ASCII letters, digits, hyphens or underscores; legacy- is reserved.
root PathBuf Required Absolute local destination; at most 4,096 UTF-8 bytes and 64 path components. It must already exist when used. Named-volume path safety rules apply.
enabled bool false Enable archive work; configuration alone does not bind or create the destination.
automatic bool false Admit eligible finalized recordings automatically when the archive worker is enabled.
source_ids Vec<String> Empty Automatic-selection filter; empty selects all sources. At most 127 distinct, nonblank IDs, each at most 256 UTF-8 bytes without control or surrounding whitespace.
minimum_age_secs u64 0 Minimum finalized recording age for automatic selection, at most 315,360,000 seconds (ten 365-day years).
evict_after_archive bool false Explicitly permit policy-driven primary-copy eviction after verification; holds, leases and durable archive indexes still apply.
maximum_object_bytes u64 4,294,967,296 Per-object admission limit, from 1 byte through 64 GiB; cannot exceed the pending-byte limit.
maximum_pending_count u32 256 Retained pending jobs, from 1 through 4,096.
maximum_pending_bytes u64 68,719,476,736 Pending-byte admission ceiling, positive and at most i64::MAX.
operation_timeout_secs u64 120 Finite archive operation deadline, from 1 through 3,600 seconds.
maximum_attempts u32 3 Maximum attempts for one durable job, from 1 through 10.

Roots cannot overlap named-volume roots or legacy media, thumbnail and metadata directories. Lexical checks do not establish filesystem identity; the bounded owned-object write/read/delete probe and persisted catalog binding detect offline or replaced destinations without recreating their roots. Keep the stable target ID with its binding; changing a configured path must not adopt an old manifest under a different root identity.

Destination availability requires a successful verified operation. Opening or binding the root alone does not establish availability; test the destination after startup and refresh its result.

String settings support existing secret references. Validation uses resolved values; ordinary settings rewrites and configuration export/import preserve raw references and unrelated sections. Older runtime settings updates that omit archive settings preserve the accepted archive section. Unknown archive fields are rejected; cloud provider selectors and credentials are not supported by this local implementation. Job journals, manifests, root bindings and integrity metadata are operational catalog state, not extra settings files.

Archive playback reuses the timeline and normal playback controls. Initial access and seeking may take longer than the first three tiers because target access and integrity checks can add work. Available local archive files can play directly without a full-file restore; unavailable or corrupt objects remain explicit errors. Restoring or removing a primary copy preserves the original media identity and archive timeline indexes.

Battery wake

Type: BatteryWakeConfig, section [battery_wake]. This is the Reolink wake middleman service, not a general Wake-on-LAN or scheduled privacy configuration.

Field Type Default Validation and meaning
enabled bool false Enable the wake service.
bind Optional Ipv4Addr Automatic Local IPv4 binding selection.
middleman_port u16 9999 Nonzero middleman port.
register_port u16 58200 Nonzero registration port; must differ from the middleman port.
heartbeat_secs u64 20 Nonzero heartbeat interval.
stale_after_secs u64 80 Must cover at least one heartbeat interval.

Logging

Type: LoggingConfig, section [logging].

Field Type Default Meaning
service ServiceLogDestination "file" "file" or "event_log"; service logging destination, with Event Log relevant to Windows service operation.

This section does not define arbitrary tracing filters, log payloads, or a diagnostics archive.

The existing diagnostic log-filter control uses a separate sibling file named log-filter, not another TOML section. The logging service saves and reloads that filter atomically. At startup a valid saved filter takes precedence over RUST_LOG; the built-in fallback is info,keeppeek=debug. A bad saved or environment filter is reported and falls back to the next usable source. This diagnostic setting is outside the two-file configuration ZIP. Preserve it with the configuration directory if you need it during a recovery rehearsal. Source: logging service.

Operational events

Type: OperationalEventsConfig, section [operational_events].

Field Type Default Meaning
warning_hold_down_secs u64 15 Delay before recording a warning transition.
outage_hold_down_secs u64 60 Delay before recording an outage.
recovery_debounce_secs u64 10 Stable-recovery debounce period.
record_short_flaps bool false Retain supported brief-flap transitions.
cameras Map of OperationalEventOverride Empty Overrides keyed by stable camera ID, with camera IP as fallback.

Type: OperationalEventOverride, section [operational_events.cameras."<camera-id-or-ip>"].

Field Type When absent
warning_hold_down_secs Optional u64 Inherit global warning hold-down.
outage_hold_down_secs Optional u64 Inherit global outage hold-down.
recovery_debounce_secs Optional u64 Inherit global recovery debounce.
record_short_flaps Optional bool Inherit the global flag.

Each effective policy requires warning hold-down no greater than outage hold-down. Outage and recovery durations cannot exceed 86400 seconds. Override keys contain 1 to 256 bytes after the nonempty check. The stable camera ID takes precedence over an IP-keyed override.

Privacy schedules

Type: PrivacyConfig, section [privacy]. The optional [privacy.default] schedule is inherited by cameras without a per-camera entry. Per-camera schedules are keyed by stable camera ID; an IP-keyed entry is accepted for migration and the runtime maps it to that camera ID.

Types: PrivacySchedule, sections [privacy.default] and [privacy.cameras."<camera-id-or-ip>"].

Field Type Default Meaning
enabled bool true Retain the policy but disable its scheduled enforcement when false.
timezone String Required IANA timezone used for recurring local windows.
windows Array of PrivacyWindow Empty Half-open weekly privacy windows; at most 64.
temporary_override Optional PrivacyOverride None Bounded persisted override that suspends matching windows.
keep_camera_connected bool true Keep camera ingress warm while delivery is blocked.

Each PrivacyWindow has weekdays (ISO values 1 through 7), start, and end in HH:MM. Equal endpoints are rejected; an end earlier than the start is an overnight interval. Both occurrences of a repeated daylight-saving local time are protected. A PrivacyOverride requires nonempty actor and reason, RFC3339 accepted_at and expires_at, and expires within 24 hours. Control-channel configuration requests replace the override actor and acceptance time with the authenticated administrator and server time before the candidate configuration is validated. Invalid privacy policy data fails validation before the server accepts camera media.

MQTT event forwarding

Types: EventForwarderConfig has one field, mqtt: MqttForwarderConfig. Section: [event_forwarder.mqtt]. Source: MQTT configuration.

Field Type Default Validation and meaning
revision u64 1 Server-managed settings revision.
enabled bool false Enable forwarding.
broker_url String "mqtt://127.0.0.1:1883" mqtt or mqtts URL with host; no user-info, non-root path, query, or fragment.
client_id String "keeppeek" Broker client ID.
instance_id String "home-nvr" KeepPeek instance identity.
forwarder_id String "mqtt" Forwarder identity.
topic_prefix String "keeppeek" 1 to 512 bytes; no leading/trailing slash, NUL, +, or #. Interior slashes are allowed.
username Optional String None Broker username, including supported references.
password Optional String None Requires username; prefer a reference to the companion secret file.
tls_ca_path Optional PathBuf System trust Custom CA path; only valid with mqtts.
qos u8 1 0, 1, or 2.
retain_events bool false Retain event publications.
retain_health bool true Retain health publications.
outbox_max_mb u64 64 1 to 65536 MiB of bounded outbox storage.
retry_min_ms u64 250 Nonzero retry minimum, no greater than maximum.
retry_max_ms u64 30000 Retry maximum, at most 3600000 ms.

Client, instance, and forwarder IDs each contain 1 to 128 bytes without NUL, /, +, or #. Do not embed credentials in broker_url; use the dedicated fields. MQTT's bounded outbox and deduplication state are in memory and reset on restart. outbox_max_mb is a runtime memory budget; it does not enable persistence. The retired mqtt-forwarder.db is removed during startup and is not replayed. See Notifications and integrations.

Credential records

Source: access persistence. Use the Administrator credential controls, not hand-written verifiers or revision changes.

Type: PersistedAccessCatalog, section [access_credentials].

Field Type Rule
version u32 Required; supported value is 1.
credentials Vec<StoredCredential> Required array, at most 128 records with unique IDs.

Type: StoredCredential, array [[access_credentials.credentials]].

Field Type Rule
id Uuid Stable named-credential identity.
name String 1 to 64 printable bytes after trimming.
description Optional String At most 256 printable bytes; empty descriptions normalize to absent.
role AccessRole "administrator" or "user".
camera_access Optional CameraAccess Per-user group and camera policy. Absent is unrestricted; new credentials explicitly default to everything.
verifier AccessKeyFingerprint SHA-256 fingerprint serialized as 32 byte-valued integers, not the raw access key. Treat it as sensitive authentication material.
created_at_ms i64 Creation time, Unix milliseconds.
rotated_at_ms Optional i64 Last rotation time; absent if never rotated.
last_used_at_ms Optional i64 Accepted by the stored record type for compatibility, but cleared on load and omitted by the canonical writer. Activity is runtime state.
expires_at_ms Optional i64 Expiry time; expired credentials are denied.
disabled bool Disabled credentials cannot authenticate.
revoked_at_ms Optional i64 Permanent revocation time.
revision u64 Nonzero credential revision used to invalidate stale sessions.
legacy bool Compatibility initial-credential marker.
initial_secret_pending bool Initial-key retrieval is still pending.

The runtime AccessCatalog.audit array is not part of PersistedAccessCatalog. Audit history, session state, and credential last-use activity reset on restart; credential identities, verifiers, roles, revisions, and lifecycle metadata persist. The legacy access.toml is a migration input, not an additional active settings file.

Type: CameraAccess, nested table belonging to the credential record, not to a camera. The all_cameras and camera_ids fields are required when the policy is present; group_ids defaults to an empty list for compatibility. Unknown fields are rejected.

Field Type Rule
all_cameras bool Explicitly grant all current and future cameras. Must not be combined with a nonempty ID list.
group_ids Vec<String> Camera configuration namespaces, such as outdoor for [outdoor.front_door]. Defaults to empty. At most 128 unique, nonblank names of at most 256 UTF-8 bytes without control characters.
camera_ids Vec<String> At most 128 unique, nonblank IDs of at most 256 UTF-8 bytes without control characters. Use server-advertised camera IDs, not names.

With all_cameras = false, access is the union of cameras in group_ids and explicit camera_ids; both lists empty grants nothing. all_cameras = true requires both lists empty and includes future groups and cameras. New credentials and absent policies default to everything. Existing explicit restrictions are not broadened on upgrade. Administrators always have full access and cannot be restricted through this policy. There is no secret substitution inside grant records.

The Administrator's user-access editor validates camera and group IDs against the current fleet and saves under the shared configuration lock. Each save increments the credential revision, rejects stale writes, and invalidates existing sessions. Grants survive configuration reload and export/import. Group grants are saved as group names, not expanded camera lists; effective access is resolved from the server's camera group membership. available_group_ids in management responses is bounded discovery metadata for the editor, not a persisted field. The editor supports up to 128 available groups using the same ID limits; an invalid or oversized inventory rejects management requests before any policy mutation. Runtime group-membership changes close affected group-scoped sessions and cancel their queued work so playback and event subscriptions cannot retain an obsolete grant. Grid audiences do not grant camera permissions. Custom roles remain unsupported, and trusted-local clients still follow the Administrator policy described above.

Peek layout registry

Source: layout persistence. Layouts use the existing main configuration file and the keeppeek.peek-layouts.v1 capability. Use the layout controls and revision-checked operations rather than manually constructing this server-managed section.

Type: StoredRegistry, section [peek_layouts].

Field Type Rule
schema_version u32 Required; supported value is 1.
revision u64 Nonzero registry revision. Stale writes conflict.
shared_layouts Vec<Layout> Nonempty server-owned layout array.
users Map of StoredUserRegistry Keyed by principal ID; holds per-user active selection and legacy private layouts.

Type: StoredUserRegistry, section [peek_layouts.users."<principal-id>"].

Field Type Rule
active_layout_id String Selected layout identity. Missing selections are repaired by the registry.
layouts Vec<Layout> Legacy private layout records; migrated to server-owned shared layouts with restricted audiences when opened.

Type: Layout, under shared_layouts[] or legacy users.<principal-id>.layouts[].

Field Type Rule
id String Unique within the registry view; nonblank, at most 128 characters.
name String Nonblank, at most 80 characters.
scope LayoutScope "shared" for canonical layouts; "private" is retained for migration.
owner_id String "server" for shared layouts; private records must match their principal.
audience LayoutAudience Defaults to everyone with an empty credential list when the entire field is absent.
activity_focus bool Whether activity can affect layout focus.
display Optional DisplaySettings Dashboard-owned display settings; absent objects use defaults. Older client writes that omit the object preserve saved values.
tiles Vec<LayoutTile> Ordered tiles, at most 64 per layout; no duplicate camera IDs or overlaps.

Type: DisplaySettings, nested display table. The dashboard editor writes the complete object with the existing registry revision. Only Administrators can change it. A User can still change their selected dashboard but cannot alter its display settings. No field resolves secret references.

Field Type Default Validation and meaning
version u32 1 Required in present objects; only version 1 is supported.
tile_shape TileShape "16:9" "16:9", "4:3", or "native".
media_fit MediaFit "contain" "contain" or "cover"; never stretch.
streaming_mode StreamingMode "smart" "smart" or "continuous".
stream_limit u32 12 Whole number from 1 through 12; each browser also enforces its own capacity.
keep_awake bool false Saved wake-lock intent; browser gesture, visibility, permission, and device policy still apply.
gap_px u32 10 Whole pixels from 0 through 24 between tile slots.
corner_radius_px u32 10 Whole pixels from 0 through 24; zero gives square video corners.

Display objects reject unknown fields, positional arrays, invalid types, and out-of-range values. The All cameras dashboard keeps immutable generated coordinates and identity, but its display object can be edited and survives camera-inventory synchronization. Preset names are derived from gap/radius values and are not an additional stored field.

Type: LayoutAudience, nested audience table.

Field Type Rule
everyone bool When true, credential_ids must be empty.
credential_ids Vec<String> At most 128 unique canonical UUID strings. Save operations check that viewer identities exist. Empty with everyone = false gives no ordinary user access; Administrators retain management access.

Type: LayoutTile, nested tiles[] array.

Field Type Rule
camera_id String Stable camera identity, not display name.
column u32 One-based grid column.
row u32 One-based grid row.
column_span u32 Positive horizontal span; tile must fit the 12-column grid.
row_span u32 Positive vertical span; tile must fit the 12-row grid.
pinned bool Tile pinning state.

The registry has a 256 KiB serialized bound and at most 32 layouts per validated view. Stored camera references are retained when a camera disappears; additions are validated against known cameras. The initial registry has revision 1 and an All cameras shared layout with ID default, activity focus enabled, and up to 64 configured cameras. Opening the store also migrates the legacy peek-layouts.json; that file is not an active persistence target.

The client-facing LayoutRegistry contains schema_version, active_layout_id, and layouts. It is not the on-disk StoredRegistry; do not paste a client exchange document over the main configuration. Grid audiences control grid visibility only. They must not be treated as permission to view, export, or control an otherwise restricted camera.

The server intersects a User's returned tiles with their camera grants. Selecting a dashboard from that filtered view updates only the principal's active selection; it does not remove other cameras from the canonical layout. Unknown or no-longer-visible dashboard selections are rejected.

Camera templates

Source: configuration templates. Templates are versioned, server-managed configuration records, not another settings file.

Type: StoredTemplateDocument, section [configuration_templates].

Field Type Rule
document_version u32 Required; supported value is 1.
templates Vec<StoredTemplate> Required array, at most 64 templates. Missing section means an empty document.

Type: StoredTemplate, array [[configuration_templates.templates]].

Field Type Rule
template_id String Unique ID, 1 to 64 ASCII letters, digits, hyphens, or underscores.
version u64 Server-managed template version.
name String Nonblank, at most 128 bytes, no control characters; names are unique ignoring case.
description String Defaults to empty and is omitted when empty; at most 1024 bytes, no control characters.
values StoredTemplateValues At least one supported setting must be present.
created_at_ms i64 Creation time, Unix milliseconds.
updated_at_ms i64 Last update time, Unix milliseconds.

Type: StoredTemplateValues, nested values table. Every field is optional and omitted when unset.

Field Type Rule
username_secret_reference String A complete valid secret reference, not an inline username.
password_secret_reference String A complete valid secret reference, not an inline password.
onvif_port u16 1..65535.
http_port u16 1..65535.
backend CameraBackend "auto", "retina", or "reo-proto".
transport CameraTransport "tcp" or "udp".
record_generic_motion_events bool Generic motion-event preference.
recording_mode CameraRecordingMode "off", "sub", "main", "both", "event-boost", or "event-only".
event_recording_duration_secs u32 1..3600 seconds.
event_pre_recording_duration_secs u32 0..30 seconds.
event_recording_stream EventRecordingStream "main" or "sub".

The template writer enforces a 16 KiB serialized document limit. The legacy configuration-templates.json is imported into this section and removed. Template references stay as tokens in the stored template; applying a template must validate the resulting camera settings.

Notification rules

Sources: rule persistence, rule model, and cooldowns.

Type: NotificationConfiguration, section [notifications].

Field Type Default and bound
rules Vec<PersistedRuleRecord> []; at most 128 rules, with unique rule IDs.

Type: PersistedRuleRecord, array [[notifications.rules]].

Field Type Meaning
id String Stable rule ID.
owner_id String Owning principal ID.
active Optional Rule Published rule; absent before first activation.
active_revision u64 Active revision used for conflict checks.
draft Rule Saved draft, which may be incomplete before activation.
draft_revision u64 Draft revision used for conflict checks.
created_at_ms i64 Creation time, Unix milliseconds.
updated_at_ms i64 Last update time, Unix milliseconds.

Rule identity, owner, and revisions must agree with the containing record. A draft is bounded to 64 KiB in its JSON representation. Activating it requires complete rule validation. Runtime RuleRecord.last_match_at_ms and last_delivery_at_ms are not persisted fields.

Type: Rule, under each record's draft and optional active tables.

Field Type Validation or default
id String 1 to 128 ASCII letters, digits, ., -, or _.
name String Nonblank, at most 128 characters.
enabled bool Required; disabled rules do not match events.
revision u64 Corresponding draft or active revision.
owner_id String Same ID character and byte limits as id.
triggers Vec<Trigger> Nonempty and unique; values below.
filter Filter Defaults to an empty filter.
schedule Schedule Required.
cooldowns Vec<Cooldown> Defaults to []; scopes must be unique.
rate_limits Vec<RateLimit> Defaults to []; scopes must be unique.
critical_bypass Optional CriticalBypass Bounded critical-event quiet-hours bypass.
enrichment EnrichmentPolicy Required.
actions Vec<Action> 1 to 8 actions.
failure FailurePolicy Required.

Trigger values are "event_created", "event_updated", "event_ended", "outage_started", "recovery", "storage_health", "recording_health", and "test".

Filters and schedules

Type: Filter, nested filter table.

Field Type Default and rule
source_ids Vec<String> []; at most 128 nonblank values.
group_ids Vec<String> []; at most 128 nonblank values.
event_kinds Vec<String> []; at most 128 nonblank values.
zones Vec<String> []; at most 128 nonblank values.
minimum_confidence Optional f64 Finite value in 0..1, inclusive.
attachment_required Optional bool Require or exclude an available non-private attachment.
minimum_duration_ms Optional u64 Minimum matching event duration.
severities Vec<Severity> []; values "info", "warning", "critical".
reviewed Optional bool Match the supplied review state.
bookmarked Optional bool Match the supplied bookmark state.

Empty filter arrays do not restrict that dimension. Filter fields describe matching inputs; their presence does not mean every camera or event producer supplies them.

Type: Schedule, nested schedule table.

Field Type Default and rule
timezone String Required recognized IANA timezone, such as "UTC".
active_windows Vec<WeeklyWindow> [], meaning no active-window restriction.
quiet_hours Optional QuietHours No quiet-hours suppression when absent.

QuietHours has one required field, windows: Vec<WeeklyWindow>. WeeklyWindow fields are:

Field Type Rule
weekdays Vec<Weekday> Nonempty and unique; "monday" through "sunday".
start_minute u16 0..1439, local minutes after midnight.
end_minute u16 0..1439, exclusive end; must differ from start.

An end earlier than the start describes an overnight window.

Cooldowns, limits, and delivery

All policy durations below are in milliseconds and must be between 1 and 2592000000 (30 days), inclusive, unless a tighter rule is stated.

Type Fields Validation
Cooldown scope: CooldownScope, duration_ms: u64 Scopes: "event", "camera_event_kind", "group", "rule", "outage".
RateLimit scope: RateLimitScope, maximum: u32, window_ms: u64 Maximum 1..10000; scopes "rule", "channel", "principal", "global".
CriticalBypass maximum: u32, window_ms: u64 Maximum 1..10.
EnrichmentPolicy deadline_ms: u64, maximum_revisions: u32, maximum_attempts: u32, maximum_attachment_bytes: u64, wake_after_deadline: bool Revisions 1..32, attempts 1..8, attachment bytes 1..4194304 (4 MiB). All fields required.
FailurePolicy maximum_attempts: u32, maximum_retry_interval_ms: u64, expiry_ms: u64 Attempts 1..10. An enabled push action requires retry interval at least 5000 ms. All fields required.

Type: Action, nested actions[] array.

Field Type Default and rule
enabled bool true.
channel Channel "browser", "push", "webhook", or "forwarder". Use channels advertised as available by the server.
destination String At most 2048 bytes after resolution. Browser actions use the owner and require an empty destination. Push uses the Pushover destination format; webhook requires an HTTP(S) URL without user-info; forwarder requires a nonblank destination.
template Template Required title: String and body: String.
attachment AttachmentPolicy "never", "when_available", or "required".
allow_second_delivery bool Required flag for staged delivery.

Template title and body limits are 256 and 4096 characters respectively; provider-specific limits can be tighter. Allowed {{field}} substitutions are source.id, source.name, event.id, event.kind, event.zone, event.confidence, event.duration, health.state, notification.stage, and notification.deep_link. These are notification template substitutions, not secret references.

Provider destinations can contain credentials, so the rule writer moves non-browser destination strings into the companion secret file and stores references. It keeps active and draft secret versions separate. Do not inline tokens into a book example or export them with a layout.

The legacy notifications.db is a rule-migration input. Delivery jobs, retry state, evaluation history, and counters are runtime state, not extra [notifications] settings. See notifications and integrations for provider setup.

Pending storage migration

Type: StorageMigration, section [storage_migration]. The server creates this journal for a validated move and consumes it during migration. Do not use it as an alternative storage editor.

Field Type Meaning
medium_term Optional StoragePathMigration Medium-term recording move.
long_term Optional StoragePathMigration Long-term recording move.
recording_catalog Optional StoragePathMigration Explicit catalog move when not covered by a recording-root move.
event_thumbnails Optional StoragePathMigration Explicit thumbnail move when not covered by a recording-root move.
recording_catalog_after_move Optional PathBuf Catalog whose stored recording paths must be rewritten after moving roots.

Each StoragePathMigration has two required PathBuf fields: from and to. Paths must be nonempty, must not contain one another, and must satisfy the migration overlap checks. A single source cannot be split into conflicting destinations. StorageMigrationPaths is a runtime helper of borrowed paths, not another TOML section.

Compatibility and completeness

  • Preserve server-managed sections when editing ordinary settings. Serializing only Config would omit the separate credential, layout, template, and notification records.
  • The legacy access, layout, template, and notification files are migration inputs only. Do not create new instances of them for new settings.
  • There is no generic [users], [permissions], [groups], or persistent runtime-profile schema in this configuration. Do not invent those sections or assume a grid's audience restricts camera access elsewhere.
  • Future layout and permission settings must extend the existing application configuration and its validation, atomic writes, and recovery coverage. Secret values remain in the existing companion secret store.
  • When a serialized struct, section name, default, limit, enum, or migration changes, update this reference in the same change and verify both load and write paths with synthetic fixtures.

For an upgrade from separate legacy stores, follow What startup migrates. For retention of runtime and recording data, see Backup and restore.

Event recording pre-roll

event-only stores only accepted event windows from the selected main or sub stream. event-boost keeps continuous sub recording and uses main quality during event windows. A nonzero event_pre_recording_duration_secs enables bounded encoded history for these modes. EventBoost then delays persistence by up to the selected duration so an event can replace pending sub coverage with main coverage. A crash can lose this uncommitted window. With zero pre-roll, existing recording modes keep their immediate admission behavior.

Changes persist in config.toml through the existing atomic writer. Camera changes use the existing source restart path when available; otherwise the response reports that a restart is required. Memory-budget changes require an application restart. Omitted updates retain existing values; cleared camera values inherit camera defaults. These settings contain no credentials. Existing supported secret references in camera credentials and URLs remain preserved. Whole-GOP eviction can shorten coverage under duration or memory pressure; health diagnostics report available coverage and the reason. Privacy, stream discontinuities and storage failures invalidate optional history.

KeepPeek documentation

Clone this wiki locally