├── .changeset ├── README.md └── config.json ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── doc_report.yml │ └── feature_request.yml ├── actions │ └── install │ │ └── action.yaml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── agent_build_publish.yaml │ ├── apply-issue-labels-to-pr.yml │ ├── autofix.ci.yaml │ ├── build.yaml │ ├── changesets.yaml │ ├── check_quotas.yml │ ├── deploy.yaml │ ├── deploy_trigger.yaml │ ├── ghcr_retention_policy.yaml │ ├── job_build_agent_image.yaml │ ├── job_changes.yaml │ ├── job_clickhouse_migration_preview.yaml │ ├── job_clickhouse_migration_production.yaml │ ├── job_deploy_agent_production.yaml │ ├── job_deploy_agent_staging.yaml │ ├── job_deploy_api_canary.yaml │ ├── job_deploy_api_enterprise.yaml │ ├── job_deploy_api_production.yaml │ ├── job_deploy_api_staging.yaml │ ├── job_deploy_logdrain_production.yaml │ ├── job_deploy_workflows.yaml │ ├── job_detect_changes.yaml │ ├── job_test_agent_integration.yaml │ ├── job_test_agent_local.yaml │ ├── job_test_api_canary.yaml │ ├── job_test_api_local.yaml │ ├── job_test_api_staging.yaml │ ├── job_test_go_api_local.yaml │ ├── job_test_unit.yaml │ ├── pr.yaml │ ├── release.yaml │ ├── semantic-pull-requests.yaml │ └── test_agent_local.yaml ├── .gitignore ├── LICENSE ├── README.md ├── Taskfile.yml ├── apps ├── agent │ ├── .golangci.yaml │ ├── .goreleaser.yaml │ ├── Dockerfile │ ├── README.md │ ├── Taskfile.yml │ ├── bruno │ │ ├── Eventrouter │ │ │ └── Events.bru │ │ ├── Liveness.bru │ │ ├── Ratelimit │ │ │ └── Ratelimit.bru │ │ └── bruno.json │ ├── buf.gen.yaml │ ├── buf.yaml │ ├── cmd │ │ ├── agent │ │ │ ├── agent.go │ │ │ └── setup.go │ │ ├── main.go │ │ └── vault │ │ │ └── generate_kek.go │ ├── config.apprunner.production.json │ ├── config.apprunner.staging.json │ ├── config.docker.json │ ├── config.production.json │ ├── config.staging.json │ ├── fly.production.toml │ ├── fly.staging.toml │ ├── gen │ │ └── proto │ │ │ ├── cluster │ │ │ └── v1 │ │ │ │ ├── clusterv1connect │ │ │ │ └── service.connect.go │ │ │ │ ├── service.openapi.yaml │ │ │ │ └── service.pb.go │ │ │ ├── errors │ │ │ └── v1 │ │ │ │ ├── errors.openapi.yaml │ │ │ │ └── errors.pb.go │ │ │ ├── gossip │ │ │ └── v1 │ │ │ │ ├── gossip.openapi.yaml │ │ │ │ ├── gossip.pb.go │ │ │ │ └── gossipv1connect │ │ │ │ └── gossip.connect.go │ │ │ ├── ratelimit │ │ │ └── v1 │ │ │ │ ├── ratelimitv1connect │ │ │ │ └── service.connect.go │ │ │ │ ├── service.openapi.yaml │ │ │ │ └── service.pb.go │ │ │ └── vault │ │ │ └── v1 │ │ │ ├── object.openapi.yaml │ │ │ ├── object.pb.go │ │ │ ├── service.openapi.yaml │ │ │ ├── service.pb.go │ │ │ └── vaultv1connect │ │ │ └── service.connect.go │ ├── go.mod │ ├── go.sum │ ├── integration │ │ ├── cluster │ │ │ └── docker │ │ │ │ └── ratelimits_test.go │ │ ├── identities │ │ │ ├── identities_ratelimits_accuracy_test.go │ │ │ ├── ratelimits_with_cost_load_test.go │ │ │ ├── token_ratelimits_test.go │ │ │ └── update_identity_with_many_keys_test.go │ │ └── keys │ │ │ ├── ratelimits_test.go │ │ │ └── update_ratelimits_test.go │ ├── package.json │ ├── pkg │ │ ├── api │ │ │ ├── agent_auth.go │ │ │ ├── ctxutil │ │ │ │ └── context.go │ │ │ ├── errors │ │ │ │ ├── internal_server_error.go │ │ │ │ └── validation_error.go │ │ │ ├── interface.go │ │ │ ├── mw_logging.go │ │ │ ├── mw_metrics.go │ │ │ ├── mw_request_id.go │ │ │ ├── mw_tracing.go │ │ │ ├── register_routes.go │ │ │ ├── routes │ │ │ │ ├── not_found │ │ │ │ │ └── handler.go │ │ │ │ ├── openapi │ │ │ │ │ └── handler.go │ │ │ │ ├── route.go │ │ │ │ ├── sender.go │ │ │ │ ├── services.go │ │ │ │ ├── v1_liveness │ │ │ │ │ ├── handler.go │ │ │ │ │ └── handler_test.go │ │ │ │ ├── v1_ratelimit_commitLease │ │ │ │ │ ├── handler.go │ │ │ │ │ └── handler_test.go │ │ │ │ ├── v1_ratelimit_multiRatelimit │ │ │ │ │ └── handler.go │ │ │ │ ├── v1_ratelimit_ratelimit │ │ │ │ │ ├── handler.go │ │ │ │ │ └── handler_test.go │ │ │ │ ├── v1_vault_decrypt │ │ │ │ │ └── handler.go │ │ │ │ ├── v1_vault_encrypt │ │ │ │ │ └── handler.go │ │ │ │ └── v1_vault_encrypt_bulk │ │ │ │ │ └── handler.go │ │ │ ├── server.go │ │ │ ├── testutil │ │ │ │ └── harness.go │ │ │ └── validation │ │ │ │ └── validator.go │ │ ├── auth │ │ │ └── authorization.go │ │ ├── batch │ │ │ ├── consume.go │ │ │ ├── metrics.go │ │ │ └── process.go │ │ ├── cache │ │ │ ├── cache.go │ │ │ ├── cache_test.go │ │ │ ├── entry.go │ │ │ ├── interface.go │ │ │ ├── middleware.go │ │ │ ├── middleware │ │ │ │ ├── metrics.go │ │ │ │ └── tracing.go │ │ │ ├── noop.go │ │ │ └── util.go │ │ ├── circuitbreaker │ │ │ ├── interface.go │ │ │ ├── lib.go │ │ │ ├── lib_test.go │ │ │ └── metrics.go │ │ ├── clickhouse │ │ │ ├── client.go │ │ │ ├── flush.go │ │ │ ├── interface.go │ │ │ ├── noop.go │ │ │ └── schema │ │ │ │ └── requests.go │ │ ├── clock │ │ │ ├── interface.go │ │ │ ├── real_clock.go │ │ │ └── test_clock.go │ │ ├── cluster │ │ │ ├── cluster.go │ │ │ ├── cluster_test.go │ │ │ ├── interface.go │ │ │ └── node.go │ │ ├── config │ │ │ ├── agent.go │ │ │ ├── json.go │ │ │ └── json_test.go │ │ ├── connect │ │ │ ├── cluster.go │ │ │ ├── middleware_headers.go │ │ │ ├── ratelimit.go │ │ │ └── service.go │ │ ├── encryption │ │ │ ├── aes.go │ │ │ └── aes_test.go │ │ ├── env │ │ │ ├── env.go │ │ │ └── env_test.go │ │ ├── events │ │ │ └── topic.go │ │ ├── gossip │ │ │ ├── cluster.go │ │ │ ├── connect.go │ │ │ ├── interface.go │ │ │ ├── rpc.go │ │ │ ├── server_test.goxx │ │ │ └── test_utils_server.go │ │ ├── heartbeat │ │ │ └── heartbeat.go │ │ ├── logging │ │ │ ├── axiom.go │ │ │ └── logger.go │ │ ├── membership │ │ │ ├── interface.go │ │ │ ├── member.go │ │ │ ├── membership_test.go │ │ │ └── serf.go │ │ ├── metrics │ │ │ ├── axiom.go │ │ │ ├── axiom_test.go │ │ │ ├── interface.go │ │ │ ├── metrics.go │ │ │ └── noop.go │ │ ├── mutex │ │ │ └── traced.go │ │ ├── openapi │ │ │ ├── config.yaml │ │ │ ├── gen.go │ │ │ ├── openapi.json │ │ │ └── spec.go │ │ ├── port │ │ │ └── free.go │ │ ├── profiling │ │ │ └── grafana.go │ │ ├── prometheus │ │ │ ├── metrics.go │ │ │ └── server.go │ │ ├── repeat │ │ │ └── every.go │ │ ├── ring │ │ │ ├── metrics.go │ │ │ ├── ring.go │ │ │ └── ring_test.go │ │ ├── testutil │ │ │ └── attack.go │ │ ├── testutils │ │ │ └── containers │ │ │ │ ├── agent.go │ │ │ │ ├── compose.go │ │ │ │ ├── redis.go │ │ │ │ └── s3.go │ │ ├── tracing │ │ │ ├── axiom.go │ │ │ ├── schema.go │ │ │ ├── trace.go │ │ │ └── util.go │ │ ├── uid │ │ │ ├── hash.go │ │ │ ├── uid.go │ │ │ └── uid_test.go │ │ ├── util │ │ │ ├── compare.go │ │ │ ├── convert.go │ │ │ ├── convert_test.go │ │ │ ├── pointer.go │ │ │ ├── random.go │ │ │ └── retry.go │ │ └── version │ │ │ └── version.go │ ├── proto │ │ ├── cluster │ │ │ └── v1 │ │ │ │ └── service.proto │ │ ├── errors │ │ │ └── v1 │ │ │ │ └── errors.proto.disabled │ │ ├── gossip │ │ │ └── v1 │ │ │ │ └── gossip.proto │ │ ├── ratelimit │ │ │ └── v1 │ │ │ │ └── service.proto │ │ └── vault │ │ │ └── v1 │ │ │ ├── object.proto │ │ │ └── service.proto │ ├── schema.json │ ├── scripts │ │ ├── deploy.bash │ │ ├── heap.bash │ │ └── profile.bash │ └── services │ │ ├── ratelimit │ │ ├── bucket.go │ │ ├── commit_lease.go │ │ ├── consistency.go │ │ ├── interface.go │ │ ├── metrics.go │ │ ├── middleware.go │ │ ├── mitigate.go │ │ ├── peer.go │ │ ├── pushpull.go │ │ ├── ratelimit.go │ │ ├── ratelimit_mitigation_test.go │ │ ├── ratelimit_multi.go │ │ ├── ratelimit_replication_test.go │ │ ├── ratelimit_test.go │ │ ├── service.go │ │ ├── sliding_window.go │ │ ├── sliding_window_test.go │ │ └── sync_with_origin.go │ │ └── vault │ │ ├── create_dek.go │ │ ├── decrypt.go │ │ ├── encrypt.go │ │ ├── encrypt_bulk.go │ │ ├── integration │ │ ├── coldstart_test.go │ │ ├── migrate_deks_test.go │ │ ├── reencryption_test.go │ │ └── reusing_deks_test.go │ │ ├── keyring │ │ ├── create_key.go │ │ ├── decode_and_decrypt_key.go │ │ ├── encrypt_and_encode_key.go │ │ ├── get_key.go │ │ ├── get_latest_key.go │ │ ├── get_or_create_key.go │ │ ├── keyring.go │ │ └── roll_keys.go │ │ ├── keys │ │ ├── key.go │ │ └── master_key.go │ │ ├── reencrypt.go │ │ ├── roll_deks.go │ │ ├── service.go │ │ └── storage │ │ ├── interface.go │ │ ├── memory.go │ │ ├── middleware │ │ └── tracing.go │ │ └── s3.go ├── api │ ├── .dev.vars.example │ ├── .gitignore │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── Dockerfile.dev │ ├── package.json │ ├── src │ │ ├── benchmarks │ │ │ └── ratelimit_latency.test.ts │ │ ├── integration │ │ │ ├── create_verify_delete_key.test.ts │ │ │ ├── identity_lifecycle.test.ts │ │ │ ├── keys_updated_at_actually_updates.ts │ │ │ ├── list_keys.test.ts │ │ │ ├── remaining_is_consistent.test.ts │ │ │ ├── sdk │ │ │ │ ├── create_and_verify.test.ts │ │ │ │ ├── create_key_then_update_identity.test.ts │ │ │ │ ├── create_key_with_permissions.ts │ │ │ │ ├── verify.test.ts │ │ │ │ └── verify_with_ratelimit.test.ts │ │ │ ├── update_key_add_remaining.test.ts │ │ │ └── verify_permissions.test.ts │ │ ├── pkg │ │ │ ├── analytics.ts │ │ │ ├── audit.ts │ │ │ ├── auth │ │ │ │ └── root_key.ts │ │ │ ├── cache │ │ │ │ ├── index.ts │ │ │ │ ├── namespaces.ts │ │ │ │ └── stale-while-revalidate.ts │ │ │ ├── db.ts │ │ │ ├── env.ts │ │ │ ├── errors │ │ │ │ ├── http.ts │ │ │ │ ├── index.ts │ │ │ │ └── openapi_responses.ts │ │ │ ├── hono │ │ │ │ ├── app.ts │ │ │ │ └── env.ts │ │ │ ├── key_migration │ │ │ │ ├── dlq_handler.ts │ │ │ │ ├── handler.ts │ │ │ │ └── message.ts │ │ │ ├── keys │ │ │ │ └── service.ts │ │ │ ├── metrics │ │ │ │ ├── axiom.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── logdrain.ts │ │ │ │ └── noop.ts │ │ │ ├── middleware │ │ │ │ ├── benchmarks.ts │ │ │ │ ├── index.ts │ │ │ │ ├── init.ts │ │ │ │ └── metrics.ts │ │ │ ├── ratelimit │ │ │ │ ├── agent.ts │ │ │ │ ├── client.ts │ │ │ │ ├── do_client.ts │ │ │ │ ├── durable_object.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ └── noop.ts │ │ │ ├── testutil │ │ │ │ ├── benchmark-harness.ts │ │ │ │ ├── common-tests.ts │ │ │ │ ├── env.ts │ │ │ │ ├── harness.ts │ │ │ │ ├── integration-harness.ts │ │ │ │ ├── load.ts │ │ │ │ └── request.ts │ │ │ ├── types │ │ │ │ └── maybe.ts │ │ │ ├── usagelimit │ │ │ │ ├── client.ts │ │ │ │ ├── durable_object.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ └── noop.ts │ │ │ ├── util │ │ │ │ ├── instrument-fetch.ts │ │ │ │ ├── retry.ts │ │ │ │ ├── revalidate_key_count.ts │ │ │ │ ├── wildcard.test.ts │ │ │ │ ├── wildcard.ts │ │ │ │ └── zod-error.ts │ │ │ └── vault.ts │ │ ├── routes │ │ │ ├── legacy_apis_listKeys.ts │ │ │ ├── legacy_keys_createKey.test.ts │ │ │ ├── legacy_keys_createKey.ts │ │ │ ├── legacy_keys_verifyKey.test.ts │ │ │ ├── legacy_keys_verifyKey.ts │ │ │ ├── schema.ts │ │ │ ├── v1_analytics_getVerifications.happy.test.ts │ │ │ ├── v1_analytics_getVerifications.ts │ │ │ ├── v1_apis_createApi.error.test.ts │ │ │ ├── v1_apis_createApi.happy.test.ts │ │ │ ├── v1_apis_createApi.security.test.ts │ │ │ ├── v1_apis_createApi.ts │ │ │ ├── v1_apis_deleteApi.error.test.ts │ │ │ ├── v1_apis_deleteApi.happy.test.ts │ │ │ ├── v1_apis_deleteApi.security.test.ts │ │ │ ├── v1_apis_deleteApi.ts │ │ │ ├── v1_apis_deleteKeys.error.test.ts │ │ │ ├── v1_apis_deleteKeys.happy.test.ts │ │ │ ├── v1_apis_deleteKeys.security.test.ts │ │ │ ├── v1_apis_deleteKeys.ts │ │ │ ├── v1_apis_getApi.error.test.ts │ │ │ ├── v1_apis_getApi.happy.test.ts │ │ │ ├── v1_apis_getApi.security.test.ts │ │ │ ├── v1_apis_getApi.ts │ │ │ ├── v1_apis_listKeys.error.test.ts │ │ │ ├── v1_apis_listKeys.happy.test.ts │ │ │ ├── v1_apis_listKeys.security.test.ts │ │ │ ├── v1_apis_listKeys.ts │ │ │ ├── v1_identities_createIdentity.error.test.ts │ │ │ ├── v1_identities_createIdentity.happy.test.ts │ │ │ ├── v1_identities_createIdentity.security.test.ts │ │ │ ├── v1_identities_createIdentity.ts │ │ │ ├── v1_identities_deleteIdentity.error.test.ts │ │ │ ├── v1_identities_deleteIdentity.happy.test.ts │ │ │ ├── v1_identities_deleteIdentity.security.test.ts │ │ │ ├── v1_identities_deleteIdentity.ts │ │ │ ├── v1_identities_getIdentity.error.test.ts │ │ │ ├── v1_identities_getIdentity.happy.test.ts │ │ │ ├── v1_identities_getIdentity.security.test.ts │ │ │ ├── v1_identities_getIdentity.ts │ │ │ ├── v1_identities_listIdentities.happy.test.ts │ │ │ ├── v1_identities_listIdentities.security.test.ts │ │ │ ├── v1_identities_listIdentities.ts │ │ │ ├── v1_identities_updateIdentity.error.test.ts │ │ │ ├── v1_identities_updateIdentity.happy.test.ts │ │ │ ├── v1_identities_updateIdentity.security.test.ts │ │ │ ├── v1_identities_updateIdentity.ts │ │ │ ├── v1_keys_addPermissions.error.test.ts │ │ │ ├── v1_keys_addPermissions.happy.test.ts │ │ │ ├── v1_keys_addPermissions.security.test.ts │ │ │ ├── v1_keys_addPermissions.ts │ │ │ ├── v1_keys_addRoles.error.test.ts │ │ │ ├── v1_keys_addRoles.happy.test.ts │ │ │ ├── v1_keys_addRoles.security.test.ts │ │ │ ├── v1_keys_addRoles.ts │ │ │ ├── v1_keys_createKey.error.test.ts │ │ │ ├── v1_keys_createKey.happy.test.ts │ │ │ ├── v1_keys_createKey.security.test.ts │ │ │ ├── v1_keys_createKey.ts │ │ │ ├── v1_keys_deleteKey.error.test.ts │ │ │ ├── v1_keys_deleteKey.happy.test.ts │ │ │ ├── v1_keys_deleteKey.security.test.ts │ │ │ ├── v1_keys_deleteKey.ts │ │ │ ├── v1_keys_getKey.error.test.ts │ │ │ ├── v1_keys_getKey.happy.test.ts │ │ │ ├── v1_keys_getKey.security.test.ts │ │ │ ├── v1_keys_getKey.ts │ │ │ ├── v1_keys_getVerifications.error.test.ts │ │ │ ├── v1_keys_getVerifications.happy.test.ts │ │ │ ├── v1_keys_getVerifications.security.test.ts │ │ │ ├── v1_keys_getVerifications.ts │ │ │ ├── v1_keys_removePermissions.error.test.ts │ │ │ ├── v1_keys_removePermissions.happy.test.ts │ │ │ ├── v1_keys_removePermissions.security.test.ts │ │ │ ├── v1_keys_removePermissions.ts │ │ │ ├── v1_keys_removeRoles.error.test.ts │ │ │ ├── v1_keys_removeRoles.happy.test.ts │ │ │ ├── v1_keys_removeRoles.security.test.ts │ │ │ ├── v1_keys_removeRoles.ts │ │ │ ├── v1_keys_setPermissions.error.test.ts │ │ │ ├── v1_keys_setPermissions.happy.test.ts │ │ │ ├── v1_keys_setPermissions.security.test.ts │ │ │ ├── v1_keys_setPermissions.ts │ │ │ ├── v1_keys_setRoles.error.test.ts │ │ │ ├── v1_keys_setRoles.happy.test.ts │ │ │ ├── v1_keys_setRoles.security.test.ts │ │ │ ├── v1_keys_setRoles.ts │ │ │ ├── v1_keys_updateKey.error.test.ts │ │ │ ├── v1_keys_updateKey.happy.test.ts │ │ │ ├── v1_keys_updateKey.security.test.ts │ │ │ ├── v1_keys_updateKey.ts │ │ │ ├── v1_keys_updateRemaining.error.test.ts │ │ │ ├── v1_keys_updateRemaining.happy.test.ts │ │ │ ├── v1_keys_updateRemaining.security.test.ts │ │ │ ├── v1_keys_updateRemaining.ts │ │ │ ├── v1_keys_verifyKey.error.test.ts │ │ │ ├── v1_keys_verifyKey.multilimit.test.ts │ │ │ ├── v1_keys_verifyKey.permissions.test.ts │ │ │ ├── v1_keys_verifyKey.ratelimit_accuracy.test.ts │ │ │ ├── v1_keys_verifyKey.test.ts │ │ │ ├── v1_keys_verifyKey.ts │ │ │ ├── v1_keys_whoami.error.test.ts │ │ │ ├── v1_keys_whoami.happy.test.ts │ │ │ ├── v1_keys_whoami.security.test.ts │ │ │ ├── v1_keys_whoami.ts │ │ │ ├── v1_liveness.test.ts │ │ │ ├── v1_liveness.ts │ │ │ ├── v1_migrations_createKey.error.test.ts │ │ │ ├── v1_migrations_createKey.happy.test.ts │ │ │ ├── v1_migrations_createKey.security.test.ts │ │ │ ├── v1_migrations_createKey.ts │ │ │ ├── v1_migrations_enqueueKeys.happy.test_disabled.ts │ │ │ ├── v1_migrations_enqueueKeys.security.test.ts │ │ │ ├── v1_migrations_enqueueKeys.ts │ │ │ ├── v1_permissions_createPermission.error.test.ts │ │ │ ├── v1_permissions_createPermission.happy.test.ts │ │ │ ├── v1_permissions_createPermission.security.test.ts │ │ │ ├── v1_permissions_createPermission.ts │ │ │ ├── v1_permissions_createRole.error.test.ts │ │ │ ├── v1_permissions_createRole.happy.test.ts │ │ │ ├── v1_permissions_createRole.security.test.ts │ │ │ ├── v1_permissions_createRole.ts │ │ │ ├── v1_permissions_deletePermission.happy.test.ts │ │ │ ├── v1_permissions_deletePermission.security.test.ts │ │ │ ├── v1_permissions_deletePermission.ts │ │ │ ├── v1_permissions_deleteRole.happy.test.ts │ │ │ ├── v1_permissions_deleteRole.security.test.ts │ │ │ ├── v1_permissions_deleteRole.ts │ │ │ ├── v1_permissions_getPermission.error.test.ts │ │ │ ├── v1_permissions_getPermission.happy.test.ts │ │ │ ├── v1_permissions_getPermission.security.test.ts │ │ │ ├── v1_permissions_getPermission.ts │ │ │ ├── v1_permissions_getRole.error.test.ts │ │ │ ├── v1_permissions_getRole.happy.test.ts │ │ │ ├── v1_permissions_getRole.security.test.ts │ │ │ ├── v1_permissions_getRole.ts │ │ │ ├── v1_permissions_listPermissions.happy.test.ts │ │ │ ├── v1_permissions_listPermissions.security.test.ts │ │ │ ├── v1_permissions_listPermissions.ts │ │ │ ├── v1_permissions_listRoles.happy.test.ts │ │ │ ├── v1_permissions_listRoles.security.test.ts │ │ │ ├── v1_permissions_listRoles.ts │ │ │ ├── v1_ratelimits_deleteOverride.error.test.ts │ │ │ ├── v1_ratelimits_deleteOverride.happy.test.ts │ │ │ ├── v1_ratelimits_deleteOverride.security.test.ts │ │ │ ├── v1_ratelimits_deleteOverride.ts │ │ │ ├── v1_ratelimits_getOverride.error.test.ts │ │ │ ├── v1_ratelimits_getOverride.happy.test.ts │ │ │ ├── v1_ratelimits_getOverride.security.test.ts │ │ │ ├── v1_ratelimits_getOverride.ts │ │ │ ├── v1_ratelimits_limit.accuracy.test.ts │ │ │ ├── v1_ratelimits_limit.consistency.test.ts.skipped │ │ │ ├── v1_ratelimits_limit.happy.test.ts │ │ │ ├── v1_ratelimits_limit.overrides.test.ts │ │ │ ├── v1_ratelimits_limit.ts │ │ │ ├── v1_ratelimits_listOverrides.error.test.ts │ │ │ ├── v1_ratelimits_listOverrides.happy.test.ts │ │ │ ├── v1_ratelimits_listOverrides.security.test.ts │ │ │ ├── v1_ratelimits_listOverrides.ts │ │ │ ├── v1_ratelimits_setOverride.error.test.ts │ │ │ ├── v1_ratelimits_setOverride.happy.test.ts │ │ │ ├── v1_ratelimits_setOverride.security.test.ts │ │ │ └── v1_ratelimits_setOverride.ts │ │ └── worker.ts │ ├── tsconfig.json │ ├── vitest.benchmark.ts │ ├── vitest.integration.ts │ ├── vitest.unit.ts │ ├── worker.capnp │ ├── wrangler.custom.toml │ └── wrangler.toml ├── chproxy │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── batch.go │ ├── buffer.go │ ├── config.go │ ├── config_test.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── otel.go ├── dashboard │ ├── .env.example │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── app │ │ ├── (app) │ │ │ ├── [...not-found] │ │ │ │ └── page.tsx │ │ │ ├── api │ │ │ │ ├── auth │ │ │ │ │ └── refresh │ │ │ │ │ │ └── route.ts │ │ │ │ └── trpc │ │ │ │ │ └── [trpc] │ │ │ │ │ └── route.ts │ │ │ ├── apis │ │ │ │ ├── [apiId] │ │ │ │ │ ├── _components │ │ │ │ │ │ ├── create-key │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── credits-setup.tsx │ │ │ │ │ │ │ │ ├── expiration-setup.tsx │ │ │ │ │ │ │ │ ├── external-id-field.tsx │ │ │ │ │ │ │ │ ├── general-setup.tsx │ │ │ │ │ │ │ │ ├── key-created-success-dialog.tsx │ │ │ │ │ │ │ │ ├── metadata-setup.tsx │ │ │ │ │ │ │ │ ├── protection-switch.tsx │ │ │ │ │ │ │ │ ├── ratelimit-setup.tsx │ │ │ │ │ │ │ │ ├── secret-key.tsx │ │ │ │ │ │ │ │ └── section-label.tsx │ │ │ │ │ │ │ ├── create-key.constants.tsx │ │ │ │ │ │ │ ├── create-key.schema.ts │ │ │ │ │ │ │ ├── create-key.utils.ts │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ ├── use-create-identity.ts │ │ │ │ │ │ │ │ ├── use-create-key.tsx │ │ │ │ │ │ │ │ ├── use-fetch-identities │ │ │ │ │ │ │ │ │ ├── create-identity-options.tsx │ │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ └── use-validate-steps.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ └── rbac-dialog-content.tsx │ │ │ │ │ ├── _overview │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── charts │ │ │ │ │ │ │ │ ├── bar-chart │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ │ │ ├── query-timeseries.schema.ts │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── line-chart │ │ │ │ │ │ │ │ │ └── hooks │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── controls │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ └── outcome-filter.tsx │ │ │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── table │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── log-details │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ ├── log-header.tsx │ │ │ │ │ │ │ │ │ │ ├── log-outcome-distribution-section.tsx │ │ │ │ │ │ │ │ │ │ ├── log-section.tsx │ │ │ │ │ │ │ │ │ │ └── roles-permissions.tsx │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── outcome-popover.tsx │ │ │ │ │ │ │ │ └── override-indicator.tsx │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ │ │ ├── logs-table.tsx │ │ │ │ │ │ │ │ ├── query-logs.schema.ts │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ ├── calculate-blocked-percentage.ts │ │ │ │ │ │ │ │ └── get-row-class.ts │ │ │ │ │ │ ├── constants.ts │ │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ └── use-filters.ts │ │ │ │ │ │ ├── logs-client.tsx │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── actions.ts │ │ │ │ │ ├── api-id-navbar.tsx │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── keys │ │ │ │ │ │ └── [keyAuthId] │ │ │ │ │ │ │ ├── [keyId] │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── charts │ │ │ │ │ │ │ │ │ ├── bar-chart │ │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ │ │ │ ├── query-timeseries.schema.ts │ │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── controls │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ │ └── outcome-filter.tsx │ │ │ │ │ │ │ │ │ │ ├── logs-live-switch.tsx │ │ │ │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── rbac │ │ │ │ │ │ │ │ │ ├── permissions.tsx │ │ │ │ │ │ │ │ │ └── rbac-buttons.tsx │ │ │ │ │ │ │ │ └── table │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── log-details │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ └── hooks │ │ │ │ │ │ │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ └── status-badge.tsx │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ │ │ │ ├── logs-table.tsx │ │ │ │ │ │ │ │ │ ├── query-logs.schema.ts │ │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ │ ├── calculate-blocked-percentage.ts │ │ │ │ │ │ │ │ │ └── get-row-class.ts │ │ │ │ │ │ │ ├── context │ │ │ │ │ │ │ │ └── logs.tsx │ │ │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ └── use-filters.ts │ │ │ │ │ │ │ ├── logs-client.tsx │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ │ ├── _components │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── controls │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── table │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── actions │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ ├── delete-key.tsx │ │ │ │ │ │ │ │ │ │ │ ├── disable-key.tsx │ │ │ │ │ │ │ │ │ │ │ ├── edit-credits │ │ │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ │ │ │ ├── edit-expiration │ │ │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ │ │ │ ├── edit-external-id │ │ │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ │ │ ├── edit-key-name.tsx │ │ │ │ │ │ │ │ │ │ │ ├── edit-metadata │ │ │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ │ │ │ ├── edit-ratelimits │ │ │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ │ │ │ ├── use-delete-key.ts │ │ │ │ │ │ │ │ │ │ │ │ ├── use-edit-credits.ts │ │ │ │ │ │ │ │ │ │ │ │ ├── use-edit-expiration.ts │ │ │ │ │ │ │ │ │ │ │ │ ├── use-edit-external-id.ts │ │ │ │ │ │ │ │ │ │ │ │ ├── use-edit-key.tsx │ │ │ │ │ │ │ │ │ │ │ │ ├── use-edit-metadata.ts │ │ │ │ │ │ │ │ │ │ │ │ ├── use-edit-ratelimits.ts │ │ │ │ │ │ │ │ │ │ │ │ └── use-update-key-status.tsx │ │ │ │ │ │ │ │ │ │ │ └── key-info.tsx │ │ │ │ │ │ │ │ │ │ ├── keys-table-action.popover.constants.tsx │ │ │ │ │ │ │ │ │ │ └── keys-table-action.popover.tsx │ │ │ │ │ │ │ │ │ ├── bar-chart │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ └── outcome-explainer.tsx │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ ├── query-timeseries.schema.ts │ │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ │ │ ├── hidden-value.tsx │ │ │ │ │ │ │ │ │ ├── last-used.tsx │ │ │ │ │ │ │ │ │ ├── selection-controls │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ └── batch-edit-external-id.tsx │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ ├── skeletons.tsx │ │ │ │ │ │ │ │ │ └── status-cell │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ └── status-badge.tsx │ │ │ │ │ │ │ │ │ │ ├── constants.tsx │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ ├── query-timeseries.schema.ts │ │ │ │ │ │ │ │ │ │ └── use-key-status.ts │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ └── use-keys-list-query.ts │ │ │ │ │ │ │ │ │ ├── keys-list.tsx │ │ │ │ │ │ │ │ │ ├── query-logs.schema.ts │ │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ │ └── get-row-class.ts │ │ │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ └── use-filters.ts │ │ │ │ │ │ │ └── keys-client.tsx │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── select.tsx │ │ │ │ │ └── settings │ │ │ │ │ │ ├── actions.ts │ │ │ │ │ │ ├── components │ │ │ │ │ │ ├── copy-api-id.tsx │ │ │ │ │ │ ├── default-bytes.tsx │ │ │ │ │ │ ├── default-prefix.tsx │ │ │ │ │ │ ├── delete-api.tsx │ │ │ │ │ │ ├── delete-protection.tsx │ │ │ │ │ │ ├── settings-client.tsx │ │ │ │ │ │ ├── status-badge.tsx │ │ │ │ │ │ ├── update-api-name.tsx │ │ │ │ │ │ └── update-ip-whitelist.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── _components │ │ │ │ │ ├── api-list-card.tsx │ │ │ │ │ ├── api-list-client.tsx │ │ │ │ │ ├── api-list-grid.tsx │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── controls │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create-api-button.tsx │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ └── hooks │ │ │ │ │ │ ├── query-timeseries.schema.ts │ │ │ │ │ │ ├── use-fetch-api-overview.ts │ │ │ │ │ │ ├── use-filters.ts │ │ │ │ │ │ └── use-query-timeseries.ts │ │ │ │ ├── actions.ts │ │ │ │ ├── navigation.tsx │ │ │ │ └── page.tsx │ │ │ ├── audit │ │ │ │ ├── actions.ts │ │ │ │ ├── components │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── controls │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── bucket-filter.tsx │ │ │ │ │ │ │ │ │ ├── events-filter.tsx │ │ │ │ │ │ │ │ │ ├── root-keys-filter.tsx │ │ │ │ │ │ │ │ │ └── users-filter.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-queries │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── logs-client.tsx │ │ │ │ │ └── table │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ ├── log-details │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── log-footer.tsx │ │ │ │ │ │ │ ├── log-header.tsx │ │ │ │ │ │ │ └── log-section.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── logs-table.tsx │ │ │ │ │ │ ├── query-logs.schema.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ └── get-row-class.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── filters.schema.ts │ │ │ │ ├── hooks │ │ │ │ │ └── use-filters.ts │ │ │ │ ├── navigation.tsx │ │ │ │ └── page.tsx │ │ │ ├── authorization │ │ │ │ ├── _components │ │ │ │ │ └── rbac-form.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── layout.tsx │ │ │ │ ├── permissions │ │ │ │ │ ├── [permissionId] │ │ │ │ │ │ ├── delete-permission.tsx │ │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ └── settings-client.tsx │ │ │ │ │ ├── empty.tsx │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ └── page.tsx │ │ │ │ └── roles │ │ │ │ │ ├── [roleId] │ │ │ │ │ ├── delete-role.tsx │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── permission-toggle.tsx │ │ │ │ │ ├── settings-client.tsx │ │ │ │ │ └── tree.tsx │ │ │ │ │ ├── empty.tsx │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── gateway-new │ │ │ │ └── page.tsx │ │ │ ├── identities │ │ │ │ ├── [identityId] │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── filter.tsx │ │ │ │ ├── navigation.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── row.tsx │ │ │ ├── layout.tsx │ │ │ ├── logs │ │ │ │ ├── components │ │ │ │ │ ├── charts │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── query-timeseries.schema.ts │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── controls │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-display │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ └── display-popover.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── methods-filter.tsx │ │ │ │ │ │ │ │ │ ├── paths-filter.tsx │ │ │ │ │ │ │ │ │ └── status-filter.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-live-switch.tsx │ │ │ │ │ │ │ ├── logs-queries │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── logs-client.tsx │ │ │ │ │ └── table │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ ├── use-logs-query.test.ts │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ ├── log-details │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── log-footer.tsx │ │ │ │ │ │ │ ├── log-header.tsx │ │ │ │ │ │ │ ├── log-meta.tsx │ │ │ │ │ │ │ └── log-section.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── logs-table.tsx │ │ │ │ │ │ └── query-logs.schema.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── context │ │ │ │ │ └── logs.tsx │ │ │ │ ├── filters.schema.ts │ │ │ │ ├── hooks │ │ │ │ │ └── use-filters.ts │ │ │ │ ├── navigation.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── overview │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ ├── ratelimits │ │ │ │ ├── [namespaceId] │ │ │ │ │ ├── _components │ │ │ │ │ │ ├── delete-dialog.tsx │ │ │ │ │ │ ├── identifier-dialog.tsx │ │ │ │ │ │ ├── namespace-delete-dialog.tsx │ │ │ │ │ │ ├── table-action-button.tsx │ │ │ │ │ │ └── table-action-popover.tsx │ │ │ │ │ ├── _overview │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── charts │ │ │ │ │ │ │ │ ├── bar-chart │ │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ │ │ └── query-timeseries.schema.ts │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── line-chart │ │ │ │ │ │ │ │ │ └── hooks │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── controls │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ └── status-filter.tsx │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── table │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── inline-filter.tsx │ │ │ │ │ │ │ │ ├── logs-actions │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── override-indicator.tsx │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ │ │ ├── logs-table.tsx │ │ │ │ │ │ │ │ ├── query-logs.schema.ts │ │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ │ ├── calculate-blocked-percentage.ts │ │ │ │ │ │ │ │ ├── format-duration.ts │ │ │ │ │ │ │ │ └── get-row-class.ts │ │ │ │ │ │ ├── context │ │ │ │ │ │ │ └── logs.tsx │ │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ └── use-filters.ts │ │ │ │ │ │ └── logs-client.tsx │ │ │ │ │ ├── logs │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── charts │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ │ └── use-fetch-timeseries.ts │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── query-timeseries.schema.ts │ │ │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── controls │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ ├── logs-filters │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ ├── identifiers-filter.tsx │ │ │ │ │ │ │ │ │ │ │ └── status-filter.tsx │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ │ ├── logs-live-switch.tsx │ │ │ │ │ │ │ │ │ ├── logs-queries │ │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-client.tsx │ │ │ │ │ │ │ └── table │ │ │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ │ ├── use-logs-query.test.tsx │ │ │ │ │ │ │ │ └── use-logs-query.ts │ │ │ │ │ │ │ │ ├── log-details │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ ├── log-footer.tsx │ │ │ │ │ │ │ │ │ ├── log-header.tsx │ │ │ │ │ │ │ │ │ ├── log-meta.tsx │ │ │ │ │ │ │ │ │ └── log-section.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── logs-actions │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── logs-table.tsx │ │ │ │ │ │ │ │ └── query-logs.schema.ts │ │ │ │ │ │ ├── constants.ts │ │ │ │ │ │ ├── context │ │ │ │ │ │ │ └── logs.tsx │ │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ ├── use-filters.test.ts │ │ │ │ │ │ │ └── use-filters.ts │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── namespace-navbar.tsx │ │ │ │ │ ├── namespace.actions.ts │ │ │ │ │ ├── overrides │ │ │ │ │ │ ├── logs-actions │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── overrides-table.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── settings │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── settings-client.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── types.ts │ │ │ │ ├── _components │ │ │ │ │ ├── control-cloud │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── controls │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── logs-datetime │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── logs-refresh.tsx │ │ │ │ │ │ │ └── logs-search │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create-namespace-button.tsx │ │ │ │ │ ├── filters.schema.ts │ │ │ │ │ ├── hooks │ │ │ │ │ │ └── use-filters.ts │ │ │ │ │ ├── namespace-card.tsx │ │ │ │ │ └── ratelimit-client.tsx │ │ │ │ ├── navigation.tsx │ │ │ │ └── page.tsx │ │ │ └── settings │ │ │ │ ├── billing │ │ │ │ ├── client.tsx │ │ │ │ ├── components │ │ │ │ │ ├── confirmation.tsx │ │ │ │ │ ├── shell.tsx │ │ │ │ │ └── usage.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── stripe │ │ │ │ │ ├── checkout │ │ │ │ │ └── page.tsx │ │ │ │ │ └── portal │ │ │ │ │ └── page.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── general │ │ │ │ ├── copy-workspace-id.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── update-workspace-name.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── root-keys │ │ │ │ ├── [keyId] │ │ │ │ │ ├── history │ │ │ │ │ │ └── access-table.tsx │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ ├── page-layout.tsx │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── permissions │ │ │ │ │ │ ├── add-permission-for-api.tsx │ │ │ │ │ │ ├── api.tsx │ │ │ │ │ │ ├── legacy.tsx │ │ │ │ │ │ ├── permission-manager-card.tsx │ │ │ │ │ │ ├── permission_toggle.tsx │ │ │ │ │ │ ├── permissions.ts │ │ │ │ │ │ └── workspace.tsx │ │ │ │ │ ├── selector.tsx │ │ │ │ │ └── update-root-key-name.tsx │ │ │ │ ├── new │ │ │ │ │ ├── client.tsx │ │ │ │ │ ├── navigation.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── root-key-nav.tsx │ │ │ │ ├── team │ │ │ │ ├── client.tsx │ │ │ │ ├── invitations.tsx │ │ │ │ ├── invite.tsx │ │ │ │ ├── members.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── role-switcher.tsx │ │ │ │ └── status-badge.tsx │ │ │ │ ├── vercel │ │ │ │ ├── client.tsx │ │ │ │ ├── loading.tsx │ │ │ │ └── page.tsx │ │ │ │ └── workspace-navbar.tsx │ │ ├── actions.ts │ │ ├── api │ │ │ ├── v1 │ │ │ │ └── vercel │ │ │ │ │ └── integration │ │ │ │ │ └── route.ts │ │ │ └── webhooks │ │ │ │ └── stripe │ │ │ │ └── route.ts │ │ ├── auth │ │ │ ├── actions.ts │ │ │ ├── banners.tsx │ │ │ ├── context │ │ │ │ ├── signin-context.tsx │ │ │ │ └── signup-context.tsx │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useSignIn.ts │ │ │ │ └── useSignUp.ts │ │ │ ├── join │ │ │ │ └── route.ts │ │ │ ├── layout.tsx │ │ │ ├── oauth-button.tsx │ │ │ ├── sign-in │ │ │ │ ├── [[...sign-in]] │ │ │ │ │ └── page.tsx │ │ │ │ ├── email-code.tsx │ │ │ │ ├── email-signin.tsx │ │ │ │ ├── email-verify.tsx │ │ │ │ ├── last_used.tsx │ │ │ │ ├── oauth-signin.tsx │ │ │ │ └── org-selector.tsx │ │ │ ├── sign-up │ │ │ │ ├── [[...sign-up]] │ │ │ │ │ └── page.tsx │ │ │ │ ├── email-code.tsx │ │ │ │ ├── email-signup.tsx │ │ │ │ └── oauth-signup.tsx │ │ │ └── sso-callback │ │ │ │ └── [[...sso-callback]] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── integrations │ │ │ └── vercel │ │ │ │ └── callback │ │ │ │ ├── client.tsx │ │ │ │ ├── exchange-code.tsx │ │ │ │ ├── loading.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── workspace.tsx │ │ ├── layout.tsx │ │ ├── new │ │ │ ├── create-api.tsx │ │ │ ├── create-ratelimit.tsx │ │ │ ├── create-workspace.tsx │ │ │ ├── keys.tsx │ │ │ └── page.tsx │ │ ├── react-query-provider.tsx │ │ ├── robots.txt │ │ └── theme-provider.tsx │ ├── components.json │ ├── components │ │ ├── confirmation-popover.tsx │ │ ├── dashboard │ │ │ ├── charts.tsx │ │ │ ├── command-menu.tsx │ │ │ ├── confirm.tsx │ │ │ ├── feedback-component.tsx │ │ │ ├── loading.tsx │ │ │ ├── navbar.tsx │ │ │ ├── page-header.tsx │ │ │ ├── root-key-table │ │ │ │ ├── index.tsx │ │ │ │ └── table.tsx │ │ │ └── visible-button.tsx │ │ ├── dialog-container.tsx │ │ ├── dialog-container │ │ │ ├── dialog-container.tsx │ │ │ ├── dialog-parts.tsx │ │ │ └── navigable-dialog.tsx │ │ ├── empty-component-spacer.tsx │ │ ├── keyboard-button.tsx │ │ ├── landing │ │ │ └── fade-in.tsx │ │ ├── logs │ │ │ ├── chart │ │ │ │ ├── components │ │ │ │ │ ├── logs-chart-error.tsx │ │ │ │ │ └── logs-chart-loading.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── utils │ │ │ │ │ ├── calculate-timepoints.ts │ │ │ │ │ ├── convert-date-to-local.ts │ │ │ │ │ └── format-timestamp.ts │ │ │ ├── checkbox │ │ │ │ ├── filter-checkbox.tsx │ │ │ │ ├── filter-item.tsx │ │ │ │ ├── filters-popover.tsx │ │ │ │ └── hooks │ │ │ │ │ └── index.ts │ │ │ ├── constants.ts │ │ │ ├── control-cloud │ │ │ │ ├── control-pill.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── utils.ts │ │ │ ├── controls-container.tsx │ │ │ ├── datetime │ │ │ │ ├── constants.ts │ │ │ │ ├── datetime-popover.tsx │ │ │ │ ├── suggestions.tsx │ │ │ │ └── types.ts │ │ │ ├── details │ │ │ │ ├── request-response-details.tsx │ │ │ │ └── resizable-panel.tsx │ │ │ ├── filter-operator-input │ │ │ │ └── index.tsx │ │ │ ├── hooks │ │ │ │ ├── use-bookmarked-filters.test.ts │ │ │ │ ├── use-bookmarked-filters.ts │ │ │ │ └── use-sort.tsx │ │ │ ├── live-switch-button │ │ │ │ └── index.tsx │ │ │ ├── llm-search │ │ │ │ ├── components │ │ │ │ │ ├── search-actions.tsx │ │ │ │ │ ├── search-example-tooltip.tsx │ │ │ │ │ ├── search-icon.tsx │ │ │ │ │ └── search-input.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── use-search-strategy.test.tsx │ │ │ │ │ └── use-search-strategy.ts │ │ │ │ └── index.tsx │ │ │ ├── overview-charts │ │ │ │ ├── hooks.tsx │ │ │ │ ├── overview-area-chart-error.tsx │ │ │ │ ├── overview-area-chart-loader.tsx │ │ │ │ ├── overview-area-chart.tsx │ │ │ │ ├── overview-bar-chart-error.tsx │ │ │ │ ├── overview-bar-chart-loader.tsx │ │ │ │ ├── overview-bar-chart.tsx │ │ │ │ ├── types.ts │ │ │ │ └── utils.tsx │ │ │ ├── queries │ │ │ │ ├── empty.tsx │ │ │ │ ├── list-group.tsx │ │ │ │ ├── queries-context.tsx │ │ │ │ ├── queries-item-row.tsx │ │ │ │ ├── queries-made-by.tsx │ │ │ │ ├── queries-overflow-tooltip.tsx │ │ │ │ ├── queries-pill.tsx │ │ │ │ ├── queries-popover.tsx │ │ │ │ ├── queries-tabs.tsx │ │ │ │ ├── queries-toast.tsx │ │ │ │ └── utils.ts │ │ │ ├── refresh-button │ │ │ │ └── index.tsx │ │ │ └── validation │ │ │ │ ├── filter.types.ts │ │ │ │ └── utils │ │ │ │ ├── nuqs-parsers.ts │ │ │ │ ├── structured-output-schema-generator.ts │ │ │ │ ├── transform-structured-output-filter-format.ts │ │ │ │ └── type-guards.ts │ │ ├── navbar-popover.tsx │ │ ├── navigation │ │ │ ├── action-button.tsx │ │ │ ├── copyable-id-button.tsx │ │ │ ├── navbar.tsx │ │ │ ├── navigation.tsx │ │ │ └── sidebar │ │ │ │ ├── app-sidebar │ │ │ │ ├── components │ │ │ │ │ ├── nav-items │ │ │ │ │ │ ├── animated-loading-spinner.tsx │ │ │ │ │ │ ├── flat-nav-item.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── nested-nav-item.tsx │ │ │ │ │ │ ├── toggle-sidebar-button.tsx │ │ │ │ │ │ └── utils.ts │ │ │ │ │ └── nav-link.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── use-api-navigation.tsx │ │ │ │ │ └── use-ratelimit-navigation.tsx │ │ │ │ └── index.tsx │ │ │ │ ├── help-button.tsx │ │ │ │ ├── sidebar-mobile.tsx │ │ │ │ ├── team-switcher.tsx │ │ │ │ ├── usage-banner.tsx │ │ │ │ ├── user-button.tsx │ │ │ │ └── workspace-navigations.tsx │ │ ├── opt-in.tsx │ │ ├── page-content.tsx │ │ ├── stats-card │ │ │ ├── components │ │ │ │ ├── chart │ │ │ │ │ ├── components │ │ │ │ │ │ ├── logs-chart-error.tsx │ │ │ │ │ │ └── logs-chart-loading.tsx │ │ │ │ │ └── stats-chart.tsx │ │ │ │ └── metric-stats.tsx │ │ │ └── index.tsx │ │ ├── ui │ │ │ ├── alert.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── chart.tsx │ │ │ ├── code.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── combobox.tsx │ │ │ ├── command.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── drover.tsx │ │ │ ├── form-combobox.tsx │ │ │ ├── icons.tsx │ │ │ ├── label.tsx │ │ │ ├── metric.tsx │ │ │ ├── popover.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── shiny-text.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ └── toaster.tsx │ │ └── virtual-table │ │ │ ├── components │ │ │ ├── empty-state.tsx │ │ │ └── loading-indicator.tsx │ │ │ ├── constants.ts │ │ │ ├── hooks │ │ │ ├── useTableData.ts │ │ │ ├── useTableHeight.ts │ │ │ └── useVirtualData.ts │ │ │ ├── index.tsx │ │ │ └── types.ts │ ├── hooks │ │ ├── use-delay-loader.tsx │ │ ├── use-keyboard-shortcut.test.tsx │ │ ├── use-keyboard-shortcut.tsx │ │ ├── use-mobile.tsx │ │ └── use-persisted-form.tsx │ ├── images │ │ ├── app.png │ │ ├── computer-user.jpg │ │ ├── laptop.jpg │ │ ├── team │ │ │ ├── andreas.jpeg │ │ │ ├── dom.jpeg │ │ │ ├── james.jpg │ │ │ └── michael.png │ │ └── unkey.svg │ ├── lib │ │ ├── audit.ts │ │ ├── auth.ts │ │ ├── auth │ │ │ ├── base-provider.ts │ │ │ ├── cookies.ts │ │ │ ├── get-auth.ts │ │ │ ├── local.ts │ │ │ ├── middleware.ts │ │ │ ├── server.ts │ │ │ ├── sessions.ts │ │ │ ├── types.ts │ │ │ ├── utils.ts │ │ │ └── workos.ts │ │ ├── cache.ts │ │ ├── clerk.ts │ │ ├── clickhouse.ts │ │ ├── create-context.tsx │ │ ├── db.ts │ │ ├── env.ts │ │ ├── fmt.ts │ │ ├── format.tsx │ │ ├── posthog.ts │ │ ├── quotas.ts │ │ ├── searchparams.tsx │ │ ├── templates-form.ts │ │ ├── trpc │ │ │ ├── client.ts │ │ │ ├── context.ts │ │ │ ├── routers │ │ │ │ ├── api │ │ │ │ │ ├── create.ts │ │ │ │ │ ├── delete.ts │ │ │ │ │ ├── keys │ │ │ │ │ │ ├── api-query.ts │ │ │ │ │ │ ├── llm-search-api-keys │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ ├── llm-search │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── utils.test.ts │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ ├── query-active-keys-timeseries │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── query-api-keys │ │ │ │ │ │ │ ├── get-all-keys.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── schema.ts │ │ │ │ │ │ ├── query-key-usage-timeseries │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── query-latest-verification │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── query-overview-logs │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ ├── query-overview-timeseries │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── timeseries.utils.ts │ │ │ │ │ │ └── toggle-key-enabled │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── overview-api-search.ts │ │ │ │ │ ├── overview │ │ │ │ │ │ ├── query-overview │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── schemas.ts │ │ │ │ │ │ └── query-timeseries │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── setDefaultBytes.ts │ │ │ │ │ ├── setDefaultPrefix.ts │ │ │ │ │ ├── updateDeleteProtection.ts │ │ │ │ │ ├── updateIpWhitelist.ts │ │ │ │ │ └── updateName.ts │ │ │ │ ├── audit │ │ │ │ │ ├── fetch.ts │ │ │ │ │ ├── llm-search │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── billing │ │ │ │ │ └── query-usage │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── schemas.ts │ │ │ │ ├── identity │ │ │ │ │ ├── create.ts │ │ │ │ │ └── query.ts │ │ │ │ ├── index.ts │ │ │ │ ├── key │ │ │ │ │ ├── create.ts │ │ │ │ │ ├── createRootKey.ts │ │ │ │ │ ├── delete.ts │ │ │ │ │ ├── deleteRootKey.ts │ │ │ │ │ ├── fetch-key-permissions.tsx │ │ │ │ │ ├── query-logs │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── query-timeseries │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── updateEnabled.ts │ │ │ │ │ ├── updateExpiration.ts │ │ │ │ │ ├── updateMetadata.ts │ │ │ │ │ ├── updateName.ts │ │ │ │ │ ├── updateOwnerId.ts │ │ │ │ │ ├── updateRatelimit.ts │ │ │ │ │ ├── updateRemaining.ts │ │ │ │ │ └── updateRootKeyName.ts │ │ │ │ ├── logs │ │ │ │ │ ├── llm-search │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── utils.test.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── query-logs │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── utils.test.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ └── query-timeseries │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ ├── org │ │ │ │ │ ├── getInvitationList.ts │ │ │ │ │ ├── getOrg.ts │ │ │ │ │ ├── getOrganizationMemberList.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── inviteMember.ts │ │ │ │ │ ├── removeMembership.ts │ │ │ │ │ ├── revokeInvitation.ts │ │ │ │ │ └── updateMembership.ts │ │ │ │ ├── plain.ts │ │ │ │ ├── ratelimit │ │ │ │ │ ├── createNamespace.ts │ │ │ │ │ ├── createOverride.ts │ │ │ │ │ ├── deleteNamespace.ts │ │ │ │ │ ├── deleteOverride.ts │ │ │ │ │ ├── llm-search │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── utils.test.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── namespace-search.ts │ │ │ │ │ ├── query-keys │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── schemas.ts │ │ │ │ │ ├── query-latency-timeseries │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── query-logs │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── query-overview-logs │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── query-timeseries │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── updateNamespaceName.ts │ │ │ │ │ └── updateOverride.ts │ │ │ │ ├── rbac.ts │ │ │ │ ├── rbac │ │ │ │ │ ├── addPermissionToRootKey.ts │ │ │ │ │ ├── connectPermissionToRole.ts │ │ │ │ │ ├── connectRoleToKey.ts │ │ │ │ │ ├── createPermission.ts │ │ │ │ │ ├── createRole.ts │ │ │ │ │ ├── deletePermission.ts │ │ │ │ │ ├── deleteRole.ts │ │ │ │ │ ├── disconnectPermissionFromRole.ts │ │ │ │ │ ├── disconnectRoleFromKey.ts │ │ │ │ │ ├── removePermissionFromRootKey.ts │ │ │ │ │ ├── updatePermission.ts │ │ │ │ │ ├── updateRole.ts │ │ │ │ │ └── upsertPermission.ts │ │ │ │ ├── stripe │ │ │ │ │ ├── cancelSubscription.ts │ │ │ │ │ ├── createSubscription.ts │ │ │ │ │ ├── uncancelSubscription.ts │ │ │ │ │ └── updateSubscription.ts │ │ │ │ ├── user │ │ │ │ │ ├── getCurrentUser.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── listMemberships.ts │ │ │ │ │ └── switchOrg.ts │ │ │ │ ├── utils │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── granularity.test.ts │ │ │ │ │ └── granularity.ts │ │ │ │ ├── vercel.ts │ │ │ │ └── workspace │ │ │ │ │ ├── changeName.ts │ │ │ │ │ ├── create.ts │ │ │ │ │ └── optIntoBeta.ts │ │ │ ├── server.ts │ │ │ └── trpc.ts │ │ ├── types.ts │ │ ├── utils.ts │ │ └── zod-helper.ts │ ├── middleware.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ └── api │ │ │ └── v1 │ │ │ ├── github │ │ │ ├── test.ts │ │ │ └── verify.ts │ │ │ └── workos │ │ │ └── webhooks.ts │ ├── postcss.config.js │ ├── providers │ │ ├── PostHogProvider.tsx │ │ └── query-time-provider.tsx │ ├── public │ │ ├── images │ │ │ ├── blog-images │ │ │ │ ├── admin-dashboard-new.png │ │ │ │ ├── admin-dashboard.png │ │ │ │ ├── ai-post │ │ │ │ │ ├── create-api.png │ │ │ │ │ └── create-root-key.png │ │ │ │ ├── cli-auth │ │ │ │ │ └── cli-auth-overview.png │ │ │ │ ├── funding │ │ │ │ │ └── funding-cover.png │ │ │ │ ├── how-to-market │ │ │ │ │ ├── tweet-example.png │ │ │ │ │ └── welcome-unkey.png │ │ │ │ ├── ocr-post │ │ │ │ │ ├── 1-create-root-key.png │ │ │ │ │ ├── 2-create-api.png │ │ │ │ │ ├── 3-dashboard.png │ │ │ │ │ ├── 4-walkthrough.gif │ │ │ │ │ └── wilfred.jpg │ │ │ │ ├── ratelimiting │ │ │ │ │ ├── analytics.png │ │ │ │ │ ├── audit.png │ │ │ │ │ ├── onboarding-1.png │ │ │ │ │ ├── onboarding-2.png │ │ │ │ │ ├── onboarding-3.png │ │ │ │ │ ├── overrides.png │ │ │ │ │ ├── ratelimit-cover.png │ │ │ │ │ └── top-analytics.png │ │ │ │ ├── secure-env │ │ │ │ │ └── example-stripe.png │ │ │ │ ├── unkey-latency.png │ │ │ │ ├── unkey-with-auth │ │ │ │ │ └── dashboard-example.png │ │ │ │ ├── usage-based-billing │ │ │ │ │ ├── monthly_active_keys.png │ │ │ │ │ └── monthly_verifications.png │ │ │ │ └── vercel │ │ │ │ │ └── vercel.png │ │ │ ├── changelog │ │ │ │ ├── 2023-12-15 │ │ │ │ │ ├── active-keys.png │ │ │ │ │ ├── billing.png │ │ │ │ │ ├── speed.png │ │ │ │ │ └── verifications.png │ │ │ │ ├── 2024-01-19 │ │ │ │ │ └── audit-logging.png │ │ │ │ ├── 2024-02-16 │ │ │ │ │ ├── attach-perm.png │ │ │ │ │ ├── connect-key.png │ │ │ │ │ ├── create-key-ui.png │ │ │ │ │ ├── create-perms.png │ │ │ │ │ ├── create-role.png │ │ │ │ │ └── permissions-details.png │ │ │ │ ├── aug-25 │ │ │ │ │ ├── error-example.png │ │ │ │ │ ├── unkey-onboard-step-1.png │ │ │ │ │ ├── unkey-onboard-step-2.png │ │ │ │ │ ├── unkey-onboard-step-3.png │ │ │ │ │ └── unkey-onboard-step-4.png │ │ │ │ ├── july-10 │ │ │ │ │ └── usage-example.png │ │ │ │ ├── sept-29 │ │ │ │ │ ├── root-key-analytics.png │ │ │ │ │ ├── unkey-template.png │ │ │ │ │ └── usage-analytics.png │ │ │ │ └── sept-8 │ │ │ │ │ ├── api-settings.png │ │ │ │ │ ├── key-analytics.png │ │ │ │ │ ├── key-settings.png │ │ │ │ │ ├── usage.png │ │ │ │ │ ├── user-account.png │ │ │ │ │ ├── workspace-setting.png │ │ │ │ │ └── workspace-settings.png │ │ │ ├── integration.png │ │ │ ├── landing │ │ │ │ ├── app-dark.png │ │ │ │ ├── app.png │ │ │ │ ├── og.png │ │ │ │ └── unkey.png │ │ │ ├── quoteImages │ │ │ │ ├── dexter-storey.jpg │ │ │ │ ├── lola.jpg │ │ │ │ ├── maximilian-kaske.jpg │ │ │ │ ├── rick-blalock.jpg │ │ │ │ └── tanmay.jpg │ │ │ ├── team │ │ │ │ ├── andreas.jpeg │ │ │ │ └── james.jpg │ │ │ └── templates │ │ │ │ ├── ai-billing.png │ │ │ │ ├── atash.png │ │ │ │ ├── bun_koyeb.png │ │ │ │ ├── express-middleware.png │ │ │ │ ├── graphql-yoga.png │ │ │ │ ├── openstatus.png │ │ │ │ ├── placeholder.png │ │ │ │ ├── ratelimit.png │ │ │ │ ├── sprintpadawan.png │ │ │ │ ├── unkey-cli.png │ │ │ │ └── unkey-stripe.png │ │ ├── next.svg │ │ ├── unkey-vercel.png │ │ └── vercel.svg │ ├── styles │ │ └── tailwind │ │ │ └── tailwind.css │ ├── tailwind.config.js │ ├── trpc.config.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── docs │ ├── CHANGELOG.md │ ├── README.md │ ├── analytics │ │ ├── overview.mdx │ │ └── quickstarts.mdx │ ├── api-reference │ │ ├── analytics │ │ │ └── get_verifications.mdx │ │ ├── apis │ │ │ ├── create.mdx │ │ │ ├── delete-keys.mdx │ │ │ ├── delete.mdx │ │ │ ├── get.mdx │ │ │ └── list-keys.mdx │ │ ├── authentication.mdx │ │ ├── errors-v2 │ │ │ ├── overview.mdx │ │ │ └── unkey │ │ │ │ ├── application │ │ │ │ ├── assertion_failed.mdx │ │ │ │ ├── invalid_input.mdx │ │ │ │ ├── protected_resource.mdx │ │ │ │ ├── service_unavailable.mdx │ │ │ │ └── unexpected_error.mdx │ │ │ │ ├── authentication │ │ │ │ ├── key_not_found.mdx │ │ │ │ ├── malformed.mdx │ │ │ │ └── missing.mdx │ │ │ │ ├── authorization │ │ │ │ ├── forbidden.mdx │ │ │ │ ├── insufficient_permissions.mdx │ │ │ │ ├── key_disabled.mdx │ │ │ │ └── workspace_disabled.mdx │ │ │ │ └── data │ │ │ │ ├── api_not_found.mdx │ │ │ │ ├── audit_log_not_found.mdx │ │ │ │ ├── identity_already_exists.mdx │ │ │ │ ├── identity_not_found.mdx │ │ │ │ ├── key_auth_not_found.mdx │ │ │ │ ├── key_not_found.mdx │ │ │ │ ├── permission_not_found.mdx │ │ │ │ ├── ratelimit_namespace_not_found.mdx │ │ │ │ ├── ratelimit_override_not_found.mdx │ │ │ │ ├── role_not_found.mdx │ │ │ │ └── workspace_not_found.mdx │ │ ├── errors │ │ │ ├── code │ │ │ │ ├── BAD_REQUEST.mdx │ │ │ │ ├── CONFLICT.mdx │ │ │ │ ├── DELETE_PROTECTED.mdx │ │ │ │ ├── DISABLED.mdx │ │ │ │ ├── EXPIRED.mdx │ │ │ │ ├── FORBIDDEN.mdx │ │ │ │ ├── INSUFFICIENT_PERMISSIONS.mdx │ │ │ │ ├── INTERNAL_SERVER_ERROR.mdx │ │ │ │ ├── NOT_FOUND.mdx │ │ │ │ ├── PRECONDITION_FAILED.mdx │ │ │ │ ├── TOO_MANY_REQUESTS.mdx │ │ │ │ └── UNAUTHORIZED.mdx │ │ │ └── introduction.mdx │ │ ├── identities │ │ │ ├── create-identity.mdx │ │ │ ├── delete-identity.mdx │ │ │ ├── get-identity.mdx │ │ │ ├── list-identities.mdx │ │ │ └── update-identity.mdx │ │ ├── keys │ │ │ ├── add-permissions.mdx │ │ │ ├── add-roles.mdx │ │ │ ├── create.mdx │ │ │ ├── delete.mdx │ │ │ ├── get.mdx │ │ │ ├── remove-permissions.mdx │ │ │ ├── remove-roles.mdx │ │ │ ├── set-permissions.mdx │ │ │ ├── set-roles.mdx │ │ │ ├── update-remaining.mdx │ │ │ ├── update.mdx │ │ │ ├── verifications.mdx │ │ │ ├── verify.mdx │ │ │ └── whoami.mdx │ │ ├── migrations │ │ │ └── create-keys.mdx │ │ ├── overview.mdx │ │ ├── permissions │ │ │ ├── create-permission.mdx │ │ │ ├── create-role.mdx │ │ │ ├── delete-permission.mdx │ │ │ ├── delete-role.mdx │ │ │ ├── get-permission.mdx │ │ │ ├── get-role.mdx │ │ │ ├── list-permissions.mdx │ │ │ └── list-roles.mdx │ │ └── ratelimits │ │ │ ├── delete-override.mdx │ │ │ ├── get-override.mdx │ │ │ ├── limit.mdx │ │ │ ├── list-overrides.mdx │ │ │ └── set-override.mdx │ ├── apis │ │ ├── features │ │ │ ├── analytics.mdx │ │ │ ├── authorization │ │ │ │ ├── api-key-screen.png │ │ │ │ ├── api-keys-navigation.png │ │ │ │ ├── axiom.png │ │ │ │ ├── connections-connected.png │ │ │ │ ├── connections.png │ │ │ │ ├── domains-permissions.png │ │ │ │ ├── domains-roles-admin.png │ │ │ │ ├── domains-roles-dns.manager.png │ │ │ │ ├── domains-roles-read-only.png │ │ │ │ ├── domains-roles.png │ │ │ │ ├── example.mdx │ │ │ │ ├── introduction.mdx │ │ │ │ ├── role-add-example.png │ │ │ │ ├── roles-and-permissions.mdx │ │ │ │ └── verifying.mdx │ │ │ ├── enabled.mdx │ │ │ ├── environments.mdx │ │ │ ├── ratelimiting │ │ │ │ ├── modes.mdx │ │ │ │ └── overview.mdx │ │ │ ├── refill.mdx │ │ │ ├── remaining.mdx │ │ │ ├── revocation.mdx │ │ │ ├── temp-keys.mdx │ │ │ └── whitelist.mdx │ │ └── introduction.mdx │ ├── audit-log │ │ ├── audit-log.png │ │ ├── introduction.mdx │ │ ├── types.mdx │ │ └── types │ │ │ ├── api_create.png │ │ │ ├── api_delete.png │ │ │ ├── api_update.png │ │ │ ├── auth_connect_permission_key.png │ │ │ ├── auth_connect_role_key.png │ │ │ ├── auth_connect_role_permission.png │ │ │ ├── auth_disconnect_permission_key.png │ │ │ ├── auth_disconnect_role_key.png │ │ │ ├── auth_disconnect_role_permission.png │ │ │ ├── key_create.png │ │ │ ├── key_delete.png │ │ │ ├── key_update.png │ │ │ ├── permission_create.png │ │ │ ├── permission_delete.png │ │ │ ├── permission_update.png │ │ │ ├── ratelimitnamespace_create.png │ │ │ ├── ratelimitnamespace_delete.png │ │ │ ├── ratelimitnamespace_update.png │ │ │ ├── ratelimitoverride_create.png │ │ │ ├── ratelimitoverride_update.png │ │ │ ├── role_create.png │ │ │ ├── role_delete.png │ │ │ ├── role_update.png │ │ │ ├── workspace_create.png │ │ │ └── workspace_update.png │ ├── concepts │ │ └── identities │ │ │ ├── overview.mdx │ │ │ └── ratelimits.mdx │ ├── docs.json │ ├── glossary.mdx │ ├── images │ │ ├── add-integration.png │ │ ├── audit-log.png │ │ ├── choose-unkey.png │ │ ├── create-api-key.png │ │ ├── create-first-api.png │ │ ├── create-root-key.png │ │ ├── create-workspace.png │ │ ├── example-key.png │ │ ├── ip-whitelist.png │ │ ├── onboard-ratelimit.png │ │ ├── per-api-analytics.png │ │ ├── per-key-analytics.png │ │ ├── planetscale-foreign.png │ │ ├── reroll-key.png │ │ ├── root-keys │ │ │ ├── copy.png │ │ │ └── permissions.png │ │ ├── select-api.png │ │ ├── select-project.png │ │ └── sign-up.png │ ├── integrations │ │ └── vercel.mdx │ ├── introduction.mdx │ ├── libraries │ │ ├── ex │ │ │ ├── functions │ │ │ │ ├── create_key.mdx │ │ │ │ ├── delete_key.mdx │ │ │ │ ├── update_key.mdx │ │ │ │ ├── update_remaining.mdx │ │ │ │ └── verify_key.mdx │ │ │ └── overview.mdx │ │ ├── go │ │ │ └── overview.mdx │ │ ├── nuxt │ │ │ └── overview.mdx │ │ ├── py │ │ │ ├── async.mdx │ │ │ ├── overview.mdx │ │ │ └── services │ │ │ │ ├── apis.mdx │ │ │ │ ├── identities.mdx │ │ │ │ ├── keys.mdx │ │ │ │ ├── migrations.mdx │ │ │ │ ├── permissions.mdx │ │ │ │ └── ratelimits.mdx │ │ ├── rs │ │ │ └── overview.mdx │ │ ├── springboot-java │ │ │ ├── api │ │ │ │ ├── get.mdx │ │ │ │ └── list.mdx │ │ │ ├── functions │ │ │ │ ├── create.mdx │ │ │ │ ├── revoke.mdx │ │ │ │ ├── update.mdx │ │ │ │ └── verify.mdx │ │ │ └── overview.mdx │ │ └── ts │ │ │ ├── cache │ │ │ ├── interface │ │ │ │ └── store.mdx │ │ │ └── overview.mdx │ │ │ ├── hono.mdx │ │ │ ├── nextjs.mdx │ │ │ ├── ratelimit │ │ │ ├── override │ │ │ │ ├── delete-override.mdx │ │ │ │ ├── get-override.mdx │ │ │ │ ├── list-overrides.mdx │ │ │ │ ├── overview.mdx │ │ │ │ └── set-override.mdx │ │ │ └── ratelimit.mdx │ │ │ └── sdk │ │ │ ├── apis │ │ │ ├── create.mdx │ │ │ ├── delete.mdx │ │ │ ├── get.mdx │ │ │ └── list-keys.mdx │ │ │ ├── identities │ │ │ ├── create-identity.mdx │ │ │ ├── delete-identity.mdx │ │ │ ├── get-identity.mdx │ │ │ ├── list-identity.mdx │ │ │ └── update-identity.mdx │ │ │ ├── keys │ │ │ ├── add-permission.mdx │ │ │ ├── add-roles.mdx │ │ │ ├── create.mdx │ │ │ ├── delete.mdx │ │ │ ├── get.mdx │ │ │ ├── remove-permission.mdx │ │ │ ├── remove-roles.mdx │ │ │ ├── set-permission.mdx │ │ │ ├── set-roles.mdx │ │ │ ├── update-remaining.mdx │ │ │ ├── update.mdx │ │ │ ├── verifications.mdx │ │ │ └── verify.mdx │ │ │ ├── migrations │ │ │ └── migrate-to-unkey.mdx │ │ │ ├── overview.mdx │ │ │ ├── permissions │ │ │ ├── create-permission.mdx │ │ │ ├── create-role.mdx │ │ │ ├── delete-permission.mdx │ │ │ ├── delete-role.mdx │ │ │ ├── get-permission.mdx │ │ │ └── get-role.mdx │ │ │ └── ratelimits │ │ │ ├── limit.mdx │ │ │ └── overrides │ │ │ ├── delete-override.mdx │ │ │ ├── get-override.mdx │ │ │ ├── list-overrides.mdx │ │ │ └── set-override.mdx │ ├── migrations │ │ ├── introduction.mdx │ │ └── keys.mdx │ ├── package.json │ ├── quickstart │ │ ├── apis │ │ │ ├── bun.mdx │ │ │ ├── express.mdx │ │ │ ├── hono.mdx │ │ │ └── nextjs.mdx │ │ ├── identities │ │ │ └── shared-ratelimits.mdx │ │ ├── onboarding │ │ │ ├── onboarding-api.mdx │ │ │ └── onboarding-ratelimiting.mdx │ │ └── ratelimiting │ │ │ ├── bun.mdx │ │ │ ├── express.mdx │ │ │ ├── hono.mdx │ │ │ └── nextjs.mdx │ ├── ratelimiting │ │ ├── automated-overrides.mdx │ │ ├── copy-key.png │ │ ├── create-root-key-permissions.png │ │ ├── introduction.mdx │ │ ├── modes.mdx │ │ ├── new-override.png │ │ ├── overrides.mdx │ │ └── wildcard-override.png │ ├── security │ │ ├── delete-protection.mdx │ │ ├── github-scanning.mdx │ │ ├── overview.mdx │ │ ├── recovering-keys.mdx │ │ └── root-keys.mdx │ ├── todo-golang-section.mint.json │ └── unkey.png ├── engineering │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── (home) │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── architecture │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── company │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── components │ │ │ ├── icon-swatch.tsx │ │ │ ├── render.tsx │ │ │ └── row.tsx │ │ ├── contributing │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── design │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── docs │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── global.css │ │ ├── infrastructure │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── layout.config.tsx │ │ ├── layout.tsx │ │ ├── rfcs │ │ │ ├── [[...slug]] │ │ │ │ ├── local-date.tsx │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ └── source.ts │ ├── content │ │ ├── architecture │ │ │ ├── index.mdx │ │ │ ├── meta.json │ │ │ └── services │ │ │ │ ├── api │ │ │ │ ├── config.mdx │ │ │ │ ├── meta.json │ │ │ │ └── ratelimiting.mdx │ │ │ │ ├── clickhouse-proxy.mdx │ │ │ │ ├── clickhouse.mdx │ │ │ │ ├── meta.json │ │ │ │ └── vault.mdx │ │ ├── company │ │ │ ├── index.mdx │ │ │ ├── meetings.mdx │ │ │ └── meta.json │ │ ├── contributing │ │ │ ├── client-structure.mdx │ │ │ ├── index.mdx │ │ │ ├── meta.json │ │ │ ├── pull-request-checks.mdx │ │ │ ├── sdk-development.mdx │ │ │ ├── seeding-db-and-clickhouse.mdx │ │ │ ├── testing.mdx │ │ │ └── workos.mdx │ │ ├── design │ │ │ ├── colors.mdx │ │ │ ├── components │ │ │ │ ├── button.mdx │ │ │ │ ├── buttons │ │ │ │ │ ├── button.tsx │ │ │ │ │ └── copy-button.examples.tsx │ │ │ │ ├── checkbox.mdx │ │ │ │ ├── copy-button.mdx │ │ │ │ ├── date-time.example.tsx │ │ │ │ ├── date-time.mdx │ │ │ │ ├── empty.mdx │ │ │ │ ├── form-chekbox.mdx │ │ │ │ ├── form-input.mdx │ │ │ │ ├── form-textarea.mdx │ │ │ │ ├── form │ │ │ │ │ ├── form-checkbox.variants.tsx │ │ │ │ │ ├── form-input.variants.tsx │ │ │ │ │ └── form-textarea.variants.tsx │ │ │ │ ├── id.mdx │ │ │ │ ├── id.valueTruncate.tsx │ │ │ │ ├── id.width.tsx │ │ │ │ ├── info-tooltip.example.tsx │ │ │ │ ├── info-tooltip.mdx │ │ │ │ ├── inline-link.example.tsx │ │ │ │ ├── inline-link.mdx │ │ │ │ ├── input.mdx │ │ │ │ ├── input │ │ │ │ │ └── input.variants.tsx │ │ │ │ ├── select.example.tsx │ │ │ │ ├── select.mdx │ │ │ │ ├── settings-card.example.tsx │ │ │ │ ├── settings-card.mdx │ │ │ │ ├── textarea.mdx │ │ │ │ ├── textarea │ │ │ │ │ └── textarea.variants.tsx │ │ │ │ ├── timestamp-example.tsx │ │ │ │ ├── timestamp-info.mdx │ │ │ │ ├── tooltip.mdx │ │ │ │ └── tooltip.onHover.tsx │ │ │ ├── icons-export-settings.png │ │ │ ├── icons.mdx │ │ │ ├── index.mdx │ │ │ └── meta.json │ │ ├── docs │ │ │ ├── api-design │ │ │ │ ├── auth.mdx │ │ │ │ ├── errors.mdx │ │ │ │ ├── index.mdx │ │ │ │ ├── meta.json │ │ │ │ └── rpc.mdx │ │ │ ├── meta.json │ │ │ └── releases │ │ │ │ ├── api.mdx │ │ │ │ └── frontend.mdx │ │ ├── infrastructure │ │ │ ├── aws │ │ │ │ ├── google-idp.mdx │ │ │ │ ├── gw-custom-attributes.png │ │ │ │ ├── index.mdx │ │ │ │ └── user-group-management.mdx │ │ │ ├── index.mdx │ │ │ ├── meta.json │ │ │ └── stripe │ │ │ │ └── subscriptions.mdx │ │ └── rfcs │ │ │ ├── 0000-template.mdx │ │ │ ├── 0001-rbac.mdx │ │ │ ├── 0002-github-secret-scanning.mdx │ │ │ ├── 0002-secret-scanning-flow.webp │ │ │ ├── 0003-key-shape.mdx │ │ │ ├── 0004-coss-starter.mdx │ │ │ ├── 0005-analytics-api.mdx │ │ │ ├── 0006-auth-migration.mdx │ │ │ ├── 0007-client-file-structure.mdx │ │ │ ├── 0008-dataplane.mdx │ │ │ ├── 0009-pricing-updates.mdx │ │ │ ├── 0010-split-monos.mdx │ │ │ ├── 0011-unkey-resource-names.mdx │ │ │ ├── 0012-stricter-linter.mdx │ │ │ ├── index.mdx │ │ │ └── meta.json │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.js │ ├── source.config.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── logdrain │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ └── worker.ts │ ├── tsconfig.json │ └── wrangler.toml ├── planetfall │ └── next-env.d.ts └── workflows │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── src │ ├── index.ts │ ├── lib │ │ ├── db.ts │ │ └── env.ts │ └── workflows │ │ ├── count_keys_per_keyspace.ts │ │ ├── invoicing.ts │ │ └── refill_keys.ts │ ├── tsconfig.json │ └── wrangler.toml ├── biome.json ├── checkly.config.ts ├── deployment ├── config │ ├── clickhouse │ │ └── etc │ │ │ ├── clickhouse-keeper │ │ │ └── keeper_config.xml │ │ │ │ └── keeper_config.xml │ │ │ └── clickhouse-server │ │ │ ├── config.d │ │ │ └── config.xml │ │ │ └── users.d │ │ │ └── users.xml │ └── prometheus.yml ├── docker-compose.yaml └── nginx.apiv2.conf ├── go ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yaml ├── Dockerfile ├── Taskfile.yml ├── apps │ └── api │ │ ├── cancel_test.go │ │ ├── config.go │ │ ├── integration │ │ ├── harness.go │ │ ├── http.go │ │ └── multi_node_ratelimiting │ │ │ ├── generate_tests │ │ │ └── main.go │ │ │ ├── generated │ │ │ ├── ratelimit_nodes1_limit100_duration1000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration1000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration1000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration3600000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration3600000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration3600000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration60000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration60000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit100_duration60000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration1000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration1000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration1000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration3600000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration3600000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration3600000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration60000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration60000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes1_limit5_duration60000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration1000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration1000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration1000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration3600000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration3600000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration3600000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration60000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration60000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit100_duration60000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration1000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration1000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration1000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration3600000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration3600000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration3600000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration60000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration60000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes3_limit5_duration60000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration1000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration1000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration1000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration3600000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration3600000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration3600000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration60000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration60000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit100_duration60000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration1000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration1000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration1000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration3600000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration3600000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration3600000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration60000_load0_90_windows10 │ │ │ │ └── generated_test.go │ │ │ ├── ratelimit_nodes9_limit5_duration60000_load10_00_windows10 │ │ │ │ └── generated_test.go │ │ │ └── ratelimit_nodes9_limit5_duration60000_load2_00_windows10 │ │ │ │ └── generated_test.go │ │ │ └── run.go │ │ ├── openapi │ │ ├── config.yaml │ │ ├── gen.go │ │ ├── generate.go │ │ ├── openapi.json │ │ ├── scalar.config.json │ │ └── spec.go │ │ ├── routes │ │ ├── reference │ │ │ └── handler.go │ │ ├── register.go │ │ ├── services.go │ │ ├── v2_apis_create_api │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ └── handler.go │ │ ├── v2_identities_create_identity │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 409_test.go │ │ │ └── handler.go │ │ ├── v2_identities_delete_identity │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 404_test.go │ │ │ └── handler.go │ │ ├── v2_liveness │ │ │ └── handler.go │ │ ├── v2_ratelimit_delete_override │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 404_test.go │ │ │ └── handler.go │ │ ├── v2_ratelimit_get_override │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 404_test.go │ │ │ └── handler.go │ │ ├── v2_ratelimit_limit │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 404_test.go │ │ │ ├── accuracy_test.go │ │ │ ├── handler.go │ │ │ └── simulation_test.gox │ │ ├── v2_ratelimit_list_overrides │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 404_test.go │ │ │ └── handler.go │ │ └── v2_ratelimit_set_override │ │ │ ├── 200_test.go │ │ │ ├── 400_test.go │ │ │ ├── 401_test.go │ │ │ ├── 403_test.go │ │ │ ├── 404_test.go │ │ │ └── handler.go │ │ └── run.go ├── buf.gen.yaml ├── buf.yaml ├── cmd │ ├── api │ │ └── main.go │ ├── healthcheck │ │ └── main.go │ └── quotacheck │ │ └── main.go ├── go.mod ├── go.sum ├── goreleaser.Dockerfile ├── internal │ └── services │ │ ├── auditlogs │ │ ├── insert.go │ │ ├── interface.go │ │ └── service.go │ │ ├── caches │ │ ├── caches.go │ │ ├── doc.go │ │ └── op.go │ │ ├── keys │ │ ├── interface.go │ │ ├── service.go │ │ ├── verify.go │ │ └── verify_root_key.go │ │ ├── permissions │ │ ├── check.go │ │ ├── interface.go │ │ └── service.go │ │ └── ratelimit │ │ ├── bucket.go │ │ ├── doc.go │ │ ├── interface.go │ │ ├── janitor.go │ │ ├── replay.go │ │ ├── sequence.go │ │ ├── service.go │ │ ├── util.go │ │ └── window.go ├── main.go ├── pkg │ ├── assert │ │ ├── all.go │ │ ├── assert.go │ │ └── doc.go │ ├── attack │ │ └── attack.go │ ├── auditlog │ │ ├── actors.go │ │ ├── doc.go │ │ ├── events.go │ │ ├── log.go │ │ └── target.go │ ├── aws │ │ └── ecs │ │ │ ├── private_dns_name.go │ │ │ └── private_dns_name_test.go │ ├── batch │ │ ├── consume.go │ │ ├── doc.go │ │ └── process.go │ ├── buffer │ │ ├── buffer.go │ │ ├── buffer_test.go │ │ └── doc.go │ ├── cache │ │ ├── cache.go │ │ ├── cache_test.go │ │ ├── doc.go │ │ ├── entry.go │ │ ├── interface.go │ │ ├── middleware.go │ │ ├── middleware │ │ │ └── tracing.go │ │ ├── noop.go │ │ └── simulation_test.go │ ├── circuitbreaker │ │ ├── doc.go │ │ ├── interface.go │ │ ├── lib.go │ │ └── lib_test.go │ ├── clickhouse │ │ ├── billable_ratelimits.go │ │ ├── billable_verifications.go │ │ ├── client.go │ │ ├── doc.go │ │ ├── flush.go │ │ ├── interface.go │ │ ├── noop.go │ │ ├── schema │ │ │ ├── databases │ │ │ │ ├── 000_README.md │ │ │ │ ├── 001_verifications │ │ │ │ │ ├── 001_database.sql │ │ │ │ │ ├── 002_raw_key_verifications_v1.sql │ │ │ │ │ ├── 003_key_verifications_per_hour_v1.sql │ │ │ │ │ ├── 004_key_verifications_per_hour_mv_v1.sql │ │ │ │ │ ├── 005_key_verifications_per_day_v1.sql │ │ │ │ │ ├── 006_key_verifications_per_day_mv_v1.sql │ │ │ │ │ ├── 007_key_verifications_per_month_v1.sql │ │ │ │ │ ├── 008_key_verifications_per_month_mv_v1.sql │ │ │ │ │ ├── 009_key_verifications_per_hour_v2.sql │ │ │ │ │ ├── 010_key_verifications_per_hour_mv_v2.sql │ │ │ │ │ ├── 011_key_verifications_per_day_v2.sql │ │ │ │ │ ├── 012_key_verifications_per_day_mv_v2.sql │ │ │ │ │ ├── 013_key_verifications_per_month_v2.sql │ │ │ │ │ ├── 014_key_verifications_per_month_mv_v2.sql │ │ │ │ │ ├── 015_key_verifications_per_hour_v3.sql │ │ │ │ │ ├── 016_key_verifications_per_hour_mv_v3.sql │ │ │ │ │ ├── 017_key_verifications_per_day_v3.sql │ │ │ │ │ ├── 018_key_verifications_per_day_mv_v3.sql │ │ │ │ │ ├── 019_key_verifications_per_month_v3.sql │ │ │ │ │ ├── 020_key_verifications_per_month_mv_v3.sql │ │ │ │ │ ├── 021_key_verifications_per_minute_v1.sql │ │ │ │ │ └── 022_key_verifications_per_minute_mv_v1.sql │ │ │ │ ├── 002_ratelimits │ │ │ │ │ ├── 000_database.sql │ │ │ │ │ ├── 001_raw_ratelimits_v1.sql │ │ │ │ │ ├── 002_ratelimits_per_minute_v1.sql │ │ │ │ │ ├── 003_ratelimits_per_minute_mv_v1.sql │ │ │ │ │ ├── 004_ratelimits_per_hour_v1.sql │ │ │ │ │ ├── 005_ratelimits_per_hour_mv_v1.sql │ │ │ │ │ ├── 006_ratelimits_per_day_v1.sql │ │ │ │ │ ├── 007_ratelimits_per_day_mv_v1.sql │ │ │ │ │ ├── 008_ratelimits_per_month_v1.sql │ │ │ │ │ ├── 009_ratelimits_per_month_mv_v1.sql │ │ │ │ │ ├── 010_ratelimits_last_used_v1.sql │ │ │ │ │ └── 011_ratelimits_last_used_mv_v1.sql │ │ │ │ ├── 003_metrics │ │ │ │ │ ├── 001_database.sql │ │ │ │ │ ├── 002_raw_api_requests_v1.sql │ │ │ │ │ ├── 003_api_requests_per_hour_v1.sql │ │ │ │ │ ├── 004_api_requests_per_hour_mv_v1.sql │ │ │ │ │ ├── 005_api_requests_per_minute_v1.sql │ │ │ │ │ ├── 006_api_requests_per_minute_mv_v1.sql │ │ │ │ │ ├── 007_api_requests_per_day_v1.sql │ │ │ │ │ └── 008_api_requests_per_day_mv_v1.sql │ │ │ │ ├── 004_billing │ │ │ │ │ ├── 001_database.sql │ │ │ │ │ ├── 002_billable_verifications_per_month_v1.sql │ │ │ │ │ ├── 003_billable_verifications_per_month_mv_v1.sql │ │ │ │ │ ├── 004_billable_verifications_v2.sql │ │ │ │ │ ├── 005_billalble_verifications_v2_mv.sql │ │ │ │ │ ├── 006_billable_ratelimits_v1.sql │ │ │ │ │ └── 006_billable_ratelimits_v1_mv.sql │ │ │ │ └── 005_business │ │ │ │ │ ├── 001_database.sql │ │ │ │ │ ├── 002_active_workspaces_per_month_v1.sql │ │ │ │ │ ├── 003_active_workspaces_keys_per_month_mv_v1.sql │ │ │ │ │ └── 003_active_workspaces_ratelimits_per_month_mv_v1.sql │ │ │ ├── embed.go │ │ │ └── requests.go │ │ └── select.go │ ├── clock │ │ ├── doc.go │ │ ├── interface.go │ │ ├── real_clock.go │ │ ├── real_clock_test.go │ │ ├── test_clock.go │ │ └── test_clock_test.go │ ├── codes │ │ ├── codes.go │ │ ├── constants_gen.go │ │ ├── doc.go │ │ ├── generate.go │ │ ├── generate_run.go │ │ ├── nil.go │ │ ├── unkey_application.go │ │ ├── unkey_auth.go │ │ └── unkey_data.go │ ├── counter │ │ ├── doc.go │ │ ├── interface.go │ │ ├── redis.go │ │ └── redis_test.go │ ├── ctxutil │ │ ├── context.go │ │ ├── context_test.go │ │ └── doc.go │ ├── db │ │ ├── api_find_by_id.sql_generated.go │ │ ├── api_insert.sql_generated.go │ │ ├── audit_log_find_target_by_id.sql_generated.go │ │ ├── audit_log_insert.sql_generated.go │ │ ├── audit_log_target_insert.sql_generated.go │ │ ├── database.go │ │ ├── doc.go │ │ ├── generate.go │ │ ├── handle_err_duplicate_key.go │ │ ├── handle_err_no_rows.go │ │ ├── identity_delete.sql_generated.go │ │ ├── identity_find_by_external_id.sql_generated.go │ │ ├── identity_find_by_id.sql_generated.go │ │ ├── identity_find_ratelimits_by_id.sql_generated.go │ │ ├── identity_insert.sql_generated.go │ │ ├── identity_insert_ratelimit.sql_generated.go │ │ ├── identity_soft_delete.sql_generated.go │ │ ├── interface.go │ │ ├── key_find_by_hash.sql_generated.go │ │ ├── key_find_by_id.sql_generated.go │ │ ├── key_find_for_verification.sql_generated.go │ │ ├── key_insert.sql_generated.go │ │ ├── keyring_find_by_id.sql_generated.go │ │ ├── keyring_insert.sql_generated.go │ │ ├── models_generated.go │ │ ├── permission_find_by_workspace_and_name.sql_generated.go │ │ ├── permission_insert.sql_generated.go │ │ ├── permission_insert_key_permission.sql_generated.go │ │ ├── permissions_by_key_id.sql_generated.go │ │ ├── querier_generated.go │ │ ├── queries.go │ │ ├── queries │ │ │ ├── api_find_by_id.sql │ │ │ ├── api_insert.sql │ │ │ ├── audit_log_find_target_by_id.sql │ │ │ ├── audit_log_insert.sql │ │ │ ├── audit_log_target_insert.sql │ │ │ ├── identity_delete.sql │ │ │ ├── identity_find_by_external_id.sql │ │ │ ├── identity_find_by_id.sql │ │ │ ├── identity_find_ratelimits_by_id.sql │ │ │ ├── identity_insert.sql │ │ │ ├── identity_insert_ratelimit.sql │ │ │ ├── identity_soft_delete.sql │ │ │ ├── key_find_by_hash.sql │ │ │ ├── key_find_by_id.sql │ │ │ ├── key_find_for_verification.sql │ │ │ ├── key_insert.sql │ │ │ ├── keyring_find_by_id.sql │ │ │ ├── keyring_insert.sql │ │ │ ├── permission_find_by_workspace_and_name.sql │ │ │ ├── permission_insert.sql │ │ │ ├── permission_insert_key_permission.sql │ │ │ ├── permissions_by_key_id.sql │ │ │ ├── ratelimit_delete_by_identity_id.sql │ │ │ ├── ratelimit_delete_many.sql │ │ │ ├── ratelimit_namespace_delete.sql │ │ │ ├── ratelimit_namespace_find_by_id.sql │ │ │ ├── ratelimit_namespace_find_by_name.sql │ │ │ ├── ratelimit_namespace_insert.sql │ │ │ ├── ratelimit_namespace_soft_delete.sql │ │ │ ├── ratelimit_override_find_by_id.sql │ │ │ ├── ratelimit_override_find_by_identifier.sql │ │ │ ├── ratelimit_override_find_matches.sql │ │ │ ├── ratelimit_override_insert.sql │ │ │ ├── ratelimit_override_list_by_namespace_id.sql │ │ │ ├── ratelimit_override_soft_delete.sql │ │ │ ├── ratelimit_override_update.sql │ │ │ ├── workspace_find_by_id.sql │ │ │ ├── workspace_hard_delete.sql │ │ │ ├── workspace_insert.sql │ │ │ ├── workspace_soft_delete.sql │ │ │ ├── workspace_update_enabled.sql │ │ │ ├── workspace_update_plan.sql │ │ │ └── workspaces_list.sql │ │ ├── ratelimit_delete_by_identity_id.sql_generated.go │ │ ├── ratelimit_delete_many.sql_generated.go │ │ ├── ratelimit_namespace_delete.sql_generated.go │ │ ├── ratelimit_namespace_find_by_id.sql_generated.go │ │ ├── ratelimit_namespace_find_by_name.sql_generated.go │ │ ├── ratelimit_namespace_insert.sql_generated.go │ │ ├── ratelimit_namespace_soft_delete.sql_generated.go │ │ ├── ratelimit_override_delete.sql_generated.go │ │ ├── ratelimit_override_find_by_id.sql_generated.go │ │ ├── ratelimit_override_find_by_identifier.sql_generated.go │ │ ├── ratelimit_override_find_matches.sql_generated.go │ │ ├── ratelimit_override_insert.sql_generated.go │ │ ├── ratelimit_override_list_by_namespace_id.sql_generated.go │ │ ├── ratelimit_override_soft_delete.sql_generated.go │ │ ├── ratelimit_override_update.sql_generated.go │ │ ├── replica.go │ │ ├── schema.sql │ │ ├── schema_embed.go │ │ ├── sqlc.json │ │ ├── workspace_find_by_id.sql_generated.go │ │ ├── workspace_hard_delete.sql_generated.go │ │ ├── workspace_insert.sql_generated.go │ │ ├── workspace_soft_delete.sql_generated.go │ │ ├── workspace_update_enabled.sql_generated.go │ │ ├── workspace_update_plan.sql_generated.go │ │ └── workspaces_list.sql_generated.go │ ├── events │ │ └── topic.go │ ├── fault │ │ ├── README.md │ │ ├── code.go │ │ ├── code_test.go │ │ ├── doc.go │ │ ├── dst_test.go │ │ ├── flatten.go │ │ ├── wrap.go │ │ ├── wrap_test.go │ │ ├── wrapped.go │ │ └── wrapped_test.go │ ├── hash │ │ ├── doc.go │ │ ├── sha256.go │ │ └── sha256_test.go │ ├── otel │ │ ├── grafana.go │ │ ├── logging │ │ │ ├── doc.go │ │ │ ├── interface.go │ │ │ ├── multi.go │ │ │ ├── noop.go │ │ │ ├── slog.go │ │ │ └── source.go │ │ ├── schema.go │ │ ├── tracing │ │ │ └── trace.go │ │ └── util.go │ ├── port │ │ ├── doc.go │ │ └── free.go │ ├── prometheus │ │ ├── metrics │ │ │ ├── buffer.go │ │ │ ├── cache.go │ │ │ ├── circuitbreaker.go │ │ │ ├── http.go │ │ │ ├── labels.go │ │ │ └── ratelimit.go │ │ └── server.go │ ├── ptr │ │ ├── deref.go │ │ └── pointer.go │ ├── rbac │ │ ├── doc.go │ │ ├── permissions.go │ │ ├── query.go │ │ ├── rbac.go │ │ └── rbac_test.go │ ├── repeat │ │ └── every.go │ ├── retry │ │ ├── retry.go │ │ └── retry_test.go │ ├── shutdown │ │ ├── doc.go │ │ ├── shutdown.go │ │ └── shutdown_test.go │ ├── sim │ │ ├── events.go │ │ ├── rng.go │ │ ├── seed.go │ │ ├── sim_test.go │ │ └── simulation.go │ ├── system_errors │ │ └── errors.go │ ├── testutil │ │ ├── containers │ │ │ ├── api.go │ │ │ ├── clickhouse.go │ │ │ ├── containers.go │ │ │ ├── doc.go │ │ │ ├── mysql.go │ │ │ ├── otel.go │ │ │ └── redis.go │ │ ├── flags.go │ │ ├── http.go │ │ └── seed │ │ │ └── seed.go │ ├── tls │ │ ├── doc.go │ │ ├── tls.go │ │ └── tls_test.go │ ├── tools │ │ └── dependencies.go │ ├── uid │ │ ├── doc.go │ │ ├── uid.go │ │ └── uid_test.go │ ├── urn │ │ ├── resource.go │ │ ├── service.go │ │ └── urn.go │ ├── version │ │ └── version.go │ └── zen │ │ ├── README.md │ │ ├── auth.go │ │ ├── auth_test.go │ │ ├── doc.go │ │ ├── handler.go │ │ ├── middleware.go │ │ ├── middleware_errors.go │ │ ├── middleware_logger.go │ │ ├── middleware_metrics.go │ │ ├── middleware_openapi_validation.go │ │ ├── middleware_tracing.go │ │ ├── route.go │ │ ├── server.go │ │ ├── server_tls_test.go │ │ ├── session.go │ │ ├── session_bind_body_test.go │ │ ├── session_bind_query_test.go │ │ └── validation │ │ ├── validator.go │ │ └── validator_test.go ├── schema.json └── scripts │ └── shard-test │ └── main.go ├── internal ├── billing │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── quota.ts │ │ ├── subscriptions.ts │ │ ├── tiers.test.ts │ │ └── tiers.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── checkly │ ├── .github │ │ └── workflow.yml │ ├── .gitignore │ ├── README.md │ ├── checkly.config.ts │ ├── package.json │ └── src │ │ ├── __checks__ │ │ ├── api │ │ │ ├── liveness.check.ts │ │ │ ├── ratelimit │ │ │ │ ├── group.ts │ │ │ │ └── v1.ratelimits.limit.check.ts │ │ │ └── v1.keys.verifyKey.check.ts │ │ └── heartbeats.check.ts │ │ ├── alert-channels.ts │ │ └── locations.ts ├── clickhouse │ ├── Dockerfile │ ├── package.json │ ├── schema │ │ ├── 000_README.md │ │ ├── 001_create_databases.sql │ │ ├── 002_create_metrics_raw_api_requests_v1.sql │ │ ├── 003_create_verifications_raw_key_verifications_v1.sql │ │ ├── 004_create_verifications_key_verifications_per_hour_v1.sql │ │ ├── 005_create_verifications_key_verifications_per_day_v1.sql │ │ ├── 006_create_verifications_key_verifications_per_month_v1.sql │ │ ├── 007_create_verifications_key_verifications_per_hour_mv_v1.sql │ │ ├── 008_create_verifications_key_verifications_per_day_mv_v1.sql │ │ ├── 009_create_verifications_key_verifications_per_month_mv_v1.sql │ │ ├── 010_create_ratelimits_raw_ratelimits_table.sql │ │ ├── 011_create_telemetry_raw_sdks_v1.sql │ │ ├── 012_create_billing_billable_verifications_per_month_v1.sql │ │ ├── 013_create_billing_billable_verifications_per_month_mv_v1.sql │ │ ├── 014_create_ratelimits_ratelimits_per_minute_v1.sql │ │ ├── 015_create_ratelimits_ratelimits_per_hour_v1.sql │ │ ├── 016_create_ratelimits_ratelimits_per_day_v1.sql │ │ ├── 017_create_ratelimits_ratelimits_per_month_v1.sql │ │ ├── 018_create_ratelimits_ratelimits_per_minute_mv_v1.sql │ │ ├── 019_create_ratelimits_ratelimits_per_hour_mv_v1.sql │ │ ├── 020_create_ratelimits_ratelimits_per_day_mv_v1.sql │ │ ├── 021_create_ratelimits_ratelimits_per_month_mv_v1.sql │ │ ├── 022_create_business_active_workspaces_per_month_v1.sql │ │ ├── 023_create_business_active_workspaces_per_month_mv_v1.sql │ │ ├── 024_create_ratelimits_last_used_mv_v1.sql │ │ ├── 025_create_billable_verifications_v2.sql │ │ ├── 026_create_billable_ratelimits_v1.sql │ │ ├── 027_add_tags_to_verifications.raw_key_verifications_v1.sql │ │ ├── 028_create_verifications.key_verifications_per_hour_v2.sql │ │ ├── 029_create_verifications.key_verifications_per_hour_mv_v2.sql │ │ ├── 030_create_verifications.key_verifications_per_day_v2.sql │ │ ├── 031_create_verifications.key_verifications_per_day_mv_v2.sql │ │ ├── 032_create_verifications.key_verifications_per_month_v2.sql │ │ ├── 033_create_verifications.key_verifications_per_month_mv_v2.sql │ │ ├── 034_billing_read_from_verifications.key_verifications_per_month_v2.sql │ │ ├── 035_business_update_active_workspaces_keys_per_month_mv_v1_read_from_verifications.key_verifications_per_month_v2.sql │ │ ├── 036_create_verifications.key_verifications_per_hour_v3.sql │ │ ├── 037_create_verifications.key_verifications_per_hour_mv_v3.sql │ │ ├── 038_create_verifications.key_verifications_per_day_v3.sql │ │ ├── 039_create_verifications.key_verifications_per_day_mv_v3.sql │ │ ├── 040_create_verifications.key_verifications_per_month_v3.sql │ │ ├── 041_create_verifications.key_verifications_per_month_mv_v3.sql │ │ ├── 042_create_api_requests_per_hour_v1.sql │ │ ├── 043_create_api_requests_per_hour_mv_v1.sql │ │ ├── 044_create_api_requests_per_minute_v1.sql │ │ ├── 045_create_api_requests_per_minute_mv_v1.sql │ │ ├── 046_create_api_requests_per_day_v1.sql │ │ ├── 047_create_api_requests_per_day_mv_v1.sql │ │ ├── 048_raw_ratelimits_metrics_indexes_v1.sql │ │ ├── 049_raw_api_metrics_ratelimit_indexes_v1.sql │ │ ├── 050_create_verifications.key_verifications_per_minute_v1.sql │ │ └── 051_create_verifications.key_verifications_per_minute_mv_v1.sql │ ├── src │ │ ├── active_keys.ts │ │ ├── billing.ts │ │ ├── client │ │ │ ├── client.ts │ │ │ ├── error.ts │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ └── noop.ts │ │ ├── index.ts │ │ ├── insert_verifications.test.ts │ │ ├── keys │ │ │ ├── active_keys.ts │ │ │ └── keys.ts │ │ ├── latest_verifications.ts │ │ ├── logs-timeseries.test.ts │ │ ├── logs.ts │ │ ├── ratelimits.ts │ │ ├── ratelimits_billing.test.ts │ │ ├── requests.ts │ │ ├── success.ts │ │ ├── telemetry.ts │ │ ├── testutil.ts │ │ ├── util.ts │ │ ├── verification_outcomes_propagate_correctly.test.ts │ │ ├── verification_tags.test.ts │ │ ├── verifications.ts │ │ └── verifications_billing.test.ts │ └── vitest.config.ts ├── db │ ├── drizzle.config.ts │ ├── drizzle │ │ ├── 0000_fat_the_hand.sql │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ └── _journal.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── schema │ │ │ ├── apis.ts │ │ │ ├── audit_logs.ts │ │ │ ├── identity.ts │ │ │ ├── index.ts │ │ │ ├── keyAuth.ts │ │ │ ├── key_migrations.ts │ │ │ ├── keys.ts │ │ │ ├── quota.ts │ │ │ ├── ratelimit.ts │ │ │ ├── rbac.ts │ │ │ ├── util │ │ │ │ ├── delete_protection.ts │ │ │ │ ├── embedded_encrypted.ts │ │ │ │ └── lifecycle_dates.ts │ │ │ ├── vercel_integration.ts │ │ │ └── workspaces.ts │ │ └── types.ts │ └── tsconfig.json ├── encoding │ ├── package.json │ ├── src │ │ ├── base64.ts │ │ └── index.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── encryption │ ├── package.json │ ├── src │ │ ├── aes-gcm.ts │ │ ├── index.ts │ │ └── key.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── events │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── hash │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── sha256.test.ts │ │ └── sha256.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── icons │ ├── LICENSE │ ├── package.json │ └── src │ │ ├── Readme.txt │ │ ├── icons │ │ ├── adjust-contrast-3.tsx │ │ ├── arrow-dot-anti-clockwise.tsx │ │ ├── arrow-dotted-rotate-anticlockwise.tsx │ │ ├── arrow-opposite-direction-y.tsx │ │ ├── arrow-right.tsx │ │ ├── arrow-up-right.tsx │ │ ├── ban.tsx │ │ ├── bars-filter.tsx │ │ ├── bolt.tsx │ │ ├── book-2.tsx │ │ ├── book-bookmark.tsx │ │ ├── bookmark.tsx │ │ ├── brackets-curly.tsx │ │ ├── bucket.tsx │ │ ├── calendar-clock.tsx │ │ ├── calendar-event.tsx │ │ ├── calendar.tsx │ │ ├── caret-down.tsx │ │ ├── caret-expand-y.tsx │ │ ├── caret-right-outline.tsx │ │ ├── caret-right.tsx │ │ ├── caret-up.tsx │ │ ├── chart-activity-2.tsx │ │ ├── chart-bar-axis-y.tsx │ │ ├── chart-pie.tsx │ │ ├── chart-usage.tsx │ │ ├── chats.tsx │ │ ├── check.tsx │ │ ├── chevron-down.tsx │ │ ├── chevron-expand-y.tsx │ │ ├── chevron-left.tsx │ │ ├── chevron-right.tsx │ │ ├── chevron-up.tsx │ │ ├── circle-caret-down.tsx │ │ ├── circle-caret-right.tsx │ │ ├── circle-check.tsx │ │ ├── circle-half-dotted-clock.tsx │ │ ├── circle-info-sparkle.tsx │ │ ├── circle-info.tsx │ │ ├── circle-lock.tsx │ │ ├── circle-question.tsx │ │ ├── clipboard-check.tsx │ │ ├── clipboard.tsx │ │ ├── clock-rotate-clockwise.tsx │ │ ├── clock.tsx │ │ ├── clone.tsx │ │ ├── code.tsx │ │ ├── coins.tsx │ │ ├── conversion.tsx │ │ ├── dots.tsx │ │ ├── eye-slash.tsx │ │ ├── eye.tsx │ │ ├── fingerprint.tsx │ │ ├── focus.tsx │ │ ├── folder-cloud.tsx │ │ ├── gauge.tsx │ │ ├── gear.tsx │ │ ├── grid.tsx │ │ ├── input-password-edit.tsx │ │ ├── input-password-settings.tsx │ │ ├── input-search.tsx │ │ ├── key-2.tsx │ │ ├── key.tsx │ │ ├── laptop-2.tsx │ │ ├── layers-2.tsx │ │ ├── layers-3.tsx │ │ ├── link-4.tsx │ │ ├── lock.tsx │ │ ├── magnifier.tsx │ │ ├── moon-stars.tsx │ │ ├── nodes.tsx │ │ ├── number-input.tsx │ │ ├── pen-writing-3.tsx │ │ ├── plus.tsx │ │ ├── progress-bar.tsx │ │ ├── pulse.tsx │ │ ├── refresh-3.tsx │ │ ├── shield-alert.tsx │ │ ├── shield-check.tsx │ │ ├── shield-key.tsx │ │ ├── shield.tsx │ │ ├── sidebar-left-hide.tsx │ │ ├── sidebar-left-show.tsx │ │ ├── sliders.tsx │ │ ├── sparkle-3.tsx │ │ ├── storage.tsx │ │ ├── sun.tsx │ │ ├── task-checked.tsx │ │ ├── task-unchecked.tsx │ │ ├── text-input.tsx │ │ ├── time-clock.tsx │ │ ├── trash.tsx │ │ ├── triangle-warning-2.tsx │ │ ├── triangle-warning.tsx │ │ ├── ufo.tsx │ │ ├── user-search.tsx │ │ ├── user.tsx │ │ └── xmark.tsx │ │ ├── index.ts │ │ ├── props.ts │ │ └── template.tsx ├── id │ ├── package.json │ ├── src │ │ ├── generate.test.ts │ │ ├── generate.ts │ │ └── index.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── keys │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── util.ts │ │ ├── v1.test.ts │ │ └── v1.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── logs │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── metrics │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── resend │ ├── emails │ │ ├── payment_issue.tsx │ │ ├── secret_scanning_key_detected.tsx │ │ ├── trial_ended.tsx │ │ └── welcome_email.tsx │ ├── package.json │ ├── src │ │ ├── client.tsx │ │ ├── components │ │ │ ├── layout.tsx │ │ │ └── signature.tsx │ │ ├── ensure-emails-render.spec.ts │ │ └── index.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── schema │ ├── package.json │ ├── src │ │ ├── auditlog.ts │ │ └── ratelimit-tinybird.ts │ └── tsconfig.json ├── tsconfig │ ├── README.md │ ├── base.json │ ├── nextjs.json │ ├── package.json │ └── react-library.json ├── ui │ ├── colors.css │ ├── components.json │ ├── css.ts │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── button.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── copy-button.tsx │ │ │ ├── date-time │ │ │ │ ├── components │ │ │ │ │ ├── actions.tsx │ │ │ │ │ ├── calendar.tsx │ │ │ │ │ └── time-split.tsx │ │ │ │ └── date-time.tsx │ │ │ ├── empty.tsx │ │ │ ├── form │ │ │ │ ├── form-checkbox.tsx │ │ │ │ ├── form-input.tsx │ │ │ │ ├── form-textarea.tsx │ │ │ │ └── index.tsx │ │ │ ├── id.tsx │ │ │ ├── info-tooltip.tsx │ │ │ ├── inline-link.tsx │ │ │ ├── input.tsx │ │ │ ├── select.tsx │ │ │ ├── settings-card.tsx │ │ │ ├── textarea.tsx │ │ │ ├── timestamp-info.tsx │ │ │ └── tooltip.tsx │ │ ├── index.ts │ │ └── lib │ │ │ └── utils.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── validation │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── vault │ ├── package.json │ └── src │ │ └── index.ts ├── vercel │ ├── package.json │ ├── src │ │ ├── client.ts │ │ └── index.ts │ └── tsconfig.json └── worker-logging │ ├── package.json │ ├── src │ ├── console.ts │ ├── index.ts │ └── interface.ts │ └── tsconfig.json ├── knip.ts ├── package.json ├── packages ├── api │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── client.test.ts │ │ ├── client.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── openapi.d.ts │ │ ├── telemetry.ts │ │ └── verify.ts │ ├── tsconfig.json │ └── tsup.config.js ├── cache │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── cache.ts │ │ ├── context.ts │ │ ├── errors.ts │ │ ├── examples │ │ │ ├── encryption.ts │ │ │ ├── memory.ts │ │ │ ├── namespaces.ts │ │ │ ├── stale-while-revalidate.ts │ │ │ └── tiered.ts │ │ ├── index.ts │ │ ├── interface.ts │ │ ├── metrics.ts │ │ ├── metrics_console.ts │ │ ├── middleware │ │ │ ├── encryption.test.ts │ │ │ ├── encryption.ts │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ └── metrics.ts │ │ ├── namespace.ts │ │ ├── stores │ │ │ ├── cloudflare.ts │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── libsql.test.ts │ │ │ ├── libsql.ts │ │ │ ├── memory.test.ts │ │ │ ├── memory.ts │ │ │ └── upstash-redis.ts │ │ ├── swr.test.ts │ │ ├── swr.ts │ │ ├── tiered.ts │ │ └── tracing.ts.todo │ ├── tsconfig.json │ ├── tsup.config.js │ └── vitest.config.ts ├── error │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── error-handling.ts │ │ ├── errors │ │ │ ├── base.ts │ │ │ ├── env-error.ts │ │ │ ├── fetch-error.ts │ │ │ └── schema-error.ts │ │ └── index.ts │ ├── tsconfig.json │ └── tsup.config.js ├── hono │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ └── index.ts │ ├── tsconfig.json │ ├── tsup.config.js │ └── vitest.config.ts ├── nextjs │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── tsup.config.js └── rbac │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── package.json │ ├── src │ ├── index.ts │ ├── permissions.test.ts │ ├── permissions.ts │ ├── queries.test.ts │ ├── queries.ts │ ├── rbac.test.ts │ ├── rbac.ts │ └── types.ts │ ├── tsconfig.json │ └── tsup.config.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tools ├── artillery │ ├── .dockerignore │ ├── .gitignore │ ├── .identifiers.csv │ ├── Dockerfile │ ├── README.md │ ├── aws.yaml │ ├── create-keys.js │ ├── fly.toml │ ├── ga.yaml │ ├── keys.verifyKey.yaml │ ├── leak.yaml │ ├── llm.yaml │ ├── main.ts │ ├── prompts.csv │ ├── r53.yaml │ ├── ratelimit.limit.yaml │ ├── ratelimits.limit.yaml │ ├── run.bash │ ├── server.yaml │ ├── timeout.yaml │ └── tinybird-proxy.yaml ├── k6 │ ├── Makefile │ ├── load.js │ └── package.json ├── local │ ├── README.md │ ├── package.json │ ├── src │ │ ├── cmd │ │ │ ├── api.ts │ │ │ ├── dashboard.ts │ │ │ ├── seed.ts │ │ │ └── seed │ │ │ │ ├── apis.ts │ │ │ │ ├── batch-helper.ts │ │ │ │ ├── batch-operations.ts │ │ │ │ ├── event-generator.ts │ │ │ │ ├── logs.ts │ │ │ │ ├── ratelimit.ts │ │ │ │ └── utils.ts │ │ ├── db.ts │ │ ├── docker.ts │ │ ├── env.ts │ │ ├── main.ts │ │ ├── prepare.ts │ │ ├── seed.ts │ │ └── util.ts │ └── tsconfig.json └── migrate │ ├── .env.example │ ├── auditlog-import.ts │ ├── axiom.ts │ ├── ch_logs.ts │ ├── debug_billing.ts │ ├── main.ts │ ├── migrate_subscription.ts │ ├── package.json │ ├── refill-migrate.ts │ ├── seed_quotas.ts │ ├── stripe.ts │ ├── timestamps.sql │ ├── tinybird-export.ts │ └── tsconfig.json ├── turbo.json └── vitest.workspace.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch" 10 | } 11 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @perkinsjr @chronark @mcstepp @MichaelUnkey @ogzhanolguncu 2 | /apps/dashboard @perkinsjr @chronark @mcstepp @ogzhanolguncu 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Unkey 2 | 3 | Contributions are what makes the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. To ensure the best possible outcome for everyone, please read this guide carefully before starting work. 4 | 5 | Our contributions guide can be found here: [engineering.unkey.com/contributing](https://engineering.unkey.com/contributing) 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord community support 4 | url: https://unkey.com/discord 5 | about: Need any help? Found any bug? Please chat with us via Discord. 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | 2 | version: 2 3 | updates: 4 | - package-ecosystem: "npm" 5 | directory: "/" 6 | open-pull-requests-limit: 2 7 | schedule: 8 | interval: "daily" 9 | ignore: 10 | - dependency-name: "*" 11 | update-types: ["version-update:semver-patch"] 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | open-pull-requests-limit: 2 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /.github/workflows/job_changes.yaml: -------------------------------------------------------------------------------- 1 | name: Changes 2 | on: 3 | workflow_call: 4 | 5 | 6 | 7 | 8 | jobs: 9 | build: 10 | name: Build Agent 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: dorny/paths-filter@v3 15 | id: changes 16 | with: 17 | filters: | 18 | clickhouse: 19 | - 'internal/clickhouse/schema/**' 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | 3 | .env* 4 | !.env.example 5 | rm 6 | 7 | .turbo 8 | node_modules 9 | dist 10 | .next 11 | 12 | .tinyb 13 | 14 | # contentlayer 15 | .contentlayer 16 | 17 | .DS_Store 18 | .vscode 19 | .dev.vars 20 | .wrangler 21 | .vitest 22 | .react-email 23 | 24 | .secrets.json 25 | -------------------------------------------------------------------------------- /apps/agent/README.md: -------------------------------------------------------------------------------- 1 |
2 |

