app

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package app implements siphon's verbs over the domain layer. CLI and TUI both call into this package; neither cares about the other.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Backup

func Backup(parent context.Context, d Deps, opt BackupOpts) (<-chan jobs.Event, string, error)

Backup dumps the source profile to a new entry in the catalog. Returns the running job's Event channel and ID.

func CDCStateDir

func CDCStateDir() string

CDCStateDir returns the per-user directory holding CDC resume state. It honors SIPHON_STATE_HOME, then XDG_STATE_HOME, then $HOME/.local/state — mirroring how internal/config resolves its config path, so tests can redirect it without writing to the real home.

func Inspect

func Inspect(ctx context.Context, d Deps, profile string) (*driver.Schema, error)

Inspect returns the live schema for the named profile.

func RequireCapability

func RequireCapability(d Deps, profileName string, requiredCap Capability) error

RequireCapability resolves profileName to its driver and checks that the driver supports requiredCap. It returns nil when supported, or a CodeUser errs.Error wrapping ErrDriverUnsupported (with an actionable hint) when not.

Callers in the presentation layer (CLI/TUI) use this for verb pre-flight so an unsupported affordance fails fast with a clear message rather than crashing partway through. It takes a profile name (not a driver.Driver) so the CLI never has to import the driver package (depguard boundary).

func Restore

func Restore(parent context.Context, d Deps, opt RestoreOpts) (<-chan jobs.Event, string, error)

Restore resolves the dump's chain (base + incrementals) and applies it in order into the target profile. For a plain (non-incremental) dump the chain is a single element. --up-to stops the chain early at the named dump.

func RunCDC

func RunCDC(parent context.Context, d Deps, opt SyncOpts) (<-chan jobs.Event, string, error)

RunCDC starts (or resumes) a continuous, unbounded sync: it tails the source's logical change stream and applies each engine-neutral CanonicalChange to the target. It works same-engine and cross-engine alike because CanonicalChange is engine-neutral and ApplyChange replays it natively on the target.

Both drivers must advertise CapCDC. On a first run (no prior state) it captures a consistent start position, takes an initial schema+data snapshot, then streams changes committed after that position. On a restart it resumes from the saved position with no snapshot.

Resume granularity is "since the last clean exit": RunCDC persists the streamer's returned final position when the stream stops (the position is tied to what was actually delivered, never ahead of it). Re-applying the tail after a crash is safe because ApplyChange is idempotent (INSERT upserts; UPDATE and DELETE target by primary key).

ctx cancellation is the normal stop signal (matching the bounded-stream convention): on cancel RunCDC persists final state and returns nil; only a non-cancel StreamChanges error is surfaced.

func Sync

func Sync(parent context.Context, d Deps, opt SyncOpts) (<-chan jobs.Event, string, error)

Sync backs up From and restores into To in a single pass. The native (homogeneous) path streams the dump through a bounded jobs.Stream — no temp file, no catalog entry — so backpressure is observable via FillPercent while a backup failure still propagates to Restore as a read error (via CloseErr), preventing a truncated dump from being committed as if clean.

When opt.CrossEngine is set the work routes through runCrossEngineSync, which is capability-gated and uses driver.SchemaInspector + driver.CanonicalTransfer for typed cross-engine snapshot transfer.

func Verify

func Verify(ctx context.Context, d Deps, dumpID string) (*driver.VerifyReport, error)

