Documentation
¶
Index ¶
- Constants
- Variables
- func AWSAuthMethodLabel(m AWSAuthMethod) string
- func AWSConfigPath() (string, error)
- func AWSCredentialsPath() (string, error)
- func AWSProfileSection(profile string) string
- func AWSSSOProfileConfigured(cfg AWSCLIConfig, profile string) bool
- func ApplyAWSConfigOnly(p *Plan)
- func ApplyBackendChoice(p *Plan, backend Backend, configuredProfile string)
- func ApplyPassphraseConfig(p *Plan)
- func BackendLabel(b Backend) string
- func BackupUserPolicyNameFor(bucket string) string
- func BucketARN(bucket string) string
- func CheckAWSConfigProfileFree(path, profile string) error
- func CheckAWSCredentialsProfileFree(path, profile string) error
- func DefaultAWSConfigureSSO(ctx context.Context, profile string) error
- func DefaultAWSLogin(ctx context.Context, profile string, region string) error
- func DefaultAWSSSOConfigured(_ context.Context, profile string) (bool, error)
- func DefaultAWSSSOLogin(ctx context.Context, profile string) error
- func DefaultBackupUserProfileFor(sessionProfile string) string
- func ErrorAdvice(err error, cfg config.Config) []string
- func IsAWSMissingCredentialsError(err error) bool
- func NormalizeConfig(cfg *config.Config)
- func ObjectARN(bucket string, prefix string) string
- func ResolveBackupUserProfile(p *Plan) string
- func ReviewText(cfgPath string, p Plan) string
- func ShouldProvisionBackupUser(p *Plan) bool
- func ValidateBackupUserProfileFor(name, sessionProfile string) error
- func ValidateBucketName(bucket string) error
- func WrapAWSLoginFlowError(profile string, err error) error
- func WrapAWSPrepareError(cfg config.Config, method AWSAuthMethod, err error) error
- func WrapAWSSSOFlowError(command string, profile string, err error) error
- func WriteAWSCredentialsProfile(path, profile, accessKeyID, secret string) error
- func WriteIAMPolicy(w io.Writer, bucket string, prefix string) error
- type AWSAuthMethod
- type AWSAuthReport
- type AWSCLIConfig
- type AWSCLIInstallConfirm
- type AWSCLIInstallPlan
- type AWSCLIInstallReport
- type AWSPrepareOptions
- type AWSPrepareReport
- type Backend
- type BackupUserError
- type BackupUserOptions
- type BackupUserReport
- type Effects
- type Engine
- func (e *Engine) DraftPath(cfgPath string) string
- func (e *Engine) InitRepo(ctx context.Context, cfg *config.Config, pass []byte, save bool) (InitResult, error)
- func (e *Engine) PrepareAWS(ctx context.Context, p *Plan) (AWSAuthReport, AWSPrepareReport, error)
- func (e *Engine) RemoveDraft(cfgPath string)
- func (e *Engine) WriteConfig(cfgPath string, p *Plan) error
- func (e *Engine) WriteDraft(cfgPath string, cfg *config.Config) error
- type EnvProbe
- type IAMPolicyDocument
- type IAMPolicyStatement
- type InitResult
- type Plan
Constants ¶
const ( // BackupUserName is the IAM user the wizard creates for day-to-day // backups. Each bucket it may reach is granted by its own customer-managed // policy, BackupUserPolicyNameFor(bucket), holding // BuildIAMPolicy(bucket, prefix) — so buckets accumulate on the user. BackupUserName = "sentra-backup" // BackupUserPolicyName is the stem of the per-bucket managed policy // names (see BackupUserPolicyNameFor) and, on its own, the name of the // single inline policy that installs from before per-bucket policies // carried on BackupUserName. The provisioner deletes that inline policy // once a managed policy covering its grant is attached. BackupUserPolicyName = "sentra-s3-backup" // DefaultBackupUserProfile is where the minted key lands in // ~/.aws/credentials when the operator leaves the field blank. Never // "default": that section is the operator's, not Sentra's. It is the // PREFERRED name, not the guaranteed one: DefaultPlan infers a session // profile of the same name from a [profile sentra] in ~/.aws/config, and // a key written there would shadow that sign-in, so consumers resolve // blank through DefaultBackupUserProfileFor / ResolveBackupUserProfile // rather than reading this constant directly. DefaultBackupUserProfile = "sentra" )
Backup-user provisioning names. Constants, not operator inputs: one fewer knob in the wizard, and doctor/docs can name them without a lookup. Only the credentials profile is chosen by the operator.
Variables ¶
var ErrBackupUserProfileDefault = errors.New("backup user profile must not be \"default\"")
ErrBackupUserProfileDefault is returned when the operator names the "default" credentials profile for the backup user. That section is the operator's everyday identity; Sentra must never write into it.
var ErrBackupUserProfileIsSession = errors.New("backup user profile is the profile setup signs in with; a static key under it would shadow that sign-in for every tool using the profile")
ErrBackupUserProfileIsSession is returned when the backup user's credentials profile is the very profile setup signs in with (Repo.S3.Profile). aws-sdk-go-v2 resolves one profile's static keys BEFORE its SSO or assume-role settings (resolveCredsFromProfile tests Credentials.HasKeys() first), so a key written under that name would make every tool using the profile authenticate as the least-privilege backup user, and the operator's own identity would be unreachable under the name they know it by. The text carries that reason: on a machine with a [profile sentra] this is the refusal the default path meets, and a bare "must differ" reads as a rule to work around rather than a trap to avoid. It is a whole sentence on its own — errors.Is callers may print it bare — and the wrap adds only the name.
var ErrConfigProfileExists = errors.New("profile is already defined in the AWS config file; a static key under its name would shadow its settings")
ErrConfigProfileExists is returned when ~/.aws/config already defines [profile NAME] for the requested backup profile. Static keys in the credentials file merge into that same profile, and aws-sdk-go-v2 resolves them ahead of the profile's SSO / role / credential_process settings — so writing the key would silently retarget every tool that uses the profile at the backup user. Sentra never edits ~/.aws/config, so the only safe answer is a different name. The sentinel is a whole sentence carrying the reason — errors.Is callers may print it bare — so the operator reads a trap to avoid rather than a name clash to resolve by editing the config file; the wrap adds only the section and the file it was found in.
var ErrCredentialsProfileExists = errors.New("aws credentials profile already holds keys")
ErrCredentialsProfileExists is returned when the target section of the AWS shared credentials file already holds an access key. Sentra never overwrites a credential it did not create: the operator may have put that key there on purpose, and a silent replacement would break every other tool reading the profile.
Functions ¶
func AWSAuthMethodLabel ¶
func AWSAuthMethodLabel(m AWSAuthMethod) string
AWSAuthMethodLabel is the human-readable sign-in method name.
func AWSConfigPath ¶
AWSConfigPath returns the AWS CLI config path, honoring AWS_CONFIG_FILE and falling back to ~/.aws/config.
func AWSCredentialsPath ¶ added in v0.4.0
AWSCredentialsPath returns the AWS shared credentials file path, honoring AWS_SHARED_CREDENTIALS_FILE and falling back to ~/.aws/credentials — the same resolution the AWS CLI and SDK use, so the key lands where the SDK will look for it.
func AWSProfileSection ¶
AWSProfileSection maps a profile name to its ~/.aws/config section header.
func AWSSSOProfileConfigured ¶
func AWSSSOProfileConfigured(cfg AWSCLIConfig, profile string) bool
AWSSSOProfileConfigured reports whether profile has a complete SSO config in cfg, supporting both the modern sso_session form and the legacy inline form.
func ApplyAWSConfigOnly ¶
func ApplyAWSConfigOnly(p *Plan)
ApplyAWSConfigOnly turns a plan into a write-config-only plan: no AWS side effects, no repo init, no keyring save.
func ApplyBackendChoice ¶
ApplyBackendChoice settles a plan once the operator picks a backend by hand in the TUI wizard — its only production caller. DefaultPlan's inference path (inferS3CompatibleFromEndpoint, above) does not call it: it upholds the same invariant its own way, by settling the backend before inferring a profile and inferring none for S3-compatible targets. Both mechanisms must keep agreeing on what they drop — only an *inferred* profile, never one the operator wrote into their own config.
Two invariants:
- AWS forbids endpoint_url.
- An S3-compatible target must not carry an AWS shared-config profile the operator never chose. blobstore.NewS3 hands a non-empty Profile to awsconfig.WithSharedConfigProfile, and aws-sdk-go-v2's resolveCredentialChain tests `sharedProfileSet` BEFORE `envConfig.Credentials.HasKeys()` — so the profile's credentials win and the endpoint's are never consulted. DefaultProfileFromConfig prefers a profile literally named "sentra", which is how `sentra local` ended up authenticating against a real AWS account.
configuredProfile is the profile from the operator's own sentra.yaml, empty when they never set one. Only an inferred profile is dropped: R2 and Wasabi credentials legitimately live in a named profile.
It deliberately does NOT touch the provisioning flags (PrepareAWS, CreateBucket, …). Those are settled later, once the operator has seen the actions stage.
func ApplyPassphraseConfig ¶
func ApplyPassphraseConfig(p *Plan)
ApplyPassphraseConfig mirrors the SavePassphrase decision into the persisted use_keyring flag, but only when the repo is being initialized.
func BackendLabel ¶
BackendLabel is the human-readable storage-backend name.
func BackupUserPolicyNameFor ¶ added in v0.4.0
BackupUserPolicyNameFor names the customer-managed policy that grants BackupUserName access to one bucket. One policy per bucket is what lets buckets accumulate on the user (IAM attaches up to ten managed policies to a user) instead of each wizard run replacing the previous run's grant, as the single inline policy used to. Bucket names are [a-z0-9.-] and at most 63 characters, so the result is always a valid IAM policy name.
func CheckAWSConfigProfileFree ¶ added in v0.5.0
CheckAWSConfigProfileFree refuses a backup profile whose [profile NAME] section exists in the AWS CLI config at path. It is the config-file half of CheckAWSCredentialsProfileFree: the credentials check catches a key that is already there, this catches a definition the key would shadow. A missing file is free. Only the "profile NAME" spelling counts — a bare [NAME] section in the config file is not a profile to the SDK or the CLI.
func CheckAWSCredentialsProfileFree ¶ added in v0.4.0
CheckAWSCredentialsProfileFree reports whether WriteAWSCredentialsProfile would accept profile at path — the same refusals, without writing. The provisioner calls it BEFORE creating an access key, so a doomed run makes no IAM mutation it would only have to undo.
func DefaultAWSConfigureSSO ¶
DefaultAWSConfigureSSO delegates first-time SSO profile setup to the AWS CLI.
func DefaultAWSLogin ¶
DefaultAWSLogin delegates browser-based AWS CLI sign-in. The AWS CLI stores temporary credentials; Sentra never receives or stores them.
func DefaultAWSSSOConfigured ¶
DefaultAWSSSOConfigured checks whether the selected profile has a complete AWS CLI SSO profile (newer sso_session or older inline sso_start_url form).
func DefaultAWSSSOLogin ¶
DefaultAWSSSOLogin delegates browser-based SSO authentication to the AWS CLI.
func DefaultBackupUserProfileFor ¶ added in v0.5.0
DefaultBackupUserProfileFor is DefaultBackupUserProfile made safe for the plan at hand: when the session profile is itself called "sentra" — this is common, since DefaultPlan prefers a [profile sentra] from ~/.aws/config — the default steps aside to "sentra-backup" rather than failing the happy path with the collision it exists to prevent. One step is enough: the session profile is a single name, so it cannot equal both. Pure, so the review line and the engine can agree on the name before anything runs.
func ErrorAdvice ¶
ErrorAdvice returns operator-facing recovery hints for an AWS setup failure. It classifies err by substring (raw AWS error text) and adds cfg context when available. Returns nil for a nil error and a single generic line when nothing else matched.
func IsAWSMissingCredentialsError ¶
IsAWSMissingCredentialsError reports whether err's text matches the AWS SDK's missing-credential phrasings. It is deliberately substring-based: the SDK returns these as plain messages, not wrapped sentinels, so errors.Is cannot classify them.
func NormalizeConfig ¶
NormalizeConfig trims the S3 fields so equal-but-padded values compare and serialize identically.
func ObjectARN ¶
ObjectARN returns the S3 ARN pattern for the objects Sentra reads and writes. An empty prefix widens the pattern to the whole bucket; a prefix scopes it so the granted identity only touches Sentra's keys.
func ResolveBackupUserProfile ¶ added in v0.5.0
ResolveBackupUserProfile is the single reading of Plan.BackupUserProfile: the operator's explicit name, else the plan-derived default. Every consumer goes through it so "blank" cannot resolve to two different sections on the review screen and in the credentials file.
func ReviewText ¶
ReviewText renders the non-secret setup plan shown before any AWS or repo side effects run. Behavior-preserving port of the deleted CLI wizard's review text; the trailing no-secrets assertion is load-bearing and must not be removed.
func ShouldProvisionBackupUser ¶ added in v0.4.0
ShouldProvisionBackupUser is the single gate for the IAM provisioning stage. Existing-credentials and skip never provision: the operator already chose a durable identity, and an IAM mutation they did not ask for is the worst surprise a setup wizard can spring.
func ValidateBackupUserProfileFor ¶ added in v0.5.0
ValidateBackupUserProfileFor is the package's only exported profile-name gate: the section-name rules plus the one that needs the plan — the name must not be sessionProfile. The wizard, the drivers and the engine all call this form, so the refusal is the same wherever the operator meets it and no caller can validate a name without the session rule.
func ValidateBucketName ¶
ValidateBucketName re-exports diag's bucket-name validation so the TUI wizard's inline field validation (commitDetails) shares one rule set with doctor's probes without internal/tui importing internal/diag directly. It is the only live bucket-name gate in the product.
func WrapAWSLoginFlowError ¶
WrapAWSLoginFlowError annotates a failed `aws login` with its profile and the recovery paths, preserving the cause.
func WrapAWSPrepareError ¶
func WrapAWSPrepareError(cfg config.Config, method AWSAuthMethod, err error) error
WrapAWSPrepareError classifies a bucket-prep failure. Missing-credential causes get method-specific guidance; everything else is a plain prepare wrap. The credential test is substring matching on raw AWS SDK text (not errors.Is) because the SDK does not expose typed sentinels for these.
func WrapAWSSSOFlowError ¶
WrapAWSSSOFlowError annotates a failed `aws configure sso` / `aws sso login` with the profile it ran for and the recovery paths, preserving the cause.
func WriteAWSCredentialsProfile ¶ added in v0.4.0
WriteAWSCredentialsProfile stores accessKeyID/secret under [profile] in the shared credentials file at path. It is a minimal-touch edit: the file is the operator's, so every byte outside the target section is preserved, including comments and unknown keys. The replacement is atomic and written through a symlinked path (atomicfile.Write): a crash mid-write must never leave a truncated credentials file, because the file is shared — a truncation strands every OTHER profile in it, and the AWS CLI, the operator's other tools, and every sentra profile stop authenticating at once. And a credentials file kept in a dotfiles repo behind a symlink must stay a symlink, or the dotfiles repo silently stops seeing the file while the CLI keeps working. The result is mode 0600: it holds a live access key.
Refusals (see validateBackupUserProfileName and ErrCredentialsProfileExists) leave the file untouched.
Types ¶
type AWSAuthMethod ¶
type AWSAuthMethod string
AWSAuthMethod names how setup makes AWS credentials available before it prepares the bucket. The string values are stable and match the CLI wizard's SetupAWSAuthMethod so config and reports read the same across both drivers.
const ( AWSAuthLogin AWSAuthMethod = "login" AWSAuthSSO AWSAuthMethod = "sso" AWSAuthExisting AWSAuthMethod = "existing" AWSAuthSkip AWSAuthMethod = "skip" )
func ResolveAWSAuthMethod ¶
func ResolveAWSAuthMethod(p *Plan) AWSAuthMethod
ResolveAWSAuthMethod picks the effective auth method for a plan, defaulting an empty method to existing credentials (when preparing AWS) or skip.
type AWSAuthReport ¶
type AWSAuthReport struct {
IdentityVerified bool
Method AWSAuthMethod
AWSCLIInstalled bool
AWSCLIManager string
LoginRan bool
SSOConfigured bool
SSOConfigureRan bool
SSOLoginRan bool
}
AWSAuthReport summarizes the optional AWS CLI auth preflight.
type AWSCLIConfig ¶
AWSCLIConfig is a parsed ~/.aws/config: section name to key/value pairs.
func LoadAWSCLIConfig ¶
func LoadAWSCLIConfig() (AWSCLIConfig, error)
LoadAWSCLIConfig reads and parses the AWS CLI config file. A missing file is not an error — it returns (nil, nil) so callers treat it as "nothing configured".
type AWSCLIInstallConfirm ¶
type AWSCLIInstallConfirm func(plan AWSCLIInstallPlan) (bool, error)
AWSCLIInstallConfirm asks whether Sentra may run the detected package manager command.
type AWSCLIInstallPlan ¶
AWSCLIInstallPlan is the package-manager command Sentra can run to install the AWS CLI for setup's SSO flow.
func DefaultAWSCLIInstallPlan ¶
func DefaultAWSCLIInstallPlan() (AWSCLIInstallPlan, bool)
DefaultAWSCLIInstallPlan returns the package-manager command Sentra would run to install the AWS CLI, and whether a supported manager was found. Its result only reaches an actual install through DefaultEnsureAWSCLI's confirm branch, which no production caller currently arms — see the note there.
type AWSCLIInstallReport ¶
AWSCLIInstallReport summarizes the AWS CLI preflight.
func DefaultEnsureAWSCLI ¶
func DefaultEnsureAWSCLI(ctx context.Context, confirm AWSCLIInstallConfirm) (AWSCLIInstallReport, error)
DefaultEnsureAWSCLI verifies that the AWS CLI is available. When it is missing and the caller supplied a confirm, it asks to run `brew install awscli` and verifies the install before continuing.
NO PRODUCTION CALLER SUPPLIES A CONFIRM. Engine.PrepareAWS passes nil, and the TUI wizard passes nil by design (huh cannot run inside a live tea.Program), so since the huh-based CLI wizard's deletion the install branch below is reachable only from tests. It is kept rather than deleted because re-enabling it is cheap: a TUI confirm modal whose "yes" runs the plan the way interactiveAWSAuthCommand already suspends the program to run `aws login` would restore brew auto-install without changing anything here. That is a feature, not a fix, so it is tracked rather than built.
A missing binary with no confirm and a missing binary with no supported package manager are therefore the same situation from the operator's side — nothing here can install it — so they share one actionable message. Naming the absent confirm instead would describe internal wiring the operator cannot act on, and ErrorAdvice has no case for it, so the TUI's modal would fall through to its generic line and never say "install the AWS CLI".
type AWSPrepareOptions ¶
AWSPrepareOptions controls the AWS-side setup work. Bucket existence is always checked; CreateBucket decides whether a missing bucket is created or reported as an error.
type AWSPrepareReport ¶
type AWSPrepareReport struct {
BucketExisted bool
BucketCreated bool
PublicAccessBlocked bool
DefaultEncryptionEnabled bool
// BackupUser is nil when provisioning was not attempted (gate false);
// otherwise the non-secret outcome, Warning set on failure.
BackupUser *BackupUserReport
}
AWSPrepareReport summarizes the AWS setup work for the final output.
func DefaultAWSPrepare ¶
func DefaultAWSPrepare(ctx context.Context, cfg *config.Config, opts AWSPrepareOptions) (AWSPrepareReport, error)
DefaultAWSPrepare performs the deterministic AWS S3 setup work chosen in the wizard. It intentionally does not create or manage IAM users. Moved verbatim from the old internal/cli/setup_awss3.go (that file has since been trimmed to identity-check and inspect wrappers only); it must build its own *S3 store because it needs the concrete *s3.Client (Store does not expose Client()), so it cannot use Effects.NewStore.
type BackupUserError ¶ added in v0.4.0
type BackupUserError struct {
Step string
AccessDenied bool
KeyLimit bool
PolicyLimit bool
KeyOrphaned string
Err error
}
BackupUserError classifies a provisioning failure so the engine can write a warning that names the fix. Step is the IAM action or "credentials". KeyLimit and PolicyLimit are the two quotas an operator can clear by hand (two access keys per user; ten managed policies per user). KeyOrphaned is the access key ID left behind when a post-mint failure's cleanup also failed — the one outcome an operator must act on by hand.
func (*BackupUserError) Error ¶ added in v0.4.0
func (e *BackupUserError) Error() string
func (*BackupUserError) Unwrap ¶ added in v0.4.0
func (e *BackupUserError) Unwrap() error
type BackupUserOptions ¶ added in v0.4.0
type BackupUserOptions struct {
// Profile is the ~/.aws/credentials section that receives the key.
Profile string
}
BackupUserOptions carries the operator's one choice into the provisioner.
type BackupUserReport ¶ added in v0.4.0
type BackupUserReport struct {
UserName string
UserCreated bool // CreateUser succeeded
UserExisted bool // EntityAlreadyExists → reused
// PolicyName is this bucket's managed policy, BackupUserPolicyNameFor(bucket).
PolicyName string
PolicyCreated bool // CreatePolicy succeeded; false when it already existed
// PolicyUpdated: an existing policy received a new default version — a
// second prefix merged in, or the canonical policy changed since it was
// written. False on a rerun that changed nothing.
PolicyUpdated bool
PolicyAttached bool // AttachUserPolicy succeeded (idempotent on rerun)
// LegacyPolicyRemoved: the inline policy from before per-bucket managed
// policies was found, fully covered by the managed one, and deleted.
LegacyPolicyRemoved bool
AccessKeyID string
Profile string
CredentialsPath string
// ProfileSwitched is set by the engine once the new identity verified
// and sentra.yaml's profile now names it.
ProfileSwitched bool
// Warning is set by the engine on any failure; setup continues on the
// signed-in session and the wizard shows this text.
Warning string
}
BackupUserReport is the NON-SECRET outcome of provisioning. It carries the access key ID (an identifier, safe to display) and never the secret: the secret exists only inside the Effects implementation, between CreateAccessKey and the credentials-file write.
func DefaultProvisionBackupUser ¶ added in v0.4.0
func DefaultProvisionBackupUser(ctx context.Context, cfg *config.Config, opts BackupUserOptions) (BackupUserReport, error)
DefaultProvisionBackupUser is the production Effects driver: it authenticates with the credential chain cfg currently names (the session that just signed in) via diag.LoadAWSConfig — the same loader the identity check used, so "which credentials" can never differ between the check and the mutation — then creates the scoped user and stores its key.
type Effects ¶
type Effects interface {
// EnsureAWSCLI verifies the AWS CLI is installed, optionally installing
// it via the confirmed package-manager plan (brew). confirm is nil in
// the TUI, which handles a missing CLI with an ErrorAdvice modal.
EnsureAWSCLI(ctx context.Context, confirm AWSCLIInstallConfirm) (AWSCLIInstallReport, error)
AWSLogin(ctx context.Context, profile string, region string) error
CheckAWSSSOConfigured(ctx context.Context, profile string) (bool, error)
AWSConfigureSSO(ctx context.Context, profile string) error
AWSSSOLogin(ctx context.Context, profile string) error
// CheckAWSSDKIdentity verifies credentials through the SDK credential
// chain; delegates to diag.CheckSDKIdentity.
CheckAWSSDKIdentity(ctx context.Context, cfg *config.Config) error
// PrepareAWS performs the deterministic bucket-side setup work.
PrepareAWS(ctx context.Context, cfg *config.Config, opts AWSPrepareOptions) (AWSPrepareReport, error)
// ProvisionBackupUser creates the scoped IAM user, attaches the canonical
// policy, mints a key, and writes it to ~/.aws/credentials — all inside
// this one call, so the secret never crosses the seam. Returns a
// non-secret report; any error is a *BackupUserError when classifiable.
ProvisionBackupUser(ctx context.Context, cfg *config.Config, opts BackupUserOptions) (BackupUserReport, error)
NewStore(ctx context.Context, cfg *config.Config) (blobstore.Store, error)
// SavePassphrase persists the passphrase to the OS keyring. The engine
// only ever calls this AFTER repo init or a verified repo.Open.
SavePassphrase(cfg *config.Config, passphrase []byte) error
}
Effects is the side-effecting seam of the setup engine. Its method set mirrors the func fields of the former cli.SetupDeps, the injection seam of the deleted huh wizard. Tests inject a fake Effects; production uses DefaultEffects.
func DefaultEffects ¶
func DefaultEffects() Effects
DefaultEffects returns the production side-effecting seam.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine sequences the side-effecting steps of setup — AWS auth + bucket prep, backup-user provisioning, config write, repo init — over an injected Effects seam. It contains NO huh forms, NO stdout writes, and NO cobra: the TUI wizard is the only sequencer, driving it from tea messages. `sentra setup` is a thin CLI launcher for that same wizard, not a second driver of the engine.
func (*Engine) DraftPath ¶
DraftPath returns the draft sibling of cfgPath: a dotfile named after the config so one directory can hold drafts for several config paths.
func (*Engine) InitRepo ¶
func (e *Engine) InitRepo(ctx context.Context, cfg *config.Config, pass []byte, save bool) (InitResult, error)
InitRepo initializes (or, if already present, verifies) the encrypted repository for cfg using pass, optionally saving pass to the OS keyring. The caller owns pass and its zeroization — the TUI wizard zeroizes its masked-input buffer. Headless port of the deleted CLI wizard's runSetupInit, minus its optional-dep nil guards: Effects methods are always present.
func (*Engine) PrepareAWS ¶
func (e *Engine) PrepareAWS(ctx context.Context, p *Plan) (AWSAuthReport, AWSPrepareReport, error)
PrepareAWS runs one pass of the AWS auth + bucket-prep + backup-user sequence for p and returns the auth report, the prepare report, and any error. It is the headless body of the deleted CLI wizard's auth+prepare loop MINUS the huh repair prompt and stdout progress: on failure it classifies and returns the error rather than prompting. Callers own the retry decision (the TUI wizard mutates the plan and calls PrepareAWS again).
func (*Engine) RemoveDraft ¶
RemoveDraft best-effort deletes the draft. A leftover non-secret draft is less harmful than turning a successful setup into a failure, so errors are swallowed.
func (*Engine) WriteConfig ¶
WriteConfig writes p.Config to cfgPath. Progress output is the driver's responsibility, not the engine's — the TUI wizard renders its own checklist.
func (*Engine) WriteDraft ¶
WriteDraft persists a non-secret setup draft next to cfgPath so an interrupted run can be resumed: the wizard writes it before provisioning and RemoveDraft clears it only on success, so a draft left on disk is the record of a run that failed partway. cli.loadSetupDraft is the reader — it pre-fills the wizard on the next launch when no real config exists. config never serializes secrets, so the draft is safe to leave on disk.
type EnvProbe ¶
type EnvProbe interface {
Getenv(key string) string
DefaultProfileFromConfig() string
HasEnvCredentials() bool
}
EnvProbe reads the ambient AWS environment so DefaultPlan stays a pure transform in tests: production wires DefaultEnvProbe, tests inject a fake. It never reads secrets — only presence of credentials and profile/region hints used to pre-fill the wizard.
func DefaultEnvProbe ¶
func DefaultEnvProbe() EnvProbe
DefaultEnvProbe reads the real process environment and ~/.aws/config.
type IAMPolicyDocument ¶
type IAMPolicyDocument struct {
Version string `json:"Version"`
Statement []IAMPolicyStatement `json:"Statement"`
}
IAMPolicyDocument is a least-privilege AWS IAM policy for Sentra, rendered as non-secret JSON for the operator to paste into AWS.
func BuildIAMPolicy ¶
func BuildIAMPolicy(bucket string, prefix string) IAMPolicyDocument
BuildIAMPolicy assembles the three-statement least-privilege policy: bucket controls used during setup and doctor, bucket listing, and object CRUD on the repo keys.
The bucket-level statements are deliberately unconditioned. HeadBucket — the doctor's reachability probe and setup's bucket-exists check and waiter — authorizes as s3:ListBucket, and its request context (like GetBucketLocation's) carries no s3:prefix key; IAM evaluates a condition on an absent key as false, so a StringLike s3:prefix condition here denies Sentra's own probes under Sentra's own recommended policy. The trade-off is that the identity can list every key NAME in the bucket; object reads and writes stay scoped to the prefix by SentraRepositoryObjects.
type IAMPolicyStatement ¶
type IAMPolicyStatement struct {
Sid string `json:"Sid"`
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource []string `json:"Resource"`
Condition map[string]any `json:"Condition,omitempty"`
}
IAMPolicyStatement is one statement in an IAMPolicyDocument. Condition is part of the general IAM shape but BuildIAMPolicy never sets it — see BuildIAMPolicy for why the bucket statements must stay unconditioned. It is kept so the simulator in iam_policy_test.go evaluates any future condition against the calls Sentra actually makes instead of ignoring it.
type InitResult ¶
InitResult reports the outcome of initializing the encrypted repository.
type Plan ¶
type Plan struct {
Config config.Config
Backend Backend
PrepareAWS bool
AWSAuthMethod AWSAuthMethod
CreateBucket bool
BlockPublicAccess bool
DefaultEncryption bool
PrintIAMPolicy bool
SavePassphrase bool
InitRepo bool
// PassphraseSource names where a non-interactive passphrase came from
// (config.PassphraseSourceFile / config.PassphraseSourceEnv), empty when the
// operator typed it into the wizard. It is a LABEL for the review screen —
// never the passphrase, never a file's contents, and never a path, so it is
// safe everywhere a plan is rendered. The engine ignores it: the secret
// itself is passed to InitRepo as an argument.
PassphraseSource string
// ProvisionBackupUser asks PrepareAWS to create the scoped IAM user and
// switch the config to its static-key profile after a login/SSO sign-in.
// Ignored for existing-credentials and skip (see ShouldProvisionBackupUser).
ProvisionBackupUser bool
// BackupUserProfile is the ~/.aws/credentials section for the minted
// key; empty means the plan-derived default, read only through
// ResolveBackupUserProfile so it steps aside from the session profile
// the same way on the review screen and in the credentials file.
BackupUserProfile string
// ProvisionedBackupUserProfile names the ~/.aws/credentials section an
// earlier attempt in the same wizard run already filled: the backup user
// exists, its key is saved there, and the engine verified the identity
// before a later step failed. The TUI wizard sets it when it adopts that
// switch on the way to its failure screen, so the retry's review can say
// the user is already there instead of "skipped" — a word that invites
// the operator to go back and ask for it again. A label only: the engine
// ignores it, and an explicit ProvisionBackupUser still wins.
ProvisionedBackupUserProfile string
}
Plan is the complete set of actions the setup wizard selected. The TUI wizard builds this and hands it to the engine; the engine never re-reads the terminal.
func DefaultPlan ¶
DefaultPlan builds the wizard's starting plan from the current config and the ambient AWS environment. Ported from the CLI wizard's defaultSetupPlan + applySetupSmartDefaults; the os.Getenv / ~/.aws/config reads now go through probe so the transform is testable without touching the real environment.