feat: keep what was loaded between runs, and start where you left off (#2)

Starting lazysecrets again now shows what you had loaded at once, and puts you back where you were.

- **Cache file** `~/.cache/lazysecrets/cache.json` (0600, written atomically): the Sources lists, vaults/scopes, secrets' metadata, Describe results and each list's cursor. **Never values**, and never repos (they're on disk anyway). Loaded on start; lists older than 2 minutes reload in the background as before. Saved every 30s when changed, and on quit.
- **Housekeeping:** entries not loaded for 30 days are dropped, as is everything from accounts no longer signed in (every cache key starts with the account ID). The file has a format version; another version is ignored and replaced. `cache: false` turns it off; `--demo` never uses it.
- **Sources are cached too:** Azure lists subscriptions over the network, so without this the Sources pane would still wait.
- **Go to** searches everything cached straight away, and its background loading now also refreshes lists from an earlier run instead of skipping them.
- **Where you left off:** the state file remembers the selected source, vault or secret per folder (the `--dir` or working directory, 100 most recent folders), and the next start in that folder goes back there quietly (no error if it's gone). Starting on a link skips it.

Docs: `cache:` in config.md, a Cache section, the place in State, and the README Privacy section now mentions the cache file.

Tests: cache round trip with a backend too slow to answer (lists and cursor come from the file), no value in the file, file mode 0600, old and signed-out entries dropped, back where it was left, not for another folder or a link.

This touches `app.go` near PR #1's changes (Model fields, the Update switch), so whichever merges second needs a small conflict fix.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Karl-Einar Bergstrøm <karl-einarb@outlook.com>
Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
keb committed 2026-10-11 09:53:32 +02:00
1 parent 17d2705e12
commit 10dc747f0f
11 files changed
+512 -12

No files matched your search

+41
View File
@@ -69,6 +69,33 @@ func (s *Store) SetUpdateCheck(c UpdateCheck) error {
return s.save(func(d *file) { d.Update = c })
}
// Place is where lazysecrets was left when it last ran in dir.
func (s *Store) Place(dir string) (Place, bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok := s.data.Places[dir]
return p, ok
}
// SetPlace remembers where lazysecrets was left in dir.
func (s *Store) SetPlace(dir string, p Place) error {
return s.save(func(d *file) {
if d.Places == nil {
d.Places = map[string]Place{}
}
d.Places[dir] = p
for len(d.Places) > maxPlaces {
oldest := ""
for k, v := range d.Places {
if oldest == "" || v.At.Before(d.Places[oldest].At) {
oldest = k
}
}
delete(d.Places, oldest)
}
})
}
// Layout is the user's pane sizing, as offsets from the default layout so
// it adapts to the terminal size.
type Layout struct {
@@ -84,8 +111,22 @@ type file struct {
Flags map[string]bool `json:"flags,omitempty"`
// Update is the last check for a newer release.
Update UpdateCheck `json:"update,omitzero"`
// Places is where lazysecrets was left, by the folder it ran in.
Places map[string]Place `json:"places,omitempty"`
}
// Place is what was selected when lazysecrets quit: an item's link, and
// the account and source it was read through.
type Place struct {
Link string `json:"link"`
Account string `json:"account"`
Source string `json:"source"`
At time.Time `json:"at"`
}
// maxPlaces bounds Places: the folders used most recently are kept.
const maxPlaces = 100
// UpdateCheck is the last look for a newer release: when, and what the
// latest was (its tag, changelog and page), so the next start within a
// day shows it without asking again.