remote

package
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ManifestSchemaVersionV1 = "pinax.cloud.manifest.v1"
	ManifestSchemaVersionV2 = "pinax.cloud.manifest.v2"
	ManifestSchemaVersion   = ManifestSchemaVersionV1
)
View Source
const (
	ConfigSchemaVersion  = "pinax.cloud.config.v1"
	SessionSchemaVersion = "pinax.cloud.session.v1"
)
View Source
const (
	SyncConfigSchemaVersion   = "pinax.sync.config.v1"
	SyncSecretsSchemaVersion  = "pinax.sync.secrets.v1"
	SourceMarkerSchemaVersion = "pinax.sync.source-marker.v1"
)

Declaration schema versions. These are the repository-tracked, portable declaration layer — distinct from the device-owned pinax.cloud.config.v1 runtime state that the compiler emits.

View Source
const (
	DeclarationFileName  = "pinax-sync.yaml"
	SecretsAssetFileName = "pinax-sync.secrets.yaml"
	SourceMarkerFileName = "pinax-sync.source.yaml"
)

DeclarationFilePaths are the CLI-authored structured assets inside the vault. Business code and agents MUST NOT assemble these by hand.

View Source
const CreateIfAbsentRevision = "__pinax_create_if_absent__"
View Source
const CryptoEnvelopeSchemaVersion = "pinax.cloud.envelope.v1"
View Source
const EnvAssetFileName = "pinax-sync.env.age"

EnvAssetFileName is the fixed, repository-tracked ciphertext dotenv asset. The path is fixed (not user-selectable) to prevent path-escape and protected- path bypass. Plaintext runtime files live under a separate managed directory.

View Source
const EnvAssetSchemaVersion = "pinax.sync.env.v1"

EnvAssetSchemaVersion is the frozen contract version for the encrypted dotenv asset. The asset is repository-tracked; its plaintext is never required to be committed and lives only in the in-memory EnvSnapshot at runtime.

View Source
const EnvRuntimeDir = "runtime"

EnvRuntimeDir is the managed, 0600, Git-ignored directory for materialized plaintext env files. Only --materialize writes here; default runs stay in memory.

View Source
const EnvRuntimeFileName = "pinax-sync.env"

EnvRuntimeFileName is the single managed materialized plaintext filename.

View Source
const MaxManifestFileBytes = 100 * 1024 * 1024

Variables

View Source
var (
	ErrObjectNotFound = errors.New("object not found")
	ErrConflict       = errors.New("revision conflict")
)
View Source
var ErrEnvAssetMissing = errors.New("sync env asset not found")

ErrEnvAssetMissing is returned when no pinax-sync.env.age exists.

View Source
var ErrNotConfigured = errors.New("cloud not configured")
View Source
var ErrSecretsAssetMissing = errors.New("sync secrets asset not found")

ErrSecretsAssetMissing is returned when no pinax-sync.secrets.yaml exists.

View Source
var ErrSyncDeclarationMissing = errors.New("sync repository declaration not found")

ErrSyncDeclarationMissing is returned when no pinax-sync.yaml exists.

View Source
var SupportedSyncBackendKinds = map[string]bool{
	"s3-direct":     true,
	"rclone-direct": true,
	"server":        true,
	"embedded":      true,
}

SupportedSyncBackendKinds is the closed set of backend kinds the declaration layer accepts. Unknown kinds are rejected so an unsupported backend cannot silently compile to a half-usable runtime config.

View Source
var ValidRemoteDeletePolicies = map[string]bool{
	"":                 true,
	"deny":             true,
	"require-approval": true,
}

ValidRemoteDeletePolicies enumerates the conservative policy values.

Functions

func ApplyCompiled added in v0.1.6

func ApplyCompiled(root string, result CompileResult, preserveCreatedAt string) error

ApplyCompiled writes the runtime config and source marker atomically through the canonical authoring boundary, preserving the existing CreatedAt when regenerating the same workspace. It backs up the prior config before writing so apply is restorable.

func BlobID

func BlobID(content []byte) string

func CleanMaterializedEnv added in v0.1.6

func CleanMaterializedEnv(root string) (bool, error)

CleanMaterializedEnv removes only the Pinax-managed materialized env file. It never removes arbitrary user-selected files and refuses symlink targets.

func DeclarationPath added in v0.1.6

func DeclarationPath(root string) string

DeclarationPath returns the path to the repository sync declaration.

func DecryptBlob

func DecryptBlob(key CryptoKey, envelope EncryptedEnvelope, aad []byte) ([]byte, error)

func EncryptionSecretRef

func EncryptionSecretRef(config Config) string

func EnvAssetPath added in v0.1.6

func EnvAssetPath(root string) string

EnvAssetPath returns the fixed path to the encrypted dotenv asset.

func EnvDocumentDigest added in v0.1.6

func EnvDocumentDigest(data []byte) string

EnvDocumentDigest returns a short stable hex digest of a plaintext dotenv document, used for daemon reload identity checks. It is computed over the raw bytes so it does not reveal structure beyond the digest.

func EnvRuntimePath added in v0.1.6

func EnvRuntimePath(root string) string

EnvRuntimePath returns the fixed path to the materialized plaintext env file.

func FormatDotenv added in v0.1.6

