go-io/docs/index.md
Snider a43a16fb0d fix: address CodeRabbit PR #2 findings
- datanode: add isFileLocked() helper to prevent RLock re-entry deadlock in Append
- io: MemoryMedium WriteMode rejects ancestor-is-file collision; EnsureDir rejects target-is-file collision
- io: copy fileModes during directory rename
- local: guard Delete/DeleteAll against removing sandbox root
- local: add TOCTOU TODO comment on validatePath symlink loop
- local: alias stdlib io→goio in medium_test.go
- datanode: alias stdlib io→goio in medium_test.go
- sqlite: add isValidTableName() whitelist to prevent table-name SQL injection in New()
- sqlite: remove duplicate WHERE clause args in List query
- sqlite: add mode field to sqliteWriteCloser; use it in Close (was hardcoded 420)
- sigil: GzipSigil.In returns nil when custom outputWriter is used (buffer was empty)
- sigil: capture hasher.Write error in HashSigil.In
- sigil: add comment explaining DecryptionFailedError hides raw AEAD error intentionally
- s3: add comment explaining WriteMode ignores mode (no POSIX on S3)
- s3_test: ListObjectsV2 mock sets IsTruncated+NextContinuationToken when maxKeys exceeded
- node: add comment explaining WriteMode ignores mode for in-memory nodes
- store: sort keys before building List entries for deterministic output
- store: add explanatory comment on NotFoundError sentinel
- workspace: replace sha256.Sum256 key derivation with HKDF (RFC 5869)
- docs: fix RFC-CORE-008 header (was RFC-025)
- docs: update import paths from forge.lthn.ai/core/go-io to dappco.re/go/core/io
- docs/RFC.md: remove duplicate Read/Write alias doc blocks

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-05 12:22:25 +01:00

4.1 KiB

title description
go-io Unified storage abstraction for Go with pluggable backends — local filesystem, S3, SQLite, in-memory, and key-value.

go-io

dappco.re/go/core/io is a storage abstraction library that provides a single Medium interface for reading and writing files across different backends. Write your code against Medium once, then swap between local disk, S3, SQLite, or in-memory storage without changing a line of business logic.

The library also includes sigil, a composable data-transformation pipeline for encoding, compression, hashing, and authenticated encryption.

Quick Start

import (
    io "dappco.re/go/core/io"
    "dappco.re/go/core/io/s3"
    "dappco.re/go/core/io/node"
)

content, _ := io.Local.Read("/etc/hostname")

sandboxMedium, _ := io.NewSandboxed("/var/data/myapp")
_ = sandboxMedium.Write("config.yaml", "key: value")

nodeTree := node.New()
nodeTree.AddData("hello.txt", []byte("world"))
tarball, _ := nodeTree.ToTar()

s3Medium, _ := s3.New(s3.Options{Bucket: "my-bucket", Client: awsClient, Prefix: "uploads/"})
_ = s3Medium.Write("photo.jpg", rawData)

Package Layout

Package Import Path Purpose
io (root) dappco.re/go/core/io Medium interface, helper functions, MemoryMedium for tests
local dappco.re/go/core/io/local Local filesystem backend with path sandboxing and symlink-escape protection
s3 dappco.re/go/core/io/s3 Amazon S3 / S3-compatible backend (Garage, MinIO, etc.)
sqlite dappco.re/go/core/io/sqlite SQLite-backed virtual filesystem (pure Go driver, no CGO)
node dappco.re/go/core/io/node In-memory filesystem implementing both Medium and fs.FS, with tar round-tripping
datanode dappco.re/go/core/io/datanode Thread-safe in-memory Medium backed by Borg's DataNode, with snapshot/restore
store dappco.re/go/core/io/store Group-namespaced key-value store (SQLite), with a Medium adapter and Go template rendering
sigil dappco.re/go/core/io/sigil Composable data transformations: encoding, compression, hashing, XChaCha20-Poly1305 encryption
workspace dappco.re/go/core/io/workspace Encrypted workspace service integrated with the Core DI container

The Medium Interface

Every storage backend implements the same 17-method interface:

type Medium interface {
    Read(path string) (string, error)
    Write(path, content string) error
    WriteMode(path, content string, mode fs.FileMode) error

    ReadStream(path string) (io.ReadCloser, error)
    WriteStream(path string) (io.WriteCloser, error)
    Open(path string) (fs.File, error)
    Create(path string) (io.WriteCloser, error)
    Append(path string) (io.WriteCloser, error)

    EnsureDir(path string) error
    List(path string) ([]fs.DirEntry, error)

    Stat(path string) (fs.FileInfo, error)
    Exists(path string) bool
    IsFile(path string) bool
    IsDir(path string) bool

    Delete(path string) error
    DeleteAll(path string) error
    Rename(oldPath, newPath string) error
}

All backends implement this interface fully. Backends where a method has no natural equivalent (e.g., EnsureDir on S3) provide a safe no-op.

Cross-Medium Operations

The root package provides helper functions that accept any Medium:

sourceMedium := io.Local
destinationMedium := io.NewMemoryMedium()
err := io.Copy(sourceMedium, "source.txt", destinationMedium, "dest.txt")

content, err := io.Read(destinationMedium, "path")
err = io.Write(destinationMedium, "path", "content")

Dependencies

Dependency Role
forge.lthn.ai/core/go-log Structured error helper (E())
forge.lthn.ai/Snider/Borg DataNode in-memory FS (used by datanode package)
github.com/aws/aws-sdk-go-v2 S3 client (used by s3 package)
golang.org/x/crypto BLAKE2, SHA-3, RIPEMD-160, XChaCha20-Poly1305 (used by sigil)
modernc.org/sqlite Pure Go SQLite driver (used by sqlite and store)
github.com/stretchr/testify Test assertions

Go version: 1.26.0

Licence: EUPL-1.2