@homeflare/alchemy 0.43.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/caddy/index.js +35 -1
- package/dist/caddy/index.js.map +3 -3
- package/dist/launchd/index.js +35 -1
- package/dist/launchd/index.js.map +3 -3
- package/dist/linux/index.js +35 -1
- package/dist/linux/index.js.map +3 -3
- package/dist/litellm/access-group-form.d.ts +26 -0
- package/dist/litellm/access-group-form.d.ts.map +1 -0
- package/dist/litellm/access-group-operations.d.ts +37 -0
- package/dist/litellm/access-group-operations.d.ts.map +1 -0
- package/dist/litellm/access-group-types.d.ts +49 -0
- package/dist/litellm/access-group-types.d.ts.map +1 -0
- package/dist/litellm/access-group.d.ts +55 -0
- package/dist/litellm/access-group.d.ts.map +1 -0
- package/dist/litellm/credential-errors.d.ts +131 -0
- package/dist/litellm/credential-errors.d.ts.map +1 -0
- package/dist/litellm/credential-form.d.ts +79 -0
- package/dist/litellm/credential-form.d.ts.map +1 -0
- package/dist/litellm/credential-operations.d.ts +84 -0
- package/dist/litellm/credential-operations.d.ts.map +1 -0
- package/dist/litellm/credential-rewrite.d.ts +6 -0
- package/dist/litellm/credential-rewrite.d.ts.map +1 -0
- package/dist/litellm/credential-types.d.ts +87 -0
- package/dist/litellm/credential-types.d.ts.map +1 -0
- package/dist/litellm/credential-values.d.ts +53 -0
- package/dist/litellm/credential-values.d.ts.map +1 -0
- package/dist/litellm/credential.d.ts +83 -0
- package/dist/litellm/credential.d.ts.map +1 -0
- package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
- package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-credential-litellm.d.ts +54 -0
- package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-keys.d.ts +53 -0
- package/dist/litellm/fake-keys.d.ts.map +1 -0
- package/dist/litellm/fake-litellm.d.ts +4 -1
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
- package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
- package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
- package/dist/litellm/fake-model-litellm.d.ts +48 -0
- package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
- package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-policy-litellm.d.ts +32 -0
- package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-registry-base.d.ts +42 -0
- package/dist/litellm/fake-registry-base.d.ts.map +1 -0
- package/dist/litellm/fake-stack.d.ts +8 -1
- package/dist/litellm/fake-stack.d.ts.map +1 -1
- package/dist/litellm/fake-team-litellm.d.ts +47 -0
- package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-tool-litellm.d.ts +28 -0
- package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
- package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
- package/dist/litellm/index.d.ts +18 -0
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +3078 -2
- package/dist/litellm/index.js.map +65 -4
- package/dist/litellm/key-errors.d.ts +169 -0
- package/dist/litellm/key-errors.d.ts.map +1 -0
- package/dist/litellm/key-form.d.ts +119 -0
- package/dist/litellm/key-form.d.ts.map +1 -0
- package/dist/litellm/key-harness.d.ts +41 -0
- package/dist/litellm/key-harness.d.ts.map +1 -0
- package/dist/litellm/key-metadata-errors.d.ts +30 -0
- package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
- package/dist/litellm/key-metadata.d.ts +22 -0
- package/dist/litellm/key-metadata.d.ts.map +1 -0
- package/dist/litellm/key-operations.d.ts +63 -0
- package/dist/litellm/key-operations.d.ts.map +1 -0
- package/dist/litellm/key-secret.d.ts +33 -0
- package/dist/litellm/key-secret.d.ts.map +1 -0
- package/dist/litellm/key.d.ts +78 -0
- package/dist/litellm/key.d.ts.map +1 -0
- package/dist/litellm/mcp-server-credential.d.ts +50 -0
- package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
- package/dist/litellm/mcp-server-errors.d.ts +101 -0
- package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
- package/dist/litellm/mcp-server-form.d.ts +56 -0
- package/dist/litellm/mcp-server-form.d.ts.map +1 -0
- package/dist/litellm/mcp-server-locate.d.ts +26 -0
- package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
- package/dist/litellm/mcp-server-operations.d.ts +63 -0
- package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
- package/dist/litellm/mcp-server-shadow.d.ts +24 -0
- package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
- package/dist/litellm/mcp-server-types.d.ts +118 -0
- package/dist/litellm/mcp-server-types.d.ts.map +1 -0
- package/dist/litellm/mcp-server-url.d.ts +11 -0
- package/dist/litellm/mcp-server-url.d.ts.map +1 -0
- package/dist/litellm/mcp-server.d.ts +95 -0
- package/dist/litellm/mcp-server.d.ts.map +1 -0
- package/dist/litellm/model-claim.d.ts +24 -0
- package/dist/litellm/model-claim.d.ts.map +1 -0
- package/dist/litellm/model-credential.d.ts +12 -0
- package/dist/litellm/model-credential.d.ts.map +1 -0
- package/dist/litellm/model-errors.d.ts +119 -0
- package/dist/litellm/model-errors.d.ts.map +1 -0
- package/dist/litellm/model-form.d.ts +78 -0
- package/dist/litellm/model-form.d.ts.map +1 -0
- package/dist/litellm/model-locate.d.ts +13 -0
- package/dist/litellm/model-locate.d.ts.map +1 -0
- package/dist/litellm/model-operations.d.ts +59 -0
- package/dist/litellm/model-operations.d.ts.map +1 -0
- package/dist/litellm/model-types.d.ts +91 -0
- package/dist/litellm/model-types.d.ts.map +1 -0
- package/dist/litellm/model.d.ts +88 -0
- package/dist/litellm/model.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-form.d.ts +19 -0
- package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-operations.d.ts +31 -0
- package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-types.d.ts +48 -0
- package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
- package/dist/litellm/policy-attachment.d.ts +61 -0
- package/dist/litellm/policy-attachment.d.ts.map +1 -0
- package/dist/litellm/policy-form.d.ts +22 -0
- package/dist/litellm/policy-form.d.ts.map +1 -0
- package/dist/litellm/policy-operations.d.ts +42 -0
- package/dist/litellm/policy-operations.d.ts.map +1 -0
- package/dist/litellm/policy-types.d.ts +57 -0
- package/dist/litellm/policy-types.d.ts.map +1 -0
- package/dist/litellm/policy.d.ts +65 -0
- package/dist/litellm/policy.d.ts.map +1 -0
- package/dist/litellm/providers.d.ts +1 -1
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/litellm/registry-errors.d.ts +62 -0
- package/dist/litellm/registry-errors.d.ts.map +1 -0
- package/dist/litellm/registry-support.d.ts +42 -0
- package/dist/litellm/registry-support.d.ts.map +1 -0
- package/dist/litellm/team-form.d.ts +26 -0
- package/dist/litellm/team-form.d.ts.map +1 -0
- package/dist/litellm/team-members.d.ts +10 -0
- package/dist/litellm/team-members.d.ts.map +1 -0
- package/dist/litellm/team-operations.d.ts +38 -0
- package/dist/litellm/team-operations.d.ts.map +1 -0
- package/dist/litellm/team-permission.d.ts +35 -0
- package/dist/litellm/team-permission.d.ts.map +1 -0
- package/dist/litellm/team-types.d.ts +101 -0
- package/dist/litellm/team-types.d.ts.map +1 -0
- package/dist/litellm/team.d.ts +67 -0
- package/dist/litellm/team.d.ts.map +1 -0
- package/dist/litellm/tool-policy-form.d.ts +21 -0
- package/dist/litellm/tool-policy-form.d.ts.map +1 -0
- package/dist/litellm/tool-policy-operations.d.ts +30 -0
- package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
- package/dist/litellm/tool-policy-types.d.ts +46 -0
- package/dist/litellm/tool-policy-types.d.ts.map +1 -0
- package/dist/litellm/tool-policy.d.ts +62 -0
- package/dist/litellm/tool-policy.d.ts.map +1 -0
- package/dist/litellm/toolset-form.d.ts +20 -0
- package/dist/litellm/toolset-form.d.ts.map +1 -0
- package/dist/litellm/toolset-operations.d.ts +39 -0
- package/dist/litellm/toolset-operations.d.ts.map +1 -0
- package/dist/litellm/toolset-types.d.ts +46 -0
- package/dist/litellm/toolset-types.d.ts.map +1 -0
- package/dist/litellm/toolset.d.ts +55 -0
- package/dist/litellm/toolset.d.ts.map +1 -0
- package/dist/openbao/index.js +35 -1
- package/dist/openbao/index.js.map +3 -3
- package/dist/ownership/rows.d.ts +11 -0
- package/dist/ownership/rows.d.ts.map +1 -1
- package/dist/postgres/connection.d.ts +14 -2
- package/dist/postgres/connection.d.ts.map +1 -1
- package/dist/postgres/database-sql.d.ts +27 -8
- package/dist/postgres/database-sql.d.ts.map +1 -1
- package/dist/postgres/fake-grants-parse.d.ts +37 -0
- package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
- package/dist/postgres/fake-grants-read.d.ts +61 -0
- package/dist/postgres/fake-grants-read.d.ts.map +1 -0
- package/dist/postgres/fake-grants-sql.d.ts +46 -0
- package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
- package/dist/postgres/fake-grants-write.d.ts +29 -0
- package/dist/postgres/fake-grants-write.d.ts.map +1 -0
- package/dist/postgres/fake-role-sql.d.ts +41 -0
- package/dist/postgres/fake-role-sql.d.ts.map +1 -0
- package/dist/postgres/fake-schema-sql.d.ts +42 -0
- package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
- package/dist/postgres/fake-sql-parse.d.ts +23 -0
- package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
- package/dist/postgres/fake-sql-quote.d.ts +12 -0
- package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
- package/dist/postgres/fake-sql.d.ts +42 -0
- package/dist/postgres/fake-sql.d.ts.map +1 -1
- package/dist/postgres/grants-attrs.d.ts +115 -0
- package/dist/postgres/grants-attrs.d.ts.map +1 -0
- package/dist/postgres/grants-declare.d.ts +88 -0
- package/dist/postgres/grants-declare.d.ts.map +1 -0
- package/dist/postgres/grants-diff.d.ts +25 -0
- package/dist/postgres/grants-diff.d.ts.map +1 -0
- package/dist/postgres/grants-errors.d.ts +137 -0
- package/dist/postgres/grants-errors.d.ts.map +1 -0
- package/dist/postgres/grants-existence.d.ts +14 -0
- package/dist/postgres/grants-existence.d.ts.map +1 -0
- package/dist/postgres/grants-ops.d.ts +65 -0
- package/dist/postgres/grants-ops.d.ts.map +1 -0
- package/dist/postgres/grants-ownership.d.ts +18 -0
- package/dist/postgres/grants-ownership.d.ts.map +1 -0
- package/dist/postgres/grants-plan-restore.d.ts +22 -0
- package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
- package/dist/postgres/grants-plan.d.ts +65 -0
- package/dist/postgres/grants-plan.d.ts.map +1 -0
- package/dist/postgres/grants-read-defaults.d.ts +17 -0
- package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
- package/dist/postgres/grants-read.d.ts +77 -0
- package/dist/postgres/grants-read.d.ts.map +1 -0
- package/dist/postgres/grants-refuse.d.ts +25 -0
- package/dist/postgres/grants-refuse.d.ts.map +1 -0
- package/dist/postgres/grants-sql.d.ts +42 -0
- package/dist/postgres/grants-sql.d.ts.map +1 -0
- package/dist/postgres/grants-words.d.ts +29 -0
- package/dist/postgres/grants-words.d.ts.map +1 -0
- package/dist/postgres/grants.d.ts +37 -0
- package/dist/postgres/grants.d.ts.map +1 -0
- package/dist/postgres/index.d.ts +31 -6
- package/dist/postgres/index.d.ts.map +1 -1
- package/dist/postgres/index.js +1998 -37
- package/dist/postgres/index.js.map +46 -7
- package/dist/postgres/installed-classifier.d.ts +13 -0
- package/dist/postgres/installed-classifier.d.ts.map +1 -0
- package/dist/postgres/providers.d.ts +16 -8
- package/dist/postgres/providers.d.ts.map +1 -1
- package/dist/postgres/psql-executor.d.ts +4 -0
- package/dist/postgres/psql-executor.d.ts.map +1 -1
- package/dist/postgres/role-attrs.d.ts +112 -0
- package/dist/postgres/role-attrs.d.ts.map +1 -0
- package/dist/postgres/role-diff.d.ts +22 -0
- package/dist/postgres/role-diff.d.ts.map +1 -0
- package/dist/postgres/role-errors.d.ts +132 -0
- package/dist/postgres/role-errors.d.ts.map +1 -0
- package/dist/postgres/role-membership-sql.d.ts +78 -0
- package/dist/postgres/role-membership-sql.d.ts.map +1 -0
- package/dist/postgres/role-provider.d.ts +11 -0
- package/dist/postgres/role-provider.d.ts.map +1 -0
- package/dist/postgres/role-scram.d.ts +8 -0
- package/dist/postgres/role-scram.d.ts.map +1 -0
- package/dist/postgres/role-secrets.d.ts +38 -0
- package/dist/postgres/role-secrets.d.ts.map +1 -0
- package/dist/postgres/role-sql.d.ts +68 -0
- package/dist/postgres/role-sql.d.ts.map +1 -0
- package/dist/postgres/role.d.ts +57 -0
- package/dist/postgres/role.d.ts.map +1 -0
- package/dist/postgres/schema-assert.d.ts +50 -0
- package/dist/postgres/schema-assert.d.ts.map +1 -0
- package/dist/postgres/schema-attrs.d.ts +51 -0
- package/dist/postgres/schema-attrs.d.ts.map +1 -0
- package/dist/postgres/schema-delete.d.ts +28 -0
- package/dist/postgres/schema-delete.d.ts.map +1 -0
- package/dist/postgres/schema-diff.d.ts +14 -0
- package/dist/postgres/schema-diff.d.ts.map +1 -0
- package/dist/postgres/schema-errors.d.ts +139 -0
- package/dist/postgres/schema-errors.d.ts.map +1 -0
- package/dist/postgres/schema-identity.d.ts +8 -0
- package/dist/postgres/schema-identity.d.ts.map +1 -0
- package/dist/postgres/schema-reconcile.d.ts +12 -0
- package/dist/postgres/schema-reconcile.d.ts.map +1 -0
- package/dist/postgres/schema-sql.d.ts +45 -0
- package/dist/postgres/schema-sql.d.ts.map +1 -0
- package/dist/postgres/schema-test-kit.d.ts +67 -0
- package/dist/postgres/schema-test-kit.d.ts.map +1 -0
- package/dist/postgres/schema.d.ts +54 -0
- package/dist/postgres/schema.d.ts.map +1 -0
- package/dist/proxmox/index.js +35 -1
- package/dist/proxmox/index.js.map +3 -3
- package/dist/release/fake-engine.d.ts +10 -0
- package/dist/release/fake-engine.d.ts.map +1 -1
- package/dist/release/index.js +35 -1
- package/dist/release/index.js.map +3 -3
- package/dist/valkey/acl-attrs.d.ts +94 -0
- package/dist/valkey/acl-attrs.d.ts.map +1 -0
- package/dist/valkey/acl-compare.d.ts +28 -0
- package/dist/valkey/acl-compare.d.ts.map +1 -0
- package/dist/valkey/acl-connection.d.ts +6 -0
- package/dist/valkey/acl-connection.d.ts.map +1 -0
- package/dist/valkey/acl-form.d.ts +62 -0
- package/dist/valkey/acl-form.d.ts.map +1 -0
- package/dist/valkey/acl-managed.d.ts +4 -0
- package/dist/valkey/acl-managed.d.ts.map +1 -0
- package/dist/valkey/acl-ops.d.ts +37 -0
- package/dist/valkey/acl-ops.d.ts.map +1 -0
- package/dist/valkey/acl-profiles.d.ts +53 -0
- package/dist/valkey/acl-profiles.d.ts.map +1 -0
- package/dist/valkey/acl-reconcile.d.ts +26 -0
- package/dist/valkey/acl-reconcile.d.ts.map +1 -0
- package/dist/valkey/acl.d.ts +24 -0
- package/dist/valkey/acl.d.ts.map +1 -0
- package/dist/valkey/connection-service.d.ts +22 -0
- package/dist/valkey/connection-service.d.ts.map +1 -0
- package/dist/valkey/connection.d.ts +59 -0
- package/dist/valkey/connection.d.ts.map +1 -0
- package/dist/valkey/errors.d.ts +165 -0
- package/dist/valkey/errors.d.ts.map +1 -0
- package/dist/valkey/fake-acl.d.ts +29 -0
- package/dist/valkey/fake-acl.d.ts.map +1 -0
- package/dist/valkey/fake-valkey.d.ts +35 -0
- package/dist/valkey/fake-valkey.d.ts.map +1 -0
- package/dist/valkey/handler-test-fixture.d.ts +20 -0
- package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
- package/dist/valkey/index.d.ts +18 -0
- package/dist/valkey/index.d.ts.map +1 -0
- package/dist/valkey/index.js +964 -0
- package/dist/valkey/index.js.map +27 -0
- package/dist/valkey/instance-attrs.d.ts +46 -0
- package/dist/valkey/instance-attrs.d.ts.map +1 -0
- package/dist/valkey/instance-form.d.ts +22 -0
- package/dist/valkey/instance-form.d.ts.map +1 -0
- package/dist/valkey/instance.d.ts +48 -0
- package/dist/valkey/instance.d.ts.map +1 -0
- package/dist/valkey/provider-socket-fixture.d.ts +8 -0
- package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
- package/dist/valkey/providers.d.ts +16 -0
- package/dist/valkey/providers.d.ts.map +1 -0
- package/dist/valkey/resp.d.ts +38 -0
- package/dist/valkey/resp.d.ts.map +1 -0
- package/dist/valkey/transport-error.d.ts +36 -0
- package/dist/valkey/transport-error.d.ts.map +1 -0
- package/dist/valkey/transport.d.ts +50 -0
- package/dist/valkey/transport.d.ts.map +1 -0
- package/docs/litellm-access-group.md +105 -0
- package/docs/litellm-credential.md +60 -0
- package/docs/litellm-key-metadata.md +56 -0
- package/docs/litellm-key.md +194 -0
- package/docs/litellm-mcp.md +187 -0
- package/docs/litellm-model.md +111 -0
- package/docs/litellm-policy-attachment.md +86 -0
- package/docs/litellm-policy.md +103 -0
- package/docs/litellm-team.md +117 -0
- package/docs/litellm-tool-policy.md +90 -0
- package/docs/litellm-toolset.md +92 -0
- package/docs/litellm.md +16 -9
- package/docs/peers.md +15 -6
- package/docs/postgres-grants-example.md +55 -0
- package/docs/postgres-grants.md +157 -0
- package/docs/postgres-role.md +126 -0
- package/docs/postgres-schema.md +139 -0
- package/docs/postgres.md +20 -3
- package/docs/valkey-transport.md +29 -0
- package/docs/valkey.md +187 -0
- package/package.json +6 -2
- package/src/ceph/ceph-argv.test.ts +1 -1
- package/src/launchd/sudo-allowlist.test.ts +2 -2
- package/src/linux/sudo-allowlist.test.ts +3 -3
- package/src/linux/sudo-runner.test.ts +2 -2
- package/src/litellm/access-group-form.ts +105 -0
- package/src/litellm/access-group-operations.ts +92 -0
- package/src/litellm/access-group-types.ts +49 -0
- package/src/litellm/access-group.test.ts +197 -0
- package/src/litellm/access-group.ts +151 -0
- package/src/litellm/credential-errors.ts +183 -0
- package/src/litellm/credential-form.test.ts +151 -0
- package/src/litellm/credential-form.ts +211 -0
- package/src/litellm/credential-guards.test.ts +241 -0
- package/src/litellm/credential-operations.ts +208 -0
- package/src/litellm/credential-recovery.test.ts +93 -0
- package/src/litellm/credential-rewrite.test.ts +164 -0
- package/src/litellm/credential-rewrite.ts +50 -0
- package/src/litellm/credential-types.ts +117 -0
- package/src/litellm/credential-values.ts +94 -0
- package/src/litellm/credential.test.ts +218 -0
- package/src/litellm/credential.ts +227 -0
- package/src/litellm/fake-access-group-litellm.ts +127 -0
- package/src/litellm/fake-credential-litellm.ts +216 -0
- package/src/litellm/fake-keys.ts +190 -0
- package/src/litellm/fake-litellm.ts +19 -9
- package/src/litellm/fake-mcp-litellm.ts +214 -0
- package/src/litellm/fake-model-litellm-post-update.ts +103 -0
- package/src/litellm/fake-model-litellm.ts +233 -0
- package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
- package/src/litellm/fake-policy-litellm.ts +186 -0
- package/src/litellm/fake-registry-base.ts +69 -0
- package/src/litellm/fake-stack.ts +17 -1
- package/src/litellm/fake-team-litellm.ts +229 -0
- package/src/litellm/fake-tool-litellm.ts +88 -0
- package/src/litellm/fake-toolset-litellm.ts +110 -0
- package/src/litellm/index.ts +140 -0
- package/src/litellm/key-delete.test.ts +212 -0
- package/src/litellm/key-errors.ts +241 -0
- package/src/litellm/key-form.ts +200 -0
- package/src/litellm/key-harness.ts +121 -0
- package/src/litellm/key-metadata-errors.ts +49 -0
- package/src/litellm/key-metadata.test.ts +156 -0
- package/src/litellm/key-metadata.ts +74 -0
- package/src/litellm/key-operations.ts +184 -0
- package/src/litellm/key-rotation.test.ts +110 -0
- package/src/litellm/key-scope.test.ts +135 -0
- package/src/litellm/key-secret.test.ts +248 -0
- package/src/litellm/key-secret.ts +137 -0
- package/src/litellm/key-transport.test.ts +166 -0
- package/src/litellm/key.test.ts +231 -0
- package/src/litellm/key.ts +213 -0
- package/src/litellm/mcp-server-adopt.test.ts +86 -0
- package/src/litellm/mcp-server-alias.test.ts +184 -0
- package/src/litellm/mcp-server-credential.ts +85 -0
- package/src/litellm/mcp-server-errors.ts +141 -0
- package/src/litellm/mcp-server-exports.test.ts +15 -0
- package/src/litellm/mcp-server-form.test.ts +241 -0
- package/src/litellm/mcp-server-form.ts +233 -0
- package/src/litellm/mcp-server-handlers.test.ts +150 -0
- package/src/litellm/mcp-server-locate.ts +66 -0
- package/src/litellm/mcp-server-operations.ts +111 -0
- package/src/litellm/mcp-server-registry.test.ts +146 -0
- package/src/litellm/mcp-server-retype.test.ts +114 -0
- package/src/litellm/mcp-server-shadow.test.ts +91 -0
- package/src/litellm/mcp-server-shadow.ts +45 -0
- package/src/litellm/mcp-server-tools.test.ts +85 -0
- package/src/litellm/mcp-server-types.ts +143 -0
- package/src/litellm/mcp-server-url.ts +66 -0
- package/src/litellm/mcp-server.test.ts +213 -0
- package/src/litellm/mcp-server.ts +225 -0
- package/src/litellm/model-claim.ts +69 -0
- package/src/litellm/model-credential.ts +45 -0
- package/src/litellm/model-errors.ts +168 -0
- package/src/litellm/model-foreign-row.test.ts +173 -0
- package/src/litellm/model-form.test.ts +228 -0
- package/src/litellm/model-form.ts +223 -0
- package/src/litellm/model-locate.test.ts +128 -0
- package/src/litellm/model-locate.ts +67 -0
- package/src/litellm/model-operations.test.ts +93 -0
- package/src/litellm/model-operations.ts +146 -0
- package/src/litellm/model-params-preserve.test.ts +119 -0
- package/src/litellm/model-recovery.test.ts +57 -0
- package/src/litellm/model-types.ts +112 -0
- package/src/litellm/model.test.ts +222 -0
- package/src/litellm/model.ts +237 -0
- package/src/litellm/policy-attachment-form.ts +103 -0
- package/src/litellm/policy-attachment-operations.ts +74 -0
- package/src/litellm/policy-attachment-types.ts +48 -0
- package/src/litellm/policy-attachment.test.ts +198 -0
- package/src/litellm/policy-attachment.ts +178 -0
- package/src/litellm/policy-form.ts +122 -0
- package/src/litellm/policy-operations.ts +112 -0
- package/src/litellm/policy-types.ts +58 -0
- package/src/litellm/policy.test.ts +214 -0
- package/src/litellm/policy.ts +146 -0
- package/src/litellm/providers.ts +24 -3
- package/src/litellm/registry-errors.ts +88 -0
- package/src/litellm/registry-exports.test.ts +66 -0
- package/src/litellm/registry-support.ts +109 -0
- package/src/litellm/team-form.ts +140 -0
- package/src/litellm/team-members.test.ts +150 -0
- package/src/litellm/team-members.ts +58 -0
- package/src/litellm/team-operations.ts +90 -0
- package/src/litellm/team-permission.ts +159 -0
- package/src/litellm/team-read.test.ts +89 -0
- package/src/litellm/team-types.ts +106 -0
- package/src/litellm/team.test.ts +206 -0
- package/src/litellm/team.ts +159 -0
- package/src/litellm/tool-policy-form.ts +94 -0
- package/src/litellm/tool-policy-operations.ts +58 -0
- package/src/litellm/tool-policy-types.ts +48 -0
- package/src/litellm/tool-policy.test.ts +172 -0
- package/src/litellm/tool-policy.ts +125 -0
- package/src/litellm/toolset-form.ts +104 -0
- package/src/litellm/toolset-operations.ts +90 -0
- package/src/litellm/toolset-types.ts +47 -0
- package/src/litellm/toolset.test.ts +198 -0
- package/src/litellm/toolset.ts +151 -0
- package/src/ownership/ownership.test.ts +33 -1
- package/src/ownership/rows.ts +53 -0
- package/src/postgres/connection.ts +42 -7
- package/src/postgres/database-bigint-serialization.test.ts +1 -0
- package/src/postgres/database-refusals.test.ts +2 -2
- package/src/postgres/database-sql.ts +36 -9
- package/src/postgres/fake-grants-parse.ts +189 -0
- package/src/postgres/fake-grants-read.ts +250 -0
- package/src/postgres/fake-grants-sql.ts +207 -0
- package/src/postgres/fake-grants-write.ts +90 -0
- package/src/postgres/fake-role-sql.ts +240 -0
- package/src/postgres/fake-schema-semantics.test.ts +36 -0
- package/src/postgres/fake-schema-sql.ts +146 -0
- package/src/postgres/fake-sql-parse.ts +86 -0
- package/src/postgres/fake-sql-quote.ts +36 -0
- package/src/postgres/fake-sql.ts +166 -46
- package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
- package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
- package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
- package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
- package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
- package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
- package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
- package/src/postgres/grants-acl.test.ts +61 -0
- package/src/postgres/grants-attrs.ts +185 -0
- package/src/postgres/grants-database.test.ts +158 -0
- package/src/postgres/grants-declare.ts +172 -0
- package/src/postgres/grants-delete-proof.test.ts +154 -0
- package/src/postgres/grants-diff.ts +101 -0
- package/src/postgres/grants-errors.ts +215 -0
- package/src/postgres/grants-existence-guards.test.ts +113 -0
- package/src/postgres/grants-existence.ts +101 -0
- package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
- package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
- package/src/postgres/grants-lifecycle.test.ts +181 -0
- package/src/postgres/grants-ops.ts +229 -0
- package/src/postgres/grants-owner-revoke.test.ts +121 -0
- package/src/postgres/grants-ownership.ts +49 -0
- package/src/postgres/grants-plan-collateral.test.ts +202 -0
- package/src/postgres/grants-plan-diff.test.ts +204 -0
- package/src/postgres/grants-plan-restore.ts +43 -0
- package/src/postgres/grants-plan-revocations.test.ts +69 -0
- package/src/postgres/grants-plan.test.ts +148 -0
- package/src/postgres/grants-plan.ts +245 -0
- package/src/postgres/grants-provenance.test.ts +134 -0
- package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
- package/src/postgres/grants-read-absent.test.ts +91 -0
- package/src/postgres/grants-read-defaults.ts +51 -0
- package/src/postgres/grants-read.ts +250 -0
- package/src/postgres/grants-refusals.test.ts +136 -0
- package/src/postgres/grants-refuse.ts +119 -0
- package/src/postgres/grants-relkind-wire.test.ts +111 -0
- package/src/postgres/grants-repair-plan.test.ts +201 -0
- package/src/postgres/grants-sql.test.ts +128 -0
- package/src/postgres/grants-sql.ts +144 -0
- package/src/postgres/grants-words.ts +34 -0
- package/src/postgres/grants.ts +110 -0
- package/src/postgres/index.ts +129 -5
- package/src/postgres/installed-classifier.ts +36 -0
- package/src/postgres/providers.ts +33 -9
- package/src/postgres/psql-executor-escaping.test.ts +67 -0
- package/src/postgres/psql-executor.test.ts +63 -2
- package/src/postgres/psql-executor.ts +74 -34
- package/src/postgres/role-adopt.test.ts +98 -0
- package/src/postgres/role-attrs.ts +137 -0
- package/src/postgres/role-diff.ts +93 -0
- package/src/postgres/role-errors.ts +206 -0
- package/src/postgres/role-guard.test.ts +60 -0
- package/src/postgres/role-identity.test.ts +197 -0
- package/src/postgres/role-lifecycle.test.ts +228 -0
- package/src/postgres/role-membership-options.test.ts +73 -0
- package/src/postgres/role-membership-repair.test.ts +153 -0
- package/src/postgres/role-membership-sql.ts +223 -0
- package/src/postgres/role-password-ascii.test.ts +53 -0
- package/src/postgres/role-password-transports.test.ts +123 -0
- package/src/postgres/role-password.test.ts +158 -0
- package/src/postgres/role-privilege.test.ts +107 -0
- package/src/postgres/role-provenance.test.ts +200 -0
- package/src/postgres/role-provider.ts +105 -0
- package/src/postgres/role-refusals.test.ts +225 -0
- package/src/postgres/role-scram.test.ts +38 -0
- package/src/postgres/role-scram.ts +40 -0
- package/src/postgres/role-secrets.ts +61 -0
- package/src/postgres/role-sql.ts +213 -0
- package/src/postgres/role-valid-until.test.ts +68 -0
- package/src/postgres/role.ts +226 -0
- package/src/postgres/schema-assert.ts +139 -0
- package/src/postgres/schema-attrs.ts +71 -0
- package/src/postgres/schema-delete-proofs.test.ts +192 -0
- package/src/postgres/schema-delete.ts +96 -0
- package/src/postgres/schema-diff.ts +44 -0
- package/src/postgres/schema-drop-2bp01.test.ts +146 -0
- package/src/postgres/schema-errors.ts +209 -0
- package/src/postgres/schema-handlers.test.ts +193 -0
- package/src/postgres/schema-identity.ts +39 -0
- package/src/postgres/schema-lifecycle.test.ts +246 -0
- package/src/postgres/schema-race.test.ts +70 -0
- package/src/postgres/schema-reconcile.ts +100 -0
- package/src/postgres/schema-refusals.test.ts +144 -0
- package/src/postgres/schema-socket.test.ts +162 -0
- package/src/postgres/schema-sql.ts +130 -0
- package/src/postgres/schema-takeover.test.ts +149 -0
- package/src/postgres/schema-test-kit.ts +85 -0
- package/src/postgres/schema.ts +130 -0
- package/src/release/binary-alias.test.ts +3 -3
- package/src/release/binary-claims.test.ts +3 -3
- package/src/release/binary-directory-umask.test.ts +65 -0
- package/src/release/fake-engine.ts +15 -0
- package/src/valkey/acl-attrs.ts +98 -0
- package/src/valkey/acl-compare.ts +57 -0
- package/src/valkey/acl-connection.ts +13 -0
- package/src/valkey/acl-delete.test.ts +127 -0
- package/src/valkey/acl-form.ts +239 -0
- package/src/valkey/acl-managed.ts +13 -0
- package/src/valkey/acl-ops.ts +122 -0
- package/src/valkey/acl-ownership.test.ts +118 -0
- package/src/valkey/acl-prefix.test.ts +38 -0
- package/src/valkey/acl-profiles.ts +81 -0
- package/src/valkey/acl-reconcile.ts +180 -0
- package/src/valkey/acl-reserved.test.ts +69 -0
- package/src/valkey/acl-revocation.test.ts +94 -0
- package/src/valkey/acl-seat-prefix.test.ts +111 -0
- package/src/valkey/acl-validation.test.ts +54 -0
- package/src/valkey/acl.test.ts +215 -0
- package/src/valkey/acl.ts +169 -0
- package/src/valkey/connection-service.ts +46 -0
- package/src/valkey/connection.ts +180 -0
- package/src/valkey/errors.ts +239 -0
- package/src/valkey/fake-acl.ts +128 -0
- package/src/valkey/fake-valkey.ts +224 -0
- package/src/valkey/handler-test-fixture.ts +43 -0
- package/src/valkey/handlers.test.ts +205 -0
- package/src/valkey/index.ts +54 -0
- package/src/valkey/instance-attrs.ts +48 -0
- package/src/valkey/instance-form.ts +104 -0
- package/src/valkey/instance.test.ts +108 -0
- package/src/valkey/instance.ts +160 -0
- package/src/valkey/provider-socket-fixture.ts +71 -0
- package/src/valkey/providers.test.ts +129 -0
- package/src/valkey/providers.ts +21 -0
- package/src/valkey/resp.ts +212 -0
- package/src/valkey/transport-bounds.test.ts +150 -0
- package/src/valkey/transport-error.ts +37 -0
- package/src/valkey/transport.test.ts +158 -0
- package/src/valkey/transport.ts +109 -0
- package/src/valkey/upstream-bounds.test.ts +14 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RESP wire the `Valkey` family speaks, hand-rolled the same way `postgres/database-sql.ts`
|
|
3
|
+
* hand-rolls its four statements: there is no `@distilled.cloud/valkey` SDK (registry 404, checked
|
|
4
|
+
* 2026-09-29), and `effect@4.0.0-rc.115` ships no Valkey/Redis client, so the family declares a
|
|
5
|
+
* `ValkeyExecutor` seam — the real socket client below, or `fake-valkey.ts` in tests — and every
|
|
6
|
+
* lifecycle function depends only on that small interface, never on `node:net`.
|
|
7
|
+
* ★ Alchemy beta.79 DOES ship Redis.connect. Its internal parser/queue cannot enforce our
|
|
8
|
+
* reply caps before allocating, and it exposes no deadline settings. The audited exception
|
|
9
|
+
* and executable measurements are in `docs/valkey-transport.md`.
|
|
10
|
+
*
|
|
11
|
+
* ⛔ RESP, NOT A TAG TO VALKEY'S C VERSION. The protocol is line-and-prefix oriented and stable
|
|
12
|
+
* across Valkey's releases; the commands this family issues (`INFO`, `CONFIG GET`, `ACL LIST`,
|
|
13
|
+
* `ACL SETUSER`, `PING`) are all long-settled. Nothing here parses a version-specific field.
|
|
14
|
+
* ★ RUNTIME-NEUTRAL. The executor is a function of `node:net` sockets, no Bun API; a Bun-only
|
|
15
|
+
* consumer would be a regression the postgres family's own `PgClient` avoided.
|
|
16
|
+
* ⛔ AN `error` EVENT WITH NO LISTENER EXITS THE PROCESS. `openSocket` listens only until
|
|
17
|
+
* `connect`. This executor installs the listener that stays for the life of the socket, and
|
|
18
|
+
* fails the in-flight read with `ValkeySocketError` (measured 2026-09-30: a peer RST after
|
|
19
|
+
* the connect listener was dropped was `uncaughtException` `read ECONNRESET`, exit 1).
|
|
20
|
+
*/
|
|
21
|
+
import * as Effect from 'effect/Effect';
|
|
22
|
+
import type { Socket } from 'node:net';
|
|
23
|
+
import { type ValkeyReply, type ValkeyTransportError } from './transport-error.ts';
|
|
24
|
+
export type { ValkeyReply, ValkeyTransportError } from './transport-error.ts';
|
|
25
|
+
export { ValkeyServerError, ValkeySocketError } from './transport-error.ts';
|
|
26
|
+
/** One command with its arguments, as the byte strings that go on the wire. */
|
|
27
|
+
export interface ValkeyCommand {
|
|
28
|
+
readonly args: ReadonlyArray<string>;
|
|
29
|
+
}
|
|
30
|
+
/** The smallest thing a lifecycle function needs from a client: send one command, read the reply
|
|
31
|
+
* as one of the three RESP top-level shapes the family interprets — a bulk string, a flat array of
|
|
32
|
+
* bulk strings, or an error. `null` is the RESP "nil" reply (a missing key). Failures are the two
|
|
33
|
+
* transport tags: a server error reply, or a socket failure. */
|
|
34
|
+
export interface ValkeyExecutor {
|
|
35
|
+
readonly send: (args: ReadonlyArray<string>) => Effect.Effect<ValkeyReply, ValkeyTransportError>;
|
|
36
|
+
}
|
|
37
|
+
/** The per-read abort, and the idle/`connect` deadline `withValkey` sets on the socket. */
|
|
38
|
+
export declare const DEFAULT_SOCKET_TIMEOUT_MS = 10000;
|
|
39
|
+
/**
|
|
40
|
+
* The real executor over a `node:net` socket. `socket` is supplied by the caller (which owns
|
|
41
|
+
* connect/close), so `withValkey` can open one socket per operation and close it after.
|
|
42
|
+
* `timeoutMs` bounds each read; the default matches the socket deadline in `connection.ts`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const makeSocketExecutor: (socket: Socket, timeoutMs?: number) => ValkeyExecutor;
|
|
45
|
+
/**
|
|
46
|
+
* Decode a flat array reply into `Map<string, string | null>` for the `key value key value`
|
|
47
|
+
* shape `CONFIG GET` answers. Null values are preserved; a missing key is `null`, never `''`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const arrayPairs: (values: ReadonlyArray<string | null>) => ReadonlyMap<string, string | null>;
|
|
50
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/valkey/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE5E,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtC;AAED;;;gEAGgE;AAChE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;CAClG;AAED,2FAA2F;AAC3F,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAOhD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,WACrB,MAAM,cACH,MAAM,KAChB,cAiCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,WACb,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,KACnC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMnC,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# LiteLLM — `LiteLLM.AccessGroup`
|
|
2
|
+
|
|
3
|
+
One row of LiteLLM's **unified access group** table (`LiteLLM_AccessGroupTable`,
|
|
4
|
+
`/v1/unified_access_group`): a named bundle of models and MCP servers that teams and keys are attached to.
|
|
5
|
+
Typed calls come from `@distilled.cloud/litellm/access_groups`, generated from LiteLLM **v1.103.0**;
|
|
6
|
+
credentials and the family's other refusals are in [litellm.md](./litellm.md).
|
|
7
|
+
|
|
8
|
+
Everything below marked "read" was read from the **live 1.103.0 container's source** on 2026-09-30 (no
|
|
9
|
+
request was sent to a proxy). What the docs page says is cited by URL.
|
|
10
|
+
|
|
11
|
+
## Which "access group" this is
|
|
12
|
+
|
|
13
|
+
LiteLLM uses the words for three different things. This resource is the first.
|
|
14
|
+
|
|
15
|
+
| Thing | Where it lives | Holds |
|
|
16
|
+
| ------------------------ | ------------------------------------------------------ | --------------------------- |
|
|
17
|
+
| **Unified access group** | `LiteLLM_AccessGroupTable`, `/v1/unified_access_group` | models, MCP servers, agents |
|
|
18
|
+
| Model access group | `access_groups` in a model's `model_info` | models only |
|
|
19
|
+
| MCP access group | a bare name on an MCP server row (`mcp_access_groups`) | MCP servers by name |
|
|
20
|
+
|
|
21
|
+
⚠️ **Toolsets are not in a unified group.** `AccessGroupCreateRequest` (`litellm/types/access_group.py`)
|
|
22
|
+
has `access_model_names`, `access_mcp_server_ids`, `access_agent_ids`, `assigned_team_ids` and
|
|
23
|
+
`assigned_key_ids`, and nothing else. A toolset is granted through `object_permission.mcp_toolsets` on a
|
|
24
|
+
team or key ([litellm-team.md](./litellm-team.md)).
|
|
25
|
+
The docs agree: docs.litellm.ai/docs/proxy/access_groups lists models, MCP servers and agents.
|
|
26
|
+
|
|
27
|
+
## The distilled operations it uses
|
|
28
|
+
|
|
29
|
+
Nothing is hand-rolled: every call is one of these, through `access-group-operations.ts`.
|
|
30
|
+
|
|
31
|
+
| step | operation (`@distilled.cloud/litellm/access_groups`) | route |
|
|
32
|
+
| ------ | ---------------------------------------------------------- | ------------------------------------- |
|
|
33
|
+
| list | `listAccessGroupsV1UnifiedAccessGroupGet` | `GET /v1/unified_access_group` |
|
|
34
|
+
| create | `createAccessGroupV1UnifiedAccessGroupPost` | `POST /v1/unified_access_group` (201) |
|
|
35
|
+
| update | `updateAccessGroupV1UnifiedAccessGroupAccessGroupIdPut` | `PUT /v1/unified_access_group/{id}` |
|
|
36
|
+
| delete | `deleteAccessGroupV1UnifiedAccessGroupAccessGroupIdDelete` | `DELETE …/{id}` (204, no body) |
|
|
37
|
+
|
|
38
|
+
Not used: the by-id `GET` (a missing id is a 404 this tag does not declare; the list decides absence
|
|
39
|
+
without it), and the `/v1/access_group` routes (registered as aliases of these on the same handlers,
|
|
40
|
+
`access_group_endpoints.py`, "Alias routes").
|
|
41
|
+
|
|
42
|
+
## Example
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { LiteLLMAccessGroup, litellmProviders } from '@homeflare/alchemy/litellm';
|
|
46
|
+
|
|
47
|
+
// An existing group, adopted by its name (needs --adopt), made to grant exactly this.
|
|
48
|
+
export class Estate extends LiteLLMAccessGroup('Estate', {
|
|
49
|
+
accessGroupName: 'estate',
|
|
50
|
+
description: 'Models and tools every estate seat may use',
|
|
51
|
+
modelNames: ['cf-code', 'cf-flash'],
|
|
52
|
+
mcpServerIds: [docs.serverId], // a LiteLLM.MCPServer: the dependency edge is the Output
|
|
53
|
+
}) {}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then provide `litellmProviders()` alongside the stack's other providers.
|
|
57
|
+
|
|
58
|
+
## Behaviour
|
|
59
|
+
|
|
60
|
+
| Concern | Rule |
|
|
61
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| Identity | `access_group_name` is **unique** (`find_unique`, a duplicate is a 409), so the name finds the one row. The id is the proxy's (a uuid) and is what state remembers. |
|
|
63
|
+
| Adopt | A live row with no state is `Unowned`; it needs `--adopt`. A matching row writes nothing. |
|
|
64
|
+
| Rename | An update of the same row (the id does not change), never a replace. |
|
|
65
|
+
| Grants | `modelNames` and `mcpServerIds` are **always compared**, as sets; an undeclared list means "grants nothing". An adopted group that grants more than declared is corrected. |
|
|
66
|
+
| Description | Compared only when declared: adopting never clears text a person wrote. Blank is refused. |
|
|
67
|
+
| Update | Partial (`exclude_unset`, a `null` list becomes `[]`), so only the fields that differ are sent. Team and key edges, agents and `access_agent_ids` are never in the body. |
|
|
68
|
+
| Removal | `defaultRemovalPolicy: 'retain'`. Opt in with `RemovalPolicy.destroy()`. Deleting a group **detaches it from every team and key** that holds it (`delete_access_group`). |
|
|
69
|
+
| Delete | Idempotent by a real read: a failed DELETE is swallowed only when the list no longer has the id. A 403 (PROXY_ADMIN only) re-raises the original error. |
|
|
70
|
+
| Write check | Reconcile lists again and fails (`LitellmRegistryNotConvergedError`) if a declared field did not land, or (`LitellmRegistryAbsentAfterWriteError`) if the row is not there. |
|
|
71
|
+
|
|
72
|
+
## Team and key membership is declared on the other side
|
|
73
|
+
|
|
74
|
+
`assigned_team_ids` and `assigned_key_ids` are **not modelled**. A team's group membership is
|
|
75
|
+
`LiteLLM.Team`'s `accessGroupIds`, and LiteLLM mirrors it into the group's `assigned_team_ids` in the same
|
|
76
|
+
transaction (`_sync_add_access_group_to_teams`). Two resources writing one edge would fight, so the group
|
|
77
|
+
never sends either field, and an adopted group keeps whatever it has there.
|
|
78
|
+
|
|
79
|
+
⚠️ **LiteLLM edits `access_model_names` itself.** Unified groups store model **names**, and
|
|
80
|
+
`management_helpers/access_group_model_sync.py` removes a name from every group when its last deployment is
|
|
81
|
+
deleted (and renames it when a deployment is renamed). A model removed elsewhere therefore shows up as a
|
|
82
|
+
plan difference on the next change. Alchemy plans from state and reads live rows only to adopt (beta.79), so
|
|
83
|
+
the correction lands on the next deploy that changes the group at all, not on its own.
|
|
84
|
+
|
|
85
|
+
## Refused before any request
|
|
86
|
+
|
|
87
|
+
A blank or padded `accessGroupName`; a blank `description`; a blank, padded or repeated entry in
|
|
88
|
+
`modelNames` or `mcpServerIds`.
|
|
89
|
+
|
|
90
|
+
## Licence gating (measured)
|
|
91
|
+
|
|
92
|
+
None. `access_group_endpoints.py` has no `premium_user` check (`grep -ci premium`: 0), and the docs page makes
|
|
93
|
+
no Enterprise statement. Writes need PROXY_ADMIN (`_require_proxy_admin`, 403), reads admin or admin-view.
|
|
94
|
+
|
|
95
|
+
## Not modelled
|
|
96
|
+
|
|
97
|
+
`assigned_team_ids`, `assigned_key_ids`, `access_agent_ids`; nothing validates that a model name or a server
|
|
98
|
+
id exists (LiteLLM stores them raw), so a dangling id is accepted and silently grants nothing.
|
|
99
|
+
|
|
100
|
+
## Unmeasured at 1.103.0
|
|
101
|
+
|
|
102
|
+
No live proxy was contacted. The fake (`fake-access-group-litellm.ts`) is built from the source and the
|
|
103
|
+
generated schema; each choice it makes is in its header. Whether the list is paged, and what the by-id
|
|
104
|
+
`GET` answers for a missing id, were not exercised. The typed fix for the undeclared 404 and 409 of this tag
|
|
105
|
+
is a distilled patch, which belongs in the distilled clone and is not part of this change.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# LiteLLM — `LiteLLM.Credential`
|
|
2
|
+
|
|
3
|
+
One row of LiteLLM's reusable credential table (`/credentials/*`): a named bag of values the
|
|
4
|
+
proxy stores **encrypted** and can bind to a deployment by `model_id`. Typed calls come from
|
|
5
|
+
`@distilled.cloud/litellm/credential_management` (generated from v1.103.0, the same schema this
|
|
6
|
+
resource's rules were read from); credentials and refusals are the family's, see
|
|
7
|
+
[litellm.md](./litellm.md).
|
|
8
|
+
|
|
9
|
+
## The values never live in a declaration
|
|
10
|
+
|
|
11
|
+
`credentialValues` is `Record<string, { fromEnv: 'NAME' }>`. Alchemy's state is unencrypted and
|
|
12
|
+
LiteLLM stores these literals encrypted in its own database (`credential_endpoints/endpoints.py`,
|
|
13
|
+
read from a 1.103.0 container), so the declaration holds only the variable's NAME; the
|
|
14
|
+
deploying process resolves the value at call time and the wire body carries the literal once.
|
|
15
|
+
A literal value in the props is refused at plan time, and so is a sensitive-keyed
|
|
16
|
+
`credentialInfo` entry — `credential_info` is stored and returned in the clear.
|
|
17
|
+
|
|
18
|
+
The reads hand the values back masked (`litellm_logging.py::_get_masked_values`: a
|
|
19
|
+
sensitive-keyed string answers `v[:2] + "****" + v[-2:]`, `*****` for a short one), so no value
|
|
20
|
+
is ever compared against or copied from the masked row (only value key names are retained): what was last written is remembered as
|
|
21
|
+
a salted scrypt seal (`valuesSeal`), and a rotated value is noticed by re-resolving and
|
|
22
|
+
re-sealing. A variable that is unset makes the state "cannot tell" — it never drifts a
|
|
23
|
+
plan-only environment. An info-only PATCH needs no value; a create, values PATCH or rewrite
|
|
24
|
+
demands every declared variable. A required rewrite refuses before DELETE if any is missing.
|
|
25
|
+
|
|
26
|
+
## Behaviour
|
|
27
|
+
|
|
28
|
+
| Concern | Rule |
|
|
29
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| Identity | `credentialName` (the table's unique column). A different declared name is a `replace`, `deleteFirst: false` — the old row survives. |
|
|
31
|
+
| Adopt | A live row with the name and no state is `Unowned`; needs `--adopt`. The full intended info and value-key maps are authoritative, including undeclared live keys. Resolvable values are stamped and sealed; removal requires all declared values. |
|
|
32
|
+
| Removal | `defaultRemovalPolicy: 'retain'` — a deployment's `model_id` may reference the row. Opt in with `RemovalPolicy.destroy()`. Delete is idempotent (`NotFound` is already-absent). |
|
|
33
|
+
| Read | `GET /credentials/by_name/{name}` — the proxy's in-memory list, not the table. A row committed to the database but not loaded in memory reads as absent; a create for it answers `409`, which fails the deploy loudly. |
|
|
34
|
+
| Update | PATCH changes values/info; DELETE + POST removes value or info keys. The regenerated SDK sends the required `credential_name` in both path and body. |
|
|
35
|
+
| Write check | Reconcile reads back; unapplied info or remaining removed key names fail with `LitellmCredentialNotConvergedError`. Values and masked fragments never enter state. |
|
|
36
|
+
|
|
37
|
+
## PATCH and removal
|
|
38
|
+
|
|
39
|
+
Measured in LiteLLM 1.103.0 `proxy/credential_endpoints/endpoints.py:312,384`: PATCH merges
|
|
40
|
+
`credential_values` by key in both database and memory. It cannot remove a value key. The
|
|
41
|
+
by-name read exposes key names even when values are masked, so removed names trigger a rewrite.
|
|
42
|
+
|
|
43
|
+
`credential_info` differs: nonempty PATCH info normally **replaces** the database map
|
|
44
|
+
(`:314–319`, except when the old map contains a literal `credential_info` key); empty info
|
|
45
|
+
leaves it unchanged. In-memory info only merges (`:385–387`), retaining old keys until restart.
|
|
46
|
+
Every PATCH sends the full intended info map. **The declaration owns the complete map**, with
|
|
47
|
+
omission meaning `{}`: undeclared live info keys, including on adoption, are removed by rewrite
|
|
48
|
+
so both stores agree. Sensitive info values are withheld; only their names are retained.
|
|
49
|
+
|
|
50
|
+
Reconcile refuses invalid declarations and `DISTILLED_DEBUG_HTTP` at entry, rechecks ownership,
|
|
51
|
+
and demands all values needed for a write before any DELETE. Debug logging could print request
|
|
52
|
+
or response values; plan-time reads refuse debug logging too. Transport failures from value-bearing POST/PATCH become a typed
|
|
53
|
+
`LitellmCredentialTransportError` containing the name and reason tag, no request or cause.
|
|
54
|
+
A failed PATCH leaves the row in place. A rewrite is still non-atomic: a failed POST after a
|
|
55
|
+
successful DELETE can leave no row. No live proxy was contacted for these regression tests.
|
|
56
|
+
|
|
57
|
+
## Not modelled
|
|
58
|
+
|
|
59
|
+
`model_id` (copying a deployment's values into a credential) — nothing in the estate declares
|
|
60
|
+
one yet; add it with a measured read of the copy semantics.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# LiteLLM — `LiteLLM.Key` metadata
|
|
2
|
+
|
|
3
|
+
How [`LiteLLM.Key`](./litellm-key.md) compares and writes a key's `metadata`, and what it never holds.
|
|
4
|
+
Read 2026-09-29 in the LiteLLM **1.103.0** PyPI wheel's source (`litellm/proxy/…`), not against a live
|
|
5
|
+
proxy. The code is `src/litellm/key-metadata.ts`; its header carries the same reasoning.
|
|
6
|
+
|
|
7
|
+
## Why `metadata` is not the declarer's alone
|
|
8
|
+
|
|
9
|
+
LiteLLM stores many per-key settings **inside** the `metadata` JSON column:
|
|
10
|
+
|
|
11
|
+
- `LiteLLM_ManagementEndpoint_MetadataFields` and `..._Premium` (`_types.py:4821-4853`): `model_rpm_limit`,
|
|
12
|
+
`model_tpm_limit`, `enforced_params`, `end_user_budget_id`, `guardrails`, `policies`, `tags`, `prompts`,
|
|
13
|
+
`logging`, `secret_manager_settings`, `allowed_passthrough_routes`, and others.
|
|
14
|
+
- `/key/generate` folds them in through `metadata_json_with_limits` (`key_management_endpoints.py:4429-4457`).
|
|
15
|
+
- `/key/update` **replaces** the column with the `metadata` it is sent (`prepare_metadata_fields`, :2312-2359).
|
|
16
|
+
When `metadata` is not sent it keeps the existing one (:2318-2319). Only `service_account_id` is
|
|
17
|
+
preserved across a replacement (`LiteLLM_Reserved_Metadata_Fields`, `_types.py:4857`; :2323-2336).
|
|
18
|
+
|
|
19
|
+
A body that carried only the declared keys therefore strips the key's guardrails, per-model caps and
|
|
20
|
+
passthrough allowlist, and the deploy reports success. Measured over the test fake, which replaces the
|
|
21
|
+
column the same way: an adopted key with `{ seat: 'a', guardrails: [...], model_rpm_limit: {...} }` and a
|
|
22
|
+
declaration of `metadata: { seat: 'b' }` lost both before this merge (`key-metadata.test.ts`).
|
|
23
|
+
|
|
24
|
+
## The merge
|
|
25
|
+
|
|
26
|
+
| Declaration | Compared | Sent (only when a declared key differs) |
|
|
27
|
+
| -------------------------- | ----------------------------------------------- | ---------------------------------------------- |
|
|
28
|
+
| `metadata` omitted | nothing | nothing |
|
|
29
|
+
| `metadata: { seat: 'a' }` | the live `seat` against `'a'` | every live key, with `seat` set as declared |
|
|
30
|
+
| `metadata: { seat: null }` | the live `seat` against `null` (absent differs) | every live key, with `seat: null`; quiet after |
|
|
31
|
+
|
|
32
|
+
A key you stop declaring is **carried, never removed**: leaving it out does not clear it, declare it
|
|
33
|
+
`null`. The alternative, a list of the keys LiteLLM owns to carry while clearing the rest, was not
|
|
34
|
+
taken: the list is per-version, a key LiteLLM adds next release would be stripped until it is updated,
|
|
35
|
+
and this way fails safe on a live credential. The scope is top-level keys, as LiteLLM reads them.
|
|
36
|
+
|
|
37
|
+
## Callback credentials are never held
|
|
38
|
+
|
|
39
|
+
`logging`, `callback_settings` and `secret_manager_settings` (`common_utils/callback_utils.py:51`,
|
|
40
|
+
`_CALLBACK_CONFIG_SLOTS`) carry `callback_vars`: an observability platform's secret keys. LiteLLM
|
|
41
|
+
encrypts them at rest (`encrypt_callback_vars`, :716), but a legacy row's are plaintext ("Legacy
|
|
42
|
+
plaintext rows pass through unchanged", `decrypt_callback_vars`, :725-731), and Alchemy persists
|
|
43
|
+
attributes unencrypted. So:
|
|
44
|
+
|
|
45
|
+
| What | What this resource does |
|
|
46
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
47
|
+
| A row with one of the slots (read, adopt) | Drops the slot from `attributes.metadata`; keeps only its **name** in `attributes.withheld`. |
|
|
48
|
+
| A declaration whose `metadata` names a slot | Refused (`LitellmKeyCallbackMetadataDeclaredError`): stored encrypted, it would never read back equal and every deploy would fail with `LitellmKeyFieldNotAppliedError`. |
|
|
49
|
+
| A metadata write onto a key that has a slot | Refused (`LitellmKeyCallbackMetadataLiveError`): the resource never read the slot, so the update could not carry it, and LiteLLM would drop it. |
|
|
50
|
+
| A write that touches no `metadata` | Goes ahead: the update body has no `metadata`, and the column is left as it is. |
|
|
51
|
+
|
|
52
|
+
Configure key logging in LiteLLM itself. ⚠️ The raw slot still crosses the wire in `/key/list`'s answer
|
|
53
|
+
(as every read of that row does), so `DISTILLED_DEBUG_HTTP`, which prints response bodies, would show a
|
|
54
|
+
legacy plaintext one on a read; it is refused only for a create, where the request carries the key.
|
|
55
|
+
UNVERIFIED: whether `/key/list` returns the stored (encrypted) or the decrypted form of `callback_vars`;
|
|
56
|
+
either is dropped before it reaches an attribute.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# LiteLLM — `LiteLLM.Key`
|
|
2
|
+
|
|
3
|
+
One virtual key of LiteLLM's `LiteLLM_VerificationToken` (`/key/*`), bound to a
|
|
4
|
+
[`LiteLLM.Budget`](./litellm-budget.md) tier, with its value **kept out of Alchemy state**. Typed
|
|
5
|
+
calls come from `@distilled.cloud/litellm/key_management`; credentials and the family's refusals are
|
|
6
|
+
in [litellm.md](./litellm.md). Read 2026-09-29 against `@homeflare/distilled-litellm@0.3.0`
|
|
7
|
+
(generated at LiteLLM **1.103.0**) and the 1.103.0 PyPI wheel's own source.
|
|
8
|
+
|
|
9
|
+
## The key value is write-only
|
|
10
|
+
|
|
11
|
+
Alchemy persists props **and** attributes unencrypted: a `Redacted` is tagged, not encrypted
|
|
12
|
+
(`State/StateEncoding.ts` writes the inner value beside the tag). So the key's value can be neither a
|
|
13
|
+
prop nor an attribute, and `LiteLLM.Key` uses the mechanism every other kit secret uses
|
|
14
|
+
(`src/secrets/write-only.ts`, the same one as the PBS webhook secret): the prop holds the **name** of
|
|
15
|
+
an environment variable.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import * as Effect from 'effect/Effect';
|
|
19
|
+
import { LiteLLMBudget, LiteLLMKey } from '@homeflare/alchemy/litellm';
|
|
20
|
+
|
|
21
|
+
export default Effect.gen(function* () {
|
|
22
|
+
const tier = yield* LiteLLMBudget('Tier', { budgetId: 'hf-tier-seat', softBudget: 25 });
|
|
23
|
+
yield* LiteLLMKey('Seat', {
|
|
24
|
+
keyAlias: 'hf-seat-a',
|
|
25
|
+
key: { fromEnv: 'HF_SEAT_A_KEY' }, // a random `sk-…` minted outside, e.g. in the vault
|
|
26
|
+
budgetId: tier.budgetId, // an Output: Alchemy creates the tier first
|
|
27
|
+
models: ['glm-5.3', 'gpt-oss-120b'],
|
|
28
|
+
metadata: { seat: 'a' },
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then provide `litellmProviders()` alongside the stack's other providers, as
|
|
34
|
+
[litellm.md](./litellm.md#example) shows.
|
|
35
|
+
|
|
36
|
+
| where the value is | it is |
|
|
37
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| the declaration / state | never: the state row holds `{ fromEnv: 'HF_SEAT_A_KEY' }`, the NAME |
|
|
39
|
+
| `diff`, a plan | a set variable is checked against the live token in memory; unset/empty skips verification |
|
|
40
|
+
| `/key/generate`'s body | once, on create, unwrapped in `createBody` and nowhere else |
|
|
41
|
+
| `/key/generate`'s response | `Redacted` (the SDK's `T.SensitiveValue`); compared with what was sent |
|
|
42
|
+
| an attribute, error or log | never: errors name the alias, the variable and the rule, not the value; a failed create is `LitellmKeyTransportError`, never the SDK's `HttpClientError` (below) |
|
|
43
|
+
|
|
44
|
+
★ **Why the value is minted outside and not by LiteLLM.** `/key/generate` does return the new key,
|
|
45
|
+
and the SDK hands it back `Redacted`. A resource has two places to put it: an attribute, which is
|
|
46
|
+
state in the clear, or nowhere, and LiteLLM never shows the plaintext again (only its hash). A key
|
|
47
|
+
minted and dropped is a row nobody can use. `/key/generate`'s own docstring supports a caller-chosen
|
|
48
|
+
value: "Must start with 'sk-' and be at least 16 characters long."
|
|
49
|
+
|
|
50
|
+
- **The value is sent only on create.** For an existing key, plan and reconcile compare a set
|
|
51
|
+
variable's sha256 with the live row's `token`, only in memory. A mismatch is a typed
|
|
52
|
+
`LitellmKeyValueMismatchError`, even when every prop is unchanged. `/key/update` drops `key`
|
|
53
|
+
and cannot rotate it; `/key/regenerate` is an Enterprise feature. **Rotate with a new alias.**
|
|
54
|
+
- **Unset or empty variables skip verification on existing keys**, including ordinary settings
|
|
55
|
+
updates. A deployer need not hold the seat key. Creation still requires the declared value.
|
|
56
|
+
- **Adopting needs no value.** Omit `key` to manage an existing key's settings. A create with no `key`
|
|
57
|
+
is refused (`LitellmKeyValueRequiredError`): it would mint a value nobody holds.
|
|
58
|
+
- **A malformed value is refused before any write**: not `sk-…`, under 16 characters (both LiteLLM's
|
|
59
|
+
rules) or containing whitespace (the house's: a renderer's trailing newline would be sent as part of
|
|
60
|
+
the value). The error names the variable and the rule, never the value.
|
|
61
|
+
- ⛔ **`DISTILLED_DEBUG_HTTP` refuses a create.** While it is set the SDK prints the first 400
|
|
62
|
+
characters of every request and response body to stderr (`@distilled.cloud/core`, read 2026-09-29),
|
|
63
|
+
and `/key/generate` carries the key in both. An update carries no value, so it is not refused.
|
|
64
|
+
- ⛔ **A create that fails on the wire does not carry the request.** `/key/generate`'s request body is
|
|
65
|
+
`{"key":"sk-…"}`, and the SDK's `HttpClientError` holds its request, so `JSON.stringify`,
|
|
66
|
+
`Bun.inspect` at depth or Effect's JSON logger printed the value from a dropped connection, from the
|
|
67
|
+
deploy that failed over it, and from a defect (`protocol-rest.ts` reads a body with `Effect.orDie`).
|
|
68
|
+
Measured 2026-09-29 with a synthetic key (`key-transport.test.ts`, each reading searched for the
|
|
69
|
+
value). `generateKey` turns both into `LitellmKeyTransportError`: the alias and the reason's tag, no
|
|
70
|
+
request, no cause. The key **may** exist afterwards (the answer can be lost after the write); the next
|
|
71
|
+
deploy sees a live key with no state and takes it with `--adopt`, and one that did not land is created.
|
|
72
|
+
The SDK's status errors (`BadRequest`, `Forbidden`, …) hold only the proxy's message and stay typed.
|
|
73
|
+
- ⚠️ **A proxy with the dashboard's `disable_custom_api_keys` setting on** answers 403 to any
|
|
74
|
+
user-defined key (`_check_custom_key_allowed`). It surfaces as the SDK's `Forbidden`.
|
|
75
|
+
UNVERIFIED whether the estate's proxy has it on.
|
|
76
|
+
- **A proxy that ignores the value** and mints its own would leave a key nobody can use, reported as
|
|
77
|
+
a success. `/key/generate` echoes the key; a different one is deleted again and refused
|
|
78
|
+
(`LitellmKeyValueNotHonouredError`). UNVERIFIED that any proxy does this.
|
|
79
|
+
|
|
80
|
+
Measured against the installed `alchemy@2.0.0-beta.79`: `src/Provider.ts:274–289` declares
|
|
81
|
+
`diff` as `Effect<Diff | void, any, DiffReq>`. `src/Plan.ts:1503–1527` invokes it before falling
|
|
82
|
+
back to props equality, so unchanged props do not bypass verification; `:750–765` also calls it
|
|
83
|
+
for stable-reference evaluation. The check reads the live token each time and persists neither
|
|
84
|
+
value nor hash. Unresolved props defer to reconcile. `key-rotation.test.ts` exercises real
|
|
85
|
+
Plan/Apply with only the environment changed, no adoption and no other drift.
|
|
86
|
+
|
|
87
|
+
## Props
|
|
88
|
+
|
|
89
|
+
| prop | type | notes |
|
|
90
|
+
| --------------- | ------------------------- | --------------------------------------------------------------------------------------- |
|
|
91
|
+
| `keyAlias` | `string` | **Identity.** Non-empty, unique per proxy. Declare an existing key's alias to adopt it. |
|
|
92
|
+
| `key` | `{ fromEnv: string }` | Write-only, create-only. Optional when the key already exists. |
|
|
93
|
+
| `budgetId` | `string \| null` | The tier. Pass `budget.budgetId` and Alchemy creates the tier first. `null` unbinds. |
|
|
94
|
+
| `models` | `string[]` | `[]` = **all models** (LiteLLM's reading). Compared order-insensitively. |
|
|
95
|
+
| `allowedRoutes` | `string[]` | Exact or wildcard routes. `[]` = no route restriction. |
|
|
96
|
+
| `teamId` | `string \| null` | `null` detaches the team. |
|
|
97
|
+
| `metadata` | `Record<string, unknown>` | The keys you manage: a **merge**, see [below](#metadata). Stored in state: no secrets. |
|
|
98
|
+
| `duration` | `string \| null` | `'30d'`, `'12h'`, … from creation. `null` = never expires. See below. |
|
|
99
|
+
|
|
100
|
+
⛔ **A declaration manages only what it names.** An omitted prop is neither compared nor sent, so a
|
|
101
|
+
live scope, budget, team or expiry stays as it is; a **new** key with no `models` starts with
|
|
102
|
+
LiteLLM's default, all models. Clearing is explicit: `models: []`, `allowedRoutes: []`,
|
|
103
|
+
`budgetId: null`, `teamId: null`, `duration: null`. Omission never clears, because clearing fails open
|
|
104
|
+
on a credential: `[]` is "all models" and "no route restriction", and an adopted key's plan says
|
|
105
|
+
`adopted`, never the `update` the apply would send (the plan cannot probe it; `--adopt` given for
|
|
106
|
+
another resource takes over every declared key whose alias is live).
|
|
107
|
+
|
|
108
|
+
Attributes: `keyAlias`, `budgetId`, `models`, `allowedRoutes`, `teamId`, `metadata`, `expires` (what
|
|
109
|
+
the row says, observed), `duration` (see below) and `withheld` (the names of the `metadata` callback
|
|
110
|
+
slots the row has, [below](#metadata)). ⛔ There is no `key`, `token` or hash attribute.
|
|
111
|
+
|
|
112
|
+
## Behaviour
|
|
113
|
+
|
|
114
|
+
| Concern | Rule |
|
|
115
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
116
|
+
| Identity | `keyAlias`. A different alias is **refused** (`LitellmKeyAliasChangedError`), not replaced: the new key would need the same value, and a retained old row still holds it. |
|
|
117
|
+
| Adopt | A live key with the alias and no state is `Unowned`; needs `--adopt`. The apply re-checks it (`refuseTakeover`) for a create the planner could not probe. |
|
|
118
|
+
| Removal | `defaultRemovalPolicy: 'retain'`: deleting a key breaks whatever holds it. Opt in with `RemovalPolicy.destroy()`. |
|
|
119
|
+
| Read | `/key/list?key_alias=…&return_full_object=true`. Two rows under one alias are refused (`LitellmKeyAmbiguousAliasError`); the column is not unique in LiteLLM's schema. |
|
|
120
|
+
| Update | `/key/update` by alias with **only the declared fields that differ**. A declared `null` on `budget_id`/`team_id`/`duration` is sent as `null`, a declared `[]` as `[]`; `metadata` is merged. |
|
|
121
|
+
| Write check | Reconcile reads back; a field the row still differs on fails with `LitellmKeyFieldNotAppliedError`. |
|
|
122
|
+
| Delete | Idempotent by the list: an absent alias writes nothing. The SDK's `KeyNotFound` (a lost race, a retried delete) re-lists and is swallowed only if the key is gone. Any other failure propagates. |
|
|
123
|
+
| Read errors | A 5xx on `/key/list` propagates typed. It is never read as "no such key" (which would go on to create one). |
|
|
124
|
+
|
|
125
|
+
### `duration`
|
|
126
|
+
|
|
127
|
+
The row carries `expires`, an absolute time, so a declared `'30d'` cannot be read back. State
|
|
128
|
+
remembers the duration this provider last wrote. Changing it, or declaring `null`, plans an `update`
|
|
129
|
+
that sends `duration` (`null` = never expires; the 1.103.0 source sets `expires` from it). Omitting it
|
|
130
|
+
leaves a live expiry alone. A key whose expiry was set or cleared by hand while the declaration names
|
|
131
|
+
a `duration` plans an update too.
|
|
132
|
+
⚠️ **An adopted key remembers nothing**, so declaring a `duration` on one re-arms its expiry once,
|
|
133
|
+
from that deploy; afterwards a plan is quiet.
|
|
134
|
+
|
|
135
|
+
## Operations used, none guessed
|
|
136
|
+
|
|
137
|
+
Every call is `@distilled.cloud/litellm/key_management`'s own operation with its own request type,
|
|
138
|
+
through `throughFetch` (`operations.ts`); nothing is hand-rolled.
|
|
139
|
+
|
|
140
|
+
| operation | route | used for |
|
|
141
|
+
| ------------------------------ | -------------------- | -------------------------------------------------------- |
|
|
142
|
+
| `listKeysKeyListGet` | `GET /key/list` | read by `key_alias`, `return_full_object: true` |
|
|
143
|
+
| `generateKeyFnKeyGeneratePost` | `POST /key/generate` | create, with the user-defined `key` |
|
|
144
|
+
| `updateKeyFnKeyUpdatePost` | `POST /key/update` | merge patch, found by `key_alias` (no `key` in the body) |
|
|
145
|
+
| `deleteKeyFnKeyDeletePost` | `POST /key/delete` | delete by `key_aliases` |
|
|
146
|
+
|
|
147
|
+
Not used: `/key/info` and `/v2/key/info` take the key or its hash, which an adopted key never has.
|
|
148
|
+
|
|
149
|
+
## Measured, and not
|
|
150
|
+
|
|
151
|
+
Measured 2026-09-29 in the 1.103.0 wheel's source (`litellm/proxy/management_endpoints/
|
|
152
|
+
key_management_endpoints.py`), not against a live proxy: `hash_token` is `sha256` hex; a duplicate
|
|
153
|
+
alias is 400 (`_enforce_unique_key_alias`, :7639); a user-defined key must start `sk-` and be 16+
|
|
154
|
+
characters (:1477-1490); `/key/update` applies `model_dump(exclude_unset=True)`, so an explicit `null`
|
|
155
|
+
clears a column and `key` is dropped (:2468-2469); `/key/delete` of an absent alias is **404** "No
|
|
156
|
+
keys found" (:4914) and of a forbidden one 403 (:4933); `/key/list` filters `key_alias` exactly, page
|
|
157
|
+
size at most 100 (:6376); `models` and `allowed_routes` are non-null `String[]` columns, hence `[]`
|
|
158
|
+
not `null` to clear.
|
|
159
|
+
|
|
160
|
+
★ **`/key/delete`'s 404 and 403 are typed by the SDK, as resource-specific tags.** The operation
|
|
161
|
+
declared 400 and 422 only, so both surfaced at runtime as core's status-mapped `NotFound`/`Forbidden`,
|
|
162
|
+
outside its error type. A distilled patch (`patches/key_management/delete_key_fn_key_delete_post.json`
|
|
163
|
+
in the distilled clone, byte-copied here) now types them as `KeyNotFound` (404, the message
|
|
164
|
+
`No keys found`) and `KeyDeleteForbidden` (403, `You are not authorized to delete this key`), each
|
|
165
|
+
matched on the status AND that phrase. `delete_key_fn` re-raises through `handle_exception_on_proxy`, so
|
|
166
|
+
the wire message is Python's `str(detail)`, `{'error': 'No keys found'}`, which the `includes` matcher
|
|
167
|
+
fits, as it fits a bare `detail`. `deleteKey` does `catchTag('KeyNotFound')`, then re-lists: the absence
|
|
168
|
+
proof is still the list, never the tag. A 404 without the phrase (a proxy in front) stays core's
|
|
169
|
+
`NotFound` and fails. UNVERIFIED against a live proxy: the wire shape is read from the wheel's source.
|
|
170
|
+
The tags ship with the next `@homeflare/distilled-litellm` release (its own changeset); the workspace copy has them now.
|
|
171
|
+
|
|
172
|
+
UNVERIFIED: that `/key/list?return_full_object=true` returns `budget_id` on a live 1.103.0 proxy (the
|
|
173
|
+
generated row type and the Prisma model both carry it; a miss fails the apply loudly as
|
|
174
|
+
`LitellmKeyFieldNotAppliedError`, never silently); the status of a `budget_id` naming no budget; a
|
|
175
|
+
retried `/key/generate` after a lost response (the SDK's default retry applies; the next deploy reads
|
|
176
|
+
the row). No live LiteLLM was contacted.
|
|
177
|
+
|
|
178
|
+
## `metadata`
|
|
179
|
+
|
|
180
|
+
LiteLLM keeps a key's guardrails, tags, per-model RPM/TPM limits, `allowed_passthrough_routes` and
|
|
181
|
+
more **inside** `metadata`, and `/key/update` replaces the column with what it is sent. So `metadata`
|
|
182
|
+
is a **merge** at the top-level key: the keys you declare are compared and written, every other live
|
|
183
|
+
key is carried into the update body unchanged, and one is cleared by declaring it `null`. Declaring
|
|
184
|
+
`{ seat: 'a' }` on a key that also has `guardrails` leaves them. Details and the measured source:
|
|
185
|
+
[litellm-key-metadata.md](./litellm-key-metadata.md). ⛔ `logging`, `callback_settings` and
|
|
186
|
+
`secret_manager_settings` carry callback secret keys: they are never held in state, a declaration
|
|
187
|
+
naming one is refused, and a metadata write onto a key that has one is refused (`withheld`).
|
|
188
|
+
|
|
189
|
+
## Not modelled
|
|
190
|
+
|
|
191
|
+
`max_budget`/`soft_budget`, `object_permission`, `blocked`, `spend` and the top-level rate limits.
|
|
192
|
+
The row read back has some of them, some it does not, and a field a declaration cannot diff is a field
|
|
193
|
+
it cannot own. Bind a `LiteLLM.Budget` for limits. `/key/update` is a merge patch for the columns, so
|
|
194
|
+
a field this resource never sends is left as it is; the ones inside `metadata` are left by the merge.
|