func FormatDotenv(values map[string]string) []byte

FormatDotenv renders values back into a canonical strict dotenv document with stable key ordering. Values are single-quoted when they contain special chars so the round-trip survives ParseDotenv without ambiguity.

func HexDigest added in v0.1.6

func HexDigest(data []byte) string

HexDigest returns a short stable hex digest of arbitrary bytes, used for the source marker's declaration_digest.

func IsNotConfigured

func IsNotConfigured(err error) bool

func IsSupportedScheme

func IsSupportedScheme(scheme string) bool

IsSupportedScheme returns true if the scheme has a registered factory.

func IsSyncConfigError added in v0.1.6

func IsSyncConfigError(err error, code string) bool

IsSyncConfigError reports whether err is a *SyncConfigError with the given code.

func KeyID added in v0.1.6

func KeyID(secretRef string) string

KeyID resolves the secret reference and returns the stable key identifier for secretRef, or an empty string when the reference cannot be resolved.

func Logout

func Logout(root string) error

func MarshalForDigest added in v0.1.6

func MarshalForDigest(c SyncConfig) []byte

MarshalForDigest serializes a SyncConfig canonically so equal configs produce equal digests regardless of map ordering or whitespace.

func MaterializeEnv added in v0.1.6

func MaterializeEnv(root string, snapshot *EnvSnapshot, allowlist []string) (string, error)

MaterializeEnv writes the snapshot's plaintext to the managed runtime path with 0600 permissions. It is the compatibility exit for external tools that cannot consume in-memory injection. Callers MUST pass an allowlist so only declared keys are written; the full snapshot is never dumped.

func ParseDotenv added in v0.1.6

func ParseDotenv(data []byte) (map[string]string, error)

ParseDotenv parses a strict, safe dotenv subset. It accepts:

KEY=value
KEY="quoted value"
KEY='quoted value'

and rejects shell execution, include directives, recursive ${...} expansion, command substitution $() and backticks, NUL / control characters, empty keys, duplicate keys and multi-line heredocs. Errors report line number and key name only — never the rejected value.

This is intentionally a strict subset of POSIX dotenv so Pinax never evaluates attacker-controlled shell syntax stored in the repository.

func PathHash

func PathHash(path string) string

func RedactedData

func RedactedData(state State) map[string]any

func Register

func Register(scheme string, factory StoreFactory)

Register registers a new BlobStore factory for a URI scheme.

func SaveEnvAsset added in v0.1.6

func SaveEnvAsset(root string, asset EnvAsset) error

SaveEnvAsset persists the encrypted dotenv asset with restrictive permissions. It never writes plaintext; ciphertext + redacted metadata only.

func SaveSecretEnvelope added in v0.1.6

func SaveSecretEnvelope(root string, env SecretEnvelope) error

SaveSecretEnvelope persists the envelope with restrictive permissions. It never writes plaintext; entries carry only ciphertext.

func SecretsAssetPath added in v0.1.6

func SecretsAssetPath(root string) string

SecretsAssetPath returns the path to the encrypted secrets asset.

func SourceMarkerPath added in v0.1.6

func SourceMarkerPath(root string) string

SourceMarkerPath returns the device-local source marker path (under cloud/ so it is treated as device runtime state, never shared).

func SupportedSyncBackendKind added in v0.1.6

func SupportedSyncBackendKind(kind string) bool

SupportedSyncBackendKind reports whether kind is a supported declaration backend.

func SupportedSyncBackendKings added in v0.1.6

func SupportedSyncBackendKings(kind string) bool

func WriteConfig added in v0.1.6

func WriteConfig(root string, config Config) error

WriteConfig persists the Capsa runtime config through the application-layer authoring boundary: atomic directory creation, restrictive permissions, and removal of the legacy JSON path. The declaration→runtime compiler reuses this writer so generated config never bypasses the canonical write path.

Types

type BlobStore

type BlobStore interface {
	// Get retrieves the object. If not found, returns ErrObjectNotFound.
	Get(ctx context.Context, key string) (data []byte, rev string, err error)

	// Put uploads the object. baseRev is the expected current revision.
	// If baseRev is CreateIfAbsentRevision, the object must not exist.
	// If baseRev is not empty and doesn't match, returns ErrConflict.
	// Returns the new revision string.
	Put(ctx context.Context, key string, data []byte, baseRev string) (newRev string, err error)

	// Stat retrieves the revision of the object. If not found, returns ErrObjectNotFound.
	Stat(ctx context.Context, key string) (rev string, err error)

	// Delete removes the object.
	Delete(ctx context.Context, key string) error
}

BlobStore abstracts the underlying blind storage system (S3, File, etc.).

func NewStore

func NewStore(ctx context.Context, endpoint string) (BlobStore, error)

NewStore instantiates a BlobStore based on the endpoint URI scheme.

type CachedBlobStore

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

CachedBlobStore decorates a BlobStore with local file caching for Get operations.

func NewCachedBlobStore

func NewCachedBlobStore(inner BlobStore, cacheDir string, maxSize int64) *CachedBlobStore

NewCachedBlobStore creates a new caching decorator.

func (*CachedBlobStore) Delete

func (c *CachedBlobStore) Delete(ctx context.Context, key string) error

Delete delegates to inner store and invalidates cache.

