Information your device sends to us
This page describes information sent by a device paired with the Console to the Console and gateway operated by kumaaa LLC to provide loopkeep. None of the synchronization or remote features listed here runs on an unpaired device. Local features — including cron, file-watch, git, and manual triggers, runs, and the inbox — continue to run entirely on the machine.
“Sent” on this page does not include these two other data flows:
- GitHub, Slack, and webhook event bodies are relay traffic traveling in the opposite direction, from the gateway to your device. Relay and temporary delivery records explains the distinction below.
- Content that a workflow intentionally posts, replies with, or notifies through a safe output goes to the third-party service you selected. It is not data synchronized to our Console.
Pairing and connection
During the browser pairing flow, your device sends:
- its device name and platform (
desktop); - the Ed25519 public key it generated; and
- a temporary
127.0.0.1callback URL and one-timestateused to match the response.
After approval, the Console records the device ID, name, platform, public key, a hash of the device
token, creation time, last-seen time, the time of the last accepted active-run snapshot (including
an empty one, recorded as runs_updated_at), and revocation time. The plaintext token is returned
to your device once and is then presented by the device to authenticate HTTPS and WebSocket
connections.
The temporary callback URL and state are not stored in this device row. We do not store the
plaintext token in the database. The private signing key stays on the device and is never sent
during pairing or later connections.
On its WebSocket connection to the gateway, the device sends the protocol version, device token,
and these capabilities: wake_signals, entitlement_push, remote_manual,
remote_run_control, remote_workflow_control, and remote_automation_stop. Authenticated API
requests also carry the bearer token, and the control plane updates the device's last-seen time no
more often than once per minute.
Other authenticated device requests
A paired device also sends these API requests to retrieve or manage information needed by editors and connector operations. Each is authenticated with the bearer token described above.
- A directory lookup sends
kindand its scope.slack-users/slack-channelsuseteam_id;github-users/github-labels/github-workflowsuse anowner/namerepository inrepo; andgithub-reposusesinstallation_id. Results are fetched from Slack or GitHub for each request and are not stored by the Console. - Creating or deleting a personal webhook sends its
name. The Console stores the name, a token hash, and creation time. A URL containing the plaintext token is returned to the device only once on creation or reissue under the same name and is not stored in the database. Deletion removes that row. - A Slack bot-token lookup sends
team_idand returns the connected workspace's bot token to the authenticated device. Its purpose and lifecycle are described in the Privacy Policy. - Connector-list, account-profile, and personal-webhook-list requests send no request body or scope query parameter beyond bearer authentication.
Workflow catalog
The daemon sends a complete per-device workflow catalog used for Console listings and controls. When it changes, the new complete catalog replaces the previous one. Each catalog contains:
- the workspace ID and display name;
- the workflow name;
- a coarse list of declared trigger types;
runnable, which says whether amanualtrigger makes the workflow startable from the Console; anddisabled, which says whether automatic triggers are disabled. Older devices that cannot report this state omit it.
The catalog does not include workflow bodies, file paths, or trigger filter values. Detailed declarations needed to route outside events are sent separately, as described next.
Trigger routing declarations
To decide which workflow on which device should receive a GitHub, Slack, or webhook event, the daemon sends a complete per-device set of external-trigger routing declarations. Its fields are:
- workspace ID and workflow name;
- app (
github,slack, orwebhook) and event name; - declared filter names and values, including filters such as
assignee,labels,actions,mentions,from,from_not,reviewer,conclusions,workflows,branches,emoji, andon; - GitHub repositories in
owner/nameform. When a declaration omits one, the daemon sends the repository resolved from that workspace's gitorigin; and - Slack channel IDs and webhook hook names.
Repository and channel coordinates and filter values are sent. Repository contents, local file paths, workflow bodies, prompts, policies, and secrets are not.
Active-run snapshot
When remote supervision is enabled, the daemon sends the latest complete snapshot of only the device's non-terminal runs. An accepted new snapshot replaces the previous one. It contains:
- workspace ID and display name;
- run ID, workflow name, state, and start time;
- the
attended,taken_over,awaiting_host,awaiting_confirm, andfinishableflags; and - the origin source, optional GitHub repository owner/name, and a resource locator. A locator is a GitHub issue number, pull request number, issue number plus comment ID, or a Slack channel ID, timestamp, and optional thread timestamp.
While the daemon knows remote_supervision_enabled is false, it does not build a snapshot and
discards any snapshot waiting to be uploaded. When remote_supervision_enabled changes from true to
false, the control plane immediately deletes all stored active-run rows for the account in the same
transaction as the entitlement change; it does not wait for another device upload. If a device
nevertheless attempts an upload while the feature is disabled, the control plane parses and
validates the request before checking the current authoritative entitlement; a valid snapshot then
receives HTTP 403 without being stored. An entitlement change cannot prevent a request already in
flight, or sent using stale entitlement state, from putting a body on the network, so this is not a
promise of absolute zero-byte traffic.
The snapshot excludes completed, failed, and aborted run history, as well as titles, bodies, authors, sessions, worktrees, engines, prompts, event logs, inbox items, and transcripts.
Console run requests and controls
A request that a person starts in the Console is first recorded in the control plane. Creating a
run request requires remote_manual_enabled; creating a control requires
remote_supervision_enabled. The gateway notification carries only the request id and
tenant_id, not the workspace, workflow, or action. The device claims the details using id and
sends back the outcome.
- A run-request record has the account and device, workspace, workflow,
status, optionalrun_idand resultmessage, and creation, expiry, and settlement times. The device result carriesid,status(acceptedorrejected), optionalrun_idfor the created run, and a shortmessage. - A control record has the account and device,
action,status, optional resultmessage, and creation, expiry, and settlement times. Actions arefinishfor an attended run (workspace and run),set_workflow_disabled(workspace, workflow, anddisabled), andpause_automation, which the Console presents as Stop. The protocol also hasresume_automation, but the current Console does not send it; resuming remains local. The device result carries the controlid,status(acceptedorrejected), and a shortmessage.
A run request must be claimed within two minutes of creation. Once claimed, its result can settle after that deadline. A control must both be claimed and settle within two minutes of creation. The control plane truncates result messages to 500 characters. Run-request records older than 30 days become cleanup targets when a new run request is created and opportunistically during some Console workflow-list reads. Control records older than 30 days become cleanup targets only when a new control is created. Both sweeps are best effort: if the corresponding operation does not occur or a sweep fails, records can remain longer than 30 days. Thirty days is not a hard retention maximum.
Relay and temporary delivery records
Subscribed GitHub, Slack, and webhook events travel from the gateway to the device. Although this is not device-to-company synchronization, delivery uses these temporary records:
- An unacknowledged queue entry holds a wake signal, target device ID, and expiry. The signal contains
id, accounttenant_id,source, optionalinstallation_id/team_id/hook_id,event,occurred_at, optional repositoryrepoand resource locatorresource, matched workspaces and workflows inmatched,replayed, and optional eventbody. - A
bodyhastitle,text,author,url,channel_name,sender,permalink, andtruncated; unavailable optional fields are omitted.textis limited to its first 64 KiB, andtruncatedistruewhen that limit cuts it short. - Once processing reaches a final outcome, the device acknowledges only the wake-signal ID. After an acknowledgement from the correct target device, the gateway deletes the body-bearing queue entry and its ID index.
- Whether a device is online or offline, the queue holds at most 1,000 unacknowledged entries per account. Each entry becomes ineligible for delivery 24 hours after creation and is never replayed after that point. A sweep that normally runs hourly physically removes expired entries, so removal from storage can happen later than the 24-hour mark. When the count limit is exceeded, the oldest entry is deleted and receives no further delivery.
- Duplicate detection keeps only the wake-signal ID and an expiry 24 hours after creation. After expiry it is no longer treated as a duplicate; physical deletion happens on the next lookup or a periodic sweep.
- Wake-signal quota and delivery-drop records are aggregate counts by UTC day and reason. They do not contain event bodies. Retention targets the current UTC day and the two preceding UTC days; older counts become cleanup targets on or after the fourth day's UTC midnight.
- For rate limiting, the gateway stores
tokens(the remaining balance) andts(the refill-time reference) in the account-wideratebucketand inwsrate:<workspace>keys containing each matched workspace ID. These records do not contain event bodies. The current implementation has no path that automatically deletes them based on age alone.
The drop-reason values are no_spec, no_match, not_visible, visibility_unknown,
dm_not_addressed, body_too_large, rate_limited, no_connections, duplicate,
quota_exceeded, workspace_rate_limited, queue_overflow, queue_expired,
queue_target_missing, and device_revoked. The Console explains the event path
and the aggregate diagnostics shown there.
Gateway cache and connection lifetimes
The gateway also holds the following copies for routing, authentication, and live presence. They are not additional device uploads, but their lifetimes determine when a control-plane change is observed at the gateway.
| Copy | Contents | Lifecycle |
|---|---|---|
| Token routing | route:token:<token-hash> maps a SHA-256 device-token hash to tenant_id; missing and revoked tokens cache a none result | A successful mapping lasts 10 minutes; a none result lasts 60 seconds |
| Trigger routing | routing:installation:*, routing:team:*, or routing:hook:* holds tenant identity and entitlements, per-device trigger declarations, and an optional personal-webhook hook name; routinglast:* holds the last known copy | The normal result, including none, lasts 10 minutes. The last known copy lasts one hour and is used only when the control plane fails |
| Device authentication | device:<token-hash> holds device_id, tenant_id, public_key, revoked, and expiresAt | It is valid for 10 minutes. Expiry is logical; a stored expired copy can remain until a later remote_hello refreshes or deletes it, but it does not authenticate the new connection on its own |
| Entitlements | entitlements and entitlements_exp hold the tenant entitlement used by the tenant gateway | A value with entitlements_exp is fresh for 10 minutes. A forwarded wake entitlement replaces it only when no fresh cached value exists; a successful pull or entitlement_update push also replaces it. During a control-plane failure, a last-known value is used only while the device-auth cache is still valid. A legacy value without entitlements_exp is treated as fresh without expiry. Expiry is logical and does not delete the value, so it can remain physically until replaced |
| Socket attachment | The live WebSocket holds authed, deviceId, and the capability booleans declared in remote_hello | It lasts for that logical WebSocket connection, including hibernation, and ends when the connection closes |
When a device_revoked push arrives, the gateway always closes matching sockets and drops matching
unacknowledged queue entries. It scans device:<token-hash> entries; only when one has the matching
device_id does it delete that device-auth copy and its corresponding route:token:<token-hash>
key. This push does not remove trigger-routing copies. A separate routing_purge removes both the
normal and last-known trigger-routing copies. Each push is best effort; if either is delayed or
missed, the relevant cache expiry or logical connection lifetime above governs when the gateway
stops using that copy.
What is never sent
We do not receive the following through this synchronization or remote supervision:
- repository and working-file contents,
.env, secret values, or the device's private key; - workflow bodies and file paths, policies, or prompts;
- event logs, inbox items and attentions, or completed-run history and transcripts; or
- active-run titles, bodies, authors, sessions, worktrees, or engines.
The device name, workspace display names, workflow names, repository and channel coordinates, trigger filters, and non-terminal run metadata listed above are sent. “We do not send code or content” is not the same as “we send no metadata.” These exclusions concern catalog, routing, and run synchronization from the device. The opposite-direction relay temporarily holds event bodies as described above.
Replacement, revocation, and deletion
- The workflow catalog, routing declarations, and accepted active-run snapshot are replaced by the latest complete data sent by the device. These control-plane rows have no ordinary time-based automatic expiry.
- When
remote_supervision_enabledchanges from true to false, the entitlement update immediately deletes every stored active-run row for the account in the same transaction. It does not wait for another upload. A later valid snapshot submitted while the feature is disabled returns HTTP 403 without being stored. - Selecting Revoke in the Console immediately marks the device revoked and deletes its workflow catalog, routing declarations, and active-run snapshot in the control-plane transaction. The control plane then stops accepting its token.
- When its revocation request succeeds,
lk logoutperforms the same control-plane transaction as Console Revoke: it revokes the token and deletes the workflow catalog, routing declarations, and active-run snapshot. - When the asynchronous Clerk
user.deletedwebhook is processed, account deletion cascades through its control-plane rows, including devices, catalogs, routing declarations, run snapshots, run requests, and controls.
Gateway propagation is separate from those control-plane row changes. Console Revoke,
lk logout, and account deletion each attempt a best-effort device_revoked push; Console
Revoke and account deletion also attempt routing_purge, but lk logout does not. Each push has
the separate cleanup scope described above. If a push is delayed or missed, it does not undo the
control-plane deletion, but the gateway may continue using the relevant copy until its logical
expiry or connection end.
Physical persistence follows the table above: an expired device-auth copy may remain until a later
remote_hello, an entitlement value until replacement, and a legacy entitlement without
entitlements_exp may have no expiry. An unacknowledged queue entry is never replayed after its
24-hour expiry and is physically removed by a later sweep. The control-plane cascade does not
directly delete gateway storage:
duplicate-detection IDs and UTC-day counts follow the expiry and cleanup rules above, while
rate-limit buckets currently have no age-based automatic deletion path.
For the overall handling of personal data, integrations, cookies, service providers, and contact details, see the Privacy Policy.