Manifest

Spec: Koder Snapshot manifest format (.kvg)

R1 — File format

  • Extension: .kvg (Koder Value Graph).
  • Encoding: UTF-8, normalized to NFC.
  • Toplevel shape: TOMLlike sections, squarebracketdelimited.
  • Signing: trailing [sig.ed25519] block over canonical bytes

    (everything before [sig.ed25519] line, sorted keys per section).

  • Max size: 10 MB (manifest only; blobs stored separately).

R2 — Required top-level sections

[snapshot]
version             = <int>            # currently 1
hostname            = <string>
created_at          = <RFC3339>
koder_user_id       = <UUID-string>
parent              = <UUID-string|null>
arch                = "x86_64"|"aarch64"
encrypted_fields    = <string[]>
encryption.recipient = <"user@koder-id">

[sig.ed25519] is appended at the bottom — its value field is the base64 Ed25519 signature over the canonical-bytes hash.

R3 — Optional sections

[system], [apps], [repos], [secrets], [files], [skips], [hooks] — all optional. Missing section means "no-op" for restore.

R4 — [system]

[system]
timezone   = "America/Sao_Paulo"
locale     = "pt_BR.UTF-8"
keyboard   = "br"
gpu_driver = "nvidia-595"   # null if no proprietary driver

Restore applies only if [system] is present AND user opt-in (default OFF — too invasive).

R5 — [apps]

Each manager has a list of explicit installs (NOT deps).

[apps]
apt = ["vim", "git", "curl"]
snap = [
  {name="android-studio", channel="stable"},
  {name="scrcpy"}
]
flatpak = [{ref="org.gimp.GIMP", origin="flathub"}]
kpkg = ["koder-tools", "koder-jet"]

Restore diffs current vs manifest, installs missing, never removes (non-destructive).

R6 — [[repos.entry]]

Each repo is an entry in the array:

[[repos.entry]]
slug   = "koder"
url    = "https://flow.koder.dev/Koder/koder.git"
path   = "~/dev/koder"
branch = "master"
dirty  = false                  # if true: warn at restore
config = "submodules: recursive"

For repos without public origin (e.g. local-only branches): URL points to kdrive://users/<u>/snapshots/<sid>/repos/<slug>.bundle.

R7 — [secrets]

Entire block is selectively-encrypted: each value is a base64 crypto_box_seal (libsodium) ciphertext against the user's published X25519 pubkey.

[secrets]
ssh_id_ed25519       = "<base64-ciphertext>"
netrc                = "<base64-ciphertext>"
gpg_secring          = "<base64-ciphertext>"
rclone_config        = "<base64-ciphertext>"
gnome_keyring_export = "<base64-ciphertext>"

Decryption requires user's local X25519 privkey, stored in gnome-keyring after first snapshot create. Recovery via BIP-39 12 words if keyring lost.

R8 — [[files.entry]]

Files are stored as tar.zst blobs in the Drive next to the manifest; manifest references by path + sha256.

[[files.entry]]
local_path = "~/Documentos"
blob       = "kdrive://users/.../snapshots/<id>/data/Documentos.tar.zst"
sha256     = "<hex>"
size_bytes = 227482624

Restore extracts blob at local_path (overwrites with prompt; or --force-overwrite flag).

R9 — [skips]

[skips]
patterns = [
  "~/.cache
common/.cache/**"
]

Capture skips these. Restore is no-op (skips already excluded from blobs).

R10 — [hooks]

[hooks]
pre_restore  = []
post_restore = [
  "/k-setup local",
  "kpkg install koder-tools",
  "ln -sf ~/dev/koder/meta/context ~/.claude"
]

Hooks run sequentially with bash -c. Failure logs but continues (warnnotfail, per policies/regression-tests.kmd resilience model).

R11 — Canonical bytes for signing

To produce the bytes signed by [sig.ed25519]:

  1. Take all sections except [sig.ed25519].
  2. Sort sections by name lexicographically.
  3. Within each section, sort keys lexicographically.
  4. Serialize to canonical TOML (no extra whitespace, = separator,

    newline-terminated).

  5. UTF-8 encode → bytes.
  6. SHA-256 → 32 bytes → Ed25519 sign with user's identity privkey.

Test templates

  • *1*Canonical serialization round-trip: parse → serialize →

    parse → byte-identical

  • *2*Sig validation: valid sig accepts; tampered byte rejects
  • *3*Cross-arch refuse: manifest x86_64 + host aarch64 → restore

    refuses without --force-cross-arch

  • *4*Encryption round-trip: encrypt with pubkey, decrypt with

    matching privkey, get original

  • *5*Wrong privkey: decrypt fails loud
  • *6*Section ordering insensitive: same manifest with sections

    reordered produces same canonical bytes

  • *7*Repos array empty: restore is no-op
  • *8*Hook failure: pre_restore[0] returns rc=1 → restore logs

    warn but continues with filesecretrepo blocks

  • *9*Multi-tenant isolation: User A's manifest references

    kdrive://users/<A>/... — restore by User B refuses (404 from Drive)

Status

Draft v0.1. Tied to snapshotsRFC001 ratification.

Source: ../home/koder/dev/koder/meta/docs/stack/specs/snapshots/manifest.kmd