Documentation
¶
Index ¶
- Variables
- func ConfigureSlog(flags *RootFlags, tuiMode bool) (cleanup func() error, err error)
- func DefaultAWSCheckSDKIdentity(ctx context.Context, cfg *config.Config) error
- func DefaultUIRunner(app tui.App) error
- func HuhAgentConfirm(prompt string) (bool, error)
- func HuhBackupApplyConfirm(prompt string) (bool, error)
- func HuhConfirm(prompt string) (bool, error)
- func NewAgent(deps AgentDeps) *cobra.Command
- func NewAgentAdviseIgnore(deps AgentDeps) *cobra.Command
- func NewAgentScan(deps AgentDeps) *cobra.Command
- func NewBackup(deps BackupDeps) *cobra.Command
- func NewCheck(deps CheckDeps) *cobra.Command
- func NewDiff(deps DiffDeps) *cobra.Command
- func NewDoctor(deps DoctorDeps) *cobra.Command
- func NewInit(deps InitDeps) *cobra.Command
- func NewLocal(deps LocalDeps) *cobra.Command
- func NewLs(deps LsDeps) *cobra.Command
- func NewMCP(deps MCPDeps) *cobra.Command
- func NewPasswd(deps PasswdDeps) *cobra.Command
- func NewPin(deps PinDeps) *cobra.Command
- func NewPolicy(deps PolicyDeps) *cobra.Command
- func NewPrune(deps PruneDeps) *cobra.Command
- func NewRecoveryKit(deps RecoveryKitDeps) *cobra.Command
- func NewRestore(deps RestoreDeps) *cobra.Command
- func NewRoot(version, commit, date string) *cobra.Command
- func NewRootWithFlags(version, commit, date string, flags *RootFlags) *cobra.Command
- func NewSchedule(deps ScheduleDeps) *cobra.Command
- func NewSetup(deps UIDeps) *cobra.Command
- func NewSnapshots(deps SnapshotsDeps) *cobra.Command
- func NewStats(deps StatsDeps) *cobra.Command
- func NewSync(deps SyncDeps) *cobra.Command
- func NewUI(deps UIDeps) *cobra.Command
- func NewUnpin(deps PinDeps) *cobra.Command
- func ResolveBuildVersion(version, commit, date string, bi func() (*debug.BuildInfo, bool)) (string, string, string)
- func SetUIAsDefault(root *cobra.Command, deps UIDeps)
- type AWSInspectReport
- type AgentDeps
- type BackupDeps
- type CheckDeps
- type DiffDeps
- type DoctorDeps
- type InitDeps
- type LocalDeps
- type LsDeps
- type MCPDeps
- type PasswdDeps
- type PinDeps
- type PolicyDeps
- type PruneDeps
- type RecoveryKitDeps
- type RepoDeps
- type RestoreDeps
- type RootFlags
- type ScheduleDeps
- type SnapshotsDeps
- type StatsDeps
- type SyncDeps
- type UIDeps
Constants ¶
This section is empty.
Variables ¶
var ErrAgentApplyFailed = errors.New("agent apply failed")
ErrAgentApplyFailed is returned after the apply summary is written when one or more approved actions failed to apply. The loop keeps going past individual failures, but the process must still exit non-zero — scripted runs check $?, not the "errors: N" stdout line.
var ErrCheckFailed = errors.New("repository check failed")
ErrCheckFailed is returned after a check report is written when the repository has integrity or operational health failures.
var ErrDoctorFailed = errors.New("sentra doctor failed")
ErrDoctorFailed is returned after a doctor report is written when one or more required checks failed.
Functions ¶
func ConfigureSlog ¶
ConfigureSlog builds and installs a slog.Default logger from the parsed root flags. Returns a cleanup func that closes any opened log file (no-op when LogFile is empty). main.go calls this once at startup; subcommand bodies use slog.Info/Warn/Error directly through slog.Default().
Behavior:
- LogLevel: parsed case-insensitively; unknown levels fall back to warn rather than panic, so a typo doesn't crash the CLI.
- LogFormat: text or json; anything else falls back to text.
- LogFile: opened append-only with 0600. Errors are returned; callers should fall back to stderr or fail the command.
- tuiMode: when true and LogFile is empty, logs go to io.Discard so the TUI's alt-screen stays clean. Setting --log-file overrides this — operators running the TUI on cron can still capture diagnostics by writing to a file.
func DefaultAWSCheckSDKIdentity ¶
DefaultAWSCheckSDKIdentity verifies credentials through the AWS SDK credential chain. Thin wrapper over diag.CheckSDKIdentity so the doctor's nil-fallback and setup's identity checker keep their names.
func DefaultUIRunner ¶
DefaultUIRunner is the production launcher: wraps the App in a tea.Program with alt-screen and runs it. Wiring this up here keeps the cmd/sentra main.go terse.
Refuses to launch when stdout isn't a TTY: alt-screen escape codes would otherwise be written into a pipe, file, or CI log, polluting the consumer's stdout with no useful output. Scripts piping `sentra` should call a JSON-emitting subcommand (`snapshots --json`, `agent scan --json`) instead.
The message names both non-interactive routes because `sentra setup` reaches this same refusal — setup is a launcher for this TUI. Telling someone who typed `setup` to run `sentra ui` restates the thing that just refused; `sentra init` is the flow that configures a repository without a terminal.
func HuhAgentConfirm ¶
HuhAgentConfirm is the production Confirm callback for the agent's per-recommendation prompt flow. Negative is "No, skip" — declining one recommendation moves to the next, it does not abort the run.
func HuhBackupApplyConfirm ¶
HuhBackupApplyConfirm is the production Confirm implementation for `sentra backup apply`. Affirmative reads as the action ("Yes, snapshot") so the operator confirms what they're authorizing rather than a generic "Yes."
func HuhConfirm ¶
HuhConfirm is the production Confirm implementation for `sentra prune`. Wired up by main.go; tests inject their own callback to keep the run deterministic.
Affirmative is "Yes, delete" and the negative is "No, abort" — wording designed so the choice reads unambiguously when the cursor lands on the default ("No, abort" is the safer pick).
func NewAgent ¶
NewAgent returns the cobra command for the `agent` parent. It owns no business logic itself; only registers `scan` as a subcommand. Future agent subcommands (e.g. `agent eval`) belong here.
func NewAgentAdviseIgnore ¶
NewAgentAdviseIgnore returns `sentra agent advise-ignore [root]`.
func NewAgentScan ¶
NewAgentScan returns the cobra command for `sentra agent scan`. Without --apply the command is dry-run: it prints a styled table of recommendations and exits. With --apply, each recommendation goes through the per-action handler map after a Confirm prompt (skippable with --yes).
Flags:
- --apply actually execute approved recommendations
- --json emit recommendations as JSON instead of a table
- --yes skip the confirm prompt under --apply
- --root <path> filesystem root to scan (default ".")
- --local-only convert heuristic findings without calling the LLM
- --no-llm alias for --local-only
- --categories a,b only triage matching finding categories/heuristics
- --config <path> sentra.yaml path (default ./sentra.yaml)
- --max-tool-calls N override the orchestrator's tool-call budget
func NewBackup ¶
func NewBackup(deps BackupDeps) *cobra.Command
NewBackup returns the cobra command for `sentra backup <path>`. Flags:
- --tag string human-readable label persisted on the snapshot
- --config path overrides the default sentra.yaml location
The command flow is:
- Load sentra.yaml (env overlays applied)
- Resolve passphrase (deps.Passphrase callback)
- Open the repo via deps.NewStore + repo.Open
- CreateSnapshot with a ui.ByteProgress reporter, repainted to stderr every progressTickInterval until the call returns
- Print the final summary (snapshot ID, files, bytes, new bytes)
func NewDiff ¶
NewDiff returns the cobra command for `sentra diff <snap-a> <snap-b>`. Default rendering is a styled table with columns "Status", "Path"; --json emits a stable {added, removed, changed} schema.
func NewDoctor ¶
func NewDoctor(deps DoctorDeps) *cobra.Command
NewDoctor returns the read-only setup and repository diagnostic command.
func NewInit ¶
NewInit returns the cobra command for `sentra init`. The command validates the destination, opens the configured store via deps, calls repo.Init, and writes sentra.yaml. --force allows clobbering an existing sentra.yaml; that's required when the user has changed passphrase / bucket / region after the fact and is fine with the store being re-bootstrapped (typically against a fresh prefix).
Flags:
- --force overwrite an existing sentra.yaml + re-bootstrap repo
- --bucket S3 bucket (required if no sentra.yaml exists yet)
- --region AWS region; e.g. us-west-2
- --profile AWS shared-config profile
- --endpoint-url MinIO/LocalStack base URL; empty for AWS
- --prefix optional key prefix inside the bucket
The flags layer on top of any existing sentra.yaml + env overlays so `sentra init --force --bucket new` preserves region/profile from the previous file unless those flags are also passed.
func NewLocal ¶
NewLocal returns the cobra command for `sentra local`, a dev-convenience flow that boots a local MinIO, points Sentra at it, and opens the TUI with the first-run wizard pre-filled for MinIO.
It never touches the real sentra.yaml: it launches against .sentra-local.yaml, seeds the wizard with the local MinIO S3 coordinates (endpoint http://localhost:9000, bucket sentra-test, region us-east-1), and exports the well-known minioadmin credentials — but only when the user has not already set AWS credentials, so a real environment is never clobbered.
func NewLs ¶
NewLs returns the cobra command for `sentra ls <snapshot>` — the CLI answer to "is my file in this snapshot?". The TUI has detail views for this; scripts and terminals had nothing.
func NewMCP ¶ added in v0.2.0
NewMCP returns the cobra command for `sentra mcp`: a Model Context Protocol server over stdio, exposing metadata-only snapshot queries and two-phase (plan → token → confirm) backup/restore to MCP clients.
stdin/stdout ARE the protocol channel, so the passphrase resolves non-interactively only (env / --passphrase-file / keyring) — a missing source is a startup error, never a prompt. Logs stay on stderr, which stdio MCP leaves free.
func NewPasswd ¶
func NewPasswd(deps PasswdDeps) *cobra.Command
NewPasswd returns the cobra command for `sentra password`. The command flow:
- Load sentra.yaml (config.Load).
- Open the blobstore via deps.NewStore.
- Read the OLD passphrase via deps.Passphrase.
- repo.Open under the old passphrase. If this fails (wrong passphrase or tampered config), the new-passphrase callback is NEVER invoked — operators get a clean "wrong passphrase" without being prompted for the new one (which would imply they authenticated successfully).
- Read the NEW passphrase via deps.NewPassphrase (file flag or interactive confirm-on-entry prompt).
- Validate: non-empty, length >= minPasswdNewPassphraseLen, not equal to the old passphrase.
- Call repo.Repo.Passwd which acquires the advisory lock, rotates salt + wrap + MAC, and writes the new config blob.
- Print a one-line summary.
Refusal cases short-circuit before any S3 write.
func NewPin ¶
NewPin returns the cobra command for `sentra pin <snapshot>`: mark a snapshot as protected so retention never drops it and deletion refuses it until unpinned.
func NewPolicy ¶
func NewPolicy(deps PolicyDeps) *cobra.Command
NewPolicy returns the command group for named backup policies.
func NewPrune ¶
NewPrune returns the cobra command for `sentra prune`. Without --apply the command is dry-run: it prints what would be deleted but does not modify the store. With --apply, the user is prompted for confirmation (huh.NewConfirm in production; PruneDeps.Confirm in tests) unless --yes is passed for scripting.
The retention policy is the union of (config defaults < flag overrides). Each --keep-* flag layers on top of the corresponding retention key from sentra.yaml.
Flags:
- --keep-last N override retention.keep_last
- --keep-daily N override retention.keep_daily
- --keep-weekly N override retention.keep_weekly
- --keep-monthly N override retention.keep_monthly
- --apply actually delete (default: dry-run)
- --yes skip the confirm prompt (apply-mode only)
- --explain print why each snapshot is kept or dropped
- --config <path> sentra.yaml path (defaults to ./sentra.yaml)
func NewRecoveryKit ¶
func NewRecoveryKit(deps RecoveryKitDeps) *cobra.Command
NewRecoveryKit returns `sentra recovery-kit`.
func NewRestore ¶
func NewRestore(deps RestoreDeps) *cobra.Command
NewRestore returns the cobra command for `sentra restore <snap-id> <dest-dir>`. The dest-dir must either not exist (then it's created) or be empty; a non-empty dest is rejected by repo.Restore to prevent silent merging on top of stale content.
Flags:
- --config override the default sentra.yaml location
Progress: a ui.ByteProgress reporter is wired into RestoreOptions and a goroutine repaints the bar to stderr on the same cadence as the backup command.
func NewRoot ¶
NewRoot returns the root cobra command without exposing the flags struct. Useful for tests / callers that don't care about reading persistent flag values back. The flags are still registered (and parsed normally), they're just not surfaced to the caller.
func NewRootWithFlags ¶
NewRootWithFlags is the wiring point for production: pass in a pointer to a RootFlags and the persistent flag values get written into it as cobra parses argv. Subcommand bodies capture the same pointer via their Deps and read the live values at run time.
Persistent flags:
- --passphrase-file <path> sourced first by the passphrase resolver
func NewSchedule ¶
func NewSchedule(deps ScheduleDeps) *cobra.Command
NewSchedule returns the command group for installing policy schedules.
func NewSetup ¶
NewSetup returns the cobra command for `sentra setup`. It is a thin launcher for the TUI setup wizard: the wizard drives setup.Engine directly, so a second huh-based wizard here would be a duplicate of the same flow against the same engine.
The command forces the wizard even when sentra.yaml already exists, which makes reconfiguring a normal supported flow. The wizard's review stage is the confirmation gate for the overwrite, so there is no --force flag.
func NewSnapshots ¶
func NewSnapshots(deps SnapshotsDeps) *cobra.Command
NewSnapshots returns the cobra command for `sentra snapshots`. Flags:
- --json emit a JSON array instead of the styled table
- --config override the default sentra.yaml location
The default output is a lipgloss table with columns `ID, Created, Tag, Files, Bytes` rendered via ui.RenderTable. The JSON output emits a stable schema: each row has id, created_at (RFC3339), tag, files (int), bytes (int), new_bytes (int).
func NewStats ¶
NewStats returns the cobra command for `sentra stats`: dedup ratio, logical-vs-stored bytes, and each snapshot's unique footprint — the "what is this repo costing me and which snapshot owns it" view.
func NewSync ¶
NewSync returns the cobra command for `sentra sync`. The command flow:
- Validate --dst-config is set (refuse with a clear error).
- Load the source's sentra.yaml via config discovery (cwd, else the user-level config).
- Load the destination's sentra.yaml from --dst-config.
- Refuse if source and destination resolve to the same bucket + prefix (a no-op at best, deadlock at worst).
- Resolve the (single) passphrase via deps.Passphrase.
- Open the source repo (verifies passphrase + config MAC).
- Open the destination store (no Open() — the dest may be empty in --init-dest mode and we'd fail before sync got to bootstrap it).
- Call repo.Repo.SyncTo with the appropriate options.
- Print the stats summary.
Refusal cases short-circuit before any S3 write.
func NewUI ¶
NewUI returns the cobra command for `sentra ui`. The command loads sentra.yaml, opens the repo, builds the App via the deps, and hands it to deps.Run. Any error from Run propagates as the command's exit code.
Flags:
- --config <path> sentra.yaml path (default ./sentra.yaml)
Future iterations might add a --readonly mode that skips the repo open and shows only static file-cached state.
func ResolveBuildVersion ¶
func ResolveBuildVersion(version, commit, date string, bi func() (*debug.BuildInfo, bool)) (string, string, string)
ResolveBuildVersion resolves the identity `sentra version` reports. A release build gets these stamped by goreleaser's ldflags and they pass through untouched. A `go install` build carries the placeholders ("dev"/"none"/"unknown"), and — since go install is the one install path that needs no release pipeline — falls back to the toolchain's embedded build info: the module version (a tag or pseudo-version), the VCS revision (shortened), and the VCS commit time. bi is a seam over debug.ReadBuildInfo so tests can inject a canned build.
func SetUIAsDefault ¶
SetUIAsDefault rewires the root command so that bare `sentra` (no subcommand and no args) falls through to `ui`. Cobra's default behavior is to print help in that case; we want the TUI to be the default landing experience as documented in the design doc.
We do this by setting the root command's RunE: cobra invokes RunE when there is no subcommand to dispatch to. We delegate straight to the ui command's RunE so the loading / error-propagation logic stays in one place.
Important: cobra ignores RunE if the user passed an unknown subcommand — it'll print "unknown command" and exit. That's the right behavior; we only want the bare-invocation path.
Types ¶
type AWSInspectReport ¶
AWSInspectReport is an alias for diag.AWSReport, preserved so existing DoctorDeps callers and tests keep compiling after the read-only AWS diagnostics moved to internal/diag.
func DefaultAWSInspect ¶
DefaultAWSInspect performs the read-only AWS checks for `sentra doctor`. Thin wrapper over diag.Inspect.
type AgentDeps ¶
type AgentDeps struct {
RepoDeps
// Actions is the registry of action verbs the orchestrator's
// system prompt will list and the --apply path will dispatch
// through. Production wires action.NewDefaultRegistry; tests
// can inject a smaller registry. Nil falls back to
// NewDefaultRegistry so a tests using zero-value AgentDeps still
// works.
Actions *action.Registry
// Provider is the LLM provider passed through to the agent. Tests
// use llm.FakeProvider; main wires the Anthropic client.
Provider llm.Provider
// ProviderForConfig builds the provider from the loaded command
// config. When set, it takes precedence over Provider so --config
// controls agent.model in production.
ProviderForConfig func(cfg *config.Config) llm.Provider
// Heuristics is the slice of heuristics the agent will run. Tests
// pass deterministic stubs; main wires the production secrets /
// large_files / etc. set.
Heuristics []heuristics.Heuristic
// Confirm prompts the user with a question and returns the answer.
// Production wires this to huh.NewConfirm; tests inject a stub.
// Skipped entirely when --yes is passed.
Confirm func(prompt string) (bool, error)
}
AgentDeps wires the side-effecting pieces of `sentra agent scan` so tests can inject a memory store, a fake LLM Provider, and stub heuristics. Production fills these from main.go; tests drop in scripted alternatives so a Scan run is fully deterministic and never makes a real LLM call.
Provider and Heuristics are required for the scan flow itself; NewStore + Passphrase open the repo; Confirm is the per-recommendation approval prompt for --apply (skipped under --yes).
type BackupDeps ¶
BackupDeps wires the side-effecting pieces of `sentra backup`. Production fills these with real implementations from main.go; tests inject a memory store and static passphrase.
type CheckDeps ¶
type CheckDeps struct {
RepoDeps
}
CheckDeps wires the side-effecting pieces of `sentra check`.
type DiffDeps ¶
type DiffDeps struct {
RepoDeps
}
DiffDeps wires the side-effecting pieces of `sentra diff`.
type DoctorDeps ¶
type DoctorDeps struct {
CheckAWSSDKIdentity func(ctx context.Context, cfg *config.Config) error
InspectAWS func(ctx context.Context, cfg *config.Config) (AWSInspectReport, error)
NewStore func(ctx context.Context, cfg *config.Config) (blobstore.Store, error)
PassphraseWithConfig func(cfg *config.Config) ([]byte, error)
Stdout io.Writer
}
DoctorDeps wires read-only diagnostics for `sentra doctor`.
type InitDeps ¶
type InitDeps struct {
// NewStore opens the blobstore the new repo will live in.
// Production wires this to the S3 store; tests pass an in-memory
// store. The cfg argument is the merged Config from sentra.yaml +
// env overlay; for `init` on a fresh dir it's typically Defaults().
NewStore func(ctx context.Context, cfg *config.Config) (blobstore.Store, error)
// Passphrase returns the passphrase bytes the new repo will be
// keyed against. Production typically wires this to
// ui.PromptPassphraseWithConfirm; tests inject a static value so
// the run is deterministic.
Passphrase func() ([]byte, error)
// Stdout receives the success summary. cobra.Command.SetOut also
// goes here so callers can wire both in one place; if you set
// SetOut on the returned command directly, that takes precedence.
Stdout io.Writer
}
InitDeps wires the side-effecting pieces of `sentra init` so tests can drop in a memory store and a deterministic passphrase. The command itself just walks the lifecycle: load config (or defaults), open the store, call repo.Init, write sentra.yaml.
type LocalDeps ¶
type LocalDeps struct {
UI UIDeps
// EnsureMinIO makes a local MinIO reachable before the UI launches: it
// returns nil once MinIO answers, starts it (docker compose) if needed, and
// returns a clear error when it can neither reach nor start it. Tests inject
// a stub; production wires the docker/health probe in cmd/sentra.
EnsureMinIO func(ctx context.Context) error
}
LocalDeps wires the side-effecting pieces of `sentra local`. UI is the same UIDeps the `ui` command uses; the command sets UI.SetupSeedConfig at run time so it must be passed in with SetupSeedConfig nil. EnsureMinIO is injected so tests exercise the command without touching docker or the network.
type LsDeps ¶
type LsDeps struct {
RepoDeps
}
LsDeps wires the side-effecting pieces of `sentra ls`.
type MCPDeps ¶ added in v0.2.0
MCPDeps wires `sentra mcp`. PassphraseFile mirrors the launch probe: a func reading the live --passphrase-file flag at run time.
type PasswdDeps ¶
type PasswdDeps struct {
NewStore func(ctx context.Context, cfg *config.Config) (blobstore.Store, error)
Passphrase func() ([]byte, error)
PassphraseWithConfig func(cfg *config.Config) ([]byte, error)
NewPassphrase func(passphraseFile string) ([]byte, error)
SavePassphrase func(cfg *config.Config, passphrase []byte) error
DeletePassphrase func(cfg *config.Config) (bool, error)
Stdout io.Writer
}
PasswdDeps wires the side-effecting pieces of `sentra password` so tests can inject deterministic callbacks. Production wires:
- NewStore: the standard S3-store factory (RetryStore-wrapped).
- Passphrase: the existing old-passphrase resolver chain (--passphrase-file → SENTRA_PASSPHRASE → keyring → prompt).
- NewPassphrase: a constrained resolver that handles ONLY the --new-passphrase-file flag and falls through to an interactive confirm-on-entry prompt. The env var SENTRA_PASSPHRASE is intentionally NOT a source for the new passphrase — env vars persist in shell history / process listings, which is the wrong default for the new secret.
- SavePassphrase: updates the OS keyring entry after a successful rotation when passphrase.use_keyring is enabled.
- DeletePassphrase: removes the OS keyring entry for `sentra password forget`.
- Stdout: where the success summary lands.
The deps shape mirrors InitDeps + a NewPassphrase callback. The new callback takes the --new-passphrase-file flag value as a parameter so production wiring doesn't need to share state with the cobra command.
type PinDeps ¶
type PinDeps struct {
RepoDeps
}
PinDeps wires the side-effecting pieces of `sentra pin` / `unpin`.
type PolicyDeps ¶
type PolicyDeps struct {
RepoDeps
Stderr io.Writer
// OS and HomeDir steer the timer-file cleanup in remove and the
// resync in add --replace; zero values fall back to the runtime
// platform and home directory. Executable is the binary a
// re-rendered timer invokes; nil means os.Executable. Runner
// loads/unloads the timer in launchd/systemd; nil means
// scheduler.ExecRunner, so tests must inject a fake.
OS string
HomeDir func() (string, error)
Executable func() (string, error)
Runner scheduler.Runner
// Now is the clock `run --if-due` measures the schedule against;
// nil means time.Now.
Now func() time.Time
// Notify posts the desktop notification after a run. nil means OFF
// (see notify.Runner); cmd/sentra wires notify.ExecRunner.
Notify notify.Runner
}
PolicyDeps wires side effects for `sentra policy`.
type PruneDeps ¶
type PruneDeps struct {
RepoDeps
// Confirm prompts the user with a question and returns the
// answer. Production wires this to huh.NewConfirm; tests inject a
// stub. Skipped entirely when --yes is passed (the Confirm
// callback is not invoked at all in that case, so test stubs that
// panic-on-call still work correctly under --yes).
Confirm func(prompt string) (bool, error)
}
PruneDeps wires the side-effecting pieces of `sentra prune`. The Confirm callback is what production replaces with the huh.NewConfirm flow; tests inject a deterministic "yes/no" function so the run is scriptable.
type RecoveryKitDeps ¶
type RecoveryKitDeps struct {
RepoDeps
}
RecoveryKitDeps wires `sentra recovery-kit`.
type RepoDeps ¶
type RepoDeps struct {
NewStore func(ctx context.Context, cfg *config.Config) (blobstore.Store, error)
Passphrase func() ([]byte, error)
PassphraseWithConfig func(cfg *config.Config) ([]byte, error)
Stdout io.Writer
}
RepoDeps is the dependency set shared by every read-path command: the blobstore factory, the two passphrase resolvers, and the stdout sink. Commands embed it and add their own extra fields (Stderr, Confirm, ...). Exported so cmd/sentra can construct it when wiring each command.
type RestoreDeps ¶
RestoreDeps wires the side-effecting pieces of `sentra restore`.
type RootFlags ¶
type RootFlags struct {
// PassphraseFile is the path passed via --passphrase-file. Empty
// means "no file source"; the resolver will fall through to the
// next priority (env / keyring / prompt).
PassphraseFile string
// LogLevel selects the slog filter level: "debug", "info",
// "warn", "error". Default "warn" keeps stderr quiet during
// normal use; cron/CI runners can set --log-level=info to capture
// completion summaries and retry events.
LogLevel string
// LogFormat selects the slog handler: "text" (the default,
// human-readable) or "json" (machine-parseable, intended for
// piping into a log aggregator).
LogFormat string
// LogFile redirects slog output to a file. Empty (default) sends
// logs to stderr — except in TUI mode (`sentra ui` or bare
// `sentra`) where stderr would corrupt the alt-screen, so the
// TUI silently discards logs unless LogFile is set.
LogFile string
}
RootFlags collects the values of persistent flags that subcommand bodies need to read at runtime. Wired through Deps so production and tests share the same plumbing — no globals.
Today this only carries --passphrase-file; future cross-cutting flags (a config-path override, a verbosity toggle) belong here too.
type ScheduleDeps ¶
type ScheduleDeps struct {
OS string
HomeDir func() (string, error)
Executable func() (string, error)
Stdout io.Writer
// Now feeds the next-run computation; nil means time.Now. A seam so
// status output is testable against a pinned clock.
Now func() time.Time
// Runner executes launchctl/systemctl for activation, deactivation and
// the status query; nil means scheduler.ExecRunner. Tests MUST inject
// a fake: the real one loads a job on the developer's machine.
Runner scheduler.Runner
}
ScheduleDeps wires filesystem and platform details for `sentra schedule`.
type SnapshotsDeps ¶
type SnapshotsDeps struct {
RepoDeps
}
SnapshotsDeps wires the side-effecting pieces of `sentra snapshots`. Production fills these from main.go; tests inject a memory store and static passphrase.
type StatsDeps ¶
type StatsDeps struct {
RepoDeps
}
StatsDeps wires the side-effecting pieces of `sentra stats`.
type SyncDeps ¶
type SyncDeps struct {
NewStore func(ctx context.Context, cfg *config.Config) (blobstore.Store, error)
Passphrase func() ([]byte, error)
PassphraseWithConfig func(cfg *config.Config) ([]byte, error)
Stdout io.Writer
}
SyncDeps wires the side-effecting pieces of `sentra sync` so tests can inject in-memory stores and a deterministic passphrase callback. Production wires:
- NewStore: the standard S3-store factory (RetryStore-wrapped). Called twice — once with the source's *config.Config, once with the destination's. Same factory both times.
- Passphrase: the existing chain (--passphrase-file → env → keyring → prompt). Called EXACTLY ONCE per sync run; the resolved bytes open BOTH endpoints (clone semantic).
- Stdout: where the success summary lands.
The deps shape mirrors PasswdDeps + a single store factory used for both ends.
type UIDeps ¶
type UIDeps struct {
RepoDeps
// ProviderForConfig builds the LLM provider behind the TUI's chat
// overlay from the loaded config. May be nil — the overlay then
// renders a configure hint; nothing else needs it.
ProviderForConfig func(cfg *config.Config) llm.Provider
// Run is the actual TUI launcher. Production wires it to a
// closure that constructs and runs a tea.Program; tests inject
// a stub that captures the constructed App and returns nil.
Run func(app tui.App) error
// Version and Commit identify the build; they reach the TUI's welcome
// splash. Plain display data, threaded from cmd/sentra. Commit may be the
// goreleaser placeholder "none".
Version string
Commit string
// SavePassphrase re-saves a rotated passphrase to the OS keyring
// after the TUI's password flow changes it. Same hook the `passwd`
// command uses. May be nil when no keyring is wired.
SavePassphrase func(cfg *config.Config, passphrase []byte) error
// DeletePassphrase removes the OS keyring entry for the configured
// repo — the Settings view's "forget keyring passphrase" action.
DeletePassphrase func(cfg *config.Config) (bool, error)
// SetupEffects overrides the setup engine's side-effecting seam. Nil
// means runUI constructs the production setup.DefaultEffects(); tests
// inject a fake to keep AWS/keyring calls out of the process.
SetupEffects setup.Effects
// SetupSeedConfig pre-fills the first-run setup wizard. When non-nil AND the
// launch lands on the first-run path (no config file present), runUI hands
// this config to the wizard as its starting point so the S3 detail fields
// (bucket/prefix/region/endpoint) come up populated — the wizard reads them
// via deps.Config → config0 → setup.DefaultPlan. It is NOT written to disk:
// the launch stays first-run and the wizard persists the config only on
// completion. `sentra local` sets this to MinIO coordinates; every other
// caller (NewUI) leaves it nil for a blank wizard. Non-secret S3 coordinates
// only — never a passphrase or credentials.
//
// `sentra setup` forces the wizard with a config present, so the seed's
// first-run precondition is now an explicit !ConfigExists term in runUI
// rather than a property of the branch.
SetupSeedConfig *config.Config
// PassphraseFile resolves the --passphrase-file path (the root persistent
// flag) at run time. The launch-routing probe (probeLaunchState) must
// honor it so a non-interactive file source resolves the same way it does
// on every command's read path — otherwise `sentra ui --passphrase-file X`
// against a keyring-off repo misroutes to the unlock gate, which cannot
// read the file. It is a func, not a plain string, because production wires
// the command deps BEFORE cobra parses argv: a value snapshot would always
// be empty, so the probe must read the live cli.RootFlags.PassphraseFile at
// run time. Nil (or a func returning "") means "no file source". Returns a
// path, never a secret.
PassphraseFile func() string
}
UIDeps wires the side-effecting pieces of `sentra ui` so tests can drop in a memory store and a stub Run callback. Run isolates the "actually launch a Bubbletea program" step behind a function so unit tests don't need a real terminal.
Source Files
¶
- agent.go
- agent_apply.go
- agent_ignore.go
- backup.go
- check.go
- config_path.go
- confirm.go
- diff.go
- doctor.go
- format.go
- init.go
- local.go
- ls.go
- mcp.go
- passphrase_deps.go
- passwd.go
- pin.go
- policy.go
- prune.go
- recovery_kit.go
- repo_open.go
- restore.go
- root.go
- schedule.go
- setup.go
- setup_awss3.go
- setup_iam_policy.go
- setup_spinner.go
- snapshots.go
- stats.go
- sync.go
- ui.go