max / makenotwork
| 1 | -- Separate credential for the server-to-server SDK key endpoints. |
| 2 | -- |
| 3 | -- `sync_apps.api_key` reaches every client: the apps compile it in with |
| 4 | -- `include_str!`, so it ships inside each binary and `strings` recovers it. |
| 5 | -- That is fine for what it is -- a public client identifier -- but |
| 6 | -- `/api/sync/keys/{claim,release,list}` authenticated on that value alone. |
| 7 | -- Anyone with a shipped binary could therefore consume the app's key cap |
| 8 | -- under `per_key` billing and pollute claim attribution. |
| 9 | -- |
| 10 | -- The keys endpoints get their own secret instead: generated in the |
| 11 | -- dashboard, shown once, held only by a developer backend, never compiled |
| 12 | -- into a client. Stored SHA-256 hashed like the api_key, with a prefix kept |
| 13 | -- for display. |
| 14 | -- |
| 15 | -- Nullable, and no backfill: an app without a secret cannot call the keys |
| 16 | -- endpoints at all. Deriving one from the api_key would carry the same |
| 17 | -- weakness forward, and no first-party client calls these routes today. |
| 18 | sync_apps ADD COLUMN IF NOT EXISTS keys_secret_hash TEXT; |
| 19 | sync_apps ADD COLUMN IF NOT EXISTS keys_secret_prefix TEXT; |
| 20 | |
| 21 | -- Lookup path for the keys endpoints. Partial: most apps never set one. |
| 22 | NOT EXISTS sync_apps_keys_secret_hash_idx |
| 23 | ON sync_apps (keys_secret_hash) |
| 24 | WHERE keys_secret_hash IS NOT NULL; |
| 25 |