Developer guide · Zikaron

Files and formats

The kinds of file the app and the command line read and write. Versioned formats carry their version inside the file.

Entries

An entry is a canonical JSON object with exactly seven members:

MemberContent
specThe spec name, zikaron/1
entryTypeThe type: genesis, history, grant, revocation, adoption, succession, annotation, or any agreed new type
authorThe author’s address, in lowercase hexadecimal
seqThe sequence number, from 0
prevThe ID of the entry before; null for the first
bodyThe body, by type
sigA 65-byte EIP-191 signature

An entry’s ID is the SHA-256 of all its bytes. The canonical form: compact, members sorted by bytes, integers in decimal, hexadecimal always lowercase. See law §3 and §4.

The command line’s ledger folder

A plain folder with one file per entry, each named with the entry ID’s 64 hexadecimal digits plus .entry. This is the folder zikaron reads and writes.

The machine folder

One per machine. Its default location is ~/.zikaron-desk.d/; the pointer file ~/.zikaron-desk can point it elsewhere: one line holding an absolute path, ending in a newline, with mode 0600. Where an earlier version kept it at ~/Library/Application Support/ZIKARON, the app keeps using that folder and migrates once.

FileContent
keys-anchor.jsonThe key store, format zikaron-desk/keybox/3, mode 0600; the wrong-try count is kept here too
identities-anchor.jsonThe identity registry, encrypted
machine.jsonMachine settings, format zikaron-desk/machine/1, plain, read before unlocking
kits/index.jsonThe record kit index, plain; see below
records/index.jsonFile names and locations at signing, encrypted
set-aside/Sealed files whose master key is gone, closed for good, kept byte for byte and numbered flat 1, 2, 3; when the folder exists, set-aside-2, -3

The machine folder is found from HOME. The app uses its own key store alone and leaves the system keychain as it is.

Data folders

One for each role of each identity, by default inside the machine folder, with the role subfolder author (Recorder) or grantee (User). Inside are four subfolders:

ledger/
The ledger, one encrypted file per entry.
kits/
Exported record kits, grant files, credentials and snapshots, and the terms files kept at signing (kits/terms/).
grants-held/
Received grants, each with the verdict of its last re-verification; grant files received are in grants-held/files/.
settings/
Settings desk.json, the pending queue queue.json, the last ledger check last-audit.json, and the lock writer.lock that lets one window write.

Data folder names and the file names in the ledger are rewritten under a local names key, which keeps addresses and entry IDs out of the file names; contents are encrypted file by file. To let another tool read a ledger, export a ledger mirror.

Encryption of local data

The local data key is derived from the master key with HKDF-SHA256 (info string zikaron/local/v1), and the names key uses the info string zikaron/names/v1. Each file is encrypted on its own with XChaCha20-Poly1305:

"zikaron-local/1\n" · kind length (1 byte) · kind · version (u16 big-endian) · nonce (24 bytes) · ciphertext and tag

Everything before the nonce is associated data, so a file is bound to its kind and format version. Writes go to a temporary file first, then rename into place.

The key store

The master key and each identity’s keys are sealed in keys-anchor.json: the passcode derives a key through scrypt (N=262144, r=8, p=1), encryption is AES-128-CTR, and a keccak MAC checks it. The passcode is 8 ASCII letters or digits; after 5 wrong tries in a row it locks, and recovery is the way on.

Whole-machine backups

File name zikaron-backup-YYYY-MM-DD.zikaron (UTC date); a second export on the same day is numbered -2, -3. The first line is zikaron-backup/1, and the second is a canonical JSON header: app, created, format, kdf (scrypt’s N, r, p and a 32-byte salt) and nonce. The body’s key is derived from the backup password with scrypt (N=262144, r=8, p=1), and the body is encrypted with XChaCha20-Poly1305, with the two header lines as associated data.

A backup holds the identity registry, each identity’s keys, every encrypted local file (opened, then placed inside), the record of the primary identity and machine.json. The master key, the passcode and the wrong-try count stay on the machine; plain exports (record kits, the index, credentials, grant files, mirrors, key files) stay outside the backup too.

Key files

A standard Ethereum keystore (V3): scrypt (N=262144, r=8, p=1) and aes-128-ctr, named UTC--<time>--<40-hex address>, mode 0600, encrypted with the file password set at export (at least 8 characters). After writing, the app reads it back and checks its format and address, and the export counts once they match.