func (*CachedBlobStore) Get

func (c *CachedBlobStore) Get(ctx context.Context, key string) ([]byte, string, error)

Get retrieves the object, using cache when available.

func (*CachedBlobStore) Put

func (c *CachedBlobStore) Put(ctx context.Context, key string, data []byte, baseRev string) (string, error)

Put delegates to inner store and invalidates cache.

func (*CachedBlobStore) Stat

func (c *CachedBlobStore) Stat(ctx context.Context, key string) (string, error)

Stat delegates to inner store.

type CompileRequest added in v0.1.6

type CompileRequest struct {
	Declaration     SyncConfig
	ResolvedSecret  string // device-local secret ref resolved from credential identity
	ResolvedEncrypt string // device-local encryption secret ref resolved from key identity
	DeviceID        string
	Now             time.Time
}

CompileRequest bundles the inputs the declaration→runtime compiler needs: the portable declaration, the device-local resolved secret references, the unique device id and a clock for receipts/marker timestamps.

type CompileResult added in v0.1.6

type CompileResult struct {
	RuntimeConfig     Config
	SourceMarker      SourceMarker
	DeclarationDigest string
}

CompileResult reports what the compiler would write, without touching disk. Apply uses Apply(); plan/doctor read Result fields directly.

func Compile added in v0.1.6

func Compile(req CompileRequest) (CompileResult, error)

Compile produces the device runtime Config and source marker from a declaration and resolved secrets. It does NOT write to disk; callers use ApplyCompiled or inspect the result for plan output. The compiler reuses the existing Capsa config normalization so generated state is identical to a manually-logged-in device.

type ConditionalWriteCapability

type ConditionalWriteCapability interface {
	SupportsConditionalWrites() bool
}

ConditionalWriteCapability reports whether Put enforces baseRev preconditions durably.

type Config

