g1t/services/repos/src/shared.rs
Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 1 | //! What isolates share with each other: a key-value store whose every value |
| 2 | //! is sealed (AES-256-GCM) under the service's own key, `REPOS_KEY`, with | |
| 3 | //! the value's own key as associated data, so a value copied onto another | |
| 4 | //! key does not open. | |
| 5 | //! | |
| 6 | //! Optional: without the `GIT_CACHE` binding or the key, nothing is shared | |
| 7 | //! and each isolate keeps only what it made itself. Self-hosted, any | |
| 8 | //! Workers KV-compatible store will do. | |
| 9 | ||
| 10 | use g1t_secrets::Sealer; | |
| 11 | use worker::Env; | |
| 12 | use worker::kv::KvStore; | |
| 13 | ||
| 14 | /// The shortest life Workers KV gives a value. | |
| 15 | pub const MIN_TTL_SECONDS: u64 = 60; | |
| 16 | ||
| 17 | pub struct Shared { | |
| 18 | kv: KvStore, | |
| 19 | sealer: Sealer, | |
| 20 | } | |
| 21 | ||
| 22 | impl Shared { | |
| 23 | pub fn from_env(env: &Env) -> Option<Shared> { | |
| 24 | let kv = env.kv("GIT_CACHE").ok()?; | |
| 25 | let sealer = Sealer::new(&env.secret("REPOS_KEY").ok()?.to_string())?; | |
| 26 | Some(Shared { kv, sealer }) | |
| 27 | } | |
| 28 | ||
| 29 | /// The value under `key`, if there is one that opens. A failure to | |
| 30 | /// read is a miss. | |
| 31 | pub async fn get(&self, key: &str) -> Option<Vec<u8>> { | |
| 32 | let sealed = self.kv.get(key).bytes().await.ok()??; | |
| 33 | self.sealer.open_bytes(&sealed, key) | |
| 34 | } | |
| 35 | ||
| 36 | /// Keeps `value` under `key` for `ttl_seconds` (at least a minute). A | |
| 37 | /// failure only costs a later miss. | |
| 38 | pub async fn put(&self, key: &str, value: &[u8], ttl_seconds: u64) { | |
| 39 | let sealed = self.sealer.seal_bytes(value, key); | |
| 40 | let put = match self.kv.put_bytes(key, &sealed) { | |
| 41 | Ok(put) => put.expiration_ttl(ttl_seconds.max(MIN_TTL_SECONDS)), | |
| 42 | Err(_) => return, | |
| 43 | }; | |
| 44 | if let Err(error) = put.execute().await { | |
| 45 | worker::console_error!("shared cache: {key} not kept: {error}"); | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | pub async fn delete(&self, key: &str) { | |
| 50 | if let Err(error) = self.kv.delete(key).await { | |
| 51 | worker::console_error!("shared cache: {key} not removed: {error}"); | |
| 52 | } | |
| 53 | } | |
| 54 | } |