Files
IronStorage/docs/repository-core.md
2026-08-09 21:41:31 +00:00

2.1 KiB

Password-store repository core

crates/storage::repository maps logical entry names directly to the upstream password-store layout: team/service is stored as team/service.gpg and no IronStorage-specific metadata is added. Discovery inventories ordinary directories, encrypted entries, .gpg-id, .gpg-id.sig, auxiliary regular files, and .git directory boundaries. Git contents are not mistaken for password-store objects, and the innermost boundary applies to a nested entry.

Logical paths are relative component sequences. Absolute paths, . and .., empty entry paths, and entry paths with a trailing slash are rejected before filesystem access. A trailing slash in display-path resolution explicitly selects a directory. If both name.gpg and name/ exist, the unadorned name is reported as ambiguous and mutations through the collision are rejected.

The repository is opened as a capability-scoped directory. All subsequent discovery, reads, temporary files, renames, and cleanup remain relative to that handle. Symlinks are rejected, and the capability layer prevents a raced link from escaping the selected vault. FIFOs, sockets, devices, and other special file types are also rejected; unrelated regular files remain visible as auxiliary repository objects.

Encrypted replacements are written to a temporary file in the destination directory, assigned private permissions on Unix, flushed to stable storage, atomically renamed over the destination, and followed by a directory sync. Before the rename, any error leaves the old entry intact and removes newly created empty directories. After the rename, the only possible observable state is the complete new entry; an interrupted directory sync is reported as durability-uncertain rather than pretending that rollback occurred. Empty ancestor cleanup uses directory-only removal and therefore stops at the store root, content, or a recipient-policy boundary.

Decrypted bytes use SecretBytes, which takes ownership without an additional copy, redacts Debug, exposes data only through explicit methods, and zeroes its allocation on drop.