Expand description
The key-pool state file: pools.json.
A key pool is several interchangeable credentials for one service. Which one to hand out next, which are cooling after a rate limit, and how often each has been used are per-machine facts, so they are kept here rather than in the vault. Three reasons, worst failure first:
save_vaultappends avault_auditrow on every update. A CI loop callingunv execwould grow the hash-chained log without bound - the same reason read events stopped being audited.save_vaultis a compare-and-swap. Concurrent reads against one vault would collide and start returning conflicts for reads.- A vault is shared; a rotation cursor is not. Two CI runners pulling from one remote vault want independent cursors.
§Why this lives in vault-core
Two programs read this file: unv and the desktop app. The app’s
app_data_dir and the CLI’s dirs::data_dir()/io.unenverse resolve to the
same directory, so one vault produces the same local_vault_key in both -
report a key rate limited from CI and the desktop shows it cooling.
That only holds while both agree on the file’s shape, its path, and the cooldown arithmetic. This project has been bitten by two implementations of one format drifting apart often enough to have a convention about it, so there is exactly one implementation and both callers use it.
§Shape
{
"version": 1,
"vaults": {
"local:/home/me/.local/share/io.unenverse/vault.db": {
"github-ci": {
"cursor": 1,
"members": {
"<entry_ck>": {
"uses": 12,
"last_used_at": "2026-08-25T11:02:31Z",
"cooling_until": "2026-08-25T11:17:31Z"
}
}
}
}
}
}Members are keyed by entry_ck - the same stable identity
version history and audit attribution use - so renaming an entry keeps its
pool state, and two entries sharing a provider never collapse into one.
Structs§
- Member
State - One member’s stored state.
Functions§
- cursor
- The cursor for a pool, or 0.
- forget
- Forget a pool’s cursor, cooldowns and counts for one vault.
- is_
cooling - Is this member cooling at
now? - iso_at
- RFC 3339 for an arbitrary instant.
- load
- Read the state file, or an empty document.
- local_
vault_ key - State is filed per vault, so connecting to a different server does not inherit the last one’s cursor - the same reasoning as resetting view state on a vault switch in the app.
- member_
state - Read one member’s state out of a loaded document.
- now_ts
- Seconds since the epoch, now.
- parse_
rfc3339 - Read one of the timestamps
iso_atwrites: exactlyYYYY-MM-DDTHH:MM:SSZ. - pick_
index - Round-robin over the members that are not cooling, starting at
cursor. - record_
use - Record that a member was handed out, and where the cursor moves to.
- remote_
vault_ key - The remote counterpart of
local_vault_key. - save
- Write the state file, creating its directory and restricting it to its owner.
- set_
cooldown - Put a member on cooldown until
until_ts, or clear its cooldown withNone. - state_
path - Where
pools.jsonlives.