type Config struct {
	SchemaVersion       string    `json:"schema_version" yaml:"schema_version"`
	BackendKind         string    `json:"backend_kind,omitempty" yaml:"backend_kind,omitempty"`
	Endpoint            string    `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
	WorkspaceID         string    `json:"workspace_id" yaml:"workspace_id"`
	DeviceID            string    `json:"device_id" yaml:"device_id"`
	SecretRef           string    `json:"secret_ref,omitempty" yaml:"secret_ref,omitempty"`
	EncryptionSecretRef string    `json:"encryption_secret_ref,omitempty" yaml:"encryption_secret_ref,omitempty"`
	S3                  *S3Config `json:"s3,omitempty" yaml:"s3,omitempty"`
	CreatedAt           string    `json:"created_at" yaml:"created_at"`
	UpdatedAt           string    `json:"updated_at" yaml:"updated_at"`
}

type CryptoKey

type CryptoKey = capsa.CryptoKey

func DeriveKey

func DeriveKey(secretRef string) (CryptoKey, error)

type DeviceSession

type DeviceSession struct {
	SchemaVersion string `json:"schema_version"`
	SessionID     string `json:"session_id"`
	DeviceID      string `json:"device_id"`
	Status        string `json:"status"`
	IssuedAt      string `json:"issued_at"`
	UpdatedAt     string `json:"updated_at"`
}

type DoctorResult

type DoctorResult struct {
	Configured   bool   `json:"configured"`
	Status       string `json:"status"`
	Code         string `json:"code,omitempty"`
	Message      string `json:"message"`
	BackendKind  string `json:"backend_kind,omitempty"`
	AuthBoundary string `json:"auth_boundary,omitempty"`
	ServerAudit  bool   `json:"server_audit"`
	Endpoint     string `json:"endpoint,omitempty"`
	Workspace    string `json:"workspace_id,omitempty"`
	DeviceID     string `json:"device_id,omitempty"`
}

func Doctor

func Doctor(root string) DoctorResult

type DotenvError added in v0.1.6

type DotenvError struct {
	Code    string
	Line    int
	Key     string
	Message string
}

DotenvError reports a strict dotenv parse failure with a stable English code and line number. Key is included only when known; the rejected value is never attached so errors stay safe to log and surface in receipts.

func (*DotenvError) Error added in v0.1.6

func (e *DotenvError) Error() string

type DriftItem added in v0.1.6

type DriftItem struct {
	Field  string `json:"field" yaml:"field"`
	Reason string `json:"reason" yaml:"reason"`
}

DriftItem is one redacted divergence between declaration and runtime state.

type DriftReport added in v0.1.6

type DriftReport struct {
	InDrift bool        `json:"in_drift" yaml:"in_drift"`
	Reasons []DriftItem `json:"reasons,omitempty" yaml:"reasons,omitempty"`
}

DriftReport describes the difference between a declaration and the local generated runtime config. Field is a redacted path; Value is never included.

func DetectDrift added in v0.1.6

func DetectDrift(declaration SyncConfig, runtime Config, marker SourceMarker) DriftReport

DetectDrift compares a declaration against the loaded runtime state and the recorded source marker. It returns redacted field names only.

type EncryptedEnvelope

type EncryptedEnvelope = capsa.EncryptedEnvelope

func EncryptBlob

func EncryptBlob(key CryptoKey, plaintext, aad []byte) (EncryptedEnvelope, error)

func EncryptManifest

func EncryptManifest(key CryptoKey, manifest Manifest) (EncryptedEnvelope, error)

type EnvAsset added in v0.1.6

type EnvAsset struct {
	SchemaVersion string `json:"schema_version" yaml:"schema_version"`
	Provider      string `json:"provider" yaml:"provider"`
	// Ciphertext is provider-specific opaque encrypted material for the whole
	// dotenv document (not per-key). Keeping one ciphertext preserves key
	// ordering and avoids leaking key names into the asset metadata.
	Ciphertext string `json:"ciphertext" yaml:"ciphertext"`
	// Digest is a short stable hex digest of the plaintext document, used for
	// daemon reload identity checks. It does NOT reveal plaintext contents.
	Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
	// KeyNames is the redacted list of declared keys (no values) so list/doctor
	// can report what is present without unlocking.
	KeyNames []string `json:"key_names,omitempty" yaml:"key_names,omitempty"`
}

EnvAsset is the repository-tracked encrypted dotenv asset. The schema carries ciphertext and redacted metadata only; plaintext key/value pairs live only in the in-memory EnvSnapshot. The asset is safe to commit, while plaintext env files are Git-ignored and Capsa-protected.

func LoadEnvAsset added in v0.1.6

func LoadEnvAsset(root string) (EnvAsset, error)

LoadEnvAsset reads the encrypted dotenv asset. A missing file is reported via ErrEnvAssetMissing so init/doctor can distinguish absent from corrupt.

type EnvAssetError added in v0.1.6

type EnvAssetError struct {
	Code    string
	Message string
}

EnvAssetError is the stable error type for env asset operations. Code is a stable English identifier; Message never carries plaintext values.

func (*EnvAssetError) Error added in v0.1.6

func (e *EnvAssetError) Error() string

type EnvReloader added in v0.1.6

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

EnvReloader is the daemon-side reload coordinator. It checks the encrypted env asset identity (content digest) between sync runs; on change it unlocks and parses a new snapshot, then atomically swaps it in for the NEXT run. The current run retains its original snapshot. On failure it keeps the last successful snapshot and reports a structured degraded code.

func NewEnvReloader added in v0.1.6

func NewEnvReloader(root string, provider UnlockProvider) *EnvReloader

NewEnvReloader creates a reloader. The initial snapshot is loaded lazily on the first RunSnapshot call so a missing asset does not block daemon startup.

func (*EnvReloader) CurrentDigest added in v0.1.6

func (r *EnvReloader) CurrentDigest() string

CurrentDigest returns the digest of the currently-active snapshot (for doctor).

func (*EnvReloader) DegradedCode added in v0.1.6

func (r *EnvReloader) DegradedCode() string

DegradedCode returns the last reload error code ("" when healthy).

func (*EnvReloader) RunSnapshot added in v0.1.6

func (r *EnvReloader) RunSnapshot() (*EnvSnapshot, ReloadStatus)

type EnvSnapshot added in v0.1.6

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

EnvSnapshot is the immutable, in-memory decrypted dotenv view used at a command or sync-run boundary. It is frozen for the lifetime of one run: the daemon may reload a NEW snapshot between runs but a single run always sees the same values. Plaintext lives only here and is never persisted, logged or passed whole to child processes.

func NewEnvSnapshot added in v0.1.6

func NewEnvSnapshot(values map[string]string, digest, source string) *EnvSnapshot

NewEnvSnapshot wraps an unlocked value map into an immutable snapshot. The caller must not mutate values after hand-off.

func ResolveEnvSnapshot added in v0.1.6

func ResolveEnvSnapshot(root string, provider UnlockProvider) (*EnvSnapshot, error)

ResolveEnvSnapshot unlocks and parses the encrypted dotenv asset into an immutable snapshot. It returns (nil, ErrEnvAssetMissing) when no asset exists so callers can distinguish "not configured" from "failed to unlock".

func (*EnvSnapshot) AllKeys added in v0.1.6

func (s *EnvSnapshot) AllKeys() []string

AllKeys returns the sorted list of declared keys (no values) for redacted reporting in list/doctor output.

func (*EnvSnapshot) ApplyAllowlist added in v0.1.6

func (s *EnvSnapshot) ApplyAllowlist(allowlist []string) map[string]string

ApplyAllowlist returns a NEW environment map suitable for a child process: only the keys in the allowlist are copied from the snapshot. The full decrypted environment is never handed to a subprocess.

func (*EnvSnapshot) Digest added in v0.1.6

func (s *EnvSnapshot) Digest() string

Digest returns the plaintext document digest used for reload identity checks.

func (*EnvSnapshot) IsEmpty added in v0.1.6

func (s *EnvSnapshot) IsEmpty() bool

IsEmpty reports whether the snapshot carries no values.

func (*EnvSnapshot) Lookup added in v0.1.6

func (s *EnvSnapshot) Lookup(key string) (string, bool)

Lookup returns the value for a key and whether it was present. It is the typed-secret read path — callers must NOT dump the whole snapshot into a child process environment.

func (*EnvSnapshot) OverlayEnv added in v0.1.6

func (s *EnvSnapshot) OverlayEnv(base []string, allowlist []string) []string

OverlayEnv returns a child-process environment built from a base environment plus the allowlisted snapshot values, respecting the precedence contract:

explicit process environment > decrypted env snapshot

Explicit flags are applied by callers BEFORE this function (they choose the base). The snapshot only fills keys that are not already set in base and are on the allowlist.

func (*EnvSnapshot) Source added in v0.1.6

func (s *EnvSnapshot) Source() string

Source returns a redacted source description (e.g. "pinax-sync.env.age").

type EnvUnlockProvider added in v0.1.6

type EnvUnlockProvider struct{}

EnvUnlockProvider resolves secret values from environment variables referenced by identity (e.g. identity "personal-sync-key" resolves from PINAX_SYNC_SECRET_PERSONAL_SYNC_KEY). It keeps the no-plaintext-in-repo invariant while supporting ephemeral CI bootstrap without a keychain.

func (EnvUnlockProvider) Lock added in v0.1.6

func (p EnvUnlockProvider) Lock(name, plaintext, identity string, kind SecretEntryKind) (SecretEntry, error)

func (EnvUnlockProvider) Name added in v0.1.6

func (EnvUnlockProvider) Name() string

func (EnvUnlockProvider) Unlock added in v0.1.6

func (p EnvUnlockProvider) Unlock(env SecretEnvelope) (map[string]string, error)

type ExtendedBlobStore

type ExtendedBlobStore interface {
	BlobStore
	List(ctx context.Context, prefix string) ([]ObjectInfo, error)
	Exists(ctx context.Context, key string) (bool, error)
	BatchStat(ctx context.Context, keys []string) (map[string]string, error)
}

ExtendedBlobStore extends BlobStore with list and batch operations.

type FakeBlobCheckRequest

type FakeBlobCheckRequest struct {
	BlobIDs []string `json:"blob_ids"`
}

type FakeBlobCheckResponse

type FakeBlobCheckResponse struct {
	MissingBlobIDs []string `json:"missing_blob_ids"`
}

type FakeContractError

type FakeContractError struct {
	Code      string `json:"code"`
	Message   string `json:"message"`
	Retryable bool   `json:"retryable"`
}

type FakeContractErrorResponse

type FakeContractErrorResponse struct {
	Error FakeContractError `json:"error"`
}

type FakeErrorResponse

type FakeErrorResponse struct {
	Code            string `json:"code"`
	Message         string `json:"message"`
	CurrentRevision string `json:"current_revision,omitempty"`
}

type FakeManifestPutRequest

type FakeManifestPutRequest struct {
	BaseRevision string         `json:"base_revision"`
	Manifest     map[string]any `json:"manifest"`
}

type FakeManifestResponse

type FakeManifestResponse struct {
	Revision string         `json:"revision"`
	Manifest map[string]any `json:"manifest,omitempty"`
}

type FakeRevisionCommitRequest

type FakeRevisionCommitRequest struct {
	BaseRevision   string   `json:"base_revision"`
	RevisionID     string   `json:"revision_id,omitempty"`
	ManifestBlobID string   `json:"manifest_blob_id"`
	BlobIDs        []string `json:"blob_ids,omitempty"`
	DeviceID       string   `json:"device_id,omitempty"`
}

type FakeRevisionCommitResponse

type FakeRevisionCommitResponse struct {
	RevisionID     string `json:"revision_id"`
	ManifestBlobID string `json:"manifest_blob_id"`
}

type FakeRevisionResponse

type FakeRevisionResponse struct {
	RevisionID     string `json:"revision_id"`
	ManifestBlobID string `json:"manifest_blob_id"`
}

type FakeServer

type FakeServer struct {
	URL string
	// contains filtered or unexported fields
}

func NewFakeServer

func NewFakeServer() *FakeServer

func (*FakeServer) Close

func (s *FakeServer) Close()

type FakeUnlockProvider added in v0.1.6

type FakeUnlockProvider struct {
	KeyResolver func() (string, error)
}

FakeUnlockProvider is a deterministic AES-GCM provider keyed by a passphrase resolved from PINAX_SYNC_FAKE_KEY (or a fixed test key). It exists so the declaration layer is testable end-to-end without an age/keychain dependency. Production deployments must register a reviewed provider; doctor flags fake.

func (FakeUnlockProvider) Lock added in v0.1.6

func (p FakeUnlockProvider) Lock(name, plaintext, identity string, kind SecretEntryKind) (SecretEntry, error)

func (FakeUnlockProvider) Name added in v0.1.6

func (p FakeUnlockProvider) Name() string

func (FakeUnlockProvider) Unlock added in v0.1.6

func (p FakeUnlockProvider) Unlock(env SecretEnvelope) (map[string]string, error)

type FileBackend

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

func NewFileBackend

func NewFileBackend(baseDir string) (*FileBackend, error)

func (*FileBackend) BatchStat

func (b *FileBackend) BatchStat(ctx context.Context, keys []string) (map[string]string, error)

BatchStat returns revisions for multiple keys.

func (*FileBackend) Delete

func (b *FileBackend) Delete(ctx context.Context, key string) error

func (*FileBackend) Exists

func (b *FileBackend) Exists(ctx context.Context, key string) (bool, error)

Exists checks if an object exists.

func (*FileBackend) Get

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

func (*FileBackend) List

func (b *FileBackend) List(ctx context.Context, prefix string) ([]ObjectInfo, error)

List returns objects under the given prefix.

func (*FileBackend) Put

func (b *FileBackend) Put(ctx context.Context, key string, data []byte, baseRev string) (string, error)

func (*FileBackend) Stat

func (b *FileBackend) Stat(ctx context.Context, key string) (string, error)

func (*FileBackend) SupportsConditionalWrites

func (b *FileBackend) SupportsConditionalWrites() bool

type LoginRequest

type LoginRequest struct {
	Endpoint            string
	WorkspaceID         string
	DeviceID            string
	SecretRef           string
	EncryptionSecretRef string
	BackendKind         string
	S3                  *S3Config
	Now                 time.Time
}

type Manifest

type Manifest struct {
	SchemaVersion string           `json:"schema_version"`
	GeneratedAt   string           `json:"generated_at"`
	EntryCount    int              `json:"entry_count"`
	Entries       []ManifestEntry  `json:"entries"`
	Deletes       []ManifestDelete `json:"deletes,omitempty"`
}

func BuildManifest

func BuildManifest(root string) (Manifest, error)

func BuildManifestV2 added in v0.1.6

func BuildManifestV2(root, deviceID string, identities map[string]ManifestIdentity) (Manifest, error)

func DecryptManifest

func DecryptManifest(key CryptoKey, envelope EncryptedEnvelope) (Manifest, error)

func (Manifest) ValidateV2 added in v0.1.6

func (manifest Manifest) ValidateV2() error

type ManifestDelete added in v0.1.3

type ManifestDelete struct {
	PathHash    string `json:"path_hash"`
	ObjectKind  string `json:"object_kind"`
	ObjectID    string `json:"object_id,omitempty"`
	TombstoneID string `json:"tombstone_id"`
	DeletedAt   string `json:"deleted_at,omitempty"`
	TrashBlobID string `json:"trash_blob_id,omitempty"`
	RevisionID  string `json:"revision_id,omitempty"`
	DeviceID    string `json:"device_id,omitempty"`
}

type ManifestEntry

type ManifestEntry struct {
	ObjectID   string `json:"object_id,omitempty"`
	RevisionID string `json:"revision_id,omitempty"`
	DeviceID   string `json:"device_id,omitempty"`
	Path       string `json:"path"`
	PathHash   string `json:"path_hash"`
	BlobID     string `json:"blob_id"`
	Size       int64  `json:"size"`
	SHA256     string `json:"sha256"`
	ObjectKind string `json:"object_kind,omitempty"`
	Mode       uint32 `json:"mode,omitempty"`
	MediaType  string `json:"media_type,omitempty"`
	UpdatedAt  string `json:"updated_at"`
}

type ManifestFileTooLargeError added in v0.1.3

type ManifestFileTooLargeError struct {
	Path  string
	Size  int64
	Limit int64
}

func (*ManifestFileTooLargeError) Error added in v0.1.3

func (e *ManifestFileTooLargeError) Error() string

type ManifestIdentity added in v0.1.6

type ManifestIdentity struct {
	ObjectID   string
	ObjectKind string
}

type ManifestUnsafePathError added in v0.1.3

type ManifestUnsafePathError struct{ Path string }

func (*ManifestUnsafePathError) Error added in v0.1.3

func (e *ManifestUnsafePathError) Error() string

type ObjectInfo

type ObjectInfo struct {
	Key          string
	Size         int64
	Revision     string
	LastModified time.Time
}

ObjectInfo describes a remote object.

type RcloneBackend

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

func NewRcloneBackend

func NewRcloneBackend(endpoint string) (*RcloneBackend, error)

func (*RcloneBackend) BatchStat

func (b *RcloneBackend) BatchStat(ctx context.Context, keys []string) (map[string]string, error)

func (*RcloneBackend) Delete

func (b *RcloneBackend) Delete(ctx context.Context, key string) error

func (*RcloneBackend) Exists

func (b *RcloneBackend) Exists(ctx context.Context, key string) (bool, error)

func (*RcloneBackend) Get

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

func (*RcloneBackend) List

func (b *RcloneBackend) List(ctx context.Context, prefix string) ([]ObjectInfo, error)

func (*RcloneBackend) Put

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

func (*RcloneBackend) Stat

func (b *RcloneBackend) Stat(ctx context.Context, key string) (string, error)

func (*RcloneBackend) SupportsConditionalWrites

func (b *RcloneBackend) SupportsConditionalWrites() bool

type ReloadStatus added in v0.1.6

type ReloadStatus struct {
	Changed  bool
	Loaded   bool
	Degraded bool
	Code     string
}

RunSnapshot returns the snapshot a sync run should use, reloading first if the encrypted asset changed since the last check. The returned snapshot is frozen for the run even if a subsequent reload succeeds. When no asset exists it returns (nil, nil). When reload fails it returns the last successful snapshot (which may be nil) and a structured ReloadStatus describing the failure.

type S3Backend

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

func NewS3Backend

func NewS3Backend(ctx context.Context, bucket string, prefix string) (*S3Backend, error)

func NewS3BackendWithOptions

func NewS3BackendWithOptions(ctx context.Context, bucket string, prefix string, options S3BackendOptions) (*S3Backend, error)

func (*S3Backend) BatchStat

func (s *S3Backend) BatchStat(ctx context.Context, keys []string) (map[string]string, error)

BatchStat returns revisions for multiple keys.

func (*S3Backend) Delete

func (s *S3Backend) Delete(ctx context.Context, key string) error

func (*S3Backend) Exists

func (s *S3Backend) Exists(ctx context.Context, key string) (bool, error)

Exists checks if an object exists.

func (*S3Backend) Get

func (s *S3Backend) Get(ctx context.Context, key string) ([]byte, string, error)

func (*S3Backend) List

func (s *S3Backend) List(ctx context.Context, prefix string) ([]ObjectInfo, error)

List returns objects with the given prefix.

func (*S3Backend) Put

func (s *S3Backend) Put(ctx context.Context, key string, data []byte, baseRev string) (string, error)

func (*S3Backend) Stat

func (s *S3Backend) Stat(ctx context.Context, key string) (string, error)

func (*S3Backend) SupportsConditionalWrites

func (s *S3Backend) SupportsConditionalWrites() bool

type S3BackendOptions

type S3BackendOptions struct {
	EndpointURL string
	Region      string
	Profile     string
	PathStyle   bool
	PathMode    string
	API         string
}

type S3Config

type S3Config struct {
	Bucket          string `json:"bucket" yaml:"bucket"`
	Prefix          string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
	Endpoint        string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
	Region          string `json:"region,omitempty" yaml:"region,omitempty"`
	Profile         string `json:"profile,omitempty" yaml:"profile,omitempty"`
	AddressingStyle string `json:"addressing_style,omitempty" yaml:"addressing_style,omitempty"`
	PathStyle       bool   `json:"path_style,omitempty" yaml:"path_style,omitempty"`
}

type SecretEntry added in v0.1.6

type SecretEntry struct {
	Identity   string `json:"identity" yaml:"identity"`
	Ciphertext string `json:"ciphertext" yaml:"ciphertext"`
}

SecretEntry is one logical identity's encrypted material. Identity is the stable logical key id; ciphertext is provider-specific and opaque.

type SecretEntryKind added in v0.1.6

type SecretEntryKind string

SecretEntryKind classifies a stored secret so the unlock path and doctor can distinguish credential vs encryption-key material. Stored as opaque metadata in the envelope; unknown kinds degrade rather than fail-open.

const (
	SecretKindCredential    SecretEntryKind = "credential"
	SecretKindEncryptionKey SecretEntryKind = "encryption_key"
)

type SecretEnvelope added in v0.1.6

type SecretEnvelope struct {
	SchemaVersion string                 `json:"schema_version" yaml:"schema_version"`
	Provider      string                 `json:"provider" yaml:"provider"`
	Secrets       map[string]SecretEntry `json:"secrets,omitempty" yaml:"secrets,omitempty"`
}

SecretEnvelope is the repository-tracked encrypted secrets asset (pinax-sync.secrets.yaml). Plaintext values are available only during an authenticated runtime unlock; the ciphertext is safe to commit.

func LoadSecretEnvelope added in v0.1.6

func LoadSecretEnvelope(root string) (SecretEnvelope, error)

LoadSecretEnvelope reads the secrets asset. A missing file is reported via ErrSecretsAssetMissing so init/doctor can distinguish absent from corrupt.

type SecretEnvelopeError added in v0.1.6

type SecretEnvelopeError struct {
	Code    string
	Message string
}

SecretEnvelopeError is the stable error type for envelope operations.

func (*SecretEnvelopeError) Error added in v0.1.6

func (e *SecretEnvelopeError) Error() string

type SecretMetadata added in v0.1.6

type SecretMetadata struct {
	Name     string `json:"name" yaml:"name"`
	Kind     string `json:"kind" yaml:"kind"`
	Identity string `json:"identity" yaml:"identity"`
	Provider string `json:"provider" yaml:"provider"`
}

SecretMetadata is the redacted, safe-to-show view of an entry.

type SourceMarker added in v0.1.6

type SourceMarker struct {
	SchemaVersion     string `json:"schema_version" yaml:"schema_version"`
	DeclarationDigest string `json:"declaration_digest,omitempty" yaml:"declaration_digest,omitempty"`
	GeneratedAt       string `json:"generated_at" yaml:"generated_at"`
	DeviceID          string `json:"device_id" yaml:"device_id"`
}

SourceMarker is the device-local record that a runtime config was generated from a declaration. It stores the declaration digest so doctor can detect drift without re-reading the (possibly absent) original declaration.

func LoadSourceMarker added in v0.1.6

func LoadSourceMarker(root string) (SourceMarker, error)

LoadSourceMarker reads the device-local source marker. Missing marker is not an error — it means the runtime config predates the declaration layer.

type State

type State struct {
	Config  Config        `json:"config"`
	Session DeviceSession `json:"session"`
}

func Load

func Load(root string) (State, error)

func Login

func Login(root string, req LoginRequest) (State, error)

func (State) GetStore

func (s State) GetStore(ctx context.Context) (BlobStore, error)

type StoreFactory

type StoreFactory func(ctx context.Context, endpoint string) (BlobStore, error)

StoreFactory is a function signature for building a BlobStore.

type SyncBackend added in v0.1.6

type SyncBackend struct {
	Kind     string    `json:"kind" yaml:"kind"` // s3-direct, rclone-direct, server, embedded
	Endpoint string    `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
	S3       *S3Config `json:"s3,omitempty" yaml:"s3,omitempty"`
}