Vault

3 |
Secure storage and encryption for per-tenant data encryption keys
4 |
5 | 6 |
7 | unkey.com 8 |
9 |
10 | 11 | 12 | 13 | ## Documentation 14 | 15 | The documentation lives [here](https://www.unkey.com/docs/contributing/services/agent/configuration). 16 | -------------------------------------------------------------------------------- /apps/agent/bruno/Liveness.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Liveness 3 | type: http 4 | seq: 1 5 | } 6 | 7 | post { 8 | url: http://localhost:8080/ratelimit.v1.RatelimitService/Liveness 9 | body: json 10 | auth: none 11 | } 12 | 13 | headers { 14 | Content-Type: application/json 15 | } 16 | 17 | body:json { 18 | {} 19 | } 20 | -------------------------------------------------------------------------------- /apps/agent/bruno/Ratelimit/Ratelimit.bru: -------------------------------------------------------------------------------- 1 | meta { 2 | name: Ratelimit 3 | type: http 4 | seq: 1 5 | } 6 | 7 | post { 8 | url: http://localhost:8081/ratelimit.v1.RatelimitService/Ratelimit 9 | body: json 10 | auth: bearer 11 | } 12 | 13 | headers { 14 | Content-Type: application/json 15 | } 16 | 17 | auth:bearer { 18 | token: agent-auth-secret 19 | } 20 | 21 | body:json { 22 | { 23 | "identifier": "chronark", 24 | "limit": 10, 25 | "duration": 10000 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/agent/bruno/bruno.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1", 3 | "name": "Agent", 4 | "type": "collection", 5 | "ignore": ["node_modules", ".git"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/agent/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | plugins: 3 | - remote: buf.build/protocolbuffers/go 4 | out: gen 5 | opt: paths=source_relative 6 | - remote: buf.build/connectrpc/go:v1.16.2 7 | out: gen 8 | opt: paths=source_relative 9 | 10 | -------------------------------------------------------------------------------- /apps/agent/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | - PACKAGE 6 | - WIRE 7 | - WIRE_JSON 8 | lint: 9 | use: 10 | - DEFAULT 11 | -------------------------------------------------------------------------------- /apps/agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agent", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "fmt": "go fmt ./... && go vet ./..." 9 | }, 10 | "keywords": [], 11 | "author": "Andreas Thomas", 12 | "license": "ISC" 13 | } 14 | -------------------------------------------------------------------------------- /apps/agent/pkg/api/interface.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/unkeyed/unkey/apps/agent/pkg/clickhouse/schema" 4 | 5 | type EventBuffer interface { 6 | BufferApiRequest(schema.ApiRequestV1) 7 | } 8 | -------------------------------------------------------------------------------- /apps/agent/pkg/api/mw_request_id.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/unkeyed/unkey/apps/agent/pkg/api/ctxutil" 7 | "github.com/unkeyed/unkey/apps/agent/pkg/uid" 8 | ) 9 | 10 | func withRequestId(next http.Handler) http.Handler { 11 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 12 | ctx := r.Context() 13 | ctx = ctxutil.SetRequestId(ctx, uid.New(uid.Request())) 14 | next.ServeHTTP(w, r.WithContext(ctx)) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /apps/agent/pkg/api/mw_tracing.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/unkeyed/unkey/apps/agent/pkg/tracing" 7 | ) 8 | 9 | func withTracing(next http.Handler) http.Handler { 10 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 11 | ctx := r.Context() 12 | ctx, span := tracing.Start(ctx, tracing.NewSpanName("api", r.URL.Path)) 13 | defer span.End() 14 | r = r.WithContext(ctx) 15 | 16 | next.ServeHTTP(w, r) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /apps/agent/pkg/cache/entry.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "container/list" 5 | "time" 6 | ) 7 | 8 | type swrEntry[T any] struct { 9 | Value T `json:"value"` 10 | 11 | Hit CacheHit `json:"hit"` 12 | // Before this time the entry is considered fresh and vaid 13 | Fresh time.Time `json:"fresh"` 14 | // Before this time, the entry should be revalidated 15 | // After this time, the entry must be discarded 16 | Stale time.Time `json:"stale"` 17 | LruElement *list.Element `json:"-"` 18 | } 19 | -------------------------------------------------------------------------------- /apps/agent/pkg/cache/middleware.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | type Middleware[T any] func(Cache[T]) Cache[T] 4 | -------------------------------------------------------------------------------- /apps/agent/pkg/circuitbreaker/metrics.go: -------------------------------------------------------------------------------- 1 | package circuitbreaker 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | "github.com/prometheus/client_golang/prometheus/promauto" 6 | ) 7 | 8 | var ( 9 | requests = promauto.NewCounterVec(prometheus.CounterOpts{ 10 | Namespace: "agent", 11 | Subsystem: "circuitbreaker", 12 | Name: "requests", 13 | }, []string{"name", "state"}) 14 | ) 15 | -------------------------------------------------------------------------------- /apps/agent/pkg/clickhouse/interface.go: -------------------------------------------------------------------------------- 1 | package clickhouse 2 | 3 | import ( 4 | "github.com/unkeyed/unkey/apps/agent/pkg/clickhouse/schema" 5 | ) 6 | 7 | type Bufferer interface { 8 | BufferApiRequest(schema.ApiRequestV1) 9 | BufferKeyVerification(schema.KeyVerificationRequestV1) 10 | } 11 | -------------------------------------------------------------------------------- /apps/agent/pkg/clickhouse/noop.go: -------------------------------------------------------------------------------- 1 | package clickhouse 2 | 3 | import ( 4 | "github.com/unkeyed/unkey/apps/agent/pkg/clickhouse/schema" 5 | ) 6 | 7 | type noop struct{} 8 | 9 | var _ Bufferer = &noop{} 10 | 11 | func (n *noop) BufferApiRequest(schema.ApiRequestV1) { 12 | 13 | } 14 | func (n *noop) BufferKeyVerification(schema.KeyVerificationRequestV1) { 15 | 16 | } 17 | 18 | func NewNoop() *noop { 19 | return &noop{} 20 | } 21 | -------------------------------------------------------------------------------- /apps/agent/pkg/clock/interface.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import "time" 4 | 5 | // Clock is an interface for getting the current time. 6 | // We're mainly using this for testing purposes, where waiting in real time 7 | // would be impractical. 8 | type Clock interface { 9 | Now() time.Time 10 | } 11 | -------------------------------------------------------------------------------- /apps/agent/pkg/clock/real_clock.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import "time" 4 | 5 | type RealClock struct { 6 | } 7 | 8 | func New() *RealClock { 9 | return &RealClock{} 10 | } 11 | 12 | var _ Clock = &RealClock{} 13 | 14 | func (c *RealClock) Now() time.Time { 15 | return time.Now() 16 | } 17 | -------------------------------------------------------------------------------- /apps/agent/pkg/cluster/interface.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | type Cluster interface { 4 | Shutdown() error 5 | FindNode(key string) (Node, error) 6 | Peers() []Node 7 | AuthToken() string 8 | 9 | // Returns its own node ID 10 | NodeId() string 11 | 12 | // Returns the number of nodes in the cluster 13 | Size() int 14 | } 15 | -------------------------------------------------------------------------------- /apps/agent/pkg/cluster/node.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | type Node struct { 4 | Id string 5 | RpcAddr string 6 | } 7 | -------------------------------------------------------------------------------- /apps/agent/pkg/gossip/test_utils_server.go: -------------------------------------------------------------------------------- 1 | package gossip 2 | 3 | // _testSimulateFailure is a test helper function that simulates a failure in the cluster 4 | // by shutting down the connect server, so other members can no longer ping it. 5 | func (s *clusterServer) _testSimulateFailure() { 6 | close(s.close) 7 | 8 | } 9 | -------------------------------------------------------------------------------- /apps/agent/pkg/membership/interface.go: -------------------------------------------------------------------------------- 1 | package membership 2 | 3 | type Membership interface { 4 | Join(addrs ...string) (int, error) 5 | Leave() error 6 | Members() ([]Member, error) 7 | SerfAddr() string 8 | SubscribeJoinEvents() <-chan Member 9 | 10 | SubscribeLeaveEvents() <-chan Member 11 | 12 | NodeId() string 13 | } 14 | -------------------------------------------------------------------------------- /apps/agent/pkg/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | type RingState struct { 4 | Nodes int `json:"nodes"` 5 | Tokens int `json:"tokens"` 6 | State string `json:"state"` 7 | } 8 | 9 | func (m RingState) Name() string { 10 | return "metric.ring.state" 11 | } 12 | -------------------------------------------------------------------------------- /apps/agent/pkg/metrics/noop.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | type noop struct { 4 | } 5 | 6 | func NewNoop() Metrics { 7 | return &noop{} 8 | 9 | } 10 | 11 | func (n *noop) Close() {} 12 | 13 | func (n *noop) Record(metric Metric) {} 14 | -------------------------------------------------------------------------------- /apps/agent/pkg/openapi/config.yaml: -------------------------------------------------------------------------------- 1 | package: openapi 2 | output: ./pkg/openapi/gen.go 3 | generate: 4 | models: true 5 | output-options: 6 | nullable-type: true 7 | -------------------------------------------------------------------------------- /apps/agent/pkg/openapi/spec.go: -------------------------------------------------------------------------------- 1 | package openapi 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | // Spec is the OpenAPI specification for the service 8 | // It's loaded from our openapi file and embedded into the binary 9 | // 10 | //go:embed openapi.json 11 | var Spec []byte 12 | -------------------------------------------------------------------------------- /apps/agent/pkg/prometheus/server.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/prometheus/client_golang/prometheus/promhttp" 8 | ) 9 | 10 | func Listen(path string, port int) error { 11 | mux := http.NewServeMux() 12 | mux.Handle(path, promhttp.Handler()) 13 | return http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), mux) 14 | } 15 | -------------------------------------------------------------------------------- /apps/agent/pkg/repeat/every.go: -------------------------------------------------------------------------------- 1 | package repeat 2 | 3 | import "time" 4 | 5 | // Every runs the given function in a go routine every d duration until the returned function is called. 6 | func Every(d time.Duration, fn func()) func() { 7 | t := time.NewTicker(d) 8 | go func() { 9 | for range t.C { 10 | fn() 11 | } 12 | }() 13 | return func() { 14 | t.Stop() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/agent/pkg/tracing/schema.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import "fmt" 4 | 5 | func NewSpanName(pkg string, method string) string { 6 | return fmt.Sprintf("%s.%s", pkg, method) 7 | } 8 | -------------------------------------------------------------------------------- /apps/agent/pkg/tracing/util.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | "go.opentelemetry.io/otel/codes" 5 | "go.opentelemetry.io/otel/trace" 6 | ) 7 | 8 | // RecordError sets the status of the span to error if the error is not nil. 9 | func RecordError(span trace.Span, err error) { 10 | if err == nil { 11 | return 12 | } 13 | span.SetStatus(codes.Error, err.Error()) 14 | } 15 | -------------------------------------------------------------------------------- /apps/agent/pkg/uid/hash.go: -------------------------------------------------------------------------------- 1 | package uid 2 | 3 | import ( 4 | "crypto/sha256" 5 | "strings" 6 | 7 | "github.com/btcsuite/btcutil/base58" 8 | ) 9 | 10 | func IdFromHash(s string, prefix ...string) string { 11 | 12 | hash := sha256.New() 13 | _, _ = hash.Write([]byte(s)) 14 | 15 | id := base58.Encode(hash.Sum(nil)) 16 | if len(prefix) > 0 && prefix[0] != "" { 17 | return strings.Join([]string{prefix[0], id}, "_") 18 | } else { 19 | return id 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /apps/agent/pkg/util/pointer.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | func Pointer[T any](t T) *T { 4 | return &t 5 | 6 | } 7 | -------------------------------------------------------------------------------- /apps/agent/pkg/util/random.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | // RandomElement returns a random element from the given slice. 8 | // 9 | // If the slice is empty, it returns the zero value of the element type. 10 | func RandomElement[T any](s []T) T { 11 | 12 | if len(s) == 0 { 13 | var t T 14 | return t 15 | } 16 | return s[rand.Intn(len(s))] 17 | } 18 | -------------------------------------------------------------------------------- /apps/agent/pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var Version string = "development" 4 | -------------------------------------------------------------------------------- /apps/agent/scripts/deploy.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | regionsResponse=$(fly platform regions --json) 5 | 6 | count=$(echo $regionsResponse | jq '. | length') 7 | 8 | # returns a comma delimited list of regions for fly cli: 'iad,ord,dfw,...' 9 | commaDelimitedRegions=$(echo $regionsResponse | jq '.[].Code' | paste -sd "," - | sed 's/"//g') 10 | 11 | fly --config=fly.production.toml scale count $count --max-per-region=1 --region=$commaDelimitedRegions -------------------------------------------------------------------------------- /apps/agent/services/vault/keys/key.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "github.com/segmentio/ksuid" 7 | ) 8 | 9 | func GenerateKey(prefix string) (id string, key []byte, err error) { 10 | 11 | key = make([]byte, 32) 12 | _, err = rand.Read(key) 13 | if err != nil { 14 | return "", nil, fmt.Errorf("failed to generate random data: %w", err) 15 | } 16 | 17 | return fmt.Sprintf("%s_%s", prefix, ksuid.New().String()), key, nil 18 | 19 | } 20 | -------------------------------------------------------------------------------- /apps/api/.dev.vars.example: -------------------------------------------------------------------------------- 1 | DATABASE_HOST= 2 | DATABASE_USERNAME= 3 | DATABASE_PASSWORD= 4 | 5 | -------------------------------------------------------------------------------- /apps/api/.gitignore: -------------------------------------------------------------------------------- 1 | .wrangler 2 | .vitest -------------------------------------------------------------------------------- /apps/api/src/pkg/cache/stale-while-revalidate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Cache reads within this time since writing will be considered fresh and can be used as is 3 | */ 4 | export const CACHE_FRESHNESS_TIME_MS = 1 * 60 * 1000; // 1 minute 5 | /** 6 | * Cache reads within this time sicne writing can be used but will run a revalidation in the background 7 | */ 8 | export const CACHE_STALENESS_TIME_MS = 24 * 60 * 60 * 1000; // 24 hours 9 | -------------------------------------------------------------------------------- /apps/api/src/pkg/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./http"; 2 | export * from "./openapi_responses"; 3 | -------------------------------------------------------------------------------- /apps/api/src/pkg/metrics/axiom.ts: -------------------------------------------------------------------------------- 1 | // idfk https://x.com/chronark_/status/1790061863918604666 2 | // @ts-ignore 3 | import "@axiomhq/js"; 4 | -------------------------------------------------------------------------------- /apps/api/src/pkg/metrics/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./interface"; 2 | export * from "./axiom"; 3 | export * from "./noop"; 4 | -------------------------------------------------------------------------------- /apps/api/src/pkg/metrics/interface.ts: -------------------------------------------------------------------------------- 1 | import type { Metric } from "@unkey/metrics"; 2 | 3 | export interface Metrics { 4 | /** 5 | * Emit stores a new metric event 6 | */ 7 | emit(metric: Metric): void; 8 | 9 | /** 10 | * flush persists all metrics to durable storage 11 | */ 12 | flush(): Promise; 13 | } 14 | -------------------------------------------------------------------------------- /apps/api/src/pkg/metrics/noop.ts: -------------------------------------------------------------------------------- 1 | import type { Metric } from "@unkey/metrics"; 2 | import type { Metrics } from "./interface"; 3 | export class NoopMetrics implements Metrics { 4 | public emit(_metric: Metric): Promise { 5 | return Promise.resolve(); 6 | } 7 | 8 | public async flush(): Promise {} 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/src/pkg/middleware/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./init"; 2 | export * from "./metrics"; 3 | export { cors } from "hono/cors"; 4 | export * from "./benchmarks"; 5 | -------------------------------------------------------------------------------- /apps/api/src/pkg/ratelimit/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | export * from "./interface"; 3 | export * from "./noop"; 4 | export * from "./durable_object"; 5 | export * from "./do_client"; 6 | -------------------------------------------------------------------------------- /apps/api/src/pkg/testutil/load.ts: -------------------------------------------------------------------------------- 1 | export async function loadTest(opts: { 2 | rps: number; 3 | seconds: number; 4 | fn: () => Promise; 5 | }): Promise { 6 | const promises: Promise[] = []; 7 | 8 | for (let s = 0; s < opts.seconds; s++) { 9 | for (let r = 0; r < opts.rps; r++) { 10 | const p = opts.fn(); 11 | promises.push(p); 12 | } 13 | await new Promise((r) => setTimeout(r, 1_000)); 14 | } 15 | 16 | return Promise.all(promises); 17 | } 18 | -------------------------------------------------------------------------------- /apps/api/src/pkg/types/maybe.ts: -------------------------------------------------------------------------------- 1 | export type MaybePromise = T | Promise; 2 | export type MaybeArray = T | Array; 3 | -------------------------------------------------------------------------------- /apps/api/src/pkg/usagelimit/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | export * from "./durable_object"; 3 | export * from "./interface"; 4 | export * from "./noop"; 5 | -------------------------------------------------------------------------------- /apps/api/src/pkg/usagelimit/noop.ts: -------------------------------------------------------------------------------- 1 | import type { LimitRequest, LimitResponse, RevalidateRequest, UsageLimiter } from "./interface"; 2 | 3 | export class NoopUsageLimiter implements UsageLimiter { 4 | public async limit(_req: LimitRequest): Promise { 5 | return { valid: true, remaining: -1 }; 6 | } 7 | 8 | public async revalidate(_req: RevalidateRequest): Promise {} 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/src/pkg/util/zod-error.ts: -------------------------------------------------------------------------------- 1 | import type { z } from "zod"; 2 | 3 | export function parseZodErrorMessage(err: z.ZodError): string { 4 | try { 5 | const arr = JSON.parse(err.message) as Array<{ 6 | message: string; 7 | path: Array; 8 | }>; 9 | const { path, message } = arr[0]; 10 | return `${path.join(".")}: ${message}`; 11 | } catch { 12 | return err.message; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/api/vitest.unit.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | // biome-ignore lint/style/noDefaultExport: required by vitest 4 | export default defineConfig({ 5 | test: { 6 | include: ["./src/**/*.test.ts"], 7 | exclude: ["./src/integration/**", "./src/routes/**", "./src/benchmarks/**"], 8 | reporters: ["html", "verbose"], 9 | outputFile: "./.vitest/html", 10 | alias: { 11 | "@/": new URL("./src/", import.meta.url).pathname, 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /apps/chproxy/.gitignore: -------------------------------------------------------------------------------- 1 | chproxy 2 | coverage.out 3 | -------------------------------------------------------------------------------- /apps/chproxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24-alpine AS builder 2 | 3 | WORKDIR /go/src/github.com/unkeyed/unkey/apps/chproxy 4 | COPY go.mod ./ 5 | 6 | COPY . . 7 | RUN go build -o bin/chproxy 8 | 9 | FROM golang:1.24-alpine 10 | RUN apk add --update curl 11 | 12 | WORKDIR /usr/local/bin 13 | COPY --from=builder /go/src/github.com/unkeyed/unkey/apps/chproxy/bin/chproxy . 14 | 15 | CMD ["/usr/local/bin/chproxy"] 16 | -------------------------------------------------------------------------------- /apps/chproxy/README.md: -------------------------------------------------------------------------------- 1 | Read more: [https://engineering.unkey.com/docs/architecture/clickhouse-proxy](https://engineering.unkey.com/docs/architecture/clickhouse-proxy) 2 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/types.ts: -------------------------------------------------------------------------------- 1 | export type SectionName = "general" | "ratelimit" | "credits" | "expiration" | "metadata"; 2 | 3 | export type SectionState = "valid" | "invalid" | "initial"; 4 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/[apiId]/constants.ts: -------------------------------------------------------------------------------- 1 | export const navigation = (apiId: string, keyAuthId: string) => [ 2 | { 3 | label: "Overview", 4 | href: `/apis/${apiId}`, 5 | segment: "overview", 6 | }, 7 | { 8 | label: "Keys", 9 | href: `/apis/${apiId}/keys/${keyAuthId}`, 10 | segment: "keys", 11 | }, 12 | { 13 | label: "API Settings", 14 | href: `/apis/${apiId}/settings`, 15 | segment: "settings", 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/components/actions/components/edit-metadata/utils.ts: -------------------------------------------------------------------------------- 1 | import type { KeyDetails } from "@/lib/trpc/routers/api/keys/query-api-keys/schema"; 2 | 3 | export const getKeyMetadataDefaults = (keyDetails: KeyDetails) => { 4 | return { 5 | metadata: { 6 | enabled: Boolean(keyDetails.metadata), 7 | data: JSON.stringify(JSON.parse(keyDetails.metadata || "{}"), null, 2) ?? undefined, 8 | }, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/components/bar-chart/query-timeseries.schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const keysListQueryTimeseriesPayload = z.object({ 4 | startTime: z.number().int(), 5 | endTime: z.number().int(), 6 | keyId: z.string(), 7 | keyAuthId: z.string(), 8 | }); 9 | 10 | export type KeysListQueryTimeseriesPayload = z.infer; 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/components/status-cell/query-timeseries.schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const keyOutcomesQueryPayload = z.object({ 4 | startTime: z.number().int(), 5 | endTime: z.number().int(), 6 | keyId: z.string(), 7 | keyAuthId: z.string(), 8 | }); 9 | 10 | export type KeyOutcomesQueryPayload = z.infer; 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/[apiId]/settings/actions.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { revalidatePath } from "next/cache"; 4 | 5 | export async function revalidate() { 6 | await revalidatePath("/", "layout"); 7 | } 8 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/_components/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_OVERVIEW_FETCH_LIMIT = 9; 2 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/apis/_components/hooks/query-timeseries.schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const verificationQueryTimeseriesPayload = z.object({ 4 | startTime: z.number().int(), 5 | endTime: z.number().int(), 6 | since: z.string(), 7 | keyspaceId: z.string(), 8 | }); 9 | 10 | export type VerificationQueryTimeseriesPayload = z.infer; 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/audit/constants.ts: -------------------------------------------------------------------------------- 1 | export const PANEL_MAX_WIDTH = 600; 2 | export const PANEL_MIN_WIDTH = 400; 3 | 4 | export const DEFAULT_DRAGGABLE_WIDTH = 500; 5 | export const MAX_DRAGGABLE_WIDTH = 800; 6 | export const MIN_DRAGGABLE_WIDTH = 300; 7 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/audit/navigation.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Navbar } from "@/components/navigation/navbar"; 4 | import { InputSearch } from "@unkey/icons"; 5 | 6 | export function Navigation() { 7 | return ( 8 | 9 | }> 10 | Audit 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/authorization/constants.ts: -------------------------------------------------------------------------------- 1 | export const navigation = [ 2 | { 3 | label: "Roles", 4 | href: "/authorization/roles", 5 | segment: "roles", 6 | }, 7 | { 8 | label: "Permissions", 9 | href: "/authorization/permissions", 10 | segment: "permissions", 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/identities/navigation.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Navbar } from "@/components/navigation/navbar"; 4 | import { Fingerprint } from "@unkey/icons"; 5 | 6 | export function Navigation() { 7 | return ( 8 | 9 | }> 10 | 11 | Identities 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/logs/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_DRAGGABLE_WIDTH = 500; 2 | 3 | export const YELLOW_STATES = ["RATE_LIMITED", "EXPIRED", "USAGE_EXCEEDED"]; 4 | export const RED_STATES = ["DISABLED", "FORBIDDEN", "INSUFFICIENT_PERMISSIONS"]; 5 | 6 | export const METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"] as const; 7 | export const STATUSES = [200, 400, 500] as const; 8 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/logs/navigation.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Navbar } from "@/components/navigation/navbar"; 4 | import { Layers3 } from "@unkey/icons"; 5 | 6 | export function Navigation() { 7 | return ( 8 | 9 | }> 10 | Logs 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/logs/types.ts: -------------------------------------------------------------------------------- 1 | export type ResponseStatus = 200 | 400 | 500; 2 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/overview/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | export default function OverviewPage() { 6 | return redirect("/apis"); 7 | } 8 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/utils/calculate-blocked-percentage.ts: -------------------------------------------------------------------------------- 1 | import type { RatelimitOverviewLog } from "@unkey/clickhouse/src/ratelimits"; 2 | 3 | export const calculateBlockedPercentage = (log: RatelimitOverviewLog) => { 4 | const totalRequests = log.blocked_count + log.passed_count; 5 | const blockRate = totalRequests > 0 ? (log.blocked_count / totalRequests) * 100 : 0; 6 | const hasMoreBlocked = blockRate > 60; 7 | 8 | return hasMoreBlocked; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_STATUS_FLAG = 0; 2 | 3 | export const DEFAULT_DRAGGABLE_WIDTH = 500; 4 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/ratelimits/[namespaceId]/types.ts: -------------------------------------------------------------------------------- 1 | export type OverrideDetails = { 2 | overrideId?: string; 3 | limit: number; 4 | duration: number; 5 | async?: boolean | null; 6 | }; 7 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/settings/constants.ts: -------------------------------------------------------------------------------- 1 | export const navigation = [ 2 | { 3 | label: "General", 4 | href: "/settings/general", 5 | segment: "general", 6 | }, 7 | { 8 | label: "Team", 9 | href: "/settings/team", 10 | segment: "team", 11 | }, 12 | { 13 | label: "Root Keys", 14 | href: "/settings/root-keys", 15 | segment: "root-keys", 16 | }, 17 | { 18 | label: "Billing", 19 | href: "/settings/billing", 20 | segment: "billing", 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/settings/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | export default function SettingsPage() { 6 | return redirect("/settings/general"); 7 | } 8 | -------------------------------------------------------------------------------- /apps/dashboard/app/(app)/settings/vercel/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Loading } from "@/components/dashboard/loading"; 2 | 3 | export default function () { 4 | // You can add any UI inside Loading, including a Skeleton. 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/actions.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | import { revalidatePath, revalidateTag } from "next/cache"; 3 | 4 | export async function revalidate(path: string, segment?: "page" | "layout") { 5 | revalidatePath(path, segment || "page"); 6 | } 7 | 8 | export async function revalidateMyTag(slug: string) { 9 | revalidateTag(slug); 10 | } 11 | 12 | export { revalidateMyTag as revalidateTag }; 13 | -------------------------------------------------------------------------------- /apps/dashboard/app/auth/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useSignUp } from "./useSignUp"; 2 | export { useSignIn } from "./useSignIn"; 3 | -------------------------------------------------------------------------------- /apps/dashboard/app/auth/sign-in/last_used.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useLocalStorage } from "usehooks-ts"; 3 | 4 | export function useLastUsed() { 5 | return useLocalStorage<"github" | "google" | "email" | undefined>("last_unkey_login", undefined); 6 | } 7 | 8 | export const LastUsed: React.FC = () => { 9 | return Last used; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/app/favicon.ico -------------------------------------------------------------------------------- /apps/dashboard/app/integrations/vercel/callback/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Loading } from "@/components/dashboard/loading"; 2 | 3 | export default function () { 4 | // You can add any UI inside Loading, including a Skeleton. 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /apps/dashboard/app/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /apps/dashboard/app/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 4 | import type { ThemeProviderProps } from "next-themes/dist/types"; 5 | 6 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 7 | return {children}; 8 | } 9 | -------------------------------------------------------------------------------- /apps/dashboard/components/dialog-container.tsx: -------------------------------------------------------------------------------- 1 | // TODO: We'll replace imports soon. Now this is required for backward compatibility 2 | export * from "./dialog-container/dialog-container"; 3 | -------------------------------------------------------------------------------- /apps/dashboard/components/empty-component-spacer.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren } from "react"; 2 | 3 | export const EmptyComponentSpacer = ({ children }: PropsWithChildren) => { 4 | return ( 5 |
6 |
{children}
7 |
8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /apps/dashboard/components/logs/chart/utils/calculate-timepoints.ts: -------------------------------------------------------------------------------- 1 | export const calculateTimePoints = (startTime: number, endTime: number) => { 2 | const points = 6; 3 | const timeRange = endTime - startTime; 4 | const step = Math.floor(timeRange / (points - 1)); 5 | 6 | return Array.from({ length: points }, (_, i) => new Date(startTime + step * i)).filter( 7 | (date) => date.getTime() <= endTime, 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /apps/dashboard/components/logs/chart/utils/convert-date-to-local.ts: -------------------------------------------------------------------------------- 1 | import { addMinutes } from "date-fns"; 2 | 3 | export const convertDateToLocal = (value: Date | number) => { 4 | const date = new Date(value); 5 | const offset = new Date().getTimezoneOffset() * -1; 6 | const localDate = addMinutes(date, offset); 7 | return localDate.getTime(); 8 | }; 9 | -------------------------------------------------------------------------------- /apps/dashboard/components/logs/constants.ts: -------------------------------------------------------------------------------- 1 | // Those two setting is being used by every log table and chart. So be carefuly when you are making changes. Consult to core team. 2 | export const HISTORICAL_DATA_WINDOW = 12 * 60 * 60 * 1000; 3 | -------------------------------------------------------------------------------- /apps/dashboard/components/logs/llm-search/components/search-icon.tsx: -------------------------------------------------------------------------------- 1 | import { Magnifier, Refresh3 } from "@unkey/icons"; 2 | 3 | type SearchIconProps = { 4 | isProcessing: boolean; 5 | }; 6 | 7 | export const SearchIcon = ({ isProcessing }: SearchIconProps) => { 8 | if (isProcessing) { 9 | return ; 10 | } 11 | 12 | return ; 13 | }; 14 | -------------------------------------------------------------------------------- /apps/dashboard/components/logs/overview-charts/types.ts: -------------------------------------------------------------------------------- 1 | export type ChartLabels = { 2 | title: string; 3 | primaryLabel: string; 4 | primaryKey: string; 5 | secondaryLabel: string; 6 | secondaryKey: string; 7 | }; 8 | 9 | export type Selection = { 10 | start: string | number; 11 | end: string | number; 12 | startTimestamp?: number; 13 | endTimestamp?: number; 14 | }; 15 | 16 | export type TimeseriesData = { 17 | originalTimestamp: number; 18 | [key: string]: unknown; 19 | }; 20 | -------------------------------------------------------------------------------- /apps/dashboard/components/logs/validation/utils/type-guards.ts: -------------------------------------------------------------------------------- 1 | import type { FieldConfig, NumberConfig, StringConfig } from "../filter.types"; 2 | 3 | // Type guards 4 | export function isNumberConfig(config: FieldConfig): config is NumberConfig { 5 | return config.type === "number"; 6 | } 7 | export function isStringConfig(config: FieldConfig): config is StringConfig { 8 | return config.type === "string"; 9 | } 10 | -------------------------------------------------------------------------------- /apps/dashboard/components/page-content.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren } from "react"; 2 | 3 | export function PageContent({ children }: PropsWithChildren) { 4 | return
{children}
; 5 | } 6 | -------------------------------------------------------------------------------- /apps/dashboard/components/stats-card/components/chart/components/logs-chart-error.tsx: -------------------------------------------------------------------------------- 1 | export const LogsChartError = () => { 2 | return ( 3 |
4 |
5 |
6 | Could not retrieve logs 7 |
8 |
9 |
10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /apps/dashboard/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; 4 | 5 | const Collapsible = CollapsiblePrimitive.Root; 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 12 | -------------------------------------------------------------------------------- /apps/dashboard/components/virtual-table/constants.ts: -------------------------------------------------------------------------------- 1 | import type { TableConfig } from "./types"; 2 | 3 | export const DEFAULT_CONFIG: TableConfig = { 4 | rowHeight: 26, 5 | loadingRows: 50, 6 | overscan: 5, 7 | throttleDelay: 350, 8 | headerHeight: 40, 9 | layoutMode: "classic", // Default to classic table layout 10 | rowBorders: false, // Default to no borders 11 | containerPadding: "px-2", // Default container padding 12 | rowSpacing: 4, // Default spacing between rows (classic mode) 13 | } as const; 14 | -------------------------------------------------------------------------------- /apps/dashboard/images/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/app.png -------------------------------------------------------------------------------- /apps/dashboard/images/computer-user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/computer-user.jpg -------------------------------------------------------------------------------- /apps/dashboard/images/laptop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/laptop.jpg -------------------------------------------------------------------------------- /apps/dashboard/images/team/andreas.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/team/andreas.jpeg -------------------------------------------------------------------------------- /apps/dashboard/images/team/dom.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/team/dom.jpeg -------------------------------------------------------------------------------- /apps/dashboard/images/team/james.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/team/james.jpg -------------------------------------------------------------------------------- /apps/dashboard/images/team/michael.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/images/team/michael.png -------------------------------------------------------------------------------- /apps/dashboard/lib/auth/middleware.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest } from "next/server"; 2 | import { updateSession } from "./sessions"; 3 | 4 | export async function authMiddleware(request: NextRequest) { 5 | return await updateSession(request); 6 | } 7 | -------------------------------------------------------------------------------- /apps/dashboard/lib/cache.ts: -------------------------------------------------------------------------------- 1 | export const tags = { 2 | api: (apiId: string): string => `api-${apiId}`, 3 | permission: (permissionId: string): string => `permission-${permissionId}`, 4 | namespace: (namespaceId: string): string => `namespace-${namespaceId}`, 5 | role: (roleId: string): string => `role-${roleId}`, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/dashboard/lib/clerk.ts: -------------------------------------------------------------------------------- 1 | export type ClerkError = { errors: { code: string; longMessage: string }[] }; 2 | -------------------------------------------------------------------------------- /apps/dashboard/lib/clickhouse.ts: -------------------------------------------------------------------------------- 1 | import { ClickHouse } from "@unkey/clickhouse"; 2 | import { env } from "./env"; 3 | 4 | export const clickhouse = new ClickHouse({ url: env().CLICKHOUSE_URL }); 5 | -------------------------------------------------------------------------------- /apps/dashboard/lib/fmt.ts: -------------------------------------------------------------------------------- 1 | export function formatNumber(n: number): string { 2 | return Intl.NumberFormat("en", { notation: "compact" }).format(n); 3 | } 4 | -------------------------------------------------------------------------------- /apps/dashboard/lib/quotas.ts: -------------------------------------------------------------------------------- 1 | import type { Quotas } from "@unkey/db"; 2 | 3 | export const freeTierQuotas: Omit = { 4 | requestsPerMonth: 150_000, 5 | logsRetentionDays: 7, 6 | auditLogsRetentionDays: 30, 7 | team: false, 8 | }; 9 | -------------------------------------------------------------------------------- /apps/dashboard/lib/trpc/client.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCReact } from "@trpc/react-query"; 2 | import type { Router } from "./routers"; 3 | 4 | export const trpc = createTRPCReact(); 5 | -------------------------------------------------------------------------------- /apps/dashboard/lib/trpc/routers/billing/query-usage/schemas.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const queryUsageResponse = z.object({ 4 | billableRatelimits: z.number(), 5 | billableVerifications: z.number(), 6 | billableTotal: z.number(), 7 | }); 8 | 9 | export type UsageResponse = z.infer; 10 | -------------------------------------------------------------------------------- /apps/dashboard/lib/trpc/routers/org/index.ts: -------------------------------------------------------------------------------- 1 | export { getOrg } from "./getOrg"; 2 | export { getOrganizationMemberList } from "./getOrganizationMemberList"; 3 | export { removeMembership } from "./removeMembership"; 4 | export { updateMembership } from "./updateMembership"; 5 | export { getInvitationList } from "./getInvitationList"; 6 | export { inviteMember } from "./inviteMember"; 7 | export { revokeInvitation } from "./revokeInvitation"; 8 | -------------------------------------------------------------------------------- /apps/dashboard/lib/trpc/routers/user/index.ts: -------------------------------------------------------------------------------- 1 | export { getCurrentUser } from "./getCurrentUser"; 2 | export { listMemberships } from "./listMemberships"; 3 | export { switchOrg } from "./switchOrg"; 4 | -------------------------------------------------------------------------------- /apps/dashboard/lib/trpc/routers/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const HOUR_IN_MS = 60 * 60 * 1000; 2 | export const DAY_IN_MS = 24 * HOUR_IN_MS; 3 | export const WEEK_IN_MS = 8 * DAY_IN_MS; 4 | export const MONTH_IN_MS = 31 * 24 * 60 * 60 * 1000; // 30 days in milliseconds 5 | export const QUARTER_IN_MS = MONTH_IN_MS * 3; 6 | -------------------------------------------------------------------------------- /apps/dashboard/lib/trpc/server.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCProxyClient, httpLink } from "@trpc/client"; 2 | import superjson from "superjson"; 3 | 4 | import { getBaseUrl } from "../utils"; 5 | import type { Router } from "./routers"; 6 | 7 | export const trpc = createTRPCProxyClient({ 8 | transformer: superjson, 9 | links: [ 10 | httpLink({ 11 | url: `${getBaseUrl()}/api/trpc`, 12 | }), 13 | ], 14 | }); 15 | -------------------------------------------------------------------------------- /apps/dashboard/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type MaybeArray = T | Array; 2 | -------------------------------------------------------------------------------- /apps/dashboard/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "tailwindcss/nesting": {}, 4 | tailwindcss: {}, 5 | "postcss-focus-visible": { 6 | replaceWith: "[data-focus-visible-added]", 7 | }, 8 | autoprefixer: {}, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/admin-dashboard-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/admin-dashboard-new.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/admin-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/admin-dashboard.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ai-post/create-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ai-post/create-api.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ai-post/create-root-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ai-post/create-root-key.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/cli-auth/cli-auth-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/cli-auth/cli-auth-overview.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/funding/funding-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/funding/funding-cover.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/how-to-market/tweet-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/how-to-market/tweet-example.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/how-to-market/welcome-unkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/how-to-market/welcome-unkey.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ocr-post/1-create-root-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ocr-post/1-create-root-key.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ocr-post/2-create-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ocr-post/2-create-api.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ocr-post/3-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ocr-post/3-dashboard.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ocr-post/4-walkthrough.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ocr-post/4-walkthrough.gif -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ocr-post/wilfred.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ocr-post/wilfred.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/analytics.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/audit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/audit.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/onboarding-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/onboarding-1.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/onboarding-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/onboarding-2.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/onboarding-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/onboarding-3.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/overrides.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/overrides.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/ratelimit-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/ratelimit-cover.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/ratelimiting/top-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/ratelimiting/top-analytics.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/secure-env/example-stripe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/secure-env/example-stripe.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/unkey-latency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/unkey-latency.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/unkey-with-auth/dashboard-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/unkey-with-auth/dashboard-example.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/usage-based-billing/monthly_active_keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/usage-based-billing/monthly_active_keys.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/usage-based-billing/monthly_verifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/usage-based-billing/monthly_verifications.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/blog-images/vercel/vercel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/blog-images/vercel/vercel.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2023-12-15/active-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2023-12-15/active-keys.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2023-12-15/billing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2023-12-15/billing.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2023-12-15/speed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2023-12-15/speed.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2023-12-15/verifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2023-12-15/verifications.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-01-19/audit-logging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-01-19/audit-logging.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-02-16/attach-perm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-02-16/attach-perm.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-02-16/connect-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-02-16/connect-key.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-02-16/create-key-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-02-16/create-key-ui.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-02-16/create-perms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-02-16/create-perms.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-02-16/create-role.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-02-16/create-role.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/2024-02-16/permissions-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/2024-02-16/permissions-details.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/aug-25/error-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/aug-25/error-example.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-1.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-2.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-3.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/aug-25/unkey-onboard-step-4.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/july-10/usage-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/july-10/usage-example.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-29/root-key-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-29/root-key-analytics.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-29/unkey-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-29/unkey-template.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-29/usage-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-29/usage-analytics.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/api-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/api-settings.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/key-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/key-analytics.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/key-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/key-settings.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/usage.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/user-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/user-account.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/workspace-setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/workspace-setting.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/changelog/sept-8/workspace-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/changelog/sept-8/workspace-settings.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/integration.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/landing/app-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/landing/app-dark.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/landing/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/landing/app.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/landing/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/landing/og.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/landing/unkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/landing/unkey.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/quoteImages/dexter-storey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/quoteImages/dexter-storey.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/quoteImages/lola.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/quoteImages/lola.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/quoteImages/maximilian-kaske.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/quoteImages/maximilian-kaske.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/quoteImages/rick-blalock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/quoteImages/rick-blalock.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/quoteImages/tanmay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/quoteImages/tanmay.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/team/andreas.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/team/andreas.jpeg -------------------------------------------------------------------------------- /apps/dashboard/public/images/team/james.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/team/james.jpg -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/ai-billing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/ai-billing.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/atash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/atash.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/bun_koyeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/bun_koyeb.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/express-middleware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/express-middleware.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/graphql-yoga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/graphql-yoga.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/openstatus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/openstatus.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/placeholder.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/ratelimit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/ratelimit.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/sprintpadawan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/sprintpadawan.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/unkey-cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/unkey-cli.png -------------------------------------------------------------------------------- /apps/dashboard/public/images/templates/unkey-stripe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/images/templates/unkey-stripe.png -------------------------------------------------------------------------------- /apps/dashboard/public/unkey-vercel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/dashboard/public/unkey-vercel.png -------------------------------------------------------------------------------- /apps/dashboard/trpc.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "trpc-tools"; 2 | 3 | import type { Router } from "@/lib/trpc/routers"; 4 | 5 | export default defineConfig({ 6 | router: {} as Router, 7 | }); 8 | -------------------------------------------------------------------------------- /apps/dashboard/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: "jsdom", 6 | alias: { "@/": new URL("./", import.meta.url).pathname }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /apps/docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @unkey/docs 2 | 3 | ## 1.1.0 4 | 5 | ### Minor Changes 6 | 7 | - 3f8d078: Adding ratelimit override API to SDK 8 | 9 | ## 1.0.1 10 | 11 | ### Patch Changes 12 | 13 | - 94d721d: Allow overriding ratelimit cost 14 | -------------------------------------------------------------------------------- /apps/docs/README.md: -------------------------------------------------------------------------------- 1 | # Unkey Docs 2 | -------------------------------------------------------------------------------- /apps/docs/api-reference/apis/create.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create an API 3 | openapi: post /v1/apis.createApi 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|---------------------| 10 | | Dec 06 2023 | Introduced endpoint | 11 | -------------------------------------------------------------------------------- /apps/docs/api-reference/apis/delete-keys.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete all keys of an API 3 | openapi: post /v1/apis.deleteKeys 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|-----------------------------| 10 | | May 26 2024 | Introduced endpoint | 11 | -------------------------------------------------------------------------------- /apps/docs/api-reference/apis/delete.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete an API 3 | description: Permanently delete an API and revoke all keys associated with it 4 | openapi: post /v1/apis.deleteApi 5 | --- 6 | 7 | ## Changelog 8 | 9 | | Date | Changes | 10 | |-------------|---------------------| 11 | | Dec 06 2023 | Introduced endpoint | 12 | -------------------------------------------------------------------------------- /apps/docs/api-reference/apis/get.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Retrieve an API 3 | openapi: get /v1/apis.getApi 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|---------------------| 10 | | Dec 06 2023 | Introduced endpoint | 11 | -------------------------------------------------------------------------------- /apps/docs/api-reference/apis/list-keys.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: List keys for an API 3 | openapi: get /v1/apis.listKeys 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|-------------------------------| 10 | | Dec 06 2023 | Introduced endpoint | 11 | | May 15 2024 | Return updatedAt timestamp | 12 | | Aug 01 2024 | Return identities | 13 | | Aug 01 2024 | Added filtering by externalId | 14 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/BAD_REQUEST.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: INSUFFICIENT_PERMISSIONS 3 | openapi-schema: ErrInsufficientPermissions 4 | --- 5 | 6 | ## Problem 7 | 8 | You do not have permission to perform this action. In most cases this means the root key you are using, is lacking permissions. 9 | 10 | ## Solution 11 | 12 | Go to the [Unkey Dashboard](https://app.unkey.com/settings/root-keys) and add the required permissions to your key. 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/CONFLICT.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CONFLICT 3 | openapi-schema: ErrConflict 4 | --- 5 | 6 | ## Problem 7 | 8 | Another resource already uses this identifier. For example workspace slugs must be unique globally. 9 | 10 | ## Solution 11 | 12 | Please choose a different name/identifier. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/DELETE_PROTECTED.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: DELETE_PROTECTED 3 | openapi-schema: ErrDeleteProtected 4 | --- 5 | 6 | ## Problem 7 | 8 | The resource you are trying to delete is protected and cannot be deleted. 9 | 10 | ## Solution 11 | 12 | Go to the [Unkey Dashboard](https://app.unkey.com) and remove the protection from the resource you are trying to delete. 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/DISABLED.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: DISABLED 3 | openapi-schema: ErrDisabled 4 | --- 5 | 6 | ## Problem 7 | 8 | The key has been disabled. 9 | 10 | ## Solution 11 | 12 | Enable this key using the [updateKey endpoint](/api-reference/keys/update) or web interface. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/EXPIRED.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: EXPIRED 3 | openapi-schema: ErrExpired 4 | --- 5 | 6 | ## Problem 7 | 8 | The key has expired and can no longer be used. 9 | 10 | ## Solution 11 | 12 | Check the `expires` field and update the key if necessary. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/FORBIDDEN.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: FORBIDDEN 3 | openapi-schema: ErrForbidden 4 | --- 5 | 6 | ## Problem 7 | 8 | We were able to authenticate you but you do not have access to the requested resources. 9 | 10 | ## Solution 11 | 12 | Use the correct key and/or double check you are requesting the correct resources. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/INSUFFICIENT_PERMISSIONS.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: INSUFFICIENT_PERMISSIONS 3 | openapi-schema: ErrInsufficientPermissions 4 | --- 5 | 6 | ## Problem 7 | 8 | You do not have permission to perform this action. In most cases this means the root key you are using, is lacking permissions. 9 | 10 | ## Solution 11 | 12 | Go to the [Unkey Dashboard](https://app.unkey.com/settings/root-keys) and add the required permissions to your key. 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/INTERNAL_SERVER_ERROR.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: INTERNAL_SERVER_ERROR 3 | openapi-schema: ErrInternalServerError 4 | --- 5 | 6 | ## Problem 7 | 8 | Something unexpected happened and we did not handle the error well. 9 | 10 | ## Solution 11 | 12 | Please get in touch on [Discord](https://unkey.com/discord) and provide the full error response. 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/NOT_FOUND.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: NOT_FOUND 3 | openapi-schema: ErrNotFound 4 | --- 5 | 6 | ## Problem 7 | 8 | The requested resource could not be found. It may have been deleted or does not exist. 9 | 10 | ## Solution 11 | 12 | Please ensure that you are providing the correct resource identifier or check if the resource has been deleted. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/PRECONDITION_FAILED.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: PRECONDITION_FAILED 3 | openapi-schema: ErrPreconditionFailed 4 | 5 | --- 6 | 7 | ## Problem 8 | 9 | The request does not meet one or more preconditions required for fulfillment. For example, that you are not flagged into a beta feature. 10 | 11 | ## Solution 12 | 13 | Please check if you meet all conditions outlined in the error message. 14 | 15 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/TOO_MANY_REQUESTS.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: TOO_MANY_REQUESTS 3 | openapi-schema: ErrTooManyRequests 4 | --- 5 | 6 | ## Problem 7 | 8 | You have made too many requests in a short period of time. 9 | 10 | ## Solution 11 | 12 | Please wait a bit and try again or increase the ratelimit on your API key. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/errors/code/UNAUTHORIZED.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: UNAUTHORIZED 3 | openapi-schema: ErrUnauthorized 4 | --- 5 | 6 | ## Problem 7 | 8 | We were unable to authorize your request. Either your key was missing, malformed or does not have the required permissions. 9 | 10 | ## Solution 11 | 12 | Check the `message` field and double check you are sending the key correctly in the `Authorization` header. 13 | 14 | If that doesn't help, ask for help on [Discord](https://unkey.com/discord) 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/identities/list-identities.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: List identities 3 | openapi: get /v1/identities.listIdentities 4 | --- 5 | 6 | 7 | Identities are in public beta. Please report any issues to [support@unkey.dev](mailto:support@unkey.dev) 8 | 9 | List all identities in the system. This will return a paginated list of identities. 10 | 11 | ## Changelog 12 | 13 | | Date | Changes | 14 | |-------------|---------------------| 15 | | Jul 17 2024 | Introduced endpoint | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/identities/update-identity.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Update an identity 3 | openapi: post /v1/identities.updateIdentity 4 | --- 5 | 6 | 7 | Identities are in public beta. Please report any issues to [support@unkey.dev](mailto:support@unkey.dev) 8 | 9 | Update an identity's metadata or limits. 10 | 11 | ## Changelog 12 | 13 | | Date | Changes | 14 | |-------------|---------------------| 15 | | Jul 17 2024 | Introduced endpoint | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/create.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create a key 3 | openapi: post /v1/keys.createKey 4 | --- 5 | 6 | Create a new key. 7 | 8 | ## Changelog 9 | 10 | | Date | Changes | 11 | |-------------|---------------------| 12 | | Dec 06 2023 | Introduced endpoint | 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/delete.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete a key 3 | description: Deleted keys are no longer valid and will not be able to be used to authenticate requests. 4 | openapi: post /v1/keys.deleteKey 5 | --- 6 | 7 | ## Changelog 8 | 9 | | Date | Changes | 10 | |-------------|---------------------| 11 | | Dec 06 2023 | Introduced endpoint | 12 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/get.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Retrieve a key by ID 3 | openapi: get /v1/keys.getKey 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|-----------------------------| 10 | | Dec 06 2023 | Introduced endpoint | 11 | | May 15 2024 | Return updatedAt timestamp | 12 | | Aug 01 2024 | Return identities | 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/remove-permissions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remove Permissions 3 | openapi: post /v1/keys.removePermissions 4 | description: Remove one or more permissions from a key. 5 | --- 6 | 7 | 8 | 9 | To use this endpoint, your root key must have the `rbac.*.remove_permission_from_key` permission. 10 | 11 | 12 | ## Changelog 13 | 14 | | Date | Changes | 15 | |-------------|---------------------| 16 | | Jul 08 2024 | Introduced endpoint | 17 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/remove-roles.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remove Roles 3 | openapi: post /v1/keys.removeRoles 4 | description: Remove one or more roles from a key. 5 | --- 6 | 7 | 8 | 9 | To use this endpoint, your root key must have the `rbac.*.remove_role_from_key` permission. 10 | 11 | 12 | ## Changelog 13 | 14 | | Date | Changes | 15 | |-------------|---------------------| 16 | | Jul 08 2024 | Introduced endpoint | 17 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/update-remaining.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Update a key's remaining limit 3 | openapi: post /v1/keys.updateRemaining 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|---------------------| 10 | | Dec 06 2023 | Introduced endpoint | 11 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/update.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Update a key 3 | description: Updates the configuration of an existing key. Omit fields to leave unchanged. 4 | openapi: post /v1/keys.updateKey 5 | --- 6 | 7 | ## Changelog 8 | 9 | | Date | Changes | 10 | |-------------|---------------------| 11 | | Dec 06 2023 | Introduced endpoint | 12 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/verifications.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Retrieve usage numbers 3 | description: Filter by `keyId` or `ownerId`. 4 | openapi: get /v1/keys.getVerifications 5 | --- 6 | 7 | ## Changelog 8 | 9 | | Date | Changes | 10 | |-------------|---------------------| 11 | | Jan 08 2024 | Introduced endpoint | 12 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/verify.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Verify a key 3 | openapi: post /v1/keys.verifyKey 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|----------------------| 10 | | Dec 06 2023 | Introduced endpoint | 11 | | Jul 08 2024 | Added `EXPIRED` code | 12 | -------------------------------------------------------------------------------- /apps/docs/api-reference/keys/whoami.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Returns data about a key 3 | openapi: post /v1/keys.whoami 4 | --- 5 | 6 | ## Changelog 7 | 8 | | Date | Changes | 9 | |-------------|-----------------------------| 10 | | Oct 07 2024 | Introduced endpoint | 11 | 12 | 13 | You may not always have easy access to the `keyId` and therefore can't use [`/v1/keys.getKey`](/api-reference/keys/get). 14 | This offers an escape hatch to send us the real key instead. 15 | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/create-permission.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create A Permission 3 | openapi: post /v1/permissions.createPermission 4 | --- 5 | 6 | 7 | 8 | To use this endpoint, your root key must have the `rbac.*.create_permission` permission. 9 | 10 | 11 | ## Changelog 12 | 13 | | Date | Changes | 14 | |-------------|---------------------| 15 | | Jul 08 2024 | Introduced endpoint | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/create-role.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create A Role 3 | openapi: post /v1/permissions.createRole 4 | --- 5 | 6 | 7 | 8 | To use this endpoint, your root key must have the `rbac.*.create_role` permission. 9 | 10 | ## Changelog 11 | 12 | | Date | Changes | 13 | |-------------|---------------------| 14 | | Jul 08 2024 | Introduced endpoint | 15 | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/delete-permission.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete A Permission 3 | openapi: post /v1/permissions.deletePermission 4 | --- 5 | 6 | 7 | 8 | To use this endpoint, your root key must have the `rbac.*.delete_permission` permission. 9 | 10 | 11 | ## Changelog 12 | 13 | | Date | Changes | 14 | |-------------|---------------------| 15 | | Jul 08 2024 | Introduced endpoint | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/delete-role.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete A Role 3 | openapi: post /v1/permissions.deleteRole 4 | --- 5 | 6 | 7 | To use this endpoint, your root key must have the `rbac.*.delete_role` permission. 8 | 9 | ## Changelog 10 | 11 | | Date | Changes | 12 | |-------------|---------------------| 13 | | Jul 08 2024 | Introduced endpoint | 14 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/get-permission.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get Permission 3 | openapi: get /v1/permissions.getPermission 4 | --- 5 | 6 | 7 | 8 | To use this endpoint, your root key must have the `rbac.*.read_permission` permission. 9 | 10 | 11 | ## Changelog 12 | 13 | | Date | Changes | 14 | |-------------|---------------------| 15 | | Jul 08 2024 | Introduced endpoint | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/get-role.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get Role 3 | openapi: get /v1/permissions.getRole 4 | --- 5 | 6 | 7 | To use this endpoint, your root key must have the `rbac.*.read_role` permission. 8 | 9 | ## Changelog 10 | 11 | | Date | Changes | 12 | |-------------|---------------------| 13 | | Jul 08 2024 | Introduced endpoint | 14 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/list-permissions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: List Permissions 3 | openapi: get /v1/permissions.listPermissions 4 | --- 5 | 6 | 7 | 8 | To use this endpoint, your root key must have the `rbac.*.read_permission` permission. 9 | 10 | ## Changelog 11 | 12 | | Date | Changes | 13 | |-------------|---------------------| 14 | | Jul 08 2024 | Introduced endpoint | 15 | -------------------------------------------------------------------------------- /apps/docs/api-reference/permissions/list-roles.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: List Roles 3 | openapi: get /v1/permissions.listRoles 4 | --- 5 | 6 | 7 | 8 | To use this endpoint, your root key must have the `rbac.*.read_role` permission. 9 | 10 | 11 | ## Changelog 12 | 13 | | Date | Changes | 14 | |-------------|---------------------| 15 | | Jul 08 2024 | Introduced endpoint | 16 | -------------------------------------------------------------------------------- /apps/docs/api-reference/ratelimits/delete-override.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete Override 3 | description: Delete an override from the system. 4 | openapi: post /v1/ratelimits.deleteOverride 5 | --- 6 | 7 | 8 | ## Changelog 9 | 10 | | Date | Changes | 11 | |-------------|---------------------| 12 | | Nov 25 2024 | Introduced endpoint | 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/ratelimits/get-override.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get Override 3 | description: Retrieve the configured override by `namespaceId` or `namespaceName`. 4 | openapi: get /v1/ratelimits.getOverride 5 | --- 6 | 7 | 8 | ## Changelog 9 | 10 | | Date | Changes | 11 | |-------------|---------------------| 12 | | Nov 25 2024 | Introduced endpoint | 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/ratelimits/limit.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ratelimit 3 | description: Ratelimit an action based on an identifier. 4 | openapi: post /v1/ratelimits.limit 5 | --- 6 | 7 | 8 | ## Changelog 9 | 10 | | Date | Changes | 11 | |-------------|---------------------| 12 | | Mar 16 2024 | Introduced endpoint | 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/ratelimits/list-overrides.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: List Overrides 3 | description: Retrieve a list of configured overrides by `namespaceId` or `namespaceName`. 4 | openapi: get /v1/ratelimits.listOverrides 5 | --- 6 | 7 | 8 | ## Changelog 9 | 10 | | Date | Changes | 11 | |-------------|---------------------| 12 | | Nov 25 2024 | Introduced endpoint | 13 | -------------------------------------------------------------------------------- /apps/docs/api-reference/ratelimits/set-override.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Set Override 3 | openapi: post /v1/ratelimits.setOverride 4 | --- 5 | 6 | Create or update an override to set specific limits for an identifier. 7 | 8 | There is no `update` endpoint. Instead you should call this endpoint again to overwrite your override. 9 | 10 | ## Changelog 11 | 12 | | Date | Changes | 13 | |-------------|---------------------| 14 | | Nov 25 2024 | Introduced endpoint | 15 | -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/api-key-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/api-key-screen.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/api-keys-navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/api-keys-navigation.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/axiom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/axiom.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/connections-connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/connections-connected.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/connections.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/domains-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/domains-permissions.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/domains-roles-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/domains-roles-admin.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/domains-roles-dns.manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/domains-roles-dns.manager.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/domains-roles-read-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/domains-roles-read-only.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/domains-roles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/domains-roles.png -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/introduction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | description: 'Access Control with Roles and Permissions' 4 | --- 5 | 6 | Role-Based Access Control (RBAC) is a security paradigm that restricts system access to authorized actors. It is based on the principle of assigning roles to actors and defining what actions or resources each role can access. We are taking this one step further and allowing you to attach arbitrary permissions to keys, for more flexibility (Coming in Q2). 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /apps/docs/apis/features/authorization/role-add-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/apis/features/authorization/role-add-example.png -------------------------------------------------------------------------------- /apps/docs/apis/features/revocation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Key Revocation 3 | description: 'Keys can be revoked at any time, from the API or the dashboard.' 4 | --- 5 | 6 | In the event that a key is compromised, you can revoke it at any time. Once the key is revoked, it can take up to 60 seconds for the key to be invalidated. Once invalidated, the key will no longer be able to be used to authenticate requests. 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /apps/docs/apis/features/whitelist.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: IP Whitelisting 3 | description: 'Unkey offers IP whitelisting to restrict requests to a specific set of IP addresses.' 4 | --- 5 | 6 | This is useful for restricting access to your API to a specific set of IP addresses, such as your own servers or a set of trusted partners. This feature is available as an addon or with an Enterprise plan. 7 | 8 | 9 | IP Whitelist example 10 | 11 | -------------------------------------------------------------------------------- /apps/docs/audit-log/audit-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/audit-log.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/api_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/api_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/api_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/api_delete.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/api_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/api_update.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/auth_connect_permission_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/auth_connect_permission_key.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/auth_connect_role_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/auth_connect_role_key.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/auth_connect_role_permission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/auth_connect_role_permission.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/auth_disconnect_permission_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/auth_disconnect_permission_key.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/auth_disconnect_role_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/auth_disconnect_role_key.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/auth_disconnect_role_permission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/auth_disconnect_role_permission.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/key_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/key_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/key_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/key_delete.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/key_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/key_update.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/permission_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/permission_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/permission_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/permission_delete.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/permission_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/permission_update.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/ratelimitnamespace_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/ratelimitnamespace_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/ratelimitnamespace_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/ratelimitnamespace_delete.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/ratelimitnamespace_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/ratelimitnamespace_update.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/ratelimitoverride_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/ratelimitoverride_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/ratelimitoverride_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/ratelimitoverride_update.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/role_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/role_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/role_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/role_delete.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/role_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/role_update.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/workspace_create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/workspace_create.png -------------------------------------------------------------------------------- /apps/docs/audit-log/types/workspace_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/audit-log/types/workspace_update.png -------------------------------------------------------------------------------- /apps/docs/images/add-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/add-integration.png -------------------------------------------------------------------------------- /apps/docs/images/audit-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/audit-log.png -------------------------------------------------------------------------------- /apps/docs/images/choose-unkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/choose-unkey.png -------------------------------------------------------------------------------- /apps/docs/images/create-api-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/create-api-key.png -------------------------------------------------------------------------------- /apps/docs/images/create-first-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/create-first-api.png -------------------------------------------------------------------------------- /apps/docs/images/create-root-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/create-root-key.png -------------------------------------------------------------------------------- /apps/docs/images/create-workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/create-workspace.png -------------------------------------------------------------------------------- /apps/docs/images/example-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/example-key.png -------------------------------------------------------------------------------- /apps/docs/images/ip-whitelist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/ip-whitelist.png -------------------------------------------------------------------------------- /apps/docs/images/onboard-ratelimit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/onboard-ratelimit.png -------------------------------------------------------------------------------- /apps/docs/images/per-api-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/per-api-analytics.png -------------------------------------------------------------------------------- /apps/docs/images/per-key-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/per-key-analytics.png -------------------------------------------------------------------------------- /apps/docs/images/planetscale-foreign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/planetscale-foreign.png -------------------------------------------------------------------------------- /apps/docs/images/reroll-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/reroll-key.png -------------------------------------------------------------------------------- /apps/docs/images/root-keys/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/root-keys/copy.png -------------------------------------------------------------------------------- /apps/docs/images/root-keys/permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/root-keys/permissions.png -------------------------------------------------------------------------------- /apps/docs/images/select-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/select-api.png -------------------------------------------------------------------------------- /apps/docs/images/select-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/select-project.png -------------------------------------------------------------------------------- /apps/docs/images/sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/images/sign-up.png -------------------------------------------------------------------------------- /apps/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/docs", 3 | "version": "1.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "mintlify dev" 7 | }, 8 | "keywords": [], 9 | "author": "Andreas Thomas & James Perkins", 10 | "devDependencies": { 11 | "mintlify": "^4.0.482" 12 | }, 13 | "dependencies": { 14 | "sharp": "^0.33.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/docs/ratelimiting/copy-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/ratelimiting/copy-key.png -------------------------------------------------------------------------------- /apps/docs/ratelimiting/create-root-key-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/ratelimiting/create-root-key-permissions.png -------------------------------------------------------------------------------- /apps/docs/ratelimiting/new-override.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/ratelimiting/new-override.png -------------------------------------------------------------------------------- /apps/docs/ratelimiting/wildcard-override.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/ratelimiting/wildcard-override.png -------------------------------------------------------------------------------- /apps/docs/unkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/docs/unkey.png -------------------------------------------------------------------------------- /apps/engineering/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts -------------------------------------------------------------------------------- /apps/engineering/app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { HomeLayout } from "fumadocs-ui/layouts/home"; 2 | import type { ReactNode } from "react"; 3 | import { baseOptions } from "../layout.config"; 4 | 5 | export default function Layout({ 6 | children, 7 | }: { 8 | children: ReactNode; 9 | }): React.ReactElement { 10 | return {children}; 11 | } 12 | -------------------------------------------------------------------------------- /apps/engineering/app/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | export default function Page() { 2 | return ( 3 |
4 |
5 |

6 | BUILD BETTER 7 |
APIS FASTER 8 |

9 |

How we work

10 |
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/engineering/app/architecture/layout.tsx: -------------------------------------------------------------------------------- 1 | import { architectureSource } from "@/app/source"; 2 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 3 | import type { ReactNode } from "react"; 4 | import { baseOptions } from "../layout.config"; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /apps/engineering/app/company/layout.tsx: -------------------------------------------------------------------------------- 1 | import { companySource } from "@/app/source"; 2 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 3 | import type { ReactNode } from "react"; 4 | import { baseOptions } from "../layout.config"; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 |
9 | 10 | {children} 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/engineering/app/components/row.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren } from "react"; 2 | 3 | export const Row: React.FC = (props) => { 4 | return
{props.children}
; 5 | }; 6 | 7 | Row.displayName = "Row"; 8 | -------------------------------------------------------------------------------- /apps/engineering/app/contributing/layout.tsx: -------------------------------------------------------------------------------- 1 | import { contributingSource } from "@/app/source"; 2 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 3 | import type { ReactNode } from "react"; 4 | import { baseOptions } from "../layout.config"; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 |
9 | 10 | {children} 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/engineering/app/design/layout.tsx: -------------------------------------------------------------------------------- 1 | import { componentSource } from "@/app/source"; 2 | import "@unkey/ui/css"; 3 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 4 | import type { ReactNode } from "react"; 5 | import { baseOptions } from "../layout.config"; 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /apps/engineering/app/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { source } from "@/app/source"; 2 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 3 | import type { ReactNode } from "react"; 4 | import { baseOptions } from "../layout.config"; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 |
9 | 10 | {children} 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/engineering/app/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/engineering/app/infrastructure/layout.tsx: -------------------------------------------------------------------------------- 1 | import { infrastructureSource } from "@/app/source"; 2 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 3 | import type { ReactNode } from "react"; 4 | import { baseOptions } from "../layout.config"; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 |
9 | 10 | {children} 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/engineering/app/rfcs/[[...slug]]/local-date.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | type Props = { 4 | date: Date | string; 5 | }; 6 | 7 | export const LocalDate: React.FC = (props) => { 8 | const date = typeof props.date === "string" ? new Date(props.date) : props.date; 9 | 10 | return {date.toLocaleDateString()}; 11 | }; 12 | -------------------------------------------------------------------------------- /apps/engineering/app/rfcs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { rfcSource } from "@/app/source"; 2 | import { DocsLayout } from "fumadocs-ui/layouts/notebook"; 3 | import type { ReactNode } from "react"; 4 | import { baseOptions } from "../layout.config"; 5 | 6 | export default function Layout({ children }: { children: ReactNode }) { 7 | return ( 8 |
9 | 10 | {children} 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/engineering/content/architecture/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | description: What do we run where and how? 4 | --- 5 | -------------------------------------------------------------------------------- /apps/engineering/content/architecture/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Architecture", 3 | "description": "How does Unkey work", 4 | "icon": "Pencil", 5 | "root": true, 6 | "pages": ["index", "---Services---"] 7 | } 8 | -------------------------------------------------------------------------------- /apps/engineering/content/architecture/services/api/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "API", 3 | "root": false, 4 | "pages": ["config", "ratelimiting"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/engineering/content/architecture/services/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Services", 3 | "icon": "Pencil", 4 | "root": false, 5 | "pages": ["vault", "clickhouse", "clickhouse-proxy"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/engineering/content/company/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | --- 4 | 5 | The company page is a place to document how we work, what we value, and how we communicate. It's a place to share our culture and values with the world. 6 | Potential candidates can get a glimpse of what it's like to work at Unkey and gauge whether they would be a good fit. 7 | -------------------------------------------------------------------------------- /apps/engineering/content/company/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Company", 3 | "root": true, 4 | "description": "Work at Unkey" 5 | } 6 | -------------------------------------------------------------------------------- /apps/engineering/content/contributing/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Contributing", 3 | "description": "oss/acc", 4 | "icon": "GitPullRequest", 5 | "root": true, 6 | "pages": [ 7 | "index", 8 | "sdk-development", 9 | "testing", 10 | "client-structure", 11 | "seeding-db-and-clickhouse", 12 | "pull-request-checks" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/engineering/content/design/components/date-time.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: DateTime 3 | --- 4 | import { Button } from "@unkey/ui" 5 | import { RenderComponentWithSnippet } from "@/app/components/render" 6 | import { Row } from "@/app/components/row" 7 | import { DateTimeExample } from "./date-time.example" 8 | 9 | 10 | ## Example 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/engineering/content/design/components/id.width.tsx: -------------------------------------------------------------------------------- 1 | import { RenderComponentWithSnippet } from "@/app/components/render"; 2 | import { Id } from "@unkey/ui"; 3 | 4 | export const WidthExample: React.FC = () => ( 5 | 6 |
7 | 8 |
9 |
10 | ); 11 | -------------------------------------------------------------------------------- /apps/engineering/content/design/components/tooltip.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tooltip 3 | --- 4 | import { OnHoverExample } from "./tooltip.onHover" 5 | 6 | ## Tooltip 7 | 8 | -------------------------------------------------------------------------------- /apps/engineering/content/design/icons-export-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/engineering/content/design/icons-export-settings.png -------------------------------------------------------------------------------- /apps/engineering/content/design/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Design", 3 | "description": "Our components", 4 | "icon": "Frame", 5 | "root": true 6 | } 7 | -------------------------------------------------------------------------------- /apps/engineering/content/docs/api-design/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "API Design", 3 | "description": "Intuitive and helpful", 4 | "icon": "Code", 5 | "pages": ["overview", "auth", "rpc", "errors"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/engineering/content/docs/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Docs", 3 | "description": "Engineering at Unkey", 4 | "icon": "Code", 5 | "root": true 6 | } 7 | -------------------------------------------------------------------------------- /apps/engineering/content/infrastructure/aws/gw-custom-attributes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/engineering/content/infrastructure/aws/gw-custom-attributes.png -------------------------------------------------------------------------------- /apps/engineering/content/infrastructure/aws/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: AWS 3 | description: How to setup AWS infrastructure 4 | --- 5 | -------------------------------------------------------------------------------- /apps/engineering/content/infrastructure/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | description: Information on setting up infrastructure for Unkey 4 | --- 5 | ## Supported clouds 6 | 7 | AWS 8 | -------------------------------------------------------------------------------- /apps/engineering/content/infrastructure/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Infrastructure", 3 | "description": "Setting up infrastructure for Unkey", 4 | "root": false, 5 | "pages": ["aws"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/engineering/content/rfcs/0002-secret-scanning-flow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unkeyed/unkey/701a463ea6dc404d6bfb918600b643806adc3862/apps/engineering/content/rfcs/0002-secret-scanning-flow.webp -------------------------------------------------------------------------------- /apps/engineering/content/rfcs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: RFCs 3 | authors: [] 4 | date: 2024-11-27 5 | --- 6 | 7 | This is a placeholder. It's needed for fumadocs to not render a 404 page. 8 | The content of this file are not displayed 9 | -------------------------------------------------------------------------------- /apps/engineering/content/rfcs/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "RFCs", 3 | "root": true, 4 | "description": "Requests For Comments" 5 | } 6 | -------------------------------------------------------------------------------- /apps/engineering/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/engineering/next.config.mjs: -------------------------------------------------------------------------------- 1 | import { createMDX } from "fumadocs-mdx/next"; 2 | 3 | const withMDX = createMDX(); 4 | 5 | /** @type {import('next').NextConfig} */ 6 | const config = { 7 | reactStrictMode: true, 8 | transpilePackages: ["@unkey/ui"], 9 | }; 10 | 11 | export default withMDX(config); 12 | -------------------------------------------------------------------------------- /apps/engineering/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "postcss-import": {}, 4 | "tailwindcss/nesting": {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /apps/logdrain/README.md: -------------------------------------------------------------------------------- 1 | # logdrainv2 2 | 3 | To install dependencies: 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run: 10 | 11 | ```bash 12 | bun run index.ts 13 | ``` 14 | 15 | This project was created using `bun init` in bun v1.0.36. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 16 | -------------------------------------------------------------------------------- /apps/logdrain/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "logdrain" 2 | main = "src/worker.ts" 3 | compatibility_date = "2024-01-17" 4 | 5 | node_compat = true 6 | 7 | 8 | routes = [ 9 | { pattern = "logdrain.unkey.dev", custom_domain = true}, 10 | { pattern = "logdrain.unkey.cloud", custom_domain = true} 11 | ] 12 | -------------------------------------------------------------------------------- /apps/planetfall/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/workflows/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /deployment/config/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | 4 | scrape_configs: 5 | - job_name: "prometheus" 6 | 7 | http_sd_configs: 8 | - url: http://apiv2:2112/sd 9 | refresh_interval: "60s" 10 | follow_redirects: true 11 | -------------------------------------------------------------------------------- /go/.gitignore: -------------------------------------------------------------------------------- 1 | unkey 2 | # Added by goreleaser init: 3 | dist/ 4 | -------------------------------------------------------------------------------- /go/apps/api/openapi/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json 2 | package: openapi 3 | output: ./gen.go 4 | generate: 5 | models: true 6 | 7 | output-options: 8 | nullable-type: true 9 | -------------------------------------------------------------------------------- /go/apps/api/openapi/generate.go: -------------------------------------------------------------------------------- 1 | package openapi 2 | 3 | //go:generate go tool oapi-codegen -config=config.yaml ./openapi.json 4 | -------------------------------------------------------------------------------- /go/apps/api/openapi/scalar.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "subdomain": "api.apidocumentation.com", 3 | "references": [ 4 | { 5 | "name": "API Reference", 6 | "path": "./openapi.json" 7 | } 8 | ], 9 | "guides": [] 10 | } 11 | -------------------------------------------------------------------------------- /go/apps/api/openapi/spec.go: -------------------------------------------------------------------------------- 1 | package openapi 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | // Spec is the OpenAPI specification for the service 8 | // It's loaded from our openapi file and embedded into the binary 9 | // 10 | //go:embed openapi.json 11 | var Spec []byte 12 | -------------------------------------------------------------------------------- /go/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | plugins: 3 | - remote: buf.build/protocolbuffers/go 4 | out: gen 5 | opt: paths=source_relative 6 | - remote: buf.build/connectrpc/go:v1.16.2 7 | out: gen 8 | opt: paths=source_relative 9 | 10 | -------------------------------------------------------------------------------- /go/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | - PACKAGE 6 | - WIRE 7 | - WIRE_JSON 8 | lint: 9 | use: 10 | - STANDARD 11 | -------------------------------------------------------------------------------- /go/goreleaser.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest as certs 2 | RUN apk --no-cache add ca-certificates 3 | 4 | # see https://goreleaser.com/errors/docker-build/#do 5 | 6 | FROM scratch 7 | 8 | COPY unkey /unkey 9 | COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs 10 | LABEL org.opencontainers.image.source=https://github.com/unkeyed/unkey/go 11 | LABEL org.opencontainers.image.description="Unkey API" 12 | 13 | ENTRYPOINT ["/unkey"] 14 | -------------------------------------------------------------------------------- /go/internal/services/auditlogs/interface.go: -------------------------------------------------------------------------------- 1 | package auditlogs 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | "github.com/unkeyed/unkey/go/pkg/auditlog" 8 | ) 9 | 10 | type AuditLogService interface { 11 | Insert(ctx context.Context, tx *sql.Tx, logs []auditlog.AuditLog) error 12 | } 13 | -------------------------------------------------------------------------------- /go/internal/services/keys/interface.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/unkeyed/unkey/go/pkg/zen" 7 | ) 8 | 9 | type KeyService interface { 10 | Verify(ctx context.Context, hash string) (VerifyResponse, error) 11 | VerifyRootKey(ctx context.Context, sess *zen.Session) (VerifyResponse, error) 12 | } 13 | 14 | type VerifyResponse struct { 15 | AuthorizedWorkspaceID string 16 | KeyID string 17 | } 18 | -------------------------------------------------------------------------------- /go/internal/services/permissions/interface.go: -------------------------------------------------------------------------------- 1 | package permissions 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/unkeyed/unkey/go/pkg/rbac" 7 | ) 8 | 9 | type PermissionService interface { 10 | Check(ctx context.Context, keyId string, query rbac.PermissionQuery) (rbac.EvaluationResult, error) 11 | } 12 | -------------------------------------------------------------------------------- /go/internal/services/ratelimit/util.go: -------------------------------------------------------------------------------- 1 | package ratelimit 2 | 3 | import "fmt" 4 | 5 | func counterKey(b bucketKey, seq int64) string { 6 | return fmt.Sprintf("%s:%d", b.toString(), seq) 7 | } 8 | -------------------------------------------------------------------------------- /go/pkg/auditlog/doc.go: -------------------------------------------------------------------------------- 1 | // Package auditlog defines types and constants for the audit logging system. 2 | // 3 | // Audit logs provide a secure, immutable record of actions taken within the 4 | // system, tracking who did what and when. This package contains standard 5 | // definitions to ensure consistency across the audit logging system. 6 | package auditlog 7 | -------------------------------------------------------------------------------- /go/pkg/batch/doc.go: -------------------------------------------------------------------------------- 1 | // Package batch provides utilities for efficiently processing items in batches. 2 | // It offers mechanisms to collect items until a batch size threshold is reached 3 | // or a time interval has elapsed, then flushes them as a group. 4 | package batch 5 | -------------------------------------------------------------------------------- /go/pkg/buffer/doc.go: -------------------------------------------------------------------------------- 1 | // Package buffer provides a generic buffered channel implementation with configurable capacity and drop behavior. 2 | // 3 | // The Buffer type encapsulates a channel and offers a simple interface to add items and 4 | // consume them safely. It supports buffering with optional drop-on-full behavior, which 5 | // is particularly useful for high-throughput logging, metrics collection, and other 6 | // scenarios where dropping newer items is preferable to blocking producers. 7 | package buffer 8 | -------------------------------------------------------------------------------- /go/pkg/cache/entry.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type swrEntry[T any] struct { 8 | Value T `json:"value"` 9 | 10 | Hit CacheHit `json:"hit"` 11 | // Before this time the entry is considered fresh and vaid 12 | Fresh time.Time `json:"fresh"` 13 | // Before this time, the entry should be revalidated 14 | // After this time, the entry must be discarded 15 | Stale time.Time `json:"stale"` 16 | } 17 | -------------------------------------------------------------------------------- /go/pkg/cache/middleware.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | type Middleware[K comparable, V any] func(Cache[K, V]) Cache[K, V] 4 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/001_verifications/001_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS verifications; 2 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/001_verifications/003_key_verifications_per_hour_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS verifications.key_verifications_per_hour_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | key_space_id String, 6 | identity_id String, 7 | key_id String, 8 | outcome LowCardinality(String), 9 | count Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, key_space_id, time, identity_id, key_id) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/001_verifications/005_key_verifications_per_day_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS verifications.key_verifications_per_day_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | key_space_id String, 6 | identity_id String, 7 | key_id String, 8 | outcome LowCardinality(String), 9 | count Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, key_space_id, time, identity_id, key_id) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/001_verifications/007_key_verifications_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS verifications.key_verifications_per_month_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | key_space_id String, 6 | identity_id String, 7 | key_id String, 8 | outcome LowCardinality(String), 9 | count Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, key_space_id, time, identity_id, key_id) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/000_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS ratelimits; 2 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/002_ratelimits_per_minute_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS ratelimits.ratelimits_per_minute_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | namespace_id String, 6 | identifier String, 7 | 8 | passed Int64, 9 | total Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, namespace_id, time, identifier) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/004_ratelimits_per_hour_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS ratelimits.ratelimits_per_hour_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | namespace_id String, 6 | identifier String, 7 | 8 | passed Int64, 9 | total Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, namespace_id, time, identifier) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/006_ratelimits_per_day_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS ratelimits.ratelimits_per_day_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | namespace_id String, 6 | identifier String, 7 | 8 | passed Int64, 9 | total Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, namespace_id, time, identifier) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/008_ratelimits_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS ratelimits.ratelimits_per_month_v1 2 | ( 3 | time DateTime, 4 | workspace_id String, 5 | namespace_id String, 6 | identifier String, 7 | 8 | passed Int64, 9 | total Int64 10 | ) 11 | ENGINE = SummingMergeTree() 12 | ORDER BY (workspace_id, namespace_id, time, identifier) 13 | ; 14 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/010_ratelimits_last_used_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS ratelimits.ratelimits_last_used_v1 2 | ( 3 | time Int64, 4 | workspace_id String, 5 | namespace_id String, 6 | identifier String, 7 | 8 | ) 9 | ENGINE = AggregatingMergeTree() 10 | ORDER BY (workspace_id, namespace_id, time, identifier) 11 | ; 12 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/002_ratelimits/011_ratelimits_last_used_mv_v1.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CREATE MATERIALIZED VIEW IF NOT EXISTS ratelimits.ratelimits_last_used_mv_v1 5 | TO ratelimits.ratelimits_last_used_v1 6 | AS 7 | SELECT 8 | workspace_id, 9 | namespace_id, 10 | identifier, 11 | maxSimpleState(time) as time 12 | FROM ratelimits.raw_ratelimits_v1 13 | GROUP BY 14 | workspace_id, 15 | namespace_id, 16 | identifier 17 | ; 18 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/003_metrics/001_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS metrics; 2 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/001_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS billing 2 | ; 3 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/002_billable_verifications_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS billing.billable_verifications_per_month_v1 2 | ( 3 | year Int, 4 | month Int, 5 | workspace_id String, 6 | count Int64 7 | ) 8 | ENGINE = SummingMergeTree() 9 | ORDER BY (workspace_id, year, month) 10 | ; 11 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/003_billable_verifications_per_month_mv_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE MATERIALIZED VIEW IF NOT EXISTS billing.billable_verifications_per_month_mv_v1 2 | TO billing.billable_verifications_per_month_v1 3 | AS 4 | SELECT 5 | workspace_id, 6 | count(*) AS count, 7 | toYear(time) AS year, 8 | toMonth(time) AS month 9 | FROM verifications.key_verifications_per_month_v2 10 | WHERE outcome = 'VALID' 11 | GROUP BY 12 | workspace_id, 13 | year, 14 | month 15 | ; 16 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/004_billable_verifications_v2.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS billing.billable_verifications_per_month_v2 2 | ( 3 | year Int, 4 | month Int, 5 | workspace_id String, 6 | count Int64 7 | ) 8 | ENGINE = SummingMergeTree() 9 | ORDER BY (workspace_id, year, month) 10 | ; 11 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/005_billalble_verifications_v2_mv.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE MATERIALIZED VIEW IF NOT EXISTS billing.billable_verifications_per_month_mv_v2 3 | TO billing.billable_verifications_per_month_v2 4 | AS SELECT 5 | workspace_id, 6 | sum(count) AS count, 7 | toYear(time) AS year, 8 | toMonth(time) AS month 9 | FROM verifications.key_verifications_per_month_v1 10 | WHERE outcome = 'VALID' 11 | GROUP BY 12 | workspace_id, 13 | year, 14 | month 15 | ; 16 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/006_billable_ratelimits_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS billing.billable_ratelimits_per_month_v1 2 | ( 3 | year Int, 4 | month Int, 5 | workspace_id String, 6 | count Int64 7 | ) 8 | ENGINE = SummingMergeTree() 9 | ORDER BY (workspace_id, year, month) 10 | ; 11 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/004_billing/006_billable_ratelimits_v1_mv.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE MATERIALIZED VIEW IF NOT EXISTS billing.billable_ratelimits_per_month_mv_v1 3 | TO billing.billable_ratelimits_per_month_v1 4 | AS SELECT 5 | workspace_id, 6 | sum(passed) AS count, 7 | toYear(time) AS year, 8 | toMonth(time) AS month 9 | FROM ratelimits.ratelimits_per_month_v1 10 | WHERE passed > 0 11 | GROUP BY 12 | workspace_id, 13 | year, 14 | month 15 | ; 16 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/005_business/001_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS business 2 | ; 3 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/005_business/002_active_workspaces_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS business.active_workspaces_per_month_v1 2 | ( 3 | time Date, 4 | workspace_id String 5 | ) 6 | ENGINE = MergeTree() 7 | ORDER BY (time) 8 | ; 9 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/005_business/003_active_workspaces_keys_per_month_mv_v1.sql: -------------------------------------------------------------------------------- 1 | CREATE MATERIALIZED VIEW IF NOT EXISTS business.active_workspaces_keys_per_month_mv_v1 2 | TO business.active_workspaces_per_month_v1 3 | AS 4 | SELECT 5 | workspace_id, toDate(time) as time 6 | FROM verifications.key_verifications_per_month_v2 7 | ; 8 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/databases/005_business/003_active_workspaces_ratelimits_per_month_mv_v1.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE MATERIALIZED VIEW IF NOT EXISTS business.active_workspaces_ratelimits_per_month_mv_v1 3 | TO business.active_workspaces_per_month_v1 4 | AS 5 | SELECT 6 | workspace_id, toDate(time) as time 7 | FROM ratelimits.ratelimits_per_month_v1 8 | ; 9 | -------------------------------------------------------------------------------- /go/pkg/clickhouse/schema/embed.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "embed" 5 | ) 6 | 7 | // Migrations are the raw sql files for the tables. 8 | // 9 | //go:embed databases/**/*.sql 10 | var Migrations embed.FS 11 | -------------------------------------------------------------------------------- /go/pkg/clock/real_clock_test.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestRealClock(t *testing.T) { 11 | clock := New() 12 | before := time.Now() 13 | now := clock.Now() 14 | after := time.Now() 15 | 16 | require.False(t, now.Before(before), "time should not be before test start") 17 | require.False(t, now.After(after), "time should not be after test end") 18 | } 19 | -------------------------------------------------------------------------------- /go/pkg/codes/generate_run.go: -------------------------------------------------------------------------------- 1 | package codes 2 | 3 | //go:generate go run generate.go 4 | -------------------------------------------------------------------------------- /go/pkg/codes/nil.go: -------------------------------------------------------------------------------- 1 | package codes 2 | 3 | // Nil represents a nil or unknown error code. It's used as a default value 4 | // when a specific error code is not available or applicable. This helps 5 | // maintain consistency in error handling even for unclassified errors. 6 | var Nil = Code{ 7 | System: SystemNil, 8 | Category: "unknown", 9 | Specific: "unknown", 10 | } 11 | -------------------------------------------------------------------------------- /go/pkg/db/generate.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | //go:generate sqlc generate 4 | // we copy all of the relevant bits into query.go and don't want the default 5 | // exports that get generated 6 | //go:generate rm delete_me.go 7 | -------------------------------------------------------------------------------- /go/pkg/db/handle_err_duplicate_key.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/go-sql-driver/mysql" 5 | ) 6 | 7 | func IsDuplicateKeyError(err error) bool { 8 | if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == 1062 { 9 | return true 10 | } 11 | 12 | return false 13 | } 14 | -------------------------------------------------------------------------------- /go/pkg/db/handle_err_no_rows.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | ) 7 | 8 | func IsNotFound(err error) bool { 9 | return errors.Is(err, sql.ErrNoRows) 10 | } 11 | -------------------------------------------------------------------------------- /go/pkg/db/queries/api_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindApiById :one 2 | SELECT * FROM apis WHERE id = ?; 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/api_insert.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertApi :exec 2 | INSERT INTO apis ( 3 | id, 4 | name, 5 | workspace_id, 6 | auth_type, 7 | key_auth_id, 8 | created_at_m, 9 | deleted_at_m 10 | ) VALUES ( 11 | ?, 12 | ?, 13 | ?, 14 | ?, 15 | ?, 16 | ?, 17 | NULL 18 | ); 19 | -------------------------------------------------------------------------------- /go/pkg/db/queries/audit_log_find_target_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindAuditLogTargetById :many 2 | SELECT sqlc.embed(audit_log_target), sqlc.embed(audit_log) 3 | FROM audit_log_target 4 | JOIN audit_log ON audit_log.id = audit_log_target.audit_log_id 5 | WHERE audit_log_target.id = sqlc.arg(id); 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: DeleteIdentity :exec 2 | DELETE FROM identities WHERE id = sqlc.arg('id') 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_find_by_external_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindIdentityByExternalID :one 2 | SELECT * FROM identities WHERE workspace_id = sqlc.arg(workspace_id) AND external_id = sqlc.arg(external_id) AND deleted = sqlc.arg(deleted); 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindIdentityByID :one 2 | SELECT * FROM identities WHERE id = sqlc.arg(id) AND deleted = sqlc.arg(deleted); 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_find_ratelimits_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindRatelimitsByIdentityID :many 2 | SELECT * FROM ratelimits WHERE identity_id = sqlc.arg(identity_id) 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_insert.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertIdentity :exec 2 | INSERT INTO `identities` ( 3 | id, 4 | external_id, 5 | workspace_id, 6 | environment, 7 | created_at, 8 | meta 9 | ) VALUES ( 10 | sqlc.arg('id'), 11 | sqlc.arg('external_id'), 12 | sqlc.arg('workspace_id'), 13 | sqlc.arg('environment'), 14 | sqlc.arg('created_at'), 15 | sqlc.arg('meta') 16 | ); 17 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_insert_ratelimit.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertIdentityRatelimit :exec 2 | INSERT INTO `ratelimits` ( 3 | id, 4 | workspace_id, 5 | identity_id, 6 | name, 7 | `limit`, 8 | duration, 9 | created_at 10 | ) VALUES ( 11 | sqlc.arg('id'), 12 | sqlc.arg('workspace_id'), 13 | sqlc.arg('identity_id'), 14 | sqlc.arg('name'), 15 | sqlc.arg('limit'), 16 | sqlc.arg('duration'), 17 | sqlc.arg('created_at') 18 | ); 19 | -------------------------------------------------------------------------------- /go/pkg/db/queries/identity_soft_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: SoftDeleteIdentity :exec 2 | UPDATE identities set deleted = 1 WHERE id = sqlc.arg('id') 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/key_find_by_hash.sql: -------------------------------------------------------------------------------- 1 | 2 | -- name: FindKeyByHash :one 3 | SELECT * FROM `keys` WHERE hash = sqlc.arg(hash); 4 | -------------------------------------------------------------------------------- /go/pkg/db/queries/key_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindKeyByID :one 2 | SELECT 3 | sqlc.embed(k), 4 | sqlc.embed(i) 5 | FROM `keys` k 6 | LEFT JOIN identities i ON k.identity_id = i.id 7 | WHERE k.id = sqlc.arg(id); 8 | -------------------------------------------------------------------------------- /go/pkg/db/queries/keyring_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindKeyringByID :one 2 | SELECT * FROM `key_auth` 3 | WHERE id = sqlc.arg(id); 4 | -------------------------------------------------------------------------------- /go/pkg/db/queries/permission_find_by_workspace_and_name.sql: -------------------------------------------------------------------------------- 1 | -- name: FindPermissionByWorkspaceAndName :one 2 | SELECT * FROM `permissions` 3 | WHERE workspace_id = sqlc.arg(workspace_id) AND name = sqlc.arg(name); 4 | -------------------------------------------------------------------------------- /go/pkg/db/queries/permission_insert.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertPermission :exec 2 | INSERT INTO `permissions` ( 3 | id, 4 | workspace_id, 5 | name, 6 | description, 7 | created_at_m 8 | ) VALUES ( 9 | sqlc.arg(id), 10 | sqlc.arg(workspace_id), 11 | sqlc.arg(name), 12 | sqlc.arg(description), 13 | sqlc.arg(created_at) 14 | ); 15 | -------------------------------------------------------------------------------- /go/pkg/db/queries/permission_insert_key_permission.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertKeyPermission :exec 2 | INSERT INTO `keys_permissions` ( 3 | key_id, 4 | permission_id, 5 | workspace_id, 6 | created_at_m 7 | ) VALUES ( 8 | sqlc.arg(key_id), 9 | sqlc.arg(permission_id), 10 | sqlc.arg(workspace_id), 11 | sqlc.arg(created_at) 12 | ); 13 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_delete_by_identity_id.sql: -------------------------------------------------------------------------------- 1 | -- name: DeleteRatelimitsByIdentityID :exec 2 | DELETE FROM ratelimits WHERE identity_id = ?; 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_delete_many.sql: -------------------------------------------------------------------------------- 1 | -- name: DeleteManyRatelimitsByIDs :exec 2 | DELETE FROM ratelimits WHERE id IN (sqlc.slice(ids)); 3 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_namespace_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: DeleteRatelimitNamespace :execresult 2 | UPDATE `ratelimit_namespaces` 3 | SET deleted_at_m = sqlc.arg(now) 4 | WHERE id = sqlc.arg(id); 5 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_namespace_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindRatelimitNamespaceByID :one 2 | SELECT * FROM `ratelimit_namespaces` 3 | WHERE id = sqlc.arg(id); 4 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_namespace_find_by_name.sql: -------------------------------------------------------------------------------- 1 | -- name: FindRatelimitNamespaceByName :one 2 | SELECT * FROM `ratelimit_namespaces` 3 | WHERE name = sqlc.arg(name) 4 | AND workspace_id = sqlc.arg(workspace_id); 5 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_namespace_insert.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertRatelimitNamespace :exec 2 | INSERT INTO 3 | `ratelimit_namespaces` ( 4 | id, 5 | workspace_id, 6 | name, 7 | created_at_m, 8 | updated_at_m, 9 | deleted_at_m 10 | ) 11 | VALUES 12 | ( 13 | sqlc.arg("id"), 14 | sqlc.arg("workspace_id"), 15 | sqlc.arg("name"), 16 | sqlc.arg(created_at), 17 | NULL, 18 | NULL 19 | ) 20 | ; 21 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_namespace_soft_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: SoftDeleteRatelimitNamespace :exec 2 | UPDATE `ratelimit_namespaces` 3 | SET 4 | deleted_at_m = sqlc.arg(now) 5 | WHERE id = sqlc.arg(id); 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_override_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindRatelimitOverrideById :one 2 | SELECT * FROM ratelimit_overrides 3 | WHERE 4 | workspace_id = sqlc.arg(workspace_id) 5 | AND id = sqlc.arg(override_id); 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_override_find_by_identifier.sql: -------------------------------------------------------------------------------- 1 | -- name: FindRatelimitOverridesByIdentifier :one 2 | SELECT * FROM ratelimit_overrides 3 | WHERE 4 | workspace_id = sqlc.arg(workspace_id) 5 | AND namespace_id = sqlc.arg(namespace_id) 6 | AND identifier = sqlc.arg(identifier); 7 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_override_find_matches.sql: -------------------------------------------------------------------------------- 1 | -- name: FindRatelimitOverrideMatches :many 2 | SELECT * FROM ratelimit_overrides 3 | WHERE 4 | workspace_id = sqlc.arg(workspace_id) 5 | AND namespace_id = sqlc.arg(namespace_id) 6 | AND sqlc.arg(identifier) LIKE 7 | REPLACE( 8 | REPLACE(identifier, '*', '%'), -- Replace * with % wildcard 9 | '_', '\\_' -- Escape underscore literals 10 | ); 11 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_override_list_by_namespace_id.sql: -------------------------------------------------------------------------------- 1 | -- name: ListRatelimitOverrides :many 2 | SELECT * FROM ratelimit_overrides 3 | WHERE 4 | workspace_id = sqlc.arg(workspace_id) 5 | AND namespace_id = sqlc.arg(namespace_id); 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_override_soft_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: SoftDeleteRatelimitOverride :exec 2 | UPDATE `ratelimit_overrides` 3 | SET 4 | deleted_at_m = sqlc.arg(now) 5 | WHERE id = sqlc.arg(id); 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/ratelimit_override_update.sql: -------------------------------------------------------------------------------- 1 | -- name: UpdateRatelimitOverride :execresult 2 | UPDATE `ratelimit_overrides` 3 | SET 4 | `limit` = sqlc.arg(windowLimit), 5 | duration = sqlc.arg(duration), 6 | async = sqlc.arg(async), 7 | updated_at_m= sqlc.arg(now) 8 | WHERE id = sqlc.arg(id); 9 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspace_find_by_id.sql: -------------------------------------------------------------------------------- 1 | -- name: FindWorkspaceByID :one 2 | SELECT * FROM `workspaces` 3 | WHERE id = sqlc.arg(id); 4 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspace_hard_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: HardDeleteWorkspace :execresult 2 | DELETE FROM `workspaces` 3 | WHERE id = sqlc.arg(id) 4 | AND delete_protection = false; 5 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspace_insert.sql: -------------------------------------------------------------------------------- 1 | -- name: InsertWorkspace :exec 2 | INSERT INTO `workspaces` ( 3 | id, 4 | org_id, 5 | name, 6 | created_at_m, 7 | tier, 8 | beta_features, 9 | features, 10 | enabled, 11 | delete_protection 12 | ) 13 | VALUES ( 14 | sqlc.arg(id), 15 | sqlc.arg(org_id), 16 | sqlc.arg(name), 17 | sqlc.arg(created_at), 18 | 'Free', 19 | '{}', 20 | '{}', 21 | true, 22 | true 23 | ); 24 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspace_soft_delete.sql: -------------------------------------------------------------------------------- 1 | -- name: SoftDeleteWorkspace :execresult 2 | UPDATE `workspaces` 3 | SET deleted_at_m = sqlc.arg(now) 4 | WHERE id = sqlc.arg(id) 5 | AND delete_protection = false; 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspace_update_enabled.sql: -------------------------------------------------------------------------------- 1 | -- name: UpdateWorkspaceEnabled :execresult 2 | UPDATE `workspaces` 3 | SET enabled = sqlc.arg(enabled) 4 | WHERE id = sqlc.arg(id) 5 | ; 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspace_update_plan.sql: -------------------------------------------------------------------------------- 1 | -- name: UpdateWorkspacePlan :execresult 2 | UPDATE `workspaces` 3 | SET plan = sqlc.arg(plan) 4 | WHERE id = sqlc.arg(id) 5 | ; 6 | -------------------------------------------------------------------------------- /go/pkg/db/queries/workspaces_list.sql: -------------------------------------------------------------------------------- 1 | -- name: ListWorkspaces :many 2 | SELECT 3 | sqlc.embed(w), 4 | sqlc.embed(q) 5 | FROM `workspaces` w 6 | LEFT JOIN quota q ON w.id = q.workspace_id 7 | WHERE w.id > sqlc.arg('cursor') 8 | ORDER BY w.id ASC 9 | LIMIT 100; 10 | -------------------------------------------------------------------------------- /go/pkg/db/schema_embed.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | // Schema is the SQL schema embedded into the binary. 8 | // This allows the application to carry its own schema definition, 9 | // which can be useful for initialization, validation, or migrations. 10 | // 11 | //go:embed schema.sql 12 | var Schema []byte 13 | -------------------------------------------------------------------------------- /go/pkg/hash/sha256_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "crypto/rand" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestSha256(t *testing.T) { 11 | for i := 0; i < 100; i++ { 12 | b := []byte{32} 13 | _, err := rand.Read(b) 14 | require.NoError(t, err) 15 | s := string(b) 16 | h := Sha256(s) 17 | require.Greater(t, len(h), 10) 18 | 19 | // check if it's consistent 20 | require.Equal(t, h, Sha256(s)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go/pkg/otel/logging/source.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "fmt" 5 | "log/slog" 6 | "runtime" 7 | ) 8 | 9 | func withSource(args []any) []any { 10 | _, file, line, ok := runtime.Caller(2) 11 | 12 | if !ok { 13 | return args 14 | } 15 | 16 | return append(args, slog.Attr{ 17 | Key: "source", 18 | Value: slog.AnyValue(fmt.Sprintf("%s:%d", file, line))}) 19 | } 20 | -------------------------------------------------------------------------------- /go/pkg/repeat/every.go: -------------------------------------------------------------------------------- 1 | package repeat 2 | 3 | import "time" 4 | 5 | // Every runs the given function in a go routine every d duration until the returned function is called. 6 | func Every(d time.Duration, fn func()) func() { 7 | t := time.NewTicker(d) 8 | go func() { 9 | for range t.C { 10 | fn() 11 | } 12 | }() 13 | return func() { 14 | t.Stop() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /go/pkg/tools/dependencies.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package main 5 | 6 | import ( 7 | // oapi-codegen generates request and response body structs from openapi.json 8 | _ "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" 9 | 10 | // sqlc generates go code from raw sql queries 11 | _ "github.com/sqlc-dev/sqlc/cmd/sqlc" 12 | ) 13 | -------------------------------------------------------------------------------- /go/pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Version gets populated during the build step via ldflags 4 | var Version string = "development" 5 | -------------------------------------------------------------------------------- /go/pkg/zen/middleware.go: -------------------------------------------------------------------------------- 1 | package zen 2 | 3 | // Middleware transforms one handler into another, typically by adding 4 | // behavior before and/or after the original handler executes. 5 | // 6 | // Middleware is used to implement cross-cutting concerns like logging, 7 | // authentication, error handling, and metrics collection. 8 | type Middleware func(handler HandleFunc) HandleFunc 9 | -------------------------------------------------------------------------------- /go/pkg/zen/validation/validator_test.go: -------------------------------------------------------------------------------- 1 | package validation 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestSchemaIsValid(t *testing.T) { 10 | v, err := New() 11 | require.NoError(t, err) 12 | 13 | valid, errors := v.validator.ValidateDocument() 14 | require.True(t, valid) 15 | require.Len(t, errors, 0) 16 | } 17 | -------------------------------------------------------------------------------- /internal/billing/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./quota"; 2 | export * from "./tiers"; 3 | export * from "./subscriptions"; 4 | -------------------------------------------------------------------------------- /internal/billing/src/quota.ts: -------------------------------------------------------------------------------- 1 | export type Quotas = { 2 | maxActiveKeys: number; 3 | maxVerifications: number; 4 | maxRatelimits: number; 5 | }; 6 | 7 | export const QUOTA = { 8 | free: { 9 | maxActiveKeys: 1000, 10 | maxVerifications: 150_000, 11 | maxRatelimits: 100_000, 12 | }, 13 | } satisfies Record; 14 | -------------------------------------------------------------------------------- /internal/billing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/billing/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | alias: { 7 | "@/": new URL("./src/", import.meta.url).pathname, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/checkly/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /internal/checkly/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "checkly-infra", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "commonjs", 7 | "author": "", 8 | "license": "ISC", 9 | "devDependencies": { 10 | "checkly": "latest", 11 | "ts-node": "10.9.1", 12 | "typescript": "5.5.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/checkly/src/__checks__/api/ratelimit/group.ts: -------------------------------------------------------------------------------- 1 | import { CheckGroup } from "checkly/constructs"; 2 | import { ALL_LOCATIONS } from "../../../locations"; 3 | 4 | export const ratelimitsV1 = new CheckGroup("/v1/ratelimits", { 5 | name: "ratelimits", 6 | locations: ALL_LOCATIONS, 7 | }); 8 | -------------------------------------------------------------------------------- /internal/checkly/src/alert-channels.ts: -------------------------------------------------------------------------------- 1 | import { SlackAlertChannel, WebhookAlertChannel } from "checkly/constructs"; 2 | 3 | // configured in the dashboard 4 | // https://app.checklyhq.com/alerts/settings/channels/edit/incidentio/218874 5 | export const incidentIo = WebhookAlertChannel.fromId("218874"); 6 | export const slack = SlackAlertChannel.fromId("240275"); 7 | -------------------------------------------------------------------------------- /internal/clickhouse/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang 2 | 3 | 4 | RUN go install github.com/pressly/goose/v3/cmd/goose@latest 5 | 6 | 7 | COPY ./schema ./schema 8 | 9 | ENV GOOSE_DRIVER=clickhouse 10 | ENV GOOSE_DBSTRING="tcp://default:password@clickhouse:9000" 11 | ENV GOOSE_MIGRATION_DIR=./schema 12 | CMD ["goose", "up"] 13 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/001_create_databases.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | 3 | CREATE DATABASE verifications; 4 | CREATE DATABASE telemetry; 5 | CREATE DATABASE metrics; 6 | CREATE DATABASE ratelimits; 7 | CREATE DATABASE business; 8 | CREATE DATABASE billing; 9 | 10 | 11 | -- +goose down 12 | DROP DATABASE verifications; 13 | DROP DATABASE telemetry; 14 | DROP DATABASE metrics; 15 | DROP DATABASE ratelimits; 16 | DROP DATABASE business; 17 | DROP DATABASE billing; 18 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/010_create_ratelimits_raw_ratelimits_table.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE ratelimits.raw_ratelimits_v1( 3 | request_id String, 4 | -- unix milli 5 | time Int64, 6 | workspace_id String, 7 | namespace_id String, 8 | identifier String, 9 | passed Bool 10 | 11 | ) 12 | ENGINE = MergeTree() 13 | ORDER BY (workspace_id, namespace_id, time, identifier) 14 | ; 15 | 16 | 17 | 18 | -- +goose down 19 | DROP TABLE ratelimits.raw_ratelimits_v1; 20 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/012_create_billing_billable_verifications_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE billing.billable_verifications_per_month_v1 3 | ( 4 | year Int, 5 | month Int, 6 | workspace_id String, 7 | count Int64 8 | ) 9 | ENGINE = SummingMergeTree() 10 | ORDER BY (workspace_id, year, month) 11 | ; 12 | 13 | 14 | -- +goose down 15 | DROP TABLE billing.billable_verifications_per_month_v1; 16 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/014_create_ratelimits_ratelimits_per_minute_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE ratelimits.ratelimits_per_minute_v1 3 | ( 4 | time DateTime, 5 | workspace_id String, 6 | namespace_id String, 7 | identifier String, 8 | 9 | passed Int64, 10 | total Int64 11 | ) 12 | ENGINE = SummingMergeTree() 13 | ORDER BY (workspace_id, namespace_id, time, identifier) 14 | ; 15 | 16 | 17 | 18 | -- +goose down 19 | DROP TABLE ratelimits.ratelimits_per_minute_v1; 20 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/015_create_ratelimits_ratelimits_per_hour_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE ratelimits.ratelimits_per_hour_v1 3 | ( 4 | time DateTime, 5 | workspace_id String, 6 | namespace_id String, 7 | identifier String, 8 | 9 | passed Int64, 10 | total Int64 11 | ) 12 | ENGINE = SummingMergeTree() 13 | ORDER BY (workspace_id, namespace_id, time, identifier) 14 | ; 15 | 16 | 17 | -- +goose down 18 | DROP TABLE ratelimits.ratelimits_per_hour_v1; 19 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/016_create_ratelimits_ratelimits_per_day_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE ratelimits.ratelimits_per_day_v1 3 | ( 4 | time DateTime, 5 | workspace_id String, 6 | namespace_id String, 7 | identifier String, 8 | 9 | passed Int64, 10 | total Int64 11 | ) 12 | ENGINE = SummingMergeTree() 13 | ORDER BY (workspace_id, namespace_id, time, identifier) 14 | ; 15 | 16 | 17 | 18 | -- +goose down 19 | DROP TABLE ratelimits.ratelimits_per_day_v1; 20 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/017_create_ratelimits_ratelimits_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE ratelimits.ratelimits_per_month_v1 3 | ( 4 | time DateTime, 5 | workspace_id String, 6 | namespace_id String, 7 | identifier String, 8 | 9 | passed Int64, 10 | total Int64 11 | ) 12 | ENGINE = SummingMergeTree() 13 | ORDER BY (workspace_id, namespace_id, time, identifier) 14 | ; 15 | 16 | 17 | 18 | -- +goose down 19 | DROP TABLE ratelimits.ratelimits_per_month_v1; 20 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/022_create_business_active_workspaces_per_month_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | CREATE TABLE business.active_workspaces_per_month_v1 3 | ( 4 | time Date, 5 | workspace_id String 6 | ) 7 | ENGINE = MergeTree() 8 | ORDER BY (time) 9 | ; 10 | 11 | 12 | -- +goose down 13 | DROP TABLE business.active_workspaces_per_month_v1; 14 | -------------------------------------------------------------------------------- /internal/clickhouse/schema/027_add_tags_to_verifications.raw_key_verifications_v1.sql: -------------------------------------------------------------------------------- 1 | -- +goose up 2 | ALTER TABLE verifications.raw_key_verifications_v1 3 | ADD COLUMN IF NOT EXISTS tags Array(String) DEFAULT []; 4 | 5 | 6 | -- +goose down 7 | 8 | 9 | 10 | ALTER TABLE verifications.raw_key_verifications_v1 11 | DROP COLUMN IF EXISTS tags; 12 | -------------------------------------------------------------------------------- /internal/clickhouse/src/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | export * from "./noop"; 3 | export * from "./interface"; 4 | -------------------------------------------------------------------------------- /internal/clickhouse/src/telemetry.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import type { Inserter } from "./client/interface"; 3 | 4 | export function insertSDKTelemetry(ch: Inserter) { 5 | return ch.insert({ 6 | table: "telemetry.raw_sdks_v1", 7 | schema: z.object({ 8 | request_id: z.string(), 9 | time: z.number().int(), 10 | runtime: z.string(), 11 | platform: z.string(), 12 | versions: z.array(z.string()), 13 | }), 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /internal/clickhouse/src/util.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | // clickhouse DateTime returns a string, which we need to parse 3 | export const dateTimeToUnix = z.string().transform((t) => new Date(t).getTime()); 4 | -------------------------------------------------------------------------------- /internal/clickhouse/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | export default defineConfig({ 3 | test: { 4 | exclude: [], 5 | bail: 1, 6 | pool: "threads", 7 | poolOptions: { 8 | threads: { 9 | singleThread: true, 10 | }, 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /internal/db/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "drizzle-kit"; 2 | 3 | export default defineConfig({ 4 | verbose: true, 5 | schema: "./src/schema/index.ts", 6 | dialect: "mysql", 7 | dbCredentials: { 8 | url: process.env.DRIZZLE_DATABASE_URL!, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/db/drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "mysql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "5", 8 | "when": 1745396754471, 9 | "tag": "0000_fat_the_hand", 10 | "breakpoints": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /internal/db/src/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./apis"; 2 | export * from "./rbac"; 3 | export * from "./keyAuth"; 4 | export * from "./keys"; 5 | export * from "./vercel_integration"; 6 | export * from "./ratelimit"; 7 | export * from "./workspaces"; 8 | export * from "./key_migrations"; 9 | export * from "./identity"; 10 | export * from "./quota"; 11 | export * from "./audit_logs"; 12 | -------------------------------------------------------------------------------- /internal/db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/encoding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/encoding", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "author": "Andreas Thomas", 8 | "devDependencies": { 9 | "typescript": "^5.5.3", 10 | "vitest": "^1.6.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/encoding/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./base64"; 2 | -------------------------------------------------------------------------------- /internal/encoding/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | alias: { 7 | "@/": new URL("./src/", import.meta.url).pathname, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/encryption/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/encryption", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "author": "Andreas Thomas", 8 | "devDependencies": { 9 | "drizzle-kit": "^0.22.7", 10 | "typescript": "^5.5.3", 11 | "vitest": "^1.6.1" 12 | }, 13 | "dependencies": { 14 | "@unkey/encoding": "workspace:^", 15 | "@unkey/error": "workspace:^" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/encryption/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./aes-gcm"; 2 | export * from "./key"; 3 | -------------------------------------------------------------------------------- /internal/encryption/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | alias: { 7 | "@/": new URL("./src/", import.meta.url).pathname, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/events/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/events", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "keywords": [], 8 | "author": "Andreas Thomas", 9 | "license": "AGPL-3.0", 10 | "devDependencies": { 11 | "@types/node": "^20.14.9", 12 | "@unkey/tsconfig": "workspace:*", 13 | "typescript": "^5.5.3" 14 | }, 15 | "dependencies": { 16 | "zod": "^3.23.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/events/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/hash/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/hash", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "author": "Andreas Thomas", 8 | "scripts": { 9 | "test": "vitest run" 10 | }, 11 | "devDependencies": { 12 | "typescript": "^5.5.3", 13 | "vitest": "^1.6.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/hash/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./sha256"; 2 | -------------------------------------------------------------------------------- /internal/hash/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | alias: { 7 | "@/": new URL("./src/", import.meta.url).pathname, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/icons/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © Nucleo 2 | 3 | Version 1.3, January 3, 2024 4 | 5 | Nucleo Icons 6 | 7 | https://nucleoapp.com/ 8 | 9 | - Redistribution of icons is prohibited. 10 | - Icons are restricted for use only within the product they are bundled with. 11 | 12 | For more details: 13 | 14 | https://nucleoapp.com/license 15 | -------------------------------------------------------------------------------- /internal/icons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/icons", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "keywords": [], 8 | "author": "Andreas Thomas", 9 | "devDependencies": { 10 | "@types/react": "^18.3.11", 11 | "@unkey/tsconfig": "workspace:^" 12 | }, 13 | "dependencies": { 14 | "react": "^18.2.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /internal/icons/src/template.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react"; 2 | import type { IconProps } from "./props"; 3 | 4 | export const Icon: React.FC = (props) => { 5 | return ; 6 | }; 7 | -------------------------------------------------------------------------------- /internal/id/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./generate"; 2 | -------------------------------------------------------------------------------- /internal/id/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/id/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | alias: { 7 | "@/": new URL("./src/", import.meta.url).pathname, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/keys/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/keys", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "author": "Andreas Thomas", 8 | "scripts": { 9 | "test": "vitest run" 10 | }, 11 | "devDependencies": { 12 | "typescript": "^5.5.3" 13 | }, 14 | "dependencies": { 15 | "@unkey/hash": "workspace:^", 16 | "base-x": "^4.0.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/keys/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./v1"; 2 | export * from "./util"; 3 | -------------------------------------------------------------------------------- /internal/keys/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | alias: { 7 | "@/": new URL("./src/", import.meta.url).pathname, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /internal/logs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/logs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "keywords": [], 8 | "author": "Andreas Thomas", 9 | "license": "AGPL-3.0", 10 | "devDependencies": { 11 | "@types/node": "^20.14.9", 12 | "@unkey/tsconfig": "workspace:*", 13 | "typescript": "^5.5.3" 14 | }, 15 | "dependencies": { 16 | "@unkey/metrics": "workspace:^", 17 | "zod": "^3.23.5" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /internal/logs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/metrics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/metrics", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "keywords": [], 8 | "author": "Andreas Thomas", 9 | "license": "AGPL-3.0", 10 | "devDependencies": { 11 | "@types/node": "^20.14.9", 12 | "@unkey/tsconfig": "workspace:*", 13 | "typescript": "^5.5.3" 14 | }, 15 | "dependencies": { 16 | "zod": "^3.23.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/metrics/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/resend/src/components/signature.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from "@react-email/text"; 2 | // biome-ignore lint/style/useImportType: not just the type 3 | import React from "react"; 4 | 5 | interface SignatureProps { 6 | signedBy: string; 7 | } 8 | 9 | export const Signature: React.FC = ({ signedBy }) => ( 10 | 11 | Cheers, 12 |
13 | {signedBy} 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /internal/resend/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | -------------------------------------------------------------------------------- /internal/resend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist", 7 | "jsx": "react" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /internal/resend/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["default"], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /internal/schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/schema", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "Andreas Thomas", 7 | "license": "AGPL-3.0", 8 | "devDependencies": { 9 | "@types/node": "^20.14.9", 10 | "@unkey/tsconfig": "workspace:*", 11 | "typescript": "^5.5.3" 12 | }, 13 | "dependencies": { 14 | "zod": "^3.23.5" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /internal/schema/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/tsconfig/README.md: -------------------------------------------------------------------------------- 1 | # `tsconfig` 2 | 3 | These are base shared `tsconfig.json`s from which all other `tsconfig.json`'s 4 | inherit from. 5 | -------------------------------------------------------------------------------- /internal/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "files": ["base.json", "nextjs.json", "react-library.json"] 6 | } 7 | -------------------------------------------------------------------------------- /internal/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx", 7 | "lib": ["ES2015"], 8 | "module": "ESNext", 9 | "target": "es6" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /internal/ui/css.ts: -------------------------------------------------------------------------------- 1 | import "./colors.css"; 2 | 3 | /** 4 | * This file exists only to be easy to import in other packages in our monorepo. 5 | * Importing css files is a bit more involved, so we import a ts file, that imports a 6 | * local css file. 7 | * 8 | * You can just do `import "@unkey/ui/css"` and it will load the css file. 9 | */ 10 | -------------------------------------------------------------------------------- /internal/ui/src/components/form/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./form-input"; 2 | export * from "./form-textarea"; 3 | export * from "./form-checkbox"; 4 | -------------------------------------------------------------------------------- /internal/ui/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /internal/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist", 7 | "jsx": "react", 8 | "baseUrl": ".", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /internal/validation/README.md: -------------------------------------------------------------------------------- 1 | `@unkey/validation` contains various shared schema or validation utils. 2 | 3 | For example user-chosen identifiers. 4 | -------------------------------------------------------------------------------- /internal/validation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/validation", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "keywords": [], 8 | "author": "Andreas Thomas", 9 | "license": "AGPL-3.0", 10 | "devDependencies": { 11 | "@types/node": "^20.14.9", 12 | "@unkey/tsconfig": "workspace:*", 13 | "typescript": "^5.5.3" 14 | }, 15 | "dependencies": { 16 | "zod": "^3.23.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/validation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /internal/vault/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/vault", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "keywords": [], 8 | "author": "", 9 | "devDependencies": { 10 | "@types/node": "^20.14.9", 11 | "@unkey/tsconfig": "workspace:*", 12 | "typescript": "^5.5.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/vercel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/vercel", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "keywords": [], 8 | "author": "Andreas Thomas", 9 | "license": "AGPL-3.0", 10 | "devDependencies": { 11 | "@types/node": "^20.14.9", 12 | "typescript": "^5.5.3" 13 | }, 14 | "dependencies": { 15 | "@unkey/error": "workspace:^", 16 | "zod": "^3.23.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/vercel/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | -------------------------------------------------------------------------------- /internal/worker-logging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/worker-logging", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "author": "Andreas Thomas", 8 | "devDependencies": { 9 | "typescript": "^5.5.3" 10 | }, 11 | "dependencies": { 12 | "@unkey/logs": "workspace:^" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/worker-logging/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./interface"; 2 | export * from "./console"; 3 | -------------------------------------------------------------------------------- /internal/worker-logging/src/interface.ts: -------------------------------------------------------------------------------- 1 | export type Fields = { 2 | [field: string]: unknown; 3 | }; 4 | 5 | export interface Logger { 6 | debug(message: string, fields?: Fields): void; 7 | info(message: string, fields?: Fields): void; 8 | warn(message: string, fields?: Fields): void; 9 | error(message: string, fields?: Fields): void; 10 | } 11 | -------------------------------------------------------------------------------- /packages/api/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./client"; 2 | export * from "./verify"; 3 | export * from "./errors"; 4 | export { and, or, type Flatten } from "@unkey/rbac"; 5 | -------------------------------------------------------------------------------- /packages/api/tsup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | format: ["cjs", "esm"], 6 | splitting: false, 7 | sourcemap: true, 8 | clean: true, 9 | bundle: true, 10 | dts: true, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/cache/src/context.ts: -------------------------------------------------------------------------------- 1 | export interface Context { 2 | waitUntil: (p: Promise) => void; 3 | } 4 | 5 | export class DefaultStatefulContext implements Context { 6 | public waitUntil(_p: Promise) { 7 | // do nothing, the promise will resolve on its own 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/cache/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./context"; 2 | export * from "./errors"; 3 | export * from "./cache"; 4 | export * from "./interface"; 5 | export * from "./metrics"; 6 | export * from "./tiered"; 7 | export * from "./metrics"; 8 | export * from "./namespace"; 9 | -------------------------------------------------------------------------------- /packages/cache/src/middleware/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./interface"; 2 | export * from "./metrics"; 3 | export * from "./encryption"; 4 | -------------------------------------------------------------------------------- /packages/cache/src/middleware/interface.ts: -------------------------------------------------------------------------------- 1 | import type { Store } from "../stores"; 2 | 3 | export type StoreMiddleware = { 4 | wrap: (store: Store) => Store; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/cache/src/stores/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./cloudflare"; 2 | export * from "./interface"; 3 | export * from "./memory"; 4 | export * from "./upstash-redis"; 5 | export * from "./libsql"; 6 | -------------------------------------------------------------------------------- /packages/cache/tsup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: { 5 | index: "src/index.ts", 6 | middleware: "src/middleware/index.ts", 7 | stores: "src/stores/index.ts", 8 | }, 9 | format: ["cjs", "esm"], 10 | treeshake: true, 11 | splitting: false, 12 | sourcemap: true, 13 | clean: true, 14 | bundle: true, 15 | dts: true, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/cache/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ["./src/**/*.test.ts"], 6 | reporters: ["html", "verbose"], 7 | outputFile: "./.vitest/html", 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/error/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @unkey/error 2 | 3 | ## 0.2.0 4 | 5 | ### Minor Changes 6 | 7 | - 53a1df1: Update licenses in package.json 8 | 9 | ## 0.1.0 10 | 11 | ### Minor Changes 12 | 13 | - 9dab761: Updating licenses 14 | 15 | ## 0.0.2 16 | 17 | ### Patch Changes 18 | 19 | - fdd625f: more customization 20 | -------------------------------------------------------------------------------- /packages/error/src/errors/env-error.ts: -------------------------------------------------------------------------------- 1 | import { BaseError } from "./base"; 2 | 3 | /** 4 | * Env Errors indicate an environment variable was not configured properly 5 | */ 6 | export class EnvError extends BaseError<{ 7 | name: string; 8 | }> { 9 | public readonly retry = false; 10 | public readonly name = EnvError.name; 11 | } 12 | -------------------------------------------------------------------------------- /packages/error/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./error-handling"; 2 | export * from "./errors/base"; 3 | export * from "./errors/fetch-error"; 4 | export * from "./errors/schema-error"; 5 | export * from "./errors/env-error"; 6 | -------------------------------------------------------------------------------- /packages/error/tsup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | format: ["cjs", "esm"], 6 | treeshake: true, 7 | splitting: false, 8 | sourcemap: true, 9 | clean: true, 10 | bundle: true, 11 | dts: true, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/hono/tsup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | format: ["cjs", "esm"], 6 | splitting: false, 7 | sourcemap: true, 8 | clean: true, 9 | bundle: true, 10 | dts: true, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/hono/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | reporters: ["html", "verbose"], 6 | outputFile: "./.vitest/html", 7 | }, 8 | resolve: { 9 | mainFields: ["module"], 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/nextjs/tsup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | format: ["cjs", "esm"], 6 | external: ["next"], 7 | splitting: false, 8 | sourcemap: true, 9 | clean: true, 10 | bundle: true, 11 | dts: true, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/rbac/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./permissions"; 2 | export * from "./queries"; 3 | export * from "./rbac"; 4 | export type { Flatten } from "./types"; 5 | -------------------------------------------------------------------------------- /packages/rbac/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/rbac/tsup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | format: ["cjs", "esm"], 6 | splitting: false, 7 | sourcemap: true, 8 | clean: true, 9 | bundle: true, 10 | dts: true, 11 | }); 12 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" 3 | - "internal/*" 4 | - "apps/*" 5 | - "tools/*" -------------------------------------------------------------------------------- /tools/artillery/.gitignore: -------------------------------------------------------------------------------- 1 | .keys.csv -------------------------------------------------------------------------------- /tools/artillery/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | RUN npm i -g artillery 6 | 7 | COPY . . 8 | 9 | ENV UNKEY_ROOT_KEY empty 10 | -------------------------------------------------------------------------------- /tools/artillery/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Artillery 3 | 4 | Run distributed load tests with artillery. 5 | 6 | Requirements: 7 | - [fly cli](https://fly.io/docs/flyctl/) 8 | - [Docker](https://www.docker.com/) 9 | - [bun](https://bun.sh/) 10 | 11 | 12 | ```bash 13 | FLY_API_KEY= 14 | ARTILLERY_CLOUD_API_KEY= 15 | UNKEY_KEY= 16 | bun run main.ts 17 | `````` -------------------------------------------------------------------------------- /tools/artillery/prompts.csv: -------------------------------------------------------------------------------- 1 | Hey how are you? 2 | Hello there how are you doing? 3 | What day is it? 4 | What's todays day? 5 | Who are you? 6 | Write me a poem about ants 7 | Can you write a poem about ants? -------------------------------------------------------------------------------- /tools/k6/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run: 5 | docker run --rm -i grafana/k6 run - < load.js 6 | -------------------------------------------------------------------------------- /tools/k6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@unkey/k6", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "Andreas Thomas", 8 | "license": "AGPL-3.0", 9 | "devDependencies": { 10 | "k6": "^0.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tools/local/src/env.ts: -------------------------------------------------------------------------------- 1 | export function marshalEnv(o: Record>): string { 2 | return Object.entries(o) 3 | .map(([comment, kvs]) => { 4 | const lines = [`# ${comment}`]; 5 | for (const [k, v] of Object.entries(kvs)) { 6 | lines.push(`${k}="${v}"`); 7 | } 8 | return lines.join("\n"); 9 | }) 10 | .join("\n\n"); 11 | } 12 | -------------------------------------------------------------------------------- /tools/local/src/seed.ts: -------------------------------------------------------------------------------- 1 | import { prepareDatabase } from "./db"; 2 | 3 | async function main() { 4 | await prepareDatabase(process.env.DRIZZLE_DATABASE_URL); 5 | process.exit(0); 6 | } 7 | 8 | main(); 9 | -------------------------------------------------------------------------------- /tools/migrate/.env.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Main db 4 | 5 | DATABASE_HOST= 6 | DATABASE_USERNAME= 7 | DATABASE_PASSWORD= 8 | -------------------------------------------------------------------------------- /tools/migrate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@unkey/tsconfig/base.json", 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "outDir": "dist", 7 | "module": "esnext", 8 | "target": "ESNext" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vitest.workspace.json: -------------------------------------------------------------------------------- 1 | ["apps/*", "packages/*", "internal/*"] 2 | --------------------------------------------------------------------------------