Integrate
Change events
Every general volume can keep an ordered feed of its changes. The mountos event command reads that feed, as a stream on stdout or as a
local REST API. Indexers, sync tools, and AI agents follow a volume without
walking the tree.
What the feed is
Four event types, create, delete, modify,
and rename, cover every change. Each event carries the full path
(renames carry the old path too), the inode, the file type, the size, and a
capture timestamp. A per-fork monotonic sequence number orders events.
Each fork has its own feed.
Events surface within a few seconds of the change. Saves that land in quick succession on the same file coalesce into one event. The feed reports that the file changed, not every write. The feed is near real time, not synchronous.
The feed exists for general volumes only. mountOS rejects a feed request against an Iceberg volume. It does not return an empty feed.
Turn it on
The feed is off by default. One volume field, eventLogRetentionPeriod, controls it. The value is in days, from 0
to 30. The field gates capture. It also sets how long events stay readable. At
0, mountOS captures no events. None accumulate. mountOS prunes events past the
window. Set the field at volume creation, or later with an edit. The
trade-off is storage growth. Longer windows keep more events. Size the window
to what consumers replay.
await client.volumes.edit(volumeId, {
eventLogRetentionPeriod: 7,
})period := int32(7)
_, err := client.Volumes.Edit(ctx, volumeID, &sdk.EditVolumeRequest{
EventLogRetentionPeriod: &period,
})use mountos_admin_sdk::EditVolumeRequest;
client.volumes.edit(volume_id, &EditVolumeRequest {
description: None,
retention_period: None,
grace_period: None,
fork_grace_period: None,
event_log_retention_period: Some(7),
}).await?;Stream to stdout
The command authenticates with the volume's access key, the same way a mount
does. The key pair can also come from MOUNTOS_ACCESS_KEY_ID and MOUNTOS_SECRET_ACCESS_KEY.
The HUB domain can come from MOUNTOS_DISCOVERY_URL.
# everything retained, oldest first
mountos event -a AKIA... -s
# the last 7 days, then keep tailing
mountos event -a AKIA... -s --since 7d --follow
# one subtree, machine readable
mountos event -a AKIA... -s --path-prefix /docs --json SEQ FORK TIMESTAMP EVENT TYPE INODE SIZE PATH
101 0 2026-06-10 09:14:02 create file 524291 18432 /docs/spec.md
102 0 2026-06-10 09:14:09 modify file 524291 20480 /docs/spec.md
103 0 2026-06-10 09:15:11 rename file 524291 20480 /docs/final.md <- /docs/spec.md
104 0 2026-06-10 09:16:40 delete file 524291 - /docs/final.md--sincefloors on time. It takes relative windows (7d,2h) or timestamps ("2025-01-01 14:30").--start-seqfloors on the sequence number instead.--path-prefixfilters to a subtree on a path component boundary./docsmatches/docs/spec.mdbut not/docs2.--followtails the feed by polling. Without--sinceor--start-seqit starts at the live end instead of replaying history.--jsonemits one JSON object per event, the same shape the REST API returns.--fork-nameselects a fork's feed. An empty value selects the main line.
Serve a local REST API
--http turns the command into a local HTTP server for tools that
poll an endpoint instead of owning a process pipe. The server daemonizes.
Pass --foreground to keep it attached to the terminal instead.
It binds 127.0.0.1 and listens on the port set by --http-port. It serves the fork selected with --fork-name when it starts. Exposure beyond loopback requires
TLS. The command rejects --http-no-loopback without --http-cert and --http-key.
mountos event -a AKIA... -s --http --http-port 9876
Callers never send the secret key. POST /v1/auth takes a signed
challenge. The body carries the access key id, a unix timestamp, a
single-use nonce, and a signature. The caller creates the signature with
HMAC-SHA256 and the secret key. The response is a bearer token.
POST /v1/auth
{
"access_key_id": "<key>",
"timestamp": 1781083200,
"nonce": "<random hex, single use>",
"signature": "<hex of HMAC-SHA256(secret, key:timestamp:nonce)>"
}
{ "token": "<bearer>", "expires_in": 3600 }GET /v1/events serves the feed. The first request takes since, start_seq, path_prefix, and limit (1 to 1000). Every
response carries a continuation_token that encodes the cursor and
the filters. The next request passes only the token. seek_to_end=true returns a token at the live end with no events.
It is the API equivalent of starting --follow without history.
curl -s "http://127.0.0.1:9876/v1/events?since=7d&path_prefix=/docs" \
-H "Authorization: Bearer $TOKEN"{
"events": [
{
"seq": 103,
"fork_id": 0,
"incarnation_id": 3,
"event_ts": 1781082911000000,
"born_at": 1781082842000000,
"inode": 524291,
"event_type": "rename",
"type": "file",
"size": 20480,
"path": "/docs/final.md",
"from_path": "/docs/spec.md"
}
],
"continuation_token": "eyJwIjoiL2RvY3MiLCJzIjoxMDMsInQiOjE3ODA0NzgxMTEwMDAwMDAsImYiOjB9",
"has_more": false
}Consumer semantics
- Ordering. Events arrive in per-fork sequence order.
seqis the resume cursor. A consumer stores the last sequence it processed, or the continuation token. It resumes from that point. - Latency. Events surface within a few seconds. The CLI tail polls on an interval. Budget seconds, not milliseconds.
- Coalescing. When a client saves a file many times in quick
succession, the feed surfaces fewer
modifyevents than writes. The feed reports that the file changed, not every write. - Gaps. Each event carries an
incarnation_id. A change between consecutive events marks a possible gap. A consumer that needs exactness treats it as a signal to reconcile the affected subtree. - Retention. mountOS prunes events older than
eventLogRetentionPerioddays. A consumer that reads at an interval shorter than the window never loses its place. - Timestamps.
event_tsis the capture wall clock in microseconds and the canonical feed time.born_atcorrelates the event with the file's version history.