Tags: cortexkit/commons
Tags
provider-usage: disclose a reading served through an ongoing failure (#… …12) A preserved last-known-good entry is currently byte-identical to a fresh one apart from fetchedAt, so a consumer cannot separate "this figure is old because the producer cannot reach the provider" from "this figure is old because nothing polled recently". Those have opposite remedies, and a consumer with only a timestamp has to guess with a wall-clock threshold -- which denies fresh-enough data in order to catch stale data. One consumer built exactly that and had a dispatch blocked on a 58-minute snapshot, which was genuinely stale but indistinguishable from a slow poll at the moment it mattered. `since` is deliberately not fetchedAt: the reading was taken when it was taken, and the failure began afterwards. The gap between them is how long the producer has been blind, which is what a staleness policy wants. Additive and absent on a fresh entry, so today's shape is byte-identical and a consumer predating the field decodes unchanged -- both pinned.
Merge pull request #11 from cortexkit/balance-axis Add prepaid balances and credit pools to ProviderUsage
store: make the database, its WAL and lease files owner-only (#9) SQLite sets no mode of its own, so open_sqlite left the file mode to the caller's umask and the shipped default was world-readable 0644. Measured on a real deployment: 11 of 11 module stores at 0644, including cortexkit-credentials. A crate that already decides WAL mode, busy timeout and foreign keys has taken responsibility for how the file behaves on disk. Leaving permissions to callers means the decision is made by the ambient umask, which is to say not made at all. A group-readable store is not a configuration this crate supports anyway: it hands out an exclusive single-writer lease, so an out-of-band reader is already outside the contract. The exposure, stated honestly rather than as an implied multi-user threat: a single-account host has no other human to read these files. What 0644 does expose them to is every process running as this user — every module, every worker, every tool — and anything that copies the tree: a backup, a restore, an install into a shared location, a container bind-mount. THE WAL IS THE HALF THAT GETS MISSED. Recently committed rows live there until a checkpoint, so protecting only the database leaves the newest data readable while the database file itself reads as correct. On the measured host the WALs are routinely larger than their databases — alfonso-core's is 23MB. Lease files are hardened too, in cortexkit-lease where they are created. Their exposure is integrity rather than privacy: the lease carries the persisted epoch that is the single-writer fence token, so a writable lease file lets a stale writer's fence be forged. Applied on OPEN rather than only at creation, because every store already deployed was created at 0644 — a creation-time-only fix protects exactly the installations with no history. A path that is not a regular file is refused rather than adjusted: following a symlink would chmod a file the caller never named, which is a privilege-escalation primitive wearing a hardening step's clothes. Each assertion is mutation-proved separately. Worth recording that the first version of the WAL test could not fail: it used a FIRST open, where SQLite creates the WAL inheriting the database's already-corrected mode. Dropping '-wal' from the protected suffixes passed it. The test now reopens a store with a leftover permissive WAL — the state an unclean shutdown leaves behind, and the only one where a permissive WAL can be waiting at open time.
cortexkit-provider-usage 0.3.0: add optional usedCount + totalCount t… …o RateWindow Additive absolute-count fields for windows where the provider knows (or can derive) the consumed and total values. Enables human-facing UIs to show '10,336 / 40,000' alongside the percentage for richer context (e.g. qwen-cloud token-plan where the percentage is entitled but the absolute pair makes the enforcement gap legible). Both fields are skip-if-none + camelCase, so unpopulated windows serialize byte-identically to 0.2.0.
cortexkit-provider-usage 0.2.0: add optional apiProvider field (#6) Adds ProviderUsage.api_provider (wire: apiProvider, camelCase, omitted when absent) carrying the canonical models.dev provider slug alongside the CodexBar provider name — e.g. "openai" for provider=="codex", "anthropic" for "claude", "google" for "gemini", "xai" for "grok". Three consumers (ALF's router, the ck CLI, astrocyte's capacity axis) each hand-roll the same CodexBar→canonical translation today; this field lets them key on one canonical name instead. Producers populate it when the canonical name is known and leave it absent for providers with no models.dev counterpart (consumers fall back to provider). Additive + skip-if-none: unpopulated entries serialize byte-identically to 0.1.
Add cortexkit-provider-usage: shared wire types for the quota usage.g… …et payload (#5) Pure serde types (ProviderUsage, Usage, RateWindow, AccountInfo, SavedResets, CreditExpiry, ExtraWindow) extracted from ai-provider-quota's model.rs so every consumer of the usage.get wire — the quota module that produces it, ALF's router reads, astrocyte's capacity axis, and the ck quota renderer — compiles against one definition and the shape cannot drift without a shared-crate PR each side reviews. Shape, not policy: read-time transform semantics (the quota module's banked-reset relaxation, which zeroes used_percent and carries the provider truth in raw_used_percent) are producer behavior documented on the fields but not enforced by these types. serde-only, no logic; serde_json is a dev-dependency for the wire-shape tests. The reserved prepaid-Balance seam is intentionally NOT included (it was never populated and is wire-neutral); it joins this crate additively when the balance axis is designed. Follows the cortexkit-model-catalog precedent.