├── .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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------