Live Sync Overview
Google Docs, but for code — at the function level, not the keystroke.
Live Sync is Aura's real-time collaboration layer. It continuously streams function bodies between teammates and AI agents working on the same repository, via the self-hosted Mothership. Instead of waiting for someone to push a branch, you see their functions appear in your working copy within seconds of them saving. Unlike Google Docs, Live Sync does not operate on characters — it operates on AST nodes, the smallest unit of code that is actually meaningful. That one design decision is why Live Sync does not destroy your syntax, does not explode into merge conflicts, and does not cost a fortune in bandwidth.
Overview
The pitch is simple: you and your colleagues should not be blind to each other's edits until the next PR. In a world where half of new code is written by AI agents and the other half is written by humans racing AI agents, ten minutes of blindness is enough for three people to rewrite the same function in three incompatible ways. Live Sync closes that window.
Here is the one-paragraph mental model. Every few seconds, Aura walks the dirty set of your working tree, identifies the functions whose AST hashes have changed, and pushes those function bodies to the Mothership. The Mothership fans them out to every peer subscribed to the repository. On each peer, Aura applies the incoming bodies at the AST level — replacing node auth::login with the new version, leaving the surrounding file untouched. If both sides edited the same function within the same sync window, a conflict is flagged and the user (or agent) is asked to pick a side. If only one side edited, the update lands silently.
You can watch it happen:
aura live sync status --watch
Output:
mothership: online (peers: 4)
outbound queue: 0 functions
inbound queue: 0 functions
last push: 2.1s ago (3 functions)
last pull: 4.8s ago (1 function)
wal: healthy (entries: 412, size: 1.2 MB)
recently synced:
<- alice billing::compute_tax 3s ago
-> (self) auth::login 8s ago
<- claude-1 billing::format_invoice 11s ago
That is the feature. Everything else in this module is depth on how it works and how to trust it.
The "not Google Docs" story
Real-time collaboration on documents is a solved problem. CRDTs, OT, Google Docs, Figma. Every engineer has used them. So the reasonable question is: why not just apply those techniques to source code?
We tried. It does not work.
Character-level CRDTs on code produce, for every keystroke, a partial parse tree. Ninety-nine percent of the time, that tree is syntactically invalid — you have half-typed an identifier, an unclosed brace, a dangling comma. Broadcasting that to your peers means their language servers explode, their autocomplete flickers, and their AST-aware tools (including Aura) refuse to index the file. Character-level sync is fine for prose because prose has no grammar that tools depend on. Code has grammar that everything depends on.
Line-level sync is no better. A line is a typographic unit, not a semantic one. Moving a function up ten lines produces a diff the size of the function. Reformatting a line — breaking an argument list across three lines instead of one — produces conflicts where there is no actual disagreement.
Live Sync's insight is that the right granularity was sitting under our noses the whole time: the function. A function is the smallest unit that is both (a) typically complete when the user hits save, and (b) independently meaningful. Sync at that unit and the whole thing clicks into place.
| Property | Google Docs | VS Code Live Share | Cursor cloud sync | Aura Live Sync | |---|---|---|---|---| | Unit | character | file buffer | file buffer | function (AST node) | | Cadence | per keystroke | continuous stream | continuous stream | ~5 seconds | | Conflict granularity | character | line | line | function | | Works offline | partial | no | no | yes (WAL queues) | | Self-hosted | no | no | no | yes (Mothership) | | AST-aware | no | no | no | yes | | Bandwidth | moderate | high | high | low (changed bodies only) |
How It Works
The sync loop is boringly simple, which is on purpose — simple loops are easier to make crash-safe.
every 5 seconds on each peer:
┌──────────────────────────────────────┐
│ 1. scan working tree dirty set │
│ 2. compute AST hash per function │
│ 3. diff against last-pushed hash │
│ 4. for each changed fn: │
│ append to WAL │
│ push body to Mothership │
│ 5. poll Mothership for inbound fns │
│ 6. for each inbound fn: │
│ if local unchanged -> apply │
│ if local changed -> conflict │
└──────────────────────────────────────┘
The loop is built on four guarantees that we discuss in depth elsewhere:
- Function-level granularity. See function-level sync for why this is the right unit and how the cadence was chosen.
- WAL-backed durability. See WAL architecture. If your laptop dies mid-push, nothing is lost.
- Cross-branch impact surfacing. See impact alerts. If a teammate on another branch modified a function you depend on, you find out within seconds.
- Conflict ceremony only when it matters. See sync conflicts. Most edits land silently. Real conflicts are rare and meaningful.
When to use it
Live Sync is designed for two specific workloads, and you should turn it on for both:
- Paired human + AI work on the same repo. You are refactoring, Claude is writing tests, Cursor is fixing lints. Without Live Sync, the three of you drift apart in seconds. With it, you converge.
- Small teams (2–15) inside the same Mothership. You do not need a PR to know Alice renamed a helper. You see it land.
When not to use it
Live Sync is not magic, and there are workloads where it is a net negative:
- Very large repos (>250k functions) on slow disks. The scan pass becomes the bottleneck. Use selective sync to narrow the set, or run Live Sync only for the module you are actively inside.
- Secrets-heavy codebases without a clear public/private split. Secrets are masked by default (see Live Sync privacy), but if your entire codebase is secrets-adjacent, you probably want manual push instead.
- Open-source repositories with untrusted drive-by contributors. Live Sync is for your inner circle of peers. It is not a drop-in replacement for pull requests from strangers.
Gotcha: Live Sync is not a substitute for commits. Your WAL is durable, but WAL entries eventually roll off. Commit, push, and review like normal. Live Sync closes the short gap between saves; Git closes the long gap between features.
Config
Live Sync is off by default on a fresh install. To turn it on:
aura live sync enable
aura live sync status
The config lives in .aura/live.toml:
[live]
enabled = true
cadence_ms = 5000 # 5 second sync window
mothership = "ws://mothership.local:7070"
[live.selective]
include = ["src/**/*.rs", "src/**/*.ts"]
exclude = ["**/test/**", "**/*.gen.rs"]
[live.privacy]
mask_secrets = true
skip_private_functions = false
See selective sync and Live Sync privacy for a full field-by-field walkthrough.
Troubleshooting
If aura live sync status shows mothership: offline for more than a minute, Live Sync has quietly degraded to local-only mode — changes still go to the WAL, but nothing leaves your machine. Run aura doctor live to diagnose, then see sync troubleshooting for the common failure modes.
A day in the life
Concretely, here is what using Live Sync looks like on a small team.
At 09:00 Alice opens the repo and starts on a billing refactor. Bob is at the airport on his phone. Claude (a Claude Code agent) is running in Alice's terminal helping her write tests. The Mothership is on a box in the office.
- 09:02 Alice edits
billing::compute_tax. Five seconds later, her push hits the Mothership. Bob's phone sees nothing (he is not running Aura), but Claude'saura_statuson the next tool call includes the new body. Claude updates the test it was writing to match the new signature. - 09:04 Claude adds a new test function
test_tax_region. It pushes. Alice's status banner updates: "claude-1 addedtest_tax_region2s ago". - 09:07 Bob lands, opens his laptop,
aura live sync pullruns automatically on first save. He pulls 14 function updates Alice pushed while he was in the air and 3 from Claude. The apply is silent — no conflicts, because Bob had not edited any of them. - 09:15 Bob edits
billing::generate_invoice— a function that callscompute_tax. At the same moment, Alice editscompute_taxagain, this time changing its signature. Five seconds later, Bob gets an impact alert: "signature ofcompute_taxchanged on Alice's branch; your callergenerate_invoicedepends on it." He updates and pushes. No PR ceremony. No rebase. - 09:30 A collision. Alice and Bob both edit
billing::compute_taxwithin the same 5-second window. Live Sync raises a conflict on Bob's side (he pushed second). The interactive picker shows both bodies. Bob picks the merged option, reviews the result, saves. That function converges.
The whole flow is designed so that the boring cases are invisible (silent apply, zero ceremony) and the interesting cases are surfaced clearly (conflict picker, impact alert). There is no middle ground of "maybe something happened; better check."
Relationship to Git
Live Sync is not a replacement for Git. Git is still where commits, branches, PRs, and long-term history live. Live Sync is the intra-session layer — the minutes-to-hours scale where Git is too heavy. You commit to Git when you are done with a logical unit of work. You Live Sync continuously so that "done with a logical unit of work" is a coordinated moment across the team.
Think of it this way: Git is for the work you want to remember. Live Sync is for the work you want others to see now.