SyncBackend describes the portable transport topology. Endpoint and S3 are topology only; provider credential values resolve at device-local unlock.

type SyncConfig added in v0.1.6

type SyncConfig struct {
	SchemaVersion string         `json:"schema_version" yaml:"schema_version"`
	Backend       SyncBackend    `json:"backend" yaml:"backend"`
	Workspace     SyncWorkspace  `json:"workspace" yaml:"workspace"`
	Secrets       SyncSecretRefs `json:"secrets" yaml:"secrets"`
	Policy        SyncPolicy     `json:"policy,omitempty" yaml:"policy,omitempty"`
}

SyncConfig is the versioned, portable repository sync declaration. It holds backend topology, logical credential/encryption identities and sync policy, but never plaintext credentials, tokens, absolute device paths or device runtime state.

func LoadSyncConfig added in v0.1.6

func LoadSyncConfig(root string) (SyncConfig, error)

LoadSyncConfig reads and validates the repository declaration. A missing file is reported via ErrSyncDeclarationMissing so callers can distinguish "not initialized" from "corrupt".

func (SyncConfig) EffectiveNamespace added in v0.1.6

func (c SyncConfig) EffectiveNamespace() string

EffectiveNamespace derives the deterministic remote namespace prefix from the stable workspace fields. It does NOT replace server-side authorization; it only makes the on-wire object layout collision-resistant.

