trust

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Folder trust — the per-directory consent gate that fires before the TUI opens a session in a previously-unseen workspace. Mirrors Claude Code's workspace-trust dialog. See yottacode-roadmap/folder-trust.md for the design.

Trust is user-scope: one file at ~/.yottacode/trusted-roots.json records every directory the user has explicitly accepted. A trusted root covers every descendant via pathUnder, so cloning a new repo under an already-trusted parent does not re-prompt.

The store is intentionally narrow:

  • JSON only. Matches the existing ~/.yottacode/ conventions (auth/openai-auth.json, config.toml, etc.).
  • No SQLite, no per-project index — trust is one decision per absolute path, recorded once.
  • Atomic write (temp + rename) so a crash mid-save can't leave a half-written roots file that silently drops every existing entry.
  • User-writable but agent-deny-listed via DefaultDenyPaths so the model's write tools can't self-grant trust.

Index

Constants

View Source
const EnvTrustAll = "YOTTACODE_TRUST_ALL"

EnvTrustAll, when set to "1", suppresses every trust prompt and proceeds as if cwd were trusted. CI escape hatch — does NOT write anything to trusted-roots.json, because CI invocations shouldn't silently grow a user's persistent trust set.

View Source
const Version = 1

Version is the on-disk schema version. Bumped only on a breaking change; readers tolerate unknown fields so additive changes don't require a bump.

Variables

View Source
var ErrUserDeclined = errUserDeclined{}

ErrUserDeclined is returned by Ensure when the user picks "No" at the trust prompt. The caller should exit non-zero without printing a redundant "error:" prefix.

Functions

func DefaultStorePath

func DefaultStorePath() (string, error)

DefaultStorePath returns ~/.yottacode/trusted-roots.json. The caller is expected to create the parent dir if writing; Load tolerates a missing file by returning an empty Store.

func Ensure

func Ensure(store *Store, storePath, cwd string, allowPaths []string, interactive bool, in io.Reader, out io.Writer) error

Ensure runs the trust gate for cwd against an io.Reader/Writer pair. Used by tests with bytes.Buffer streams; production TUI path uses EnsureInteractive instead (which drives a Bubbletea picker).

  • If IsTrusted(store, cwd, allowPaths) returns true: no-op.
  • Else, if interactive==false (non-TTY oneshot mode): no-op. Matches Claude's `-p` behavior — trust verification is skipped in non-interactive runs.
  • Else: render Prompt to in/out. On Yes, add cwd to the store and save. On No, return ErrUserDeclined.

func EnsureInteractive

func EnsureInteractive(store *Store, storePath, cwd string, allowPaths []string) error

EnsureInteractive is the production TUI entry point: same gating as Ensure, but the prompt step runs a Bubbletea picker (Up/Down/Enter) instead of the line-based fallback. Caller must have confirmed stdin/stdout are TTYs.

func IsInteractiveStream

func IsInteractiveStream(f *os.File) bool

IsInteractiveStream reports whether f is a terminal. Wrapped here so the trust package can decide without pulling golang.org/x/term into every caller; the same check appears in cmd/yottacode/main.go for the update-prompt path.

func IsTrusted

func IsTrusted(s *Store, cwd string, allowPaths []string) bool

IsTrusted reports whether cwd is trusted under any of the signals that satisfy the gate:

  1. cwd is in trusted-roots.json (exact or subfolder)
  2. EnvTrustAll is set to "1" (CI escape hatch — does not persist)
  3. cwd is at or under any entry in allowPaths (passed via --allow-paths or $YOTTACODE_ALLOW_PATHS — explicit per-session declaration, also does not persist)

Signals 2 and 3 do not write to the store; they expire when the env var or flag goes away.

func Save

func Save(path string, s *Store) error

Save writes s to path atomically (temp + rename). Creates the parent dir at 0700 if missing. The file is 0644 — trust roots are not secrets, just user preferences.

Types

type PromptResult

type PromptResult int

PromptResult records what the user chose at the trust gate.

const (
	PromptYes PromptResult = iota
	PromptNo
)

func Prompt

func Prompt(in io.Reader, out io.Writer, cwd string) (PromptResult, error)

Prompt renders the first-launch trust dialog to out and reads a single-character answer from in. The wording mirrors Claude Code's prompt for users who arrive from there; plain-text only per [[feedback_no_emoji_in_ui]].

Treated as Yes: "y", "Y", "yes", "Yes", "1" Treated as No: everything else (including empty input — the

safe default is to NOT trust)

func PromptInteractive

func PromptInteractive(cwd string) (PromptResult, error)

PromptInteractive opens the Bubbletea trust dialog and returns the user's choice. Caller is responsible for guarding with IsInteractiveStream — Bubbletea against a piped stdin/stdout produces garbled output. On Ctrl+C / Esc the result is PromptNo (mirrors the text Prompt).

type Root

type Root struct {
	Path      string    `json:"path"`
	TrustedAt time.Time `json:"trusted_at"`
}

Root is one trusted directory entry. Path is absolute and filepath.Clean-ed at the moment the user said Yes. TrustedAt is informational — printed by `yottacode trust list`, not consulted for any gating.

type Store

type Store struct {
	Version int    `json:"version"`
	Roots   []Root `json:"roots"`
}

Store is the in-memory representation of trusted-roots.json.

func Load

func Load(path string) (*Store, error)

Load reads the store at path. A missing file is a normal first- run state and returns an empty Store, not an error. A malformed file is an error so the user can fix it instead of silently running with an erased trust set.

func (*Store) Add

func (s *Store) Add(path string) (bool, error)

Add inserts a root if not already present (by exact path match). Subfolder inheritance via Contains is intentional — adding /foo when /foo/bar is already trusted records /foo too, because the user has now explicitly named the broader root. Returns true if the entry is new.

func (*Store) Clear

func (s *Store) Clear()

Clear empties the store. The next launch in every previously trusted directory will prompt again.

func (*Store) Contains

func (s *Store) Contains(path string) bool

Contains reports whether path equals or is descended from any trusted root. Path is absolutized; entries already are.

func (*Store) Remove

func (s *Store) Remove(path string) (bool, error)

Remove deletes a root by exact-path match. Returns true if an entry was removed.

Jump to

Keyboard shortcuts

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