Verify checks the integrity of a dump entry by recomputing the sha256 of the dump file and comparing it against the checksum recorded in the meta sidecar. It is stateless — no DB connection is required (Phase B checksums only; Phase F adds envelope-header validation via the driver's Verify method).

Types

type BackupOpts

type BackupOpts struct {
	Profile          string
	IncludeTables    []string
	ExcludeTables    []string
	ExcludeDataFrom  []string
	SchemaOnly       bool
	DataOnly         bool
	CompressionLevel int
	Parallel         int
	Incremental      bool
	BaseID           string
}

BackupOpts configures the Backup verb.

type CDCState

type CDCState struct {
	JobID          string    `json:"job_id"`
	Source         string    `json:"source_profile"`
	Target         string    `json:"target_profile"`
	LastAppliedLSN string    `json:"last_applied_lsn,omitempty"`
	LastBinlogFile string    `json:"last_binlog_file,omitempty"`
	LastBinlogPos  uint64    `json:"last_binlog_pos,omitempty"`
	UpdatedAt      time.Time `json:"updated_at"`
}

CDCState is persisted between runs so a long-running continuous sync can resume from the last applied position after a restart.

type Capability

type Capability string

Capability identifies a specific feature flag a verb requires. The string values mirror the driver.Capabilities field semantics and appear in the user-facing hint when a driver lacks the capability.

const (
	CapIncremental             Capability = "incremental"
	CapNativeStream            Capability = "native-stream"
	CapPerTable                Capability = "per-table"
	CapSchemaOnly              Capability = "schema-only"
	CapDataOnly                Capability = "data-only"
	CapParallel                Capability = "parallel"
	CapCompression             Capability = "compression"
	CapBinaryFormat            Capability = "binary-format"
	CapCrossEngineSource       Capability = "cross-engine-source"
	CapCrossEngineTarget       Capability = "cross-engine-target"
	CapCDC                     Capability = "cdc"
	CapNativeBackpressure      Capability = "native-backpressure"
	CapCrossVersionIncremental Capability = "cross-version-incremental"
)

type ChainOutcome

type ChainOutcome struct {
	Root      string
	DumpIDs   []string
	SizeBytes int64
	Pruned    bool     // true = scheduled for (or performed) deletion
	Deleted   []string // dump IDs actually deleted (Apply only)
	Errors    []string // per-dump deletion failures (Apply only)
}

ChainOutcome is one chain's place in the plan, flattened for reporting.

type Deps

type Deps struct {
	Profiles *profile.Store
	Dumps    *dumps.Catalog
	Runner   *jobs.Runner
	Drivers  DriverGetter
	// Auditor records destructive operations; nil is a no-op. It is also the
	// interception seam for 2FA gating (a pre-check before the verb) and
	// telemetry (timing/outcome), so those reuse these call sites.
	Auditor audit.Auditor
	// Gate, if set, is consulted before a destructive verb runs and can block it
	// (e.g. require 2FA / destructive confirmation for a profile's group).
	Gate Gate
	// Actor is the OS user attributed in audit records.
	Actor string
}

Deps bundles every dependency the app verbs need. CLI and TUI build one Deps at startup and pass it to every verb. Makes mocking trivial.

type DriverGetter

type DriverGetter interface {
	Get(name string) (driver.Driver, error)
}

DriverGetter is satisfied by internal/driver.Get. Wrapped to allow mocking.

func DefaultDrivers

func DefaultDrivers() DriverGetter

DefaultDrivers returns a DriverGetter backed by the global driver registry. Presentation layers (CLI, TUI) use this so they never import internal/driver directly — dependency flows through the application layer.

type Gate

type Gate interface {
	Authorize(ctx context.Context, op audit.Op, profile string) error
}

Gate authorizes a destructive operation before it runs. A nil Gate allows everything. Returning a non-nil error blocks the verb.

type PruneOpts

type PruneOpts struct {
	Profile string
	Policy  dumps.RetentionPolicy
	Apply   bool
}

PruneOpts configures a prune run. Policy is resolved by the caller (the CLI maps config + flags into it), so this layer stays config-agnostic. Profile scopes the catalog to one profile's dumps ("" = all profiles). Apply performs deletions; otherwise the run is a dry-run that only computes the plan.

type PruneResult

type PruneResult struct {
	Profile   string
	Apply     bool
	Outcomes  []ChainOutcome
	Reclaimed int64 // bytes freed (sum of successfully deleted dumps; Apply only)
	Failed    int   // count of dumps that failed to delete
}

PruneResult is the structured outcome the CLI renders.

func Prune

func Prune(ctx context.Context, d Deps, opt PruneOpts) (*PruneResult, error)

Prune groups the catalog into chains, plans retention over them, and (when Apply) deletes the pruned chains. It is synchronous (like Verify): prune is a list + a few deletes, not a long stream, so it returns a structured result directly rather than over the job channel.

Deletion is chain-aware and leaf-inward: within a pruned chain, incrementals are removed before the base, so an interrupted prune leaves at worst a complete shorter chain — never a base missing under a surviving incremental.

type RestoreOpts

type RestoreOpts struct {
	Profile      string
	DumpID       string
	TargetTables []string
	SchemaOnly   bool
	DataOnly     bool
	Clean        bool
	UpTo         string // optional: stop applying the chain after this dump ID
}

RestoreOpts configures the Restore verb.

type SyncOpts

type SyncOpts struct {
	From   string
	To     string
	Stream bool
	Tables []string
	// CrossEngine routes through the canonical-schema path (e.g. postgres→mysql)
	// instead of the native homogeneous stream. Gated on driver capability.
	CrossEngine bool
	// Continuous requests CDC follow mode: routes to RunCDC, which tails the
	// source change stream and applies each change to the target (same-engine
	// and cross-engine), resumable via the CDC state file.
	Continuous bool
}

SyncOpts configures the Sync verb.

Jump to

Keyboard shortcuts

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