You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(sync): per-path sync direction (spec.sync.paths[].direction: bi|up|down)
A single bidirectional folder carries both inputs (code, local->pod) and
outputs (results, pod->local), so a bad local write can clobber
pod-generated data. Finer per-subpath direction inside one folder is
impossible in Syncthing (.stignore lives in the directory; per-path
folder types were rejected upstream), so direction is a per-mapping
(per-folder) setting.
Config: spec.sync.paths entries now accept a structured form alongside
the compact "local:remote" string (which stays fully backward
compatible and marshals back unchanged):
paths:
- local: .
remote: /workspace
direction: down # bi (default) | up | down
up = local sendonly / pod receiveonly (code push-only; nothing on the
pod can alter local files). down = pod sendonly / local receiveonly
(the pod is the authority; a local write can never clobber pod
results). The schema is per-path from day one so lifting the current
single-mapping limit later will not change config shape.
Plumbing: syncengine.Pair carries the resolved direction;
`okdev sync --mode` defaults to the configured path's direction with an
explicit flag winning; `okdev up` starts sync in that direction and
skips the two-phase bootstrap for directional folders (one side is
already sendonly, so the final folder types are safe from the first
start). Direction participates in the sync config hash (explicit bi
hashes like the legacy default, so upgrades do not restart existing
sessions). `up --reset-workspace` and `sync reset-remote` are rejected
under "down": they reseed the remote from local, which would sync back
as deletions over the pod's data. `status --details` shows the
direction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Advanced command. Starts detached background sync by default; use `--foreground` for sync debugging, or explicit one-way sync (`up`/`down`).
307
+
-`--mode` defaults to the configured path's `direction` (`spec.sync.paths[].direction`, falling back to `bi`); an explicit flag overrides it for that invocation. See the config manifest for choosing a persistent direction — `down` makes the pod the authority so local writes can never clobber pod-generated results.
307
308
- For default `--mode bi`, no-op when background sync is already active for the session.
-`--reset`: check local-to-hub sync and mesh receiver health, then reset only what is broken. Skips the local sync teardown when the primary sync is already healthy. For sessions with mesh receivers, probes each receiver and re-runs mesh setup only when broken or disconnected receivers are found.
| `paths` | `[]string` | — | Mappings in `local:remote` format (max 1 entry) |
236
+
| `paths` | `[]entry` | — | Sync mappings (max 1 entry). Each entry is either the compact string `local:remote`, or a mapping `{local, remote, direction}` with an optional per-path `direction` |
237
+
| `paths[].direction` | `string` | `bi` | Sync direction for this mapping's folder: `bi`(bidirectional), `up` (local is the authority: local sendonly, pod receiveonly), `down` (pod is the authority: pod sendonly, local receiveonly) |
237
238
| `syncthing.version` | `string` | `v1.29.7` | Local Syncthing binary version |
| `syncthing.compression` | `bool` | `false` | Use Syncthing `always` compression for peer connections instead of the default `metadata` mode |
243
244
| `syncthing.versioningDays` | `int` | `30` | Keep files overwritten or deleted by sync in `.stversions` for this many days (staggered versioning, both sides); `0` disables |
244
245
245
-
**Validation:** `engine` must be `syncthing`; each `paths[]` entry must be `local:remote`; `versioningDays` must be `>= 0`.
246
+
**Validation:** `engine` must be `syncthing`; each `paths[]` entry must have non-empty local and remote; `direction` must be `bi`, `up`, or `down`; `versioningDays` must be `>= 0`.
247
+
248
+
**Sync direction.** `direction` applies to a mapping's whole synced folder — finer per-subpath direction inside one folder is impossible with Syncthing (ignore rules live in the directory itself; per-path folder types were rejected upstream). Pick the direction by what the folder carries:
249
+
250
+
- `bi` (default): source code you edit on both sides. Either side can overwrite the other, so **keep generated outputs (results, checkpoints, logs) out of the synced path** — write them to a non-synced location and fetch with `okdev cp` or `okdev jobs logs`. Versioning (`.stversions/`) is the safety net, not the design.
251
+
- `up`: the folder is a code drop. Nothing the pod writes can ever touch local files; pod-side writes into the folder are flagged by Syncthing but not propagated.
252
+
- `down`: the folder carries pod-generated results. A stray local write (for example an empty file from a failed redirect) can never clobber the pod's data. `okdev up --reset-workspace` and `okdev sync reset-remote` are rejected in this direction because they reseed the remote from local.
253
+
254
+
An explicit `okdev sync --mode` overrides the configured direction for that invocation. Changing `direction` takes effect on the next `okdev up` (it restarts sync with the new folder types). Mesh receiver pods are always receiveonly regardless of direction.
255
+
256
+
```yaml
257
+
# compact form (direction defaults to bi):
258
+
paths:
259
+
- .:/workspace
260
+
261
+
# structured form with an explicit direction:
262
+
paths:
263
+
- local: .
264
+
remote: /workspace
265
+
direction: up
266
+
```
246
267
247
268
Local ignore rules come from the synced workspace's `.stignore`. `okdev init` writes a starter `.stignore` for built-in templates, and `okdev up` creates one with default patterns if the local sync root does not already have one. Editing `.stignore` takes effect automatically as Syncthing notices the change, but it does not remove files that were already synced to the remote workspace. For faster initial syncs, consider ignoring large generated build outputs or local test artifacts such as `debug/`, `release/`, caches, and dataset directories when they do not need to exist remotely.
0 commit comments