Live Sync Privacy
What leaves your machine, what stays. Secrets are masked, private functions can opt out, and every byte on the wire is auditable.
Live Sync ships source code between peers, which means privacy is a first-class concern. The defaults are conservative: secrets are pattern-matched and masked before any push, private functions can be excluded either by convention or by explicit marker, and every byte that leaves your disk is inspectable with aura_live_sync_status. This page explains exactly what the privacy boundary is, how masking works, how to audit the traffic, and the residual risks you should plan for. The short version: Aura never sends more than the function bodies inside your include set, and it scrubs anything that looks like a secret, but you should still think about what is in your include set before you turn sync on.
Overview
There are three layers of privacy in Live Sync, each operating at a different scope:
- Transport privacy. The link to the Mothership is TLS-encrypted. Peer-to-peer fan-out is end-to-end when the Mothership is configured for it. No plaintext ever traverses the public internet.
- Scope privacy. Selective sync determines which files and functions even reach the sync pipeline. Anything outside the include set is invisible.
- Content privacy. Within synced functions, secret masking replaces patterns that look like credentials with placeholders before the body is compressed and pushed.
The contract is: what is in your include set may leave your machine, with secrets masked. Everything else stays local.
How It Works
Layer 1: transport
Every Live Sync connection uses TLS with the Mothership. On the public internet, the Mothership requires a valid certificate; on a LAN, you can run with a self-signed cert trusted by peers. Peer identities are signed client certificates; rogue clients cannot join a repo without being registered.
If you run the Mothership on your own network, the wire format is:
peer ──TLS──> Mothership ──TLS──> peers
The Mothership sees function bodies in plaintext (inside the TLS tunnel) because it has to route them by aura_id. If you do not want the Mothership to see plaintext, enable end-to-end mode:
[live]
e2e = true
In e2e mode, bodies are additionally encrypted with a repo-wide symmetric key that peers share out-of-band. The Mothership routes by aura_id only; the bodies pass through opaque. Trade-off: e2e mode disables server-side features like impact alerts that depend on reading bodies.
Layer 2: scope
Selective sync is the primary privacy tool. If your include glob is src/public/**, nothing in src/private/ is ever scanned, never mind pushed. See selective sync for the full semantics.
For per-function opt-out, add the @aura-local marker:
/// @aura-local — contains hardcoded creds for local dev only
fn dev_login() {
let token = "dev-token-abc123";
// ...
}
Marked functions are excluded from outbound pushes at the scan stage — before masking, before compression, before the wire.
Layer 3: content masking
Even inside included functions, Aura scrubs patterns that look like secrets before the body leaves the process. Default detectors:
| Pattern | Example | Replacement |
|---|---|---|
| AWS key IDs | AKIA... | AKIA****MASKED**** |
| AWS secret keys | 40-char base64 strings near "aws" | ****MASKED**** |
| Generic API keys | sk_live_..., key_..., pk_... | ****MASKED**** |
| JWT tokens | eyJ... 3-part | ****MASKED**** |
| Private keys | -----BEGIN .* PRIVATE KEY----- | whole block replaced |
| Bearer tokens | Authorization: Bearer ... | token masked |
| Custom regex | configurable | configurable |
Masking runs on the function body text, not the AST. It is a pre-push string transformation. The local file on your disk is unchanged.
local body:
fn connect() {
let key = "sk_live_51HqF...yJ2nK";
let jwt = "eyJhbGciOi...";
client::new(key, jwt)
}
body on the wire:
fn connect() {
let key = "****MASKED****";
let jwt = "****MASKED****";
client::new(key, jwt)
}
Masked content is replaced consistently — the same input produces the same ****MASKED**** placeholder so that AST hashes remain stable across peers for bodies that differ only in masked regions.
Custom masking rules
Add project-specific detectors:
[[live.privacy.mask]]
name = "internal-token"
pattern = '''INT_TOK_[A-Z0-9]{24}'''
replacement = "INT_TOK_****"
[[live.privacy.mask]]
name = "customer-email"
pattern = '''[a-z0-9._%+-]+@internal\.example\.com'''
replacement = "<email>"
Patterns are Rust regex syntax. Tested against every function body before push.
Private-by-default mode
For repositories where the norm is "nothing leaves unless I say so":
[live.privacy]
default_private = true
In this mode, functions do not sync unless explicitly marked public:
/// @aura-public
fn compute_tax(cart: &Cart) -> Tax {
// ...
}
This is the inverse of @aura-local. Pick one mode and stick with it across the repo — mixing styles gets confusing fast.
Auditing traffic
The single most important privacy control is knowing what actually went out. aura_live_sync_status and its CLI equivalent give you a full ledger.
aura live sync status --trace --n 20
recent pushes:
3s billing::compute_tax 276 B masked: 0
8s billing::format_invoice 412 B masked: 1 (1x generic-api-key)
21s auth::login 198 B masked: 2 (1x jwt, 1x bearer)
recent pulls:
11s from alice billing::fmt 340 B
Every record shows size, masking hits, and timestamp. For a complete historic audit:
aura live sync audit --since 1d --json > audit.json
The JSON contains, per push: the aura_id, the masked body's hash, the number and kind of redactions, the timestamp, and the destination Mothership. It does not contain the body text itself — the audit log is not a secondary leak channel.
Spot-checking a specific push
To see exactly what went out for a particular record:
aura live sync show-push <record_id>
This prints the masked body as it appeared on the wire, so you can confirm the masking worked. Unmasked originals are not retrievable from the audit log.
What does not sync
A concrete list of things that never leave your machine:
- Files outside the
includeglobs. - Files matching any
excludeglob. - Functions marked
@aura-local(or unmarked, indefault_privatemode). - File paths relative to your disk root (Aura uses repo-relative paths only).
- Environment variables.
- Files referenced by symlinks outside the repo.
- Git history, commit messages, author identities not tied to the Aura peer ID.
- Editor state: open tabs, cursor positions (except the active-function heartbeat if enabled in presence), undo history, unsaved buffers.
What does sync
With defaults on:
- Function bodies for every function in an included file, with secrets masked.
aura_id, content hash, author peer ID, timestamp for each push.- Heartbeats with active-function name if presence is enabled.
- Conflict resolution metadata.
Residual risks
Honest enumeration of what can still go wrong:
- Mask false negatives. A novel secret format not covered by default patterns will leak unless you add a custom rule. Audit periodically:
aura live sync audit --since 7d | grep masked:0and eyeball the bodies if you are worried. - Secrets in variable names, function names, or comments. Masks work on body tokens; a function named
aws_access_key_AKIAIOSFODNN7EXAMPLEwill be pushed as-is (the function signature is structural, not masked). Do not put secrets in identifiers. - Mothership operator visibility. Without e2e, the Mothership sees masked bodies. If you do not trust the operator, turn on e2e or self-host.
- Peer compromise. Once a function leaves your machine, it is on the other peer's disk. A compromised teammate laptop leaks what it has. This is the same risk as Git.
Gotcha: Masking is belt-and-suspenders, not a replacement for keeping secrets out of source. Use a real secret manager (Vault, AWS Secrets Manager, 1Password) for production secrets. Masking is there to catch the stray dev-token you forgot about, not to license committing creds.
Config
[live.privacy]
default_private = false
mask_secrets = true
e2e_encryption = false
[live.privacy.masks]
# Built-in detectors enabled by default.
aws = true
generic_api_keys = true
jwt = true
bearer_tokens = true
private_key_blocks = true
[[live.privacy.mask]]
# Custom rule
name = "internal-token"
pattern = '''INT_TOK_[A-Z0-9]{24}'''
replacement = "INT_TOK_****"
Troubleshooting
A secret leaked anyway. First, rotate it — assume it is burned. Then: aura live sync audit --contains "<partial_secret>" to see which push carried it. Add a custom mask rule so it does not happen again. File an issue if it was a common pattern that should be in the defaults.
Mask is too aggressive and hiding real code. Tune your custom patterns or remove a built-in detector:
[live.privacy.masks]
generic_api_keys = false
E2e mode breaks impact alerts. That is expected — impact detection needs plaintext on the server. Either disable e2e, or accept that impacts must be computed client-side (slower).
Team-level policy
Privacy settings can be enforced at the Mothership level, so they apply consistently across every peer. An admin configures mothership.toml:
[policy.privacy]
require_mask_secrets = true # peers that disable masking are rejected
require_e2e = false # optional — pushes without e2e rejected if true
enforce_default_private = false # peers must have default_private on
When a peer pushes with disallowed settings, the Mothership refuses the push and returns an error. This prevents a single misconfigured laptop from becoming the weak link on a team where everyone else is careful.
Compliance notes
For teams with SOC 2, ISO 27001, or similar obligations, Live Sync provides the primitives you need to demonstrate control:
- Transport encryption — documented, inspectable via TLS handshake.
- At-rest encryption — the WAL can be placed on an encrypted volume; Aura does not add its own encryption layer by default (it would duplicate OS-level FDE).
- Audit trail —
aura live sync auditgives a complete record of what was sent, to whom, when, and with what redactions. - Access control — peers must present a client certificate signed by the repo's CA. Revocation propagates within one Mothership tick.
- Data retention — the WAL's compaction policy is explicit and configurable; set it to match your retention requirement.
None of this makes compliance automatic — it is on you to establish policies and evidence them — but it gives you a working substrate.