# Selective Sync > *You decide what flows. Directories, files, specific functions. The default is "the code I work in, nothing else."* **Selective sync lets you shape the set of functions Live Sync watches, pushes, and pulls.** Include globs narrow the scan to the modules you care about. Exclude globs keep generated code, tests, and vendored third-party out of the stream. Per-function opt-outs let you pin individual functions as local-only — useful for experiments, personal debug helpers, and anything you do not want a teammate to see land on their copy yet. The whole thing is expressed in a single `[live.selective]` block in `.aura/live.toml`, with sensible defaults that cover most repos without configuration. ## Overview Not every function in a repo is worth syncing. Generated protobuf clients churn on every build and produce meaningless AST changes. Snapshot test fixtures change in bulk but do not matter for cross-peer awareness. Vendored dependencies should never leave your disk. And sometimes you have a local tweak — a print statement, a feature flag, a debug shim — that you explicitly do not want teammates to inherit. Selective sync answers all of these. The mental model: **Live Sync's scan walks the working tree, applies an include filter, applies an exclude filter, then applies a per-function opt-out filter**. Anything that survives all three is eligible for push and for conflict checking on pull. ```text working tree │ ▼ [include globs] ──────── only files matching any include pattern │ ▼ [exclude globs] ──────── drop files matching any exclude pattern │ ▼ [per-fn opt-out] ──────── drop functions tagged @aura-local │ ▼ sync candidate set ``` ## How It Works The filter is evaluated on every scan tick. Globs are standard double-star globs (`**/*.rs`), matched with forward slashes on all platforms. Order does not matter — include and exclude are sets, not sequences. A file is in the candidate set if: 1. It matches **at least one** include pattern, AND 2. It matches **no** exclude pattern. A function is in the candidate set if: 1. Its file is in the candidate set, AND 2. It is not tagged with a per-function opt-out marker. ## Config The config file is `.aura/live.toml`. Live Sync re-reads it whenever it changes. ```toml [live.selective] # Default: everything source-like. Override to narrow. include = [ "src/**/*.rs", "src/**/*.ts", "src/**/*.py", "crates/**/*.rs", ] # Default: the obvious noise. Extend to taste. exclude = [ "**/target/**", "**/node_modules/**", "**/.next/**", "**/dist/**", "**/build/**", "**/*.gen.rs", "**/*.pb.rs", "**/*_generated.ts", "**/vendored/**", "**/fixtures/**", "**/*.snap", ] # Per-function opt-out marker. # Functions with this marker in a leading doc/attribute are not pushed. local_only_marker = "@aura-local" ``` ### Using include to narrow on huge repos On a monorepo with ten services and you are only working on `billing`, include just that module: ```toml [live.selective] include = ["services/billing/**/*.rs"] ``` You will still **receive** pushes from other services if they impact you ([impact alerts](/impact-alerts) flag cross-module changes), but your scan cost collapses to the billing subtree. This is the single most effective perf lever on large repos — see [bandwidth and perf](/bandwidth-and-perf). ### Stacking includes for multi-language projects Include is a union. List every language you care about: ```toml include = [ "backend/**/*.rs", "frontend/**/*.ts", "frontend/**/*.tsx", "shared/**/*.proto", ] ``` ### Excluding tests Opinions differ. The default **includes** tests because they often encode intent and you want teammates to see your test changes in real time. If you disagree: ```toml [live.selective] exclude = ["**/tests/**", "**/*_test.rs", "**/*.spec.ts"] ``` ## Per-function opt-out For finer control than file globs, tag individual functions as local-only. The marker is a string the parser looks for in the function's leading doc comment or attribute. Default: `@aura-local`. ### Rust ```rust /// Personal debug helper. @aura-local fn dump_state_to_file() { std::fs::write("/tmp/state.json", serde_json::to_string(&STATE).unwrap()).unwrap(); } ``` ### TypeScript ```typescript /** Local experiment. @aura-local */ function probeTimings() { console.time("work"); // ... } ``` ### Python ```python def _debug_hook(): """@aura-local — trace only, never ship.""" import ipdb; ipdb.set_trace() ``` Marked functions are filtered out of **outbound** pushes. They are not filtered out of **inbound** — if a teammate pushed one, you would receive it. But by construction, if you both mark it, neither pushes. ### Listing local-only functions ```bash aura live selective local-only ``` ```text local-only functions (3): debug::dump_state_to_file src/debug.rs:41 experiments::probe_timings src/experiments.ts:14 tests::_debug_hook tests/helpers.py:8 ``` ### Flipping a function to shared Remove the marker and save. Next tick, it goes out. ## Precedence and conflicts If a file matches both include and exclude, **exclude wins.** This is intentional — exclude is usually the "I am sure" rule ("never this directory"), while include is often looser ("anything that looks like source"). If a per-function opt-out is on a function in an included file, the function is skipped but the file is still scanned. Other functions in the same file push normally. ## Ignoring a specific AST kind Beyond files and named functions, you can exclude whole AST kinds — e.g. never sync trait `impl` blocks, or never sync closures. This is rarely needed but it exists: ```toml [live.selective.ast] exclude_kinds = ["closure_expression", "macro_invocation"] ``` > **Gotcha:** Excluding an AST kind excludes it everywhere, including in files you explicitly include. Use sparingly and document why. ## Verifying what will be synced Before enabling Live Sync on a fresh repo, inspect the candidate set: ```bash aura live selective show ``` ```text include patterns: 4 exclude patterns: 11 files in candidate: 1,247 functions in candidate: 14,902 filtered by exclude: 3,421 filtered by marker: 19 sample included: src/billing/compute_tax.rs src/billing/format_invoice.rs ... sample excluded: target/debug/build/... src/proto/billing.pb.rs [matches **/*.pb.rs] ``` A quick `show` after every config edit is cheap insurance against "why is my debug function on Alice's laptop." ## Interaction with [Live Sync privacy](/live-sync-privacy) Selective sync decides **what functions participate**. Privacy decides **what parts of a function are masked** inside the participating set. Secrets are scrubbed from function bodies regardless of include/exclude — see [Live Sync privacy](/live-sync-privacy) for details on masking and the private-by-default flag. ## Troubleshooting **A file I expect to sync is not syncing.** Run `aura live selective why path/to/file.rs`. Aura prints which rule filtered it (include miss, exclude hit, or not dirty). **A file I do not want to sync is leaking.** Same command — confirms the file is currently in the candidate set, then you know which exclude to add. **Per-function marker not respected.** The marker must be in the leading comment or attribute of the function. Markers in the body are ignored (they are inside the AST node, not attached to it). Also check you spelled it exactly — no trailing period. **Config edit not picked up.** Aura watches `.aura/live.toml` for changes. If the watch missed it (rare), `aura live reload` forces a reread. ## Recipes A few patterns that come up repeatedly. ### "Work on one service in a monorepo" ```toml [live.selective] include = ["services/billing/**/*.{rs,ts}"] exclude = ["**/target/**", "**/node_modules/**"] ``` Scan cost drops to the service subtree; you still receive cross-service impact alerts for functions you call outside the include set. ### "Hide my personal debug module" ```toml [live.selective] exclude = ["src/debug_personal/**"] ``` The entire directory is invisible to sync. Even listing the files in `.gitignore` separately is still a good idea if you do not want them in Git either. ### "Share only the public API while building" ```toml [live.selective] default_private = true include = ["src/api/**"] ``` Every function outside `src/api/**` is opt-out by default. Inside, every function syncs. Combine with `@aura-public` marker for fine-grained overrides. ### "Polyglot repo, language-scoped includes" ```toml [live.selective] include = [ "backend/**/*.rs", "cli/**/*.rs", "web/**/*.{ts,tsx}", "shared/**/*.proto", ] ``` ### "Exclude generated noise" ```toml [live.selective] exclude = [ "**/*.pb.rs", "**/*.pb.go", "**/*_generated.ts", "**/*.sql.go", "**/migrations/**", ] ``` ## Interaction with Git Selective sync is independent of `.gitignore`. A file can be gitignored and still sync — useful for shared dev helpers that should not hit main but that teammates want to see in real time. Conversely, a file can be in Git and excluded from sync. The two filters are separate. That said, most teams keep them aligned. A common pattern: ```toml [live.selective] respect_gitignore = true ``` With `respect_gitignore = true`, Aura treats every gitignored path as implicitly excluded from sync. Explicit excludes still stack on top. ## Defaults we ship Aura's first-run defaults try to be reasonable without being presumptuous. Here is what `aura live init` writes on a new repo: ```toml [live.selective] respect_gitignore = true include = ["src/**/*", "crates/**/*", "app/**/*", "services/**/*"] exclude = [ "**/target/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/.next/**", "**/.turbo/**", "**/coverage/**", "**/*.gen.rs", "**/*.pb.rs", "**/*_generated.*", "**/vendor/**", "**/vendored/**", "**/.venv/**", ] local_only_marker = "@aura-local" ``` If your repo uses non-standard layouts (e.g. Python's `src-less` convention, or a custom tooling directory), edit `include` on first enable. ## Migration: broadening or narrowing later Changing `include`/`exclude` after Live Sync is already running is safe. On the next scan tick, newly-included files are parsed and their current hashes are pushed as baseline; newly-excluded files are simply stopped — peers retain their last received copy. One subtle note: narrowing `include` does not retract previously synced functions from peers' copies. Those functions are still on their disk. You can force a "sync reset" if you want a clean slate: ```bash aura live sync reset --confirm ``` This rewinds your WAL and replays a baseline snapshot under the new filter. Most teams do not need this — natural edits will overwrite old bodies soon enough. ## See Also - [Live Sync overview](/live-sync-overview) - [Bandwidth and perf](/bandwidth-and-perf) - [Live Sync privacy](/live-sync-privacy) - [Presence and cursors](/presence-and-cursors)