🛡️Gatekeeper/ SDKs

📦 @orkait/sdk#

Typed, zero-dependency TypeScript client for the Gatekeeper API.

Runs anywhere fetch exists - Node 18+, browsers, Cloudflare Workers.

🚀 Usage#

Construct a GatekeeperCore once, then plug in only the services you need - each is a standalone class over the same core, so unused services tree-shake away.

import {
    GatekeeperCore,
    AuthService,
    KeysService,
    BillingService,
    isMfaChallenge,
} from '@orkait/sdk';
 
const core = new GatekeeperCore({ baseUrl: 'https://gatekeeper-api.example.workers.dev' });
 
// Auth
const auth = new AuthService(core);
const { accessToken } = await auth.signup({ email: 'a@b.com', password: 'hunter2pass' });
core.setToken(accessToken);
const me = await auth.me();
 
// MFA - if login returns a challenge instead of tokens, complete it
const login = await auth.login({ email: 'a@b.com', password: 'hunter2pass' });
if (isMfaChallenge(login)) {
    // const mfa = new MfaService(core);
    // await mfa.verifyChallenge(login.challengeToken, '123456');
}
 
// API keys
const keys = new KeysService(core);
const { plainTextKey } = await keys.create({ tenantId: 't1', scopes: ['read'] });
 
// Billing
const billing = new BillingService(core);
const plans = await billing.plans();

🧩 Design#

PropertyDetail
Compositionnew XService(core) - each service depends only on core.request() / core.requestText()
Coreholds baseUrl, bearer token (mutable via core.setToken()), and the fetch impl
Custom transportpass options.fetch (a Workers service binding, a test SELF.fetch, a mock)
Dependenciesnone (uses global fetch)
Tree-shakingroot named exports + sideEffects: false - unused services drop from the bundle
Errorsnon-ok responses throw GatekeeperError with .status; .retryAfter holds the Retry-After seconds on 429
Envelopeunwraps the API's { ok, data, error } automatically - methods return data

⚙️ Configuration#

Global config on the core, per-service defaults on the service (overridable per call):

const core = new GatekeeperCore({
    baseUrl,
    token,
    timeoutMs: 10_000,
    defaultHeaders: { 'x-trace': 'abc' },
    userAgent: 'myapp/1.0',
});
 
// or from env: GATEKEEPER_BASE_URL + GATEKEEPER_TOKEN
const core2 = GatekeeperCore.fromEnv();
 
// per-service defaults
const keys = new KeysService(core, { defaultScopes: ['read'], defaultQuotaPeriod: 'month' });
const audit = new AuditService(core, { defaultFormat: 'csv' });
const tenants = new TenantsService(core, { defaultRole: 'admin' });

Constants ship with their service: QUOTA_PERIODS, AUDIT_EXPORT_FORMATS, TENANT_ROLES.

🔌 Services#

ServiceWhat it does
AuthServicesignup, login, refresh, password reset, MFA login
TenantsServicecreate tenants, manage members
MfaServiceTOTP enroll / verify / disable
KeysServiceAPI keys: create, validate, JWT exchange
UsageServicequota checks + metered usage
PermissionsServicetenant RBAC roles + checks
WebhooksServiceendpoints, dispatch, deliveries
AuditServicerecord / query / export audit log
BillingServiceplans, entitlements, invoice, payment providers + checkout
OAuthServiceprovider authorize + callback
RateLimitServicestandalone rate-limit check
PlatformBillingServiceplatform: override a tenant's subscription
PlatformJobsServiceplatform: list / inspect / retry background jobs
PlatformServiceAccountsServiceplatform: create / list / revoke service accounts

Each maps one-to-one to an API route group, fully typed. Construct any subset over a shared GatekeeperCore. Per-service usage + methods: click a service above.