func (SyncConfig) Normalized added in v0.1.6

func (c SyncConfig) Normalized() SyncConfig

Normalized returns a copy with trimmed/derived fields, suitable for stable comparison (drift detection) and compilation.

func (SyncConfig) Validate added in v0.1.6

func (c SyncConfig) Validate() error

Validate enforces the declaration contract: schema version, supported backend, required workspace/encryption identity and absence of plaintext-sensitive or absolute-path fields. It returns stable English error codes for every failure.

type SyncConfigError added in v0.1.6

type SyncConfigError struct {
	Code    string
	Field   string
	Message string
}

SyncConfigError is the stable validation error returned by Validate. Code is a stable English identifier; Field is the redacted field path (never the offending value).

func (*SyncConfigError) Error added in v0.1.6

func (e *SyncConfigError) Error() string

type SyncPolicy added in v0.1.6

type SyncPolicy struct {
	// RemoteDeletePolicy controls whether local deletions may propagate to the
	// remote. "deny" (default) refuses; "require-approval" needs explicit --yes.
	RemoteDeletePolicy string `json:"remote_delete_policy,omitempty" yaml:"remote_delete_policy,omitempty"`
	// NewDeviceMode defaults to "pull-only": a device with no local sync receipt
	// must not upload local deletions or replace remote state on first bootstrap.
	NewDeviceMode string `json:"new_device_mode,omitempty" yaml:"new_device_mode,omitempty"`
}

