From 347c4b1b5702b5da8f776cce9cfc3fa17ec2f186 Mon Sep 17 00:00:00 2001 From: Virgil Date: Mon, 30 Mar 2026 23:02:53 +0000 Subject: [PATCH] refactor(ax): trim prose comments to examples --- datanode/medium.go | 33 ++++++--------------------------- doc.go | 10 ++++------ io.go | 7 ------- local/medium.go | 10 ---------- node/node.go | 28 ++-------------------------- s3/s3.go | 12 ------------ sigil/crypto_sigil.go | 42 +----------------------------------------- sigil/sigil.go | 11 ++++------- sigil/sigils.go | 40 +++++----------------------------------- sqlite/sqlite.go | 8 ++------ store/doc.go | 10 ++++------ store/medium.go | 4 +--- store/store.go | 2 -- workspace/doc.go | 2 -- 14 files changed, 29 insertions(+), 190 deletions(-) diff --git a/datanode/medium.go b/datanode/medium.go index 574679c..b05c647 100644 --- a/datanode/medium.go +++ b/datanode/medium.go @@ -1,9 +1,7 @@ -// Package datanode provides an io.Medium implementation backed by Borg's DataNode. -// -// medium := datanode.New() -// _ = medium.Write("jobs/run.log", "started") -// snapshot, _ := medium.Snapshot() -// restored, _ := datanode.FromTar(snapshot) +// medium := datanode.New() +// _ = medium.Write("jobs/run.log", "started") +// snapshot, _ := medium.Snapshot() +// restored, _ := datanode.FromTar(snapshot) package datanode import ( @@ -36,7 +34,7 @@ var ( // snapshot, _ := medium.Snapshot() type Medium struct { dataNode *borgdatanode.DataNode - directorySet map[string]bool // explicit directories that exist without file contents + directorySet map[string]bool lock sync.RWMutex } @@ -101,8 +99,6 @@ func normaliseEntryPath(filePath string) string { return filePath } -// --- io.Medium interface --- - func (medium *Medium) Read(filePath string) (string, error) { medium.lock.RLock() defer medium.lock.RUnlock() @@ -139,7 +135,6 @@ func (medium *Medium) Write(filePath, content string) error { } medium.dataNode.AddData(filePath, []byte(content)) - // ensure parent directories are tracked medium.ensureDirsLocked(path.Dir(filePath)) return nil } @@ -160,8 +155,6 @@ func (medium *Medium) EnsureDir(filePath string) error { return nil } -// ensureDirsLocked marks a directory and all ancestors as existing. -// Caller must hold medium.lock. func (medium *Medium) ensureDirsLocked(directoryPath string) { for directoryPath != "" && directoryPath != "." { medium.directorySet[directoryPath] = true @@ -226,7 +219,6 @@ func (medium *Medium) Delete(filePath string) error { return nil } - // Remove the file by creating a new DataNode without it if err := medium.removeFileLocked(filePath); err != nil { return core.E("datanode.Delete", core.Concat("failed to delete file: ", filePath), err) } @@ -253,7 +245,6 @@ func (medium *Medium) DeleteAll(filePath string) error { found = true } - // Remove all files under prefix entries, err := medium.collectAllLocked() if err != nil { return core.E("datanode.DeleteAll", core.Concat("failed to inspect tree: ", filePath), err) @@ -267,7 +258,6 @@ func (medium *Medium) DeleteAll(filePath string) error { } } - // Remove explicit directories under prefix for directoryPath := range medium.directorySet { if directoryPath == filePath || core.HasPrefix(directoryPath, prefix) { delete(medium.directorySet, directoryPath) @@ -466,7 +456,7 @@ func (medium *Medium) Exists(filePath string) bool { filePath = normaliseEntryPath(filePath) if filePath == "" { - return true // root always exists + return true } _, err := medium.dataNode.Stat(filePath) if err == nil { @@ -490,9 +480,6 @@ func (medium *Medium) IsDir(filePath string) bool { return medium.directorySet[filePath] } -// --- internal helpers --- - -// hasPrefixLocked checks if any file path starts with prefix. Caller holds lock. func (medium *Medium) hasPrefixLocked(prefix string) (bool, error) { entries, err := medium.collectAllLocked() if err != nil { @@ -511,7 +498,6 @@ func (medium *Medium) hasPrefixLocked(prefix string) (bool, error) { return false, nil } -// collectAllLocked returns all file paths in the DataNode. Caller holds lock. func (medium *Medium) collectAllLocked() ([]string, error) { var names []string err := dataNodeWalkDir(medium.dataNode, ".", func(filePath string, entry fs.DirEntry, err error) error { @@ -542,9 +528,6 @@ func (medium *Medium) readFileLocked(filePath string) ([]byte, error) { return data, nil } -// removeFileLocked removes a single file by rebuilding the DataNode. -// This is necessary because Borg's DataNode doesn't expose a Remove method. -// Caller must hold medium.lock write lock. func (medium *Medium) removeFileLocked(target string) error { entries, err := medium.collectAllLocked() if err != nil { @@ -565,8 +548,6 @@ func (medium *Medium) removeFileLocked(target string) error { return nil } -// --- writeCloser buffers writes and flushes to DataNode on Close --- - type writeCloser struct { medium *Medium path string @@ -587,8 +568,6 @@ func (writer *writeCloser) Close() error { return nil } -// --- fs types for explicit directories --- - type dirEntry struct { name string } diff --git a/doc.go b/doc.go index 6b938f8..b94d1bd 100644 --- a/doc.go +++ b/doc.go @@ -1,7 +1,5 @@ -// Package io exposes CoreGO's storage surface. -// -// medium, _ := io.NewSandboxed("/srv/app") -// _ = medium.Write("config/app.yaml", "port: 8080") -// backup, _ := io.NewSandboxed("/srv/backup") -// _ = io.Copy(medium, "data/report.json", backup, "daily/report.json") +// medium, _ := io.NewSandboxed("/srv/app") +// _ = medium.Write("config/app.yaml", "port: 8080") +// backup, _ := io.NewSandboxed("/srv/backup") +// _ = io.Copy(medium, "data/report.json", backup, "daily/report.json") package io diff --git a/io.go b/io.go index f4c4744..4c3074e 100644 --- a/io.go +++ b/io.go @@ -188,7 +188,6 @@ type MemoryMedium struct { modTimes map[string]time.Time } -// MockMedium is a compatibility alias for MemoryMedium. type MockMedium = MemoryMedium var _ Medium = (*MemoryMedium)(nil) @@ -203,8 +202,6 @@ func NewMemoryMedium() *MemoryMedium { } } -// NewMockMedium is a compatibility alias for NewMemoryMedium. -// // Example: medium := io.NewMockMedium() // _ = medium.Write("config/app.yaml", "port: 8080") func NewMockMedium() *MemoryMedium { @@ -396,14 +393,12 @@ func (medium *MemoryMedium) WriteStream(path string) (goio.WriteCloser, error) { return medium.Create(path) } -// MemoryFile implements fs.File for MemoryMedium. type MemoryFile struct { name string content []byte offset int64 } -// MockFile is a compatibility alias for MemoryFile. type MockFile = MemoryFile func (file *MemoryFile) Stat() (fs.FileInfo, error) { @@ -423,14 +418,12 @@ func (file *MemoryFile) Close() error { return nil } -// MemoryWriteCloser implements WriteCloser for MemoryMedium. type MemoryWriteCloser struct { medium *MemoryMedium path string data []byte } -// MockWriteCloser is a compatibility alias for MemoryWriteCloser. type MockWriteCloser = MemoryWriteCloser func (writeCloser *MemoryWriteCloser) Write(data []byte) (int, error) { diff --git a/local/medium.go b/local/medium.go index 95aa5bb..a6bfde8 100644 --- a/local/medium.go +++ b/local/medium.go @@ -23,7 +23,6 @@ var unrestrictedFileSystem = (&core.Fs{}).NewUnrestricted() // _ = medium.Write("config/app.yaml", "port: 8080") func New(root string) (*Medium, error) { absoluteRoot := absolutePath(root) - // Example: local.New("/srv/app") resolves macOS "/var" to "/private/var" before sandbox checks. if resolvedRoot, err := resolveSymlinksPath(absoluteRoot); err == nil { absoluteRoot = resolvedRoot } @@ -181,16 +180,12 @@ func (medium *Medium) sandboxedPath(path string) string { return core.Path(currentWorkingDir(), normalisePath(path)) } - // Use a cleaned absolute path to resolve all .. and . internally - // before joining with the root. This is a standard way to sandbox paths. clean := cleanSandboxPath(path) - // If root is "/", allow absolute paths through if medium.filesystemRoot == dirSeparator() { return clean } - // Join cleaned relative path with root return core.Path(medium.filesystemRoot, core.TrimPrefix(clean, dirSeparator())) } @@ -199,7 +194,6 @@ func (medium *Medium) validatePath(path string) (string, error) { return medium.sandboxedPath(path), nil } - // Split the cleaned path into components parts := splitPathParts(cleanSandboxPath(path)) current := medium.filesystemRoot @@ -208,16 +202,12 @@ func (medium *Medium) validatePath(path string) (string, error) { realNext, err := resolveSymlinksPath(next) if err != nil { if core.Is(err, syscall.ENOENT) { - // Part doesn't exist, we can't follow symlinks anymore. - // Since the path is already Cleaned and current is safe, - // appending a component to current will not escape. current = next continue } return "", err } - // Verify the resolved part is still within the root if !isWithinRoot(medium.filesystemRoot, realNext) { logSandboxEscape(medium.filesystemRoot, path, realNext) return "", fs.ErrPermission diff --git a/node/node.go b/node/node.go index a62d88a..95b7f2b 100644 --- a/node/node.go +++ b/node/node.go @@ -129,20 +129,14 @@ func (node *Node) WalkNode(root string, fn fs.WalkDirFunc) error { // Example: options := node.WalkOptions{MaxDepth: 1, SkipErrors: true} type WalkOptions struct { - // MaxDepth limits how many directory levels to descend. 0 means unlimited. - MaxDepth int - // Filter, if set, is called for each entry. Return true to include the - // entry (and descend into it if it is a directory). - Filter func(entryPath string, entry fs.DirEntry) bool - // SkipErrors suppresses errors (e.g. nonexistent root) instead of - // propagating them through the callback. + MaxDepth int + Filter func(entryPath string, entry fs.DirEntry) bool SkipErrors bool } // Example: _ = nodeTree.WalkWithOptions(".", callback, node.WalkOptions{MaxDepth: 1, SkipErrors: true}) func (node *Node) WalkWithOptions(root string, fn fs.WalkDirFunc, options WalkOptions) error { if options.SkipErrors { - // If root doesn't exist, silently return nil. if _, err := node.Stat(root); err != nil { return nil } @@ -160,7 +154,6 @@ func (node *Node) WalkWithOptions(root string, fn fs.WalkDirFunc, options WalkOp result := fn(entryPath, entry, err) - // After visiting a directory at MaxDepth, prevent descending further. if result == nil && options.MaxDepth > 0 && entry != nil && entry.IsDir() && entryPath != root { rel := core.TrimPrefix(entryPath, root) rel = core.TrimPrefix(rel, "/") @@ -181,7 +174,6 @@ func (node *Node) ReadFile(name string) ([]byte, error) { if !ok { return nil, core.E("node.ReadFile", core.Concat("path not found: ", name), fs.ErrNotExist) } - // Return a copy to prevent callers from mutating internal state. result := make([]byte, len(file.content)) copy(result, file.content) return result, nil @@ -217,7 +209,6 @@ func (node *Node) CopyTo(target coreio.Medium, sourcePath, destPath string) erro } if !info.IsDir() { - // Single file copy file, ok := node.files[sourcePath] if !ok { return core.E("node.CopyTo", core.Concat("path not found: ", sourcePath), fs.ErrNotExist) @@ -225,7 +216,6 @@ func (node *Node) CopyTo(target coreio.Medium, sourcePath, destPath string) erro return target.Write(destPath, string(file.content)) } - // Directory: walk and copy all files underneath prefix := sourcePath if prefix != "" && !core.HasSuffix(prefix, "/") { prefix += "/" @@ -247,8 +237,6 @@ func (node *Node) CopyTo(target coreio.Medium, sourcePath, destPath string) erro return nil } -// ---------- Medium interface: fs.FS methods ---------- - func (node *Node) Open(name string) (fs.File, error) { name = core.TrimPrefix(name, "/") if dataFile, ok := node.files[name]; ok { @@ -289,7 +277,6 @@ func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) { name = "" } - // Disallow reading a file as a directory. if info, err := node.Stat(name); err == nil && !info.IsDir() { return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrInvalid} } @@ -332,8 +319,6 @@ func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) { return entries, nil } -// ---------- Medium interface: read/write ---------- - func (node *Node) Read(filePath string) (string, error) { filePath = core.TrimPrefix(filePath, "/") file, ok := node.files[filePath] @@ -365,8 +350,6 @@ func (node *Node) EnsureDir(_ string) error { return nil } -// ---------- Medium interface: existence checks ---------- - func (node *Node) Exists(filePath string) bool { _, err := node.Stat(filePath) return err == nil @@ -386,8 +369,6 @@ func (node *Node) IsDir(filePath string) bool { return info.IsDir() } -// ---------- Medium interface: mutations ---------- - func (node *Node) Delete(filePath string) error { filePath = core.TrimPrefix(filePath, "/") if _, ok := node.files[filePath]; ok { @@ -443,8 +424,6 @@ func (node *Node) List(filePath string) ([]fs.DirEntry, error) { return node.ReadDir(filePath) } -// ---------- Medium interface: streams ---------- - func (node *Node) Create(filePath string) (goio.WriteCloser, error) { filePath = core.TrimPrefix(filePath, "/") return &nodeWriter{node: node, path: filePath}, nil @@ -472,9 +451,6 @@ func (node *Node) WriteStream(filePath string) (goio.WriteCloser, error) { return node.Create(filePath) } -// ---------- Internal types ---------- - -// nodeWriter buffers writes and commits them to the Node on Close. type nodeWriter struct { node *Node path string diff --git a/s3/s3.go b/s3/s3.go index 384b8ce..bc55040 100644 --- a/s3/s3.go +++ b/s3/s3.go @@ -1,5 +1,3 @@ -// Package s3 stores io.Medium data in S3 objects. -// // Example: client := awss3.NewFromConfig(aws.Config{Region: "us-east-1"}) // Example: medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"}) // Example: _ = medium.Write("reports/daily.txt", "done") @@ -45,11 +43,8 @@ var _ coreio.Medium = (*Medium)(nil) // Example: medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"}) type Options struct { - // Bucket is the target S3 bucket name. Bucket string - // Client is the AWS S3 client or test double used for requests. Client Client - // Prefix is prepended to every object key. Prefix string } @@ -109,8 +104,6 @@ func New(options Options) (*Medium, error) { } func (medium *Medium) objectKey(filePath string) string { - // Clean the path using a leading "/" to sandbox traversal attempts, - // then strip the "/" prefix. This ensures ".." can't escape. clean := path.Clean("/" + filePath) if clean == "/" { clean = "" @@ -181,7 +174,6 @@ func (medium *Medium) IsFile(filePath string) bool { if key == "" { return false } - // A "file" in S3 is an object whose key does not end with "/" if core.HasSuffix(key, "/") { return false } @@ -223,7 +215,6 @@ func (medium *Medium) DeleteAll(filePath string) error { return core.E("s3.DeleteAll", "path is required", fs.ErrInvalid) } - // First, try deleting the exact key _, err := medium.client.DeleteObject(context.Background(), &awss3.DeleteObjectInput{ Bucket: aws.String(medium.bucket), Key: aws.String(key), @@ -232,7 +223,6 @@ func (medium *Medium) DeleteAll(filePath string) error { return core.E("s3.DeleteAll", core.Concat("failed to delete object: ", key), err) } - // Then delete all objects under the prefix prefix := key if !core.HasSuffix(prefix, "/") { prefix += "/" @@ -561,8 +551,6 @@ func (medium *Medium) IsDir(filePath string) bool { return len(listOutput.Contents) > 0 || len(listOutput.CommonPrefixes) > 0 } -// --- Internal types --- - type fileInfo struct { name string size int64 diff --git a/sigil/crypto_sigil.go b/sigil/crypto_sigil.go index f3a27c0..31947bc 100644 --- a/sigil/crypto_sigil.go +++ b/sigil/crypto_sigil.go @@ -14,35 +14,23 @@ import ( ) var ( - // InvalidKeyError is returned when the encryption key is not 32 bytes. InvalidKeyError = core.E("sigil.InvalidKeyError", "invalid key size, must be 32 bytes", nil) - // CiphertextTooShortError is returned when the ciphertext is too short to decrypt. CiphertextTooShortError = core.E("sigil.CiphertextTooShortError", "ciphertext too short", nil) - // DecryptionFailedError is returned when decryption or authentication fails. DecryptionFailedError = core.E("sigil.DecryptionFailedError", "decryption failed", nil) - // NoKeyConfiguredError is returned when no encryption key has been set. NoKeyConfiguredError = core.E("sigil.NoKeyConfiguredError", "no encryption key configured", nil) ) -// PreObfuscator customises the bytes mixed in before and after encryption. type PreObfuscator interface { - // Obfuscate transforms plaintext before encryption using the provided entropy. - // The entropy is typically the encryption nonce, ensuring the transformation - // is unique per-encryption without additional random generation. Obfuscate(data []byte, entropy []byte) []byte - // Deobfuscate reverses the transformation after decryption. - // Must be called with the same entropy used during Obfuscate. Deobfuscate(data []byte, entropy []byte) []byte } -// Example: cipherSigil, _ := sigil.NewChaChaPolySigil(key) type XORObfuscator struct{} -// Obfuscate XORs the data with a key stream derived from the entropy. func (obfuscator *XORObfuscator) Obfuscate(data []byte, entropy []byte) []byte { if len(data) == 0 { return data @@ -50,7 +38,6 @@ func (obfuscator *XORObfuscator) Obfuscate(data []byte, entropy []byte) []byte { return obfuscator.transform(data, entropy) } -// Deobfuscate reverses the XOR transformation (XOR is symmetric). func (obfuscator *XORObfuscator) Deobfuscate(data []byte, entropy []byte) []byte { if len(data) == 0 { return data @@ -58,7 +45,6 @@ func (obfuscator *XORObfuscator) Deobfuscate(data []byte, entropy []byte) []byte return obfuscator.transform(data, entropy) } -// transform applies XOR with an entropy-derived key stream. func (obfuscator *XORObfuscator) transform(data []byte, entropy []byte) []byte { result := make([]byte, len(data)) keyStream := obfuscator.deriveKeyStream(entropy, len(data)) @@ -68,12 +54,10 @@ func (obfuscator *XORObfuscator) transform(data []byte, entropy []byte) []byte { return result } -// deriveKeyStream creates a deterministic key stream from entropy. func (obfuscator *XORObfuscator) deriveKeyStream(entropy []byte, length int) []byte { stream := make([]byte, length) h := sha256.New() - // Generate key stream in 32-byte blocks blockNum := uint64(0) offset := 0 for offset < length { @@ -92,10 +76,8 @@ func (obfuscator *XORObfuscator) deriveKeyStream(entropy []byte, length int) []b return stream } -// ShuffleMaskObfuscator adds byte shuffling on top of XOR masking. type ShuffleMaskObfuscator struct{} -// Obfuscate shuffles bytes and applies a mask derived from entropy. func (obfuscator *ShuffleMaskObfuscator) Obfuscate(data []byte, entropy []byte) []byte { if len(data) == 0 { return data @@ -104,16 +86,13 @@ func (obfuscator *ShuffleMaskObfuscator) Obfuscate(data []byte, entropy []byte) result := make([]byte, len(data)) copy(result, data) - // Generate permutation and mask from entropy perm := obfuscator.generatePermutation(entropy, len(data)) mask := obfuscator.deriveMask(entropy, len(data)) - // Apply mask first, then shuffle for i := range result { result[i] ^= mask[i] } - // Shuffle using Fisher-Yates with deterministic seed shuffled := make([]byte, len(data)) for i, p := range perm { shuffled[i] = result[p] @@ -122,7 +101,6 @@ func (obfuscator *ShuffleMaskObfuscator) Obfuscate(data []byte, entropy []byte) return shuffled } -// Deobfuscate reverses the shuffle and mask operations. func (obfuscator *ShuffleMaskObfuscator) Deobfuscate(data []byte, entropy []byte) []byte { if len(data) == 0 { return data @@ -130,16 +108,13 @@ func (obfuscator *ShuffleMaskObfuscator) Deobfuscate(data []byte, entropy []byte result := make([]byte, len(data)) - // Generate permutation and mask from entropy perm := obfuscator.generatePermutation(entropy, len(data)) mask := obfuscator.deriveMask(entropy, len(data)) - // Unshuffle first for i, p := range perm { result[p] = data[i] } - // Remove mask for i := range result { result[i] ^= mask[i] } @@ -147,20 +122,17 @@ func (obfuscator *ShuffleMaskObfuscator) Deobfuscate(data []byte, entropy []byte return result } -// generatePermutation creates a deterministic permutation from entropy. func (obfuscator *ShuffleMaskObfuscator) generatePermutation(entropy []byte, length int) []int { perm := make([]int, length) for i := range perm { perm[i] = i } - // Use entropy to seed a deterministic shuffle h := sha256.New() h.Write(entropy) h.Write([]byte("permutation")) seed := h.Sum(nil) - // Fisher-Yates shuffle with deterministic randomness for i := length - 1; i > 0; i-- { h.Reset() h.Write(seed) @@ -175,7 +147,6 @@ func (obfuscator *ShuffleMaskObfuscator) generatePermutation(entropy []byte, len return perm } -// deriveMask creates a mask byte array from entropy. func (obfuscator *ShuffleMaskObfuscator) deriveMask(entropy []byte, length int) []byte { mask := make([]byte, length) h := sha256.New() @@ -199,12 +170,11 @@ func (obfuscator *ShuffleMaskObfuscator) deriveMask(entropy []byte, length int) return mask } -// Example: cipherSigil, _ := sigil.NewChaChaPolySigil(key) // Example: cipherSigil, _ := sigil.NewChaChaPolySigilWithObfuscator(key, &sigil.ShuffleMaskObfuscator{}) type ChaChaPolySigil struct { Key []byte Obfuscator PreObfuscator - randomReader goio.Reader // for testing injection + randomReader goio.Reader } // Example: cipherSigil, _ := sigil.NewChaChaPolySigil([]byte("0123456789abcdef0123456789abcdef")) @@ -244,7 +214,6 @@ func NewChaChaPolySigilWithObfuscator(key []byte, obfuscator PreObfuscator) (*Ch return cipherSigil, nil } -// In encrypts plaintext with the configured pre-obfuscator. func (sigil *ChaChaPolySigil) In(data []byte) ([]byte, error) { if sigil.Key == nil { return nil, NoKeyConfiguredError @@ -258,7 +227,6 @@ func (sigil *ChaChaPolySigil) In(data []byte) ([]byte, error) { return nil, core.E("sigil.ChaChaPolySigil.In", "create cipher", err) } - // Generate nonce nonce := make([]byte, aead.NonceSize()) reader := sigil.randomReader if reader == nil { @@ -268,21 +236,16 @@ func (sigil *ChaChaPolySigil) In(data []byte) ([]byte, error) { return nil, core.E("sigil.ChaChaPolySigil.In", "read nonce", err) } - // Pre-obfuscate the plaintext using nonce as entropy - // This ensures CPU encryption routines never see raw plaintext obfuscated := data if sigil.Obfuscator != nil { obfuscated = sigil.Obfuscator.Obfuscate(data, nonce) } - // Encrypt the obfuscated data - // Output: [nonce | ciphertext | auth tag] ciphertext := aead.Seal(nonce, nonce, obfuscated, nil) return ciphertext, nil } -// Out decrypts ciphertext and reverses the pre-obfuscation step. func (sigil *ChaChaPolySigil) Out(data []byte) ([]byte, error) { if sigil.Key == nil { return nil, NoKeyConfiguredError @@ -301,17 +264,14 @@ func (sigil *ChaChaPolySigil) Out(data []byte) ([]byte, error) { return nil, CiphertextTooShortError } - // Extract nonce from ciphertext nonce := data[:aead.NonceSize()] ciphertext := data[aead.NonceSize():] - // Decrypt obfuscated, err := aead.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, core.E("sigil.ChaChaPolySigil.Out", "decrypt ciphertext", DecryptionFailedError) } - // Deobfuscate using the same nonce as entropy plaintext := obfuscated if sigil.Obfuscator != nil { plaintext = sigil.Obfuscator.Deobfuscate(obfuscated, nonce) diff --git a/sigil/sigil.go b/sigil/sigil.go index 77df934..c75463c 100644 --- a/sigil/sigil.go +++ b/sigil/sigil.go @@ -1,14 +1,11 @@ -// Package sigil chains reversible byte transformations. -// -// hexSigil, _ := sigil.NewSigil("hex") -// gzipSigil, _ := sigil.NewSigil("gzip") -// encoded, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil}) -// decoded, _ := sigil.Untransmute(encoded, []sigil.Sigil{hexSigil, gzipSigil}) +// hexSigil, _ := sigil.NewSigil("hex") +// gzipSigil, _ := sigil.NewSigil("gzip") +// encoded, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil}) +// decoded, _ := sigil.Untransmute(encoded, []sigil.Sigil{hexSigil, gzipSigil}) package sigil import core "dappco.re/go/core" -// Sigil transforms byte slices. type Sigil interface { // Example: encoded, _ := hexSigil.In([]byte("payload")) In(data []byte) ([]byte, error) diff --git a/sigil/sigils.go b/sigil/sigils.go index 36d82df..41e15c5 100644 --- a/sigil/sigils.go +++ b/sigil/sigils.go @@ -20,11 +20,8 @@ import ( "golang.org/x/crypto/sha3" ) -// ReverseSigil is a Sigil that reverses the bytes of the payload. -// It is a symmetrical Sigil, meaning that the In and Out methods perform the same operation. type ReverseSigil struct{} -// In reverses the bytes of the data. func (sigil *ReverseSigil) In(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -36,16 +33,12 @@ func (sigil *ReverseSigil) In(data []byte) ([]byte, error) { return reversed, nil } -// Out reverses the bytes of the data. func (sigil *ReverseSigil) Out(data []byte) ([]byte, error) { return sigil.In(data) } -// HexSigil is a Sigil that encodes/decodes data to/from hexadecimal. -// The In method encodes the data, and the Out method decodes it. type HexSigil struct{} -// In encodes the data to hexadecimal. func (sigil *HexSigil) In(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -55,7 +48,6 @@ func (sigil *HexSigil) In(data []byte) ([]byte, error) { return dst, nil } -// Out decodes the data from hexadecimal. func (sigil *HexSigil) Out(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -65,11 +57,8 @@ func (sigil *HexSigil) Out(data []byte) ([]byte, error) { return dst, err } -// Base64Sigil is a Sigil that encodes/decodes data to/from base64. -// The In method encodes the data, and the Out method decodes it. type Base64Sigil struct{} -// In encodes the data to base64. func (sigil *Base64Sigil) In(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -79,7 +68,6 @@ func (sigil *Base64Sigil) In(data []byte) ([]byte, error) { return dst, nil } -// Out decodes the data from base64. func (sigil *Base64Sigil) Out(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -89,13 +77,10 @@ func (sigil *Base64Sigil) Out(data []byte) ([]byte, error) { return dst[:n], err } -// GzipSigil is a Sigil that compresses/decompresses data using gzip. -// The In method compresses the data, and the Out method decompresses it. type GzipSigil struct { outputWriter goio.Writer } -// In compresses the data using gzip. func (sigil *GzipSigil) In(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -115,7 +100,6 @@ func (sigil *GzipSigil) In(data []byte) ([]byte, error) { return b.Bytes(), nil } -// Out decompresses the data using gzip. func (sigil *GzipSigil) Out(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -132,11 +116,8 @@ func (sigil *GzipSigil) Out(data []byte) ([]byte, error) { return out, nil } -// JSONSigil is a Sigil that compacts or indents JSON data. -// The Out method is a no-op. type JSONSigil struct{ Indent bool } -// In compacts or indents the JSON data. func (sigil *JSONSigil) In(data []byte) ([]byte, error) { if data == nil { return nil, nil @@ -158,27 +139,20 @@ func (sigil *JSONSigil) In(data []byte) ([]byte, error) { return []byte(compact), nil } -// Out is a no-op for JSONSigil. func (sigil *JSONSigil) Out(data []byte) ([]byte, error) { - // For simplicity, Out is a no-op. The primary use is formatting. return data, nil } -// HashSigil is a Sigil that hashes the data using a specified algorithm. -// The In method hashes the data, and the Out method is a no-op. type HashSigil struct { Hash crypto.Hash } -// Use NewHashSigil to hash payloads with a specific crypto.Hash. -// -// hashSigil := sigil.NewHashSigil(crypto.SHA256) -// digest, _ := hashSigil.In([]byte("payload")) +// hashSigil := sigil.NewHashSigil(crypto.SHA256) +// digest, _ := hashSigil.In([]byte("payload")) func NewHashSigil(h crypto.Hash) *HashSigil { return &HashSigil{Hash: h} } -// In hashes the data. func (sigil *HashSigil) In(data []byte) ([]byte, error) { var hasher goio.Writer switch sigil.Hash { @@ -219,7 +193,6 @@ func (sigil *HashSigil) In(data []byte) ([]byte, error) { case crypto.BLAKE2b_512: hasher, _ = blake2b.New512(nil) default: - // MD5SHA1 is not supported as a direct hash return nil, core.E("sigil.HashSigil.In", "hash algorithm not available", nil) } @@ -227,16 +200,13 @@ func (sigil *HashSigil) In(data []byte) ([]byte, error) { return hasher.(interface{ Sum([]byte) []byte }).Sum(nil), nil } -// Out is a no-op for HashSigil. func (sigil *HashSigil) Out(data []byte) ([]byte, error) { return data, nil } -// Use NewSigil("hex") or NewSigil("gzip") to construct a sigil by name. -// -// hexSigil, _ := sigil.NewSigil("hex") -// gzipSigil, _ := sigil.NewSigil("gzip") -// transformed, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil}) +// hexSigil, _ := sigil.NewSigil("hex") +// gzipSigil, _ := sigil.NewSigil("gzip") +// transformed, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil}) func NewSigil(name string) (Sigil, error) { switch name { case "reverse": diff --git a/sqlite/sqlite.go b/sqlite/sqlite.go index 081e11c..0552d94 100644 --- a/sqlite/sqlite.go +++ b/sqlite/sqlite.go @@ -13,7 +13,7 @@ import ( core "dappco.re/go/core" coreio "dappco.re/go/core/io" - _ "modernc.org/sqlite" // Pure Go SQLite driver + _ "modernc.org/sqlite" ) // Example: medium, _ := sqlite.New(sqlite.Options{Path: ":memory:"}) @@ -26,9 +26,7 @@ type Medium struct { var _ coreio.Medium = (*Medium)(nil) type Options struct { - // Path is the SQLite database path. Use ":memory:" for tests. - Path string - // Table is the table name used for file storage. Empty defaults to "files". + Path string Table string } @@ -564,8 +562,6 @@ func (medium *Medium) IsDir(filePath string) bool { return isDir } -// --- Internal types --- - type fileInfo struct { name string size int64 diff --git a/store/doc.go b/store/doc.go index 5101af0..2851aea 100644 --- a/store/doc.go +++ b/store/doc.go @@ -1,7 +1,5 @@ -// Package store maps grouped keys onto SQLite rows. -// -// keyValueStore, _ := store.New(store.Options{Path: ":memory:"}) -// _ = keyValueStore.Set("app", "theme", "midnight") -// medium := keyValueStore.AsMedium() -// _ = medium.Write("app/theme", "midnight") +// keyValueStore, _ := store.New(store.Options{Path: ":memory:"}) +// _ = keyValueStore.Set("app", "theme", "midnight") +// medium := keyValueStore.AsMedium() +// _ = medium.Write("app/theme", "midnight") package store diff --git a/store/medium.go b/store/medium.go index b33c937..20a32c4 100644 --- a/store/medium.go +++ b/store/medium.go @@ -172,7 +172,7 @@ func (medium *Medium) List(entryPath string) ([]fs.DirEntry, error) { } if key != "" { - return nil, nil // leaf node, nothing beneath + return nil, nil } all, err := medium.store.GetAll(group) @@ -276,8 +276,6 @@ func (medium *Medium) IsDir(entryPath string) bool { return err == nil && entryCount > 0 } -// --- fs helper types --- - type keyValueFileInfo struct { name string size int64 diff --git a/store/store.go b/store/store.go index f82216c..6afebc5 100644 --- a/store/store.go +++ b/store/store.go @@ -11,7 +11,6 @@ import ( ) // Example: _, err := keyValueStore.Get("app", "theme") -// err matches store.NotFoundError when the key is missing. var NotFoundError = errors.New("key not found") // Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"}) @@ -20,7 +19,6 @@ type Store struct { } type Options struct { - // Path is the SQLite database path. Use ":memory:" for tests. Path string } diff --git a/workspace/doc.go b/workspace/doc.go index b7e301b..f949c37 100644 --- a/workspace/doc.go +++ b/workspace/doc.go @@ -1,5 +1,3 @@ -// Package workspace creates encrypted workspaces on top of io.Medium. -// // Example: service, _ := workspace.New(workspace.Options{CryptProvider: cryptProvider}) // workspaceID, _ := service.CreateWorkspace("alice", "pass123") // _ = service.SwitchWorkspace(workspaceID)