Documentation
¶
Overview ¶
Package vfs is the top-level virtual filesystem implementation.
Block layer: 4 KiB pages, content-addressable via blake3 (256-bit). Block IDs are the lowercase hex of the blake3 digest of the post-encryption ciphertext, so identical plaintext blocks dedupe IFF they share the same recipient set + ephemeral key. (For practical dedup across a fleet, derive the encryption key deterministically from a domain salt — see BlockKeyMode.)
Index ¶
- Constants
- Variables
- func Pad(plaintext []byte) []byte
- func SetInstance(v *VFS)
- func Shutdown(_ context.Context) error
- type BlockID
- type Cache
- type Config
- type Crypto
- type FS
- func (fs *FS) Create(p string, mode uint32) (*Inode, error)
- func (fs *FS) Lookup(p string) (*Inode, error)
- func (fs *FS) Mkdir(p string, mode uint32) (*Inode, error)
- func (fs *FS) Open(ctx context.Context, p string) (*File, error)
- func (fs *FS) PathOfInode(id InodeID) (string, error)
- func (fs *FS) ReadDir(p string) ([]*Inode, error)
- func (fs *FS) Remove(p string) error
- func (fs *FS) Sync(ctx context.Context) error
- type File
- type Inode
- type InodeID
- type Stats
- type VFS
Constants ¶
const BlockSize = 4096
BlockSize is the canonical page size. Bumping requires a new on-disk format magic byte; do not change without coordination.
Variables ¶
var Version = "dev"
Version is overridden at build time via -ldflags "-X github.com/hanzoai/vfs.Version=...".
Functions ¶
func Pad ¶
Pad zero-pads or truncates a payload to BlockSize. Plaintext blocks are always exactly BlockSize bytes before encryption so that block boundaries don't leak file lengths to the backend (each block in isolation looks identical in size after age framing — a few hundred bytes of header + tag + BlockSize body).
func SetInstance ¶ added in v0.4.3
func SetInstance(v *VFS)
SetInstance attaches a VFS handle for the mounted routes. Calling this with nil disables the data-plane routes. Idempotent.
Types ¶
type BlockID ¶
type BlockID string
BlockID is the content hash of an encrypted block (blake3-256, hex).
func (BlockID) Path ¶
Path returns the canonical backend key for a block ID. Pattern:
blocks/<first-2-hex>/<full-hash>.zap.age
The 2-hex shard prefix (256 fan-out) keeps single-directory listings tractable for object stores that paginate.
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is an in-memory LRU bound by total bytes. Used as the hot tier in front of the backend. The on-disk cache layer (NVMe write-back) is a future addition (0.2.0); v0.1.0 keeps everything resident in RAM up to MaxBytes.
func NewCache ¶
NewCache creates an LRU cache holding up to maxBytes worth of blocks. maxBytes <= 0 disables eviction (unlimited; for tests).
type Config ¶
type Config struct {
Backend backend.Backend
Crypto *Crypto
CacheMax int64 // bytes; <=0 disables eviction (unbounded)
}
Config holds construction parameters.
type Crypto ¶
type Crypto struct {
// contains filtered or unexported fields
}
Crypto wraps a set of age recipients (write-side) and identities (read-side) for per-block encryption. luxfi/age supports classical X25519 + hybrid PQ ML-KEM-768 recipients in the same recipient list.
func NewCrypto ¶
NewCrypto constructs a Crypto with the given recipients (encrypt targets) and identities (decrypt keys). A node that only writes can pass nil identities; a read-only node can pass nil recipients.
func (*Crypto) Encrypt ¶
Encrypt produces an age-encrypted ciphertext for the given plaintext block using the configured recipients.
func (*Crypto) HasIdentities ¶
HasIdentities reports whether read decryption is possible.
func (*Crypto) HasRecipients ¶
HasRecipients reports whether write encryption is possible.
type FS ¶
type FS struct {
// contains filtered or unexported fields
}
FS is a multi-file VFS layered on top of the block VFS. Inode tree is in memory; persisted as a single encrypted blob on Sync.
func NewFS ¶
NewFS opens (or creates) a filesystem over the given block VFS. If the metadata blob exists on the backend, the tree is rehydrated; otherwise an empty filesystem with a single root directory is initialised.
func (*FS) Lookup ¶
Lookup resolves a `/`-delimited path to an inode. Returns nil + error matching os.ErrNotExist when any segment is missing.
func (*FS) Open ¶
Open returns a File handle for the given path. The path must already exist (use Create first for new files).
func (*FS) PathOfInode ¶
PathOfInode walks the inode tree from the root and returns the absolute path for the given inode ID. Returns os.ErrNotExist when the ID is unknown. O(depth) — used by the darwin FUSE driver, which gets ops as (inodeID, name) tuples instead of bazil.org/fuse's node-pointer style.
type File ¶
type File struct {
// contains filtered or unexported fields
}
File is a multi-block file handle. Reads/writes are byte-addressed; the implementation transparently fetches, modifies, and re-encrypts 4 KiB blocks. Dirty blocks are buffered in a per-handle write set until Sync flushes them to the backend (and updates the inode's block list in the FS metadata tree).
Concurrency: a File is owned by a single goroutine. Concurrent FS access across multiple Files is supported by FS's RWMutex.
func (*File) Close ¶
Close flushes pending writes and releases the handle. Mirrors os.File.Close — must be called even on read-only handles to allow the FS to release any resources.
func (*File) ReadAt ¶
ReadAt reads len(p) bytes at the given offset. Returns io.EOF when reading past the end of the file (matching os.File semantics: when fewer bytes than requested are available, returns those bytes plus io.EOF).
func (*File) Sync ¶
Sync flushes all dirty blocks to the backend, updates the inode's block list, and persists the FS metadata tree. After a successful Sync the file's bytes are durable.
func (*File) Truncate ¶
Truncate sets the file size, dropping any blocks past the new end. Extending past the current size zero-pads (creates dirty zero blocks up to the new end).
func (*File) WriteAt ¶
WriteAt writes len(p) bytes at the given offset, extending the file as needed. Partial-block writes do read-modify-write to avoid clobbering adjacent bytes.
Sync MUST be called to flush dirty blocks to the backend; without Sync the writes live only in this File's in-memory dirty set and will be lost on close-without-sync.
type Inode ¶
type Inode struct {
ID InodeID `json:"id"`
Name string `json:"name,omitempty"`
Parent InodeID `json:"parent,omitempty"`
Mode uint32 `json:"mode"` // POSIX mode bits
Size uint64 `json:"size"` // exact bytes
Mtime time.Time `json:"mtime"`
Atime time.Time `json:"atime"`
Ctime time.Time `json:"ctime"`
Blocks []BlockID `json:"blocks,omitempty"` // file content (ordered)
Children map[string]InodeID `json:"children,omitempty"` // dir entries
}
Inode is the on-disk representation of a file or directory.
We store the inode tree as a single encrypted JSON blob at metadataKey. For >100K files / >1M inodes this becomes too large to reload on every mount; in that scale we shard the metadata blob (`metadata/<inode_id>.zap.age`) and keep an in-memory btree. The shard cutover is post-1.0.
type InodeID ¶
type InodeID uint64
InodeID is a monotonically-assigned per-inode handle. ID 1 is always the root directory.
const RootInode InodeID = 1
RootInode is the well-known root.
type VFS ¶
type VFS struct {
// contains filtered or unexported fields
}
VFS is the top-level virtual filesystem handle. It wraps a Backend (object store) and a Crypto (per-block age encryption), with an LRU cache of decrypted blocks in front.
Concurrency: all methods are safe for concurrent use.
func Instance ¶ added in v0.4.3
func Instance() *VFS
Instance returns the currently attached VFS handle (or nil).
func (*VFS) GetBlock ¶
GetBlock fetches a block by ID, decrypts, and returns the plaintext (still zero-padded to BlockSize — the caller is responsible for remembering the logical size).
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
vfs
command
|
|
|
vfs-cost
command
vfs-cost — print backend cost comparison for a workload.
|
vfs-cost — print backend cost comparison for a workload. |
|
pkg
|
|
|
backend
Package backend defines the object-store interface that VFS writes encrypted blocks against.
|
Package backend defines the object-store interface that VFS writes encrypted blocks against. |
|
backend/file
Package file is a filesystem-backed Backend for local dev.
|
Package file is a filesystem-backed Backend for local dev. |
|
backend/s3
Package s3 is an AWS S3 / S3-compatible Backend.
|
Package s3 is an AWS S3 / S3-compatible Backend. |
|
cost
Package cost computes operational cost for VFS workloads across the pluggable backends VFS supports.
|
Package cost computes operational cost for VFS workloads across the pluggable backends VFS supports. |
|
mount
Package mount is the FUSE/WASI mount layer.
|
Package mount is the FUSE/WASI mount layer. |
|
Package replica is the ONE shared HA-SQLite substrate every Hanzo service uses (HIP-0107): per-org SQLite on the local disk for speed, its durable copy in hanzoai/vfs (SeaweedFS-backed object store), a deterministic single-writer election (Rendezvous/HRW over IAM membership — no coordinator, no lock service, no Postgres, no Redis), and per-org at-rest encryption via KMS.
|
Package replica is the ONE shared HA-SQLite substrate every Hanzo service uses (HIP-0107): per-org SQLite on the local disk for speed, its durable copy in hanzoai/vfs (SeaweedFS-backed object store), a deterministic single-writer election (Rendezvous/HRW over IAM membership — no coordinator, no lock service, no Postgres, no Redis), and per-org at-rest encryption via KMS. |