| 1 | import { Random } from "../../Random.ts"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Secret from "../SecretsStore/Secret.ts"; |
| 4 | import { Store as SecretsStore } from "../SecretsStore/SecretsStore.ts"; |
| 5 | |
| 6 | /** |
| 7 | * The account-wide Secrets Store that backs every secret used by the |
| 8 | * state store worker. `SecretsStore` adopts the single store that |
| 9 | * already exists on the account, or creates one if none exists. |
| 10 | */ |
| 11 | export const Store = SecretsStore("StateStoreSecrets"); |
| 12 | |
| 13 | /** |
| 14 | * The randomly generated bearer token value. Generated once on create |
| 15 | * and persisted in alchemy state, so subsequent deploys keep the same |
| 16 | * value unless the resource is replaced. |
| 17 | */ |
| 18 | export const TokenValue = Random("StateStoreAuthTokenValue"); |
| 19 | |
| 20 | /** |
| 21 | * The name of the secret in the Cloudflare Secrets Store that contains the bearer token. |
| 22 | */ |
| 23 | export const AuthTokenSecretName = "AlchemyStateStoreToken" as const; |
| 24 | |
| 25 | /** |
| 26 | * The bearer token used to authenticate every request to the state |
| 27 | * store worker. The value comes from {@link TokenValue} and lives in |
| 28 | * the account-wide Cloudflare Secrets Store so it can be bound into |
| 29 | * the worker without bundling the raw string. |
| 30 | */ |
| 31 | export const AuthToken = Effect.gen(function* () { |
| 32 | const store = yield* Store; |
| 33 | const random = yield* TokenValue; |
| 34 | return yield* Secret.Secret(AuthTokenSecretName, { |
| 35 | name: AuthTokenSecretName, |
| 36 | store, |
| 37 | value: random.text, |
| 38 | }); |
| 39 | }); |
| 40 | |
| 41 | /** |
| 42 | * A 32-byte (256-bit) random value, hex-encoded, that seeds the |
| 43 | * AES-CTR key used to encrypt resource state at rest. Generated once |
| 44 | * and persisted, so the ciphertext stored by the Durable Object can |
| 45 | * always be decrypted by subsequent worker boots. |
| 46 | */ |
| 47 | export const EncryptionKeyValue = Random("StateStoreEncryptionKeyValue", { |
| 48 | bytes: 32, |
| 49 | }); |
| 50 | |
| 51 | export const EncryptionKeySecretName = |
| 52 | "AlchemyStateStoreEncryptionKey" as const; |
| 53 | |
| 54 | /** |
| 55 | * The encryption key secret. The raw hex-encoded bytes live inside |
| 56 | * Cloudflare's Secrets Store; the Durable Object binds to it at |
| 57 | * runtime to derive an AES-CTR `CryptoKey` via Web Crypto's |
| 58 | * `subtle.importKey`. |
| 59 | */ |
| 60 | export const EncryptionKey = Effect.gen(function* () { |
| 61 | const store = yield* Store; |
| 62 | const random = yield* EncryptionKeyValue; |
| 63 | return yield* Secret.Secret("StateStoreEncryptionKey", { |
| 64 | name: EncryptionKeySecretName, |
| 65 | store, |
| 66 | value: random.text, |
| 67 | }); |
| 68 | }); |