Record kits

A folder whose format is set by zikaron.kit/1 §7:

kit-3fa9c2e1/ manifest.json the manifest entries/<64 hex>.zk1 entries, one file each files/<path> original files and attachments files/verify.md verification notes for the receiver proofs/<path> on-chain proofs

The manifest lists every entry’s ID, every file’s path, size and SHA-256, and the transaction behind each proof; a file in the kit outside the manifest fails the check. The kit ID is the SHA-256 of the manifest’s bytes. Paths inside a kit use a-z, 0-9, ., _ and - alone; when attachments were renamed, files/zikaron-names.json maps original names to kit names, and the rename rule is <rewritten name>-<first eight of the original name’s SHA-256>.<extension>.

The exported folder is named kit- plus the first eight characters of the content fingerprint of the chosen record with the lowest sequence number; when the choice holds other entries alone it is kit, and a taken name is numbered -2, -3.

The record kit index

kits/index.json in the machine folder, format name zikaron.kits-index/1, kept plain so that other apps on the same machine can read it; see Integration. Each exported kit gets one row, and exporting again to the same path replaces that row:

FieldContent
idThe kit ID
createdThe moment of export, in Unix seconds
rootThe ID of the ledger’s creation entry
pathThe kit’s absolute path
contentsThe content fingerprints of the originals in the kit
note_mdThe note
linkThe fetch address (the kit’s https address online), optional

Grant codes and credentials

The text zikaron-grant: followed by one or more segments joined by .. Each segment is a grant entry’s canonical bytes in base64url, padding left off; with several segments, they run from the original author’s grant to the current one. The whole is capped at 2,953 bytes, exactly the capacity of one QR code (version 40, byte mode, error correction level L). The format is in zikaron.kit/1 §6.

A credential folder is named badge- plus the first ten characters of the grant ID; inside, badge.txt is the text as it is, and badge.svg is its QR code.

Grant files

Extension .zkgrant, named grant- plus the first ten characters of the grant ID. It is a container: the first line is zikaron-kit-file/1, and each item after it is written as “path in the kit, newline, decimal length, newline, bytes, newline”, the manifest first and the rest in byte order. It is capped at 64 MiB in all and 4096 items. Inside are:

  • manifest.json;
  • entries/*.zk1: the entries of every hop in the grant chain, together with the issuer’s ledger when the grant is in this role’s ledger;
  • files/zikaron-grant.txt (the grant code), files/terms/… (the terms files), files/verify.md, and optionally files/publish.txt (the published address set in the data folder that exported the grant file).

Ledger mirrors

<chosen folder>/ZIKARON-backup/<40-hex address>/<author|grantee>/ mirror.json kind desk-mirror, version 1 entries/<64 hex> entry bytes, named by ID alone held/<path> received grants and so on

When the folder already holds an older mirror, only the new entries are added. To use it as the command line’s --ledger folder, copy the files in entries/ into an empty folder and add .entry to each file name.

Derivation paths

Recorderm/44'/60'/0'/0/0
Userm/44'/60'/0'/0/1

Agreed literals

bytes-sha256/1
The mark of how a record was made, one for files, folders and Git repositories alike, meaning the content fingerprint is the SHA-256 of a run of bytes. For a file, the bytes are the whole file; for a folder, the canonical JSON {"files":[{"path":…,"sha256":…}…]} sorted by relative path (symbolic links and special files are skipped and counted, and an empty folder is refused); for a Git repository, the current commit object’s bytes without the commit <length>\0 header, the same as git cat-file commit HEAD | shasum -a 256. The toolchain paired with it is the SHA-256 of this literal itself: 0xb7f3c4a226684d6da684b0ac82d36ed5bcba7bb61d09c955bff9437f84a52a4d.
retraction
The type name of a deletion entry. In the body, subject is required: the ID of a record in this ledger, 0x followed by 64 lowercase hexadecimal digits; note_md is optional. Other tools list it as an unknown type (UNKNOWN_TYPE) and the ledger’s verdict stays as it was; this app’s reading lives in zikaron_glue::retraction, shared by the app and the command line.
for
An extra member in a record’s body: app, identity (hex20) and ref, with an optional seat. Once any of them is filled in, the first three are required.
upstream
An extra member in a grant’s body, holding the ID of the upstream grant; zikaron.kit/1 reads it as a sublicense.