Skip to content

Filesystem backend recipe

cas/backend/fs is the durable, default backend: each object is one file under <base>/<fan-out dirs>/<full-hex-digest>, written atomically.

When to use it

  • a local, persistent object store for a single host
  • a developer tool or CLI that needs durable, inspectable artifacts
  • the simplest backend to reason about before writing a custom one

Example

package main

import (
    "context"
    "fmt"

    "github.com/dmundt/go-cask/cas"
    fsbackend "github.com/dmundt/go-cask/cas/backend/fs"
    jsoncodec "github.com/dmundt/go-cask/cas/codec/json"
    sha256 "github.com/dmundt/go-cask/cas/hash/sha256"
)

type Note struct {
    Text string `json:"text"`
}

func (n *Note) Type() string             { return "note@1" }
func (n *Note) References() []cas.Digest { return nil }

func main() {
    ctx := context.Background()

    backend, err := fsbackend.New("./repo")
    if err != nil {
        panic(err)
    }

    store := cas.New(backend, jsoncodec.New[*Note](), sha256.New())

    ref, err := store.Put(ctx, &Note{Text: "hello"})
    if err != nil {
        panic(err)
    }
    fmt.Println(ref)
}

Layout and options

The default layout is the Git-like FanOut=2, FanLevels=1 (aa/<full hex>). Both are configurable:

package layout

import fsbackend "github.com/dmundt/go-cask/cas/backend/fs"

func newBackend() (*fsbackend.Backend, error) {
    return fsbackend.New("./repo",
        fsbackend.WithFanOut(2),
        fsbackend.WithFanLevels(1),
    )
}

Good fit

  • local content-addressable stores
  • developer tooling and CLIs
  • object graphs and immutable content handling
  • examples that need a persistent, inspectable layout

Constraints

  • the backend is a raw storage layer only — it does not own application semantics, codecs, or hashing
  • one filesystem backend base directory belongs to exactly one store; do not nest one store's base inside another's
  • the backend never verifies the digest of what it stores or returns — use cas.Verify for that