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
- Variables
- func DefaultStorePath() (string, error)
- func Ensure(store *Store, storePath, cwd string, allowPaths []string, interactive bool, ...) error
- func EnsureInteractive(store *Store, storePath, cwd string, allowPaths []string) error
- func IsInteractiveStream(f *os.File) bool
- func IsTrusted(s *Store, cwd string, allowPaths []string) bool
- func Save(path string, s *Store) error
- type PromptResult
- type Root
- type Store
Constants ¶
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.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
IsTrusted reports whether cwd is trusted under any of the signals that satisfy the gate:
- cwd is in trusted-roots.json (exact or subfolder)
- EnvTrustAll is set to "1" (CI escape hatch — does not persist)
- 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.
Types ¶
type PromptResult ¶
type PromptResult int
PromptResult records what the user chose at the trust gate.
const ( PromptYes PromptResult = iota PromptNo )
func Prompt ¶
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 ¶
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 ¶
Store is the in-memory representation of trusted-roots.json.
func Load ¶
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 ¶
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.