SyncPolicy captures approval-gated, safety-critical knobs. The default is the most conservative posture so an accidental apply cannot widen the blast radius.

type SyncSecretRefs added in v0.1.6

type SyncSecretRefs struct {
	CredentialID    string `json:"credential_id,omitempty" yaml:"credential_id,omitempty"`
	EncryptionKeyID string `json:"encryption_key_id" yaml:"encryption_key_id"`
}

SyncSecretRefs holds logical credential and encryption key identities. Each device resolves these to its local profile, keychain or secret manager; raw values never enter the declaration.

type SyncWorkspace added in v0.1.6

type SyncWorkspace struct {
	TenantID    string `json:"tenant_id,omitempty" yaml:"tenant_id,omitempty"`
	AppID       string `json:"app_id,omitempty" yaml:"app_id,omitempty"`
	WorkspaceID string `json:"workspace_id" yaml:"workspace_id"`
}

SyncWorkspace carries stable namespace fields. The effective remote namespace is derived deterministically; full multi-tenant authorization is a separate server capability and is NOT provided by direct transport.

type UnlockProvider added in v0.1.6

type UnlockProvider interface {
	// Name is the stable provider identifier written into the envelope.
	Name() string
	// Unlock decrypts the envelope into plaintext values. Plaintext MUST NOT
	// escape the caller; it lives only in the unlocking runtime.
	Unlock(env SecretEnvelope) (map[string]string, error)
	// Lock encrypts a single plaintext value under the given identity, returning
	// an entry suitable for SecretEnvelope.Secrets.
	Lock(name string, plaintext string, identity string, kind SecretEntryKind) (SecretEntry, error)
}

UnlockProvider resolves a SecretEnvelope to plaintext name→value pairs using a device-local identity. Implementations must fail-closed on any missing or wrong identity and never log plaintext. The interface is provider-neutral so the first version can ship a deterministic fake + env provider and later swap in age/keychain without changing the CLI contract.

func ResolveUnlockProvider added in v0.1.6

func ResolveUnlockProvider(providerName string) (UnlockProvider, error)

ResolveUnlockProvider selects the provider for an envelope. Unknown providers fail-closed; the caller surfaces a runnable recovery command.

Jump to

Keyboard shortcuts

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