Configuration — v0.9.0
Advanced configuration and schema inspection for Alloy Edge v0.9.0
This is an advanced reference. Start with
Track Folder v0.9.0; link creates
the required files and run selects them from the installation scope.
Files and locations
| File | Purpose |
|---|---|
edge-manager.yaml | Cloud connection, identity seed, process supervision, and restart policy |
edge-sync.yaml | Tracked folder, upload behavior, disk cleanup, lifecycle, and processing pipeline |
| Transform YAML | Optional per-recording transformation or redaction rules |
| Scope | Configuration | State |
|---|---|---|
| User | $XDG_CONFIG_HOME/alloy-edge or ~/.config/alloy-edge | $XDG_STATE_HOME/alloy-edge or ~/.local/state/alloy-edge |
| Folder-local | <track-folder>/.alloy | <track-folder>/.alloy/state |
| System | /etc/alloy-edge | /var/lib/alloy-edge |
Use --user, --system, or --local to select an installation explicitly.
Without a scope, Alloy Edge discovers the nearest valid folder-local installation,
then the user installation, then the system installation. A malformed installation
is an error; it does not silently fall back.
edge-manager.yaml
Top-level fields
| Field | Default | Purpose |
|---|---|---|
backend_url | Alloy production backend | Alloy backend endpoint. ALLOY_BACKEND_URL overrides it. |
state_dir | Scope-specific state directory | Manager state, credentials, desired state, and child configuration. ALLOY_STATE_DIR overrides it. |
seed_state | {} | Bootstrap identity and transport used until the device is approved. |
local_state | — | Locally owned tags and processes that cloud configuration cannot overwrite. |
supervisor | Built-in defaults | Advanced restart, backoff, stability, and shutdown tuning. |
Credentials and device identity live under the installation state directory.
Deleting state is not a harmless cache clear. Use alloy-edge unlink with the
installation's scope when intentionally removing a linked installation.
seed_state
alloy-edge link writes and manages credentials for new installations. The seed
fields remain available for legacy or manually authored installations:
| Field | Default | Purpose |
|---|---|---|
api_key | — | Enrollment-only provisioning key used until approval. Prefer alloy-edge link; runtime commands use the persisted device key. |
edge_id | Hostname, then generated UUID | Stable device identifier reported to Alloy. |
tags | {} | Free-form strings for grouping and filtering devices. |
transport.http.poll_secs | 15 | Seconds between desired-state checks. |
processes | [] | Bootstrap processes used before the first successful cloud sync. Prefer local_state.processes for locally owned processes. |
local_state.processes
| Field | Default | Purpose |
|---|---|---|
name | — | Required process name used in logs and reported state. |
command | — | Required command to execute. |
enabled | true | Keep the process declared but stopped when false. |
restart | on_failure | Restart policy: always, on_failure, or never. |
shell | false | Run through /bin/sh -c when shell expansion is required. |
trigger | Start on apply | Start at boot, on a five-field cron schedule, or when connectivity returns. |
duration | — | Stop the process after a bounded duration. |
requires_auth | false | Hold the process until Alloy approves the device. Set this for alloy-edge sync. |
files | {} | Inline configuration files attached to the process. |
Set local_state.report: true to report a safe, read-only projection
of locally pinned state to Alloy. The default is false.
A locally owned Track Folder sync process has this shape:
local_state:
report: true
processes:
- name: sync
command: alloy-edge sync -c /data/recordings/.alloy/edge-sync.yaml
enabled: true
restart: on_failure
trigger: boot
requires_auth: truesupervisor
| Field | Default | Purpose |
|---|---|---|
max_restarts | 10 | Restarts allowed before a child is marked crashed. |
max_backoff_secs | 60 | Maximum exponential restart backoff. |
stable_secs | 60 | Runtime required before the restart count resets. Must be greater than zero. |
graceful_timeout_secs | 10 | Grace after SIGTERM before SIGKILL. |
edge-sync.yaml
v0.9 uses strict schema version 1. Only input_dir is required. Cleanup has no
size or age limit by default, with a default maximum of 100,000 files.
version: 1
input_dir: /data/recordings
file_pattern: "*.mcap,*.json,*.jsonl"
upload_delay: 30s
cleanup:
max_folder_size: 10GB
max_file_age: 72h
max_file_count: 1000
lifecycle:
original:
after: keepDurations use Go-style strings such as 5s, 1m, and 72h. Sizes and
bandwidth limits use values such as 500MB, 10GB, and 5MB.
Scanning and upload
| Field | Default | Purpose |
|---|---|---|
version | — | Set to 1 for the current strict schema. An absent value enters legacy compatibility mode. |
input_dir | — | Required folder to scan. |
file_pattern | *.mcap,*.json,*.jsonl | Comma-separated file globs. .tmp entries are reserved and removed. |
cycle_time | 1s | Safety-net scan interval. |
upload_delay | 30s | Minimum age before a footer-less recording is eligible. |
upload_order | oldest_first | Dispatch ready files using oldest_first or newest_first. |
mcap_require_footer | false | Require an MCAP footer instead of falling back to file age. |
upload_type | signed_url | signed_url, none, or feature-gated opendal. |
bwlimit | Unlimited | Outbound bandwidth limit. |
max_concurrent_uploads | 1 | Number of files uploaded in parallel. |
part_concurrency | 4 | Multipart upload parts sent in parallel for one file. |
metadata | {} | Free-form metadata included in upload requests when supported. |
scan_exclude | [] | Subdirectory basenames to skip; dot-directories are always skipped. |
fs_event_enabled | true | Use filesystem events for low-latency scans while retaining periodic scans. |
The upload_settings block exposes advanced protocol controls:
| Field | Default | Purpose |
|---|---|---|
multipart | true | Use the unified /upload/* protocol. Set false for the legacy single-PUT broker. |
multipart_endpoint | /upload/init | Override the unified-protocol initialization endpoint. |
multipart_include_metadata | false | Include metadata in the multipart initialization request. |
signed_url_endpoint | Top-level/default endpoint | Override the legacy single-PUT broker endpoint. |
part_concurrency | Top-level/default value | Override concurrent part uploads within one multipart upload. |
Cleanup and upload state
| Field | Default | Purpose |
|---|---|---|
cleanup.max_folder_size | Unbounded | Delete oldest eligible files when the tracked folder exceeds this size. |
cleanup.max_file_age | — | Delete eligible files older than this duration. |
cleanup.max_file_count | 100000 | Delete oldest eligible files when the count exceeds this limit. |
state_dir | Scope-specific state directory | Holds sync state and shared device credentials. |
index_store.backend | sqlite | Per-file upload state: sqlite or jsonl. Switching backends migrates automatically. |
index_store.path | Derived from state_dir | Optional JSONL path and SQLite migration source. |
Files currently uploading or open by another process are not removed by cleanup.
Deprecated top-level cleanup fields, txlog_path, credentials_dir, keep_files,
and the flat redaction block remain readable for compatibility but should not be
used in new v0.9 configuration.
Lifecycle
Lifecycle controls what happens to the original and to each transformed artifact:
lifecycle:
original:
after: keep
upload: false
cleanup:
include_in_storage_cleanup: false
transform:
after: move
move_to: .alloy-redacted
cleanup:
include_in_storage_cleanup: false
on_rule_error: skip_record
on_reader_error: skip_tail
output_compression: inherit
audit:
jsonl_path: .alloy/state/redaction-audit.jsonl
embed_in_mcap: true
max_bytes: 5MB
max_files: 2| Field | Default | Purpose |
|---|---|---|
original.after | keep | keep/keep_continue, keep_stop, delete, or move the source. |
original.upload | false | Upload the untransformed original if it reaches the end of the pipeline. |
transform.after | keep | keep, delete, or move the transformed artifact. |
<stage>.move_to | Stage-specific dot-directory | Destination when after: move; relative paths resolve from input_dir. |
<stage>.cleanup.include_in_storage_cleanup | false | Include moved files in the global size, age, and count limits. |
transform.on_rule_error | skip_record | skip_record, skip_file, or explicitly opt into pass_original. |
transform.on_reader_error | skip_tail | skip_tail, abort, or recover malformed MCAP data. |
transform.output_compression | inherit | inherit, none, zstd, or lz4. |
transform.audit.jsonl_path | — | Write an optional rotating JSONL audit sidecar. |
transform.audit.embed_in_mcap | true | Embed the audit record in the transformed MCAP. |
transform.audit.max_bytes | 5MB | Roll the sidecar before it exceeds this size; 0 disables rotation. |
transform.audit.max_files | 2 | Rotated generations to retain; must be at least one. |
Processing pipeline
The optional ordered pipeline can filter, transform, upload, and retain different artifacts from one recording:
pipeline_trigger: delay-after-close
pipeline:
- transform: .alloy/redaction.yaml
transform_suffix: redacted
upload: true
transform_after: delete
original_after: delete
file_pattern: "**/camera/*.mcap"
filter:
require_topics: ["/nav/**"]
min_duration: 60s
min_messages: 100
min_size: 10MB
if: "done == true"
if_at: any| Field | Default | Purpose |
|---|---|---|
pipeline_trigger | delay-after-close | Start after the close delay, or use close to start as soon as the file closes. |
transform | — | Rules file used to create a transformed artifact. |
transform_suffix | Step index | Infix added to the transformed filename. |
upload | false | Upload the transformed artifact. Requires transform. |
original_after | lifecycle.original.after | Control source flow after this step. |
transform_after | lifecycle.transform.after | Keep, delete, or move the transformed artifact. |
file_pattern | All files | Restrict the step to matching paths. |
filter.mcap_require_footer | — | Wait and retry when an MCAP footer is absent. |
filter.require_topics | [] | Require matching topics; glob patterns are supported. |
filter.min_duration | — | Require a minimum recording duration. |
filter.min_messages | — | Require a minimum message count. |
filter.min_size | — | Require a minimum file size. |
filter.if | — | Evaluate a Jinja2 expression against JSON or MCAP record content. |
filter.if_at | any | Select records for if: any, first, last, an index, or a range. |
filter.require_fields | — | Equality-map shorthand for simple if conditions. |
All configured filter conditions must match for a step to run. A content miss continues to the next step rather than failing the file.
Inspect, validate, and edit configuration
The v0.9 CLI is the canonical source for the complete schema installed on a device:
# Authored manager and every referenced sync/transform file
alloy-edge config show
# Deterministic JSON plus the complete Draft 2020-12 schemas
alloy-edge config show --json --schema
# Validate the selected installation and every referenced file
alloy-edge config checkSelect manager or an exact configured process name to narrow config show.
Add --no-comments for plain YAML, or use --json --comments for source-ordered
comments alongside the semantic JSON projection.
Prefer the configuration CLI for small edits. It preserves unrelated comments and styles, validates a staged candidate, and atomically replaces the source:
alloy-edge config set track-folder /data/recordings
alloy-edge config set sync.input_dir /data/recordings
alloy-edge config set manager.state_dir /var/lib/alloy-edgeProcess names form the first segment of an address, such as sync.input_dir.
Use alloy-edge config show to see the configured process names before editing.
After changing configuration, restart alloy-edge run or its service. Validate a
change without backend calls or child processes with:
alloy-edge run --user --dry-runFor transformation rules, continue to the Redaction reference.