@shelbyb/daywalker-sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,8 +34,8 @@ npm install @shelbyb/daywalker-sdk
34
34
  # Generate Ed25519 keypair and write .env.local
35
35
  npx daywalker init --service my-service-slug
36
36
 
37
- # Or register the public key immediately with Daywalker Auth:
38
- npx daywalker init --service my-service-slug --api-key srv_live_...
37
+ # Or register the public key immediately with Daywalker Auth (domain is required by the API):
38
+ npx daywalker init --service my-service-slug --domain app.example.com --api-key srv_live_...
39
39
 
40
40
  # Pipe JSON secrets directly into Vercel, Fly.io, DigitalOcean, or AWS SSM:
41
41
  npx daywalker init --service my-service-slug --api-key srv_live_... --json
@@ -43,7 +43,7 @@ npx daywalker init --service my-service-slug --api-key srv_live_... --json
43
43
 
44
44
  `daywalker init`:
45
45
  - Generates an Ed25519 keypair locally using WebCrypto (`crypto.subtle`)
46
- - Registers the public JWK directly with `POST https://auth.daywalker.dev/api/v1/keys/register`
46
+ - Registers the public JWK directly with `POST https://auth.daywalker.dev/api/v1/keys/register` (`Authorization: Bearer <API key>`, body `{ keyId, domain, publicKey }`; responds `201`)
47
47
  - Writes `DAYWALKER_SERVICE_SLUG`, `DAYWALKER_KEY_ID`, `DAYWALKER_SIGNING_KEY`, and `DAYWALKER_AUTH_URL` to `.env.local`
48
48
  - No route injectors or public `/.well-known/` endpoints needed
49
49
 
@@ -193,6 +193,19 @@ const result = await client.validateToken(incomingJwt);
193
193
 
194
194
  ---
195
195
 
196
+ ## API Reference (per the [OpenAPI spec](https://auth.daywalker.dev/docs))
197
+
198
+ | Endpoint | SDK usage | Notes |
199
+ |---|---|---|
200
+ | `POST /api/v1/tokens/validate` | `client.validateToken()` | Body `{ token, serviceSlug }`; signed with `Signature-Input`, `Signature`, `Content-Digest`. Errors: `400` (`DIGEST_MISMATCH`/missing fields), `401` (`INVALID_SIGNATURE`/invalid token), `403`, `429`. Response includes `valid`, `tokenId`, `tokenName`, `userId`, `stage`, `rateLimit`, `rateLimitHourly` and `X-RateLimit-*` / `X-RateLimit-Hourly-*` headers |
201
+ | `POST /api/v1/keys/register` | `npx daywalker init` / `rotate` | Bearer auth; body `{ keyId, domain, publicKey }` |
202
+ | `POST /api/v1/leases/issue` | `client.issueLease(ttlSeconds?)` | Signed request returns `{ lease, tokenType, expiresAt, jwksUri }` (EdDSA JWT) |
203
+ | `GET /.well-known/jwks.json` | `client.fetchJwks()` | Daywalker public JWKS for offline lease verification |
204
+
205
+ Inbound tokens may be supplied as `x-api-token: srv_live_...` or `Authorization: Bearer <JWT or API key>`.
206
+
207
+ ---
208
+
196
209
  ## Request Signing Specification (RFC 9421 & RFC 9530)
197
210
 
198
211
  Each outbound request signed by `@shelbyb/daywalker-sdk` includes:
package/dist/cli.js CHANGED
@@ -171,6 +171,7 @@ async function rotate(opts) {
171
171
  const slug = String(opts.service ?? existing.get("DAYWALKER_SERVICE_SLUG") ?? process.env.DAYWALKER_SERVICE_SLUG ?? "").trim().toLowerCase();
172
172
  const apiKey = String(opts["api-key"] ?? existing.get("DAYWALKER_API_KEY") ?? process.env.DAYWALKER_API_KEY ?? "").trim() || null;
173
173
  const authUrl = String(opts["auth-url"] ?? existing.get("DAYWALKER_AUTH_URL") ?? process.env.DAYWALKER_AUTH_URL ?? "https://auth.daywalker.dev").replace(/\/+$/, "");
174
+ const rotateDomain = publicHostname(String(opts.domain ?? existing.get("DAYWALKER_DOMAIN") ?? process.env.DAYWALKER_DOMAIN ?? ""));
174
175
  const key = await generateSigningKey();
175
176
  if (apiKey && slug) {
176
177
  if (!isJson)
@@ -179,6 +180,7 @@ async function rotate(opts) {
179
180
  authUrl,
180
181
  apiKey,
181
182
  serviceSlug: slug,
183
+ domain: rotateDomain,
182
184
  publicJwk: key.publicJwk,
183
185
  });
184
186
  if (regResult.ok) {
package/dist/client.d.ts CHANGED
@@ -42,6 +42,21 @@ export declare class Daywalker {
42
42
  serviceSlug?: string;
43
43
  offlineOnly?: boolean;
44
44
  }): Promise<TokenPayload>;
45
+ /**
46
+ * Exchanges an RFC 9421 signed request for a short-lived EdDSA JWT lease (POST /api/v1/leases/issue).
47
+ */
48
+ issueLease(ttlSeconds?: number): Promise<{
49
+ lease: string;
50
+ tokenType?: string;
51
+ expiresAt?: string;
52
+ jwksUri?: string;
53
+ }>;
54
+ /**
55
+ * Fetches Daywalker's public JWKS (GET /.well-known/jwks.json) for offline lease verification.
56
+ */
57
+ fetchJwks(): Promise<{
58
+ keys: PublicJwk[];
59
+ }>;
45
60
  /**
46
61
  * The public JWK(s) configured for this service.
47
62
  */
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { resolveConfig } from "./config.js";
2
2
  import { loadSigningKey, signRequest, verifyJwtLease, maskSecret, redactSecrets, } from "./crypto/index.js";
3
- import { DaywalkerAuthError, DaywalkerRateLimitError, } from "./types.js";
3
+ import { DaywalkerAuthError, DaywalkerError, DaywalkerRateLimitError, } from "./types.js";
4
4
  function parseRetryAfter(headerValue) {
5
5
  if (!headerValue)
6
6
  return undefined;
@@ -311,6 +311,29 @@ export class Daywalker {
311
311
  }
312
312
  return result.data;
313
313
  }
314
+ /**
315
+ * Exchanges an RFC 9421 signed request for a short-lived EdDSA JWT lease (POST /api/v1/leases/issue).
316
+ */
317
+ async issueLease(ttlSeconds) {
318
+ const res = await this.fetch("/api/v1/leases/issue", {
319
+ method: "POST",
320
+ body: ttlSeconds ? { ttlSeconds } : {},
321
+ });
322
+ const json = (await res.json().catch(() => ({})));
323
+ if (res.status !== 201 && res.status !== 200) {
324
+ throw new DaywalkerAuthError(redactSecrets(String(json.message || json.error || `Lease issue failed with HTTP ${res.status}`)), res.status, json.error);
325
+ }
326
+ return json;
327
+ }
328
+ /**
329
+ * Fetches Daywalker's public JWKS (GET /.well-known/jwks.json) for offline lease verification.
330
+ */
331
+ async fetchJwks() {
332
+ const res = await globalThis.fetch(`${this._config.authUrl.replace(/\/+$/, "")}/.well-known/jwks.json`, { headers: { Accept: "application/json" } });
333
+ if (!res.ok)
334
+ throw new DaywalkerError(`Could not fetch JWKS (HTTP ${res.status})`, res.status);
335
+ return (await res.json());
336
+ }
314
337
  /**
315
338
  * The public JWK(s) configured for this service.
316
339
  */
@@ -296,6 +296,9 @@ export async function registerPublicKey(params) {
296
296
  const authUrl = (params.authUrl ?? "https://auth.daywalker.dev").replace(/\/+$/, "");
297
297
  const registerUrl = `${authUrl}/api/v1/keys/register`;
298
298
  const doFetch = params.fetchFn ?? globalThis.fetch;
299
+ if (!params.domain) {
300
+ return { ok: false, status: 400, error: "domain is required to register a key (set --domain or DAYWALKER_DOMAIN)" };
301
+ }
299
302
  try {
300
303
  const res = await doFetch(registerUrl, {
301
304
  method: "POST",
@@ -306,8 +309,7 @@ export async function registerPublicKey(params) {
306
309
  },
307
310
  body: JSON.stringify({
308
311
  keyId: params.publicJwk.kid,
309
- domain: params.domain ?? undefined,
310
- serviceSlug: params.serviceSlug ?? undefined,
312
+ domain: params.domain,
311
313
  publicKey: params.publicJwk,
312
314
  }),
313
315
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shelbyb/daywalker-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Validate Daywalker API tokens and make domain-signed calls to Daywalker Auth from any service.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",