Plan Phase 5: per-machine targeting with only/never labels.
Machine identity = hostname + os:<GOOS> + arch:<GOARCH> + tag:<name>. Entry-level only/never fields for selective restore/checkpoint. Local tags file for machine-specific labels. Steps 28–32 planned. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -613,11 +613,51 @@ The rotation process:
|
|||||||
|
|
||||||
Plaintext entries are untouched.
|
Plaintext entries are untouched.
|
||||||
|
|
||||||
### Planned: Multi-Repo + Per-Machine Inclusion (Phase 5)
|
### Per-Machine Targeting (Phase 5)
|
||||||
|
|
||||||
Support for multiple repos on a single server, and per-machine
|
Entries can be targeted to specific machines using `only` and `never`
|
||||||
inclusion rules (e.g., "this file only applies to Linux machines" or
|
labels. A machine's identity is a set of labels computed at runtime:
|
||||||
"this directory is only for the workstation"). Design TBD.
|
|
||||||
|
- **Short hostname:** `vade` (before the first dot, lowercased)
|
||||||
|
- **OS:** `os:linux`, `os:darwin`, `os:windows` (from `runtime.GOOS`)
|
||||||
|
- **Architecture:** `arch:amd64`, `arch:arm64` (from `runtime.GOARCH`)
|
||||||
|
- **Tags:** `tag:work`, `tag:server` (from `<repo>/tags`, local-only)
|
||||||
|
|
||||||
|
**Manifest fields on Entry:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
files:
|
||||||
|
- path: ~/.bashrc.linux
|
||||||
|
only: [os:linux] # restore/checkpoint only on Linux
|
||||||
|
...
|
||||||
|
- path: ~/.ssh/work-config
|
||||||
|
only: [tag:work] # only on machines tagged "work"
|
||||||
|
...
|
||||||
|
- path: ~/.config/heavy
|
||||||
|
never: [arch:arm64] # everywhere except ARM
|
||||||
|
...
|
||||||
|
- path: ~/.special
|
||||||
|
only: [vade] # only on host "vade"
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Matching rules:**
|
||||||
|
- `only` set → entry applies if *any* label matches the machine
|
||||||
|
- `never` set → entry excluded if *any* label matches
|
||||||
|
- Both set → error (mutually exclusive)
|
||||||
|
- Neither set → applies everywhere (current behavior)
|
||||||
|
|
||||||
|
**Operations affected:**
|
||||||
|
- `restore` — skip non-matching entries
|
||||||
|
- `checkpoint` — skip non-matching entries (don't clobber stored version)
|
||||||
|
- `status` — report non-matching entries as `skipped`
|
||||||
|
- `add`, `list`, `verify`, `diff` — operate on all entries regardless
|
||||||
|
|
||||||
|
**Tags file:** `<repo>/tags`, one tag per line, not synced. Each
|
||||||
|
machine defines its own tags. `sgard init` adds `tags` to `.gitignore`.
|
||||||
|
|
||||||
|
**Label format:** bare string = hostname, `prefix:value` = typed matcher.
|
||||||
|
The `tag:` prefix in `only`/`never` maps to bare names in the tags file.
|
||||||
|
|
||||||
### Future: Manifest Signing (Phase 6)
|
### Future: Manifest Signing (Phase 6)
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ ARCHITECTURE.md for design details.
|
|||||||
|
|
||||||
## Up Next
|
## Up Next
|
||||||
|
|
||||||
Phase 5: Multi-Repo + Per-Machine Inclusion (to be planned).
|
Phase 5: Per-Machine Targeting (Steps 28–32). Ready for Step 28.
|
||||||
|
|
||||||
## Known Issues / Decisions Deferred
|
## Known Issues / Decisions Deferred
|
||||||
|
|
||||||
@@ -91,3 +91,4 @@ Phase 5: Multi-Repo + Per-Machine Inclusion (to be planned).
|
|||||||
| 2026-03-24 | 25 | Real FIDO2: go-libfido2 bindings, build tag gating, CLI wiring, nix sgard-fido2 package. |
|
| 2026-03-24 | 25 | Real FIDO2: go-libfido2 bindings, build tag gating, CLI wiring, nix sgard-fido2 package. |
|
||||||
| 2026-03-24 | 26 | Test cleanup: tightened lint, 3 combo tests (encrypted+locked, dir-only+locked, toggle), stale doc fixes. |
|
| 2026-03-24 | 26 | Test cleanup: tightened lint, 3 combo tests (encrypted+locked, dir-only+locked, toggle), stale doc fixes. |
|
||||||
| 2026-03-24 | 27 | Phase 4 polish: e2e test (TLS+encryption+locked+push/pull), final doc review. Phase 4 complete. |
|
| 2026-03-24 | 27 | Phase 4 polish: e2e test (TLS+encryption+locked+push/pull), final doc review. Phase 4 complete. |
|
||||||
|
| 2026-03-24 | — | Phase 5 planned (Steps 28–32): machine identity, targeting, tags, proto update, polish. |
|
||||||
|
|||||||
@@ -276,9 +276,45 @@ Depends on Steps 17, 18.
|
|||||||
- [x] E2e test: integration/phase4_test.go covering TLS + encryption + locked files + push/pull
|
- [x] E2e test: integration/phase4_test.go covering TLS + encryption + locked files + push/pull
|
||||||
- [x] Verify: all tests pass, lint clean, both binaries compile
|
- [x] Verify: all tests pass, lint clean, both binaries compile
|
||||||
|
|
||||||
## Phase 5: Multi-Repo + Per-Machine Inclusion
|
## Phase 5: Per-Machine Targeting
|
||||||
|
|
||||||
(To be planned)
|
### Step 28: Machine Identity + Targeting Core
|
||||||
|
|
||||||
|
- [ ] `manifest/manifest.go`: add `Only []string` and `Never []string` to Entry (yaml tags `only,omitempty` / `never,omitempty`)
|
||||||
|
- [ ] `garden/identity.go`: `Identity(repoRoot string) []string` — returns machine's label set: short hostname, `os:<GOOS>`, `arch:<GOARCH>`, `tag:<name>` from `<repo>/tags`
|
||||||
|
- [ ] `garden/targeting.go`: `EntryApplies(entry, labels) bool` — match logic: `only` → any match, `never` → no match, error if both set
|
||||||
|
- [ ] `garden/tags.go`: `LoadTags(repoRoot)`, `SaveTag(repoRoot, tag)`, `RemoveTag(repoRoot, tag)` — read/write `<repo>/tags` file
|
||||||
|
- [ ] `garden/garden.go`: `Init` appends `tags` to `.gitignore`
|
||||||
|
- [ ] Tests: identity computation, tag load/save, matching (only, never, both-error, bare hostname, os:, arch:, tag:, neither)
|
||||||
|
|
||||||
|
### Step 29: Operations Respect Targeting
|
||||||
|
|
||||||
|
- [ ] `garden/garden.go`: `Checkpoint` skips entries where `!EntryApplies`
|
||||||
|
- [ ] `garden/garden.go`: `Restore` skips entries where `!EntryApplies`
|
||||||
|
- [ ] `garden/garden.go`: `Status` reports `skipped` for non-matching entries (or omits them — TBD)
|
||||||
|
- [ ] `garden/garden.go`: `Add` accepts `Only`/`Never` in `AddOptions`
|
||||||
|
- [ ] Tests: checkpoint/restore/status skip non-matching, add with targeting
|
||||||
|
|
||||||
|
### Step 30: Targeting CLI Commands
|
||||||
|
|
||||||
|
- [ ] `cmd/sgard/tag.go`: `sgard tag add <name>`, `sgard tag remove <name>`, `sgard tag list`
|
||||||
|
- [ ] `cmd/sgard/identity.go`: `sgard identity` — print full label set
|
||||||
|
- [ ] `cmd/sgard/add.go`: `--only` and `--never` flags (comma-separated or repeated)
|
||||||
|
- [ ] `cmd/sgard/target.go`: `sgard target <path> --only <labels>`, `--never <labels>`, `--clear`
|
||||||
|
- [ ] Tests: tag file CRUD, identity output
|
||||||
|
|
||||||
|
### Step 31: Proto + Sync Update
|
||||||
|
|
||||||
|
- [ ] `proto/sgard/v1/sgard.proto`: add `repeated string only` and `repeated string never` to ManifestEntry
|
||||||
|
- [ ] `server/convert.go`: update proto ↔ manifest conversion
|
||||||
|
- [ ] Regenerate proto: `make proto`
|
||||||
|
- [ ] Tests: round-trip conversion with targeting fields
|
||||||
|
|
||||||
|
### Step 32: Phase 5 Polish
|
||||||
|
|
||||||
|
- [ ] Update ARCHITECTURE.md, README.md, CLAUDE.md, PROGRESS.md
|
||||||
|
- [ ] E2e test: add entries with targeting, push/pull, restore on different identity
|
||||||
|
- [ ] Verify: all tests pass, lint clean, both binaries compile
|
||||||
|
|
||||||
## Phase 6: Manifest Signing
|
## Phase 6: Manifest Signing
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user