encreplica

package
v1.5.51 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package encreplica is a replicate.ReplicaClient that PQ-encrypts every LTX segment with a per-base age key BEFORE it touches durable storage, and decrypts on read — the SOLE at-rest boundary for the replica stream, using the SAME key as the whole-file path (store.OrgKey). This is what makes hanzoai/replicate safe for per-org data: without it the replica stream is plaintext and every base's SQLite pages hit the backend unencrypted.

Why a client and not replicate's built-in age

replicate v0.8.0's Replica.AgeRecipients encrypts the LTX BEFORE the file/s3 client calls ltx.PeekHeader for a timestamp, so those clients reject the ciphertext. Encrypting INSIDE the client — after the plaintext header is read — is both the working path and the correct single-boundary design.

On-storage framing (self-describing, backend-agnostic)

[8 bytes BE plaintext length][8 bytes BE unix-milli timestamp][age ciphertext]

The plaintext length and timestamp are read from the plaintext LTX header at write time and stored in the clear PREFIX so LTXFiles can report an accurate FileInfo (size + CreatedAt) — which replicate's restore requires (it checks Size >= ltx.HeaderSize and drives a resumable reader off Size) — WITHOUT leaking any base data (the LTX body is entirely inside the age ciphertext).

The backend is any path-addressed Blobs store: local-fs for dev/test, S3 or hanzoai/vfs in production (durability/scale, Pillar 3). Encryption is in this client, identical for every backend.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Blobs

type Blobs interface {
	Put(ctx context.Context, key string, data []byte) error
	Get(ctx context.Context, key string) ([]byte, error)
	List(ctx context.Context, prefix string) ([]string, error)
	Delete(ctx context.Context, key string) error
	DeleteAll(ctx context.Context) error
}

Blobs is the minimal path-addressed byte store the client persists to. Keys use "/" separators. Get returns an error satisfying errors.Is(err, os.ErrNotExist) when the key is absent. Implementations are goroutine-safe.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the per-base age-encrypting replica client.

func New

func New(blobs Blobs, recipient age.Recipient, identity age.Identity) (*Client, error)

New builds an encrypting client over blobs for one base. recipient encrypts, identity decrypts — both are the base's key (store.OrgKey).

func (*Client) DeleteAll

func (c *Client) DeleteAll(ctx context.Context) error

DeleteAll removes every LTX file for this base.

func (*Client) DeleteLTXFiles

func (c *Client) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error

DeleteLTXFiles removes the named LTX files.

func (*Client) Init

func (c *Client) Init(ctx context.Context) error

Init is a no-op — the backend is created lazily on first Put.

func (*Client) LTXFiles

func (c *Client) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error)

LTXFiles enumerates stored LTX files at a level, reading the cleartext prefix (size + timestamp) so restore gets accurate FileInfo without decrypting.

func (*Client) OpenLTXFile

func (c *Client) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error)

OpenLTXFile returns a reader over the DECRYPTED LTX plaintext, honoring the requested offset/size (into the plaintext).

func (*Client) SetLogger

func (c *Client) SetLogger(l *slog.Logger)

SetLogger sets the logger.

func (*Client) Type

func (c *Client) Type() string

Type identifies this client.

func (*Client) WriteLTXFile

func (c *Client) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (*ltx.FileInfo, error)

WriteLTXFile reads the plaintext LTX, records size+timestamp from its header, age-encrypts the body, and stores [prefix][ciphertext].

type LocalBlobs

type LocalBlobs struct {
	// contains filtered or unexported fields
}

LocalBlobs is a directory-backed Blobs for dev/test. Production swaps in an S3- or hanzoai/vfs-backed Blobs; the age encryption boundary lives in Client and is identical for every backend.

func NewLocalBlobs

func NewLocalBlobs(dir string) *LocalBlobs

NewLocalBlobs returns a directory-backed Blobs rooted at dir.

func (*LocalBlobs) Delete

func (b *LocalBlobs) Delete(_ context.Context, key string) error

Delete removes key; a missing key is not an error.

func (*LocalBlobs) DeleteAll

func (b *LocalBlobs) DeleteAll(_ context.Context) error

DeleteAll removes every blob for this base.

func (*LocalBlobs) Get

func (b *LocalBlobs) Get(_ context.Context, key string) ([]byte, error)

Get returns key's bytes; a missing key yields an error satisfying errors.Is(err, os.ErrNotExist).

func (*LocalBlobs) List

func (b *LocalBlobs) List(_ context.Context, prefix string) ([]string, error)

List returns the full keys of the (non-directory) entries directly under prefix. A missing prefix yields an empty slice (not an error).

func (*LocalBlobs) Put

func (b *LocalBlobs) Put(_ context.Context, key string, data []byte) error

Put writes key atomically (temp + rename).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL