@pithy-sh/core 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access/access.d.ts +172 -0
- package/dist/access/access.d.ts.map +1 -0
- package/dist/access/access.js +147 -0
- package/dist/access/acting.d.ts +49 -0
- package/dist/access/acting.d.ts.map +1 -0
- package/dist/access/acting.js +21 -0
- package/dist/access/policyCheck.d.ts +33 -0
- package/dist/access/policyCheck.d.ts.map +1 -0
- package/dist/access/policyCheck.js +48 -0
- package/dist/access/usage.d.ts +83 -0
- package/dist/access/usage.d.ts.map +1 -0
- package/dist/access/usage.js +104 -0
- package/dist/access/visibility.d.ts +66 -0
- package/dist/access/visibility.d.ts.map +1 -0
- package/dist/access/visibility.js +86 -0
- package/dist/audit/auditEvent.d.ts +5 -0
- package/dist/audit/auditEvent.d.ts.map +1 -1
- package/dist/audit/auditEvent.js +2 -0
- package/dist/audit/recorder.d.ts +10 -2
- package/dist/audit/recorder.d.ts.map +1 -1
- package/dist/capability/bindings.d.ts +47 -2
- package/dist/capability/bindings.d.ts.map +1 -1
- package/dist/capability/bindings.js +117 -7
- package/dist/capability/capability.d.ts +110 -28
- package/dist/capability/capability.d.ts.map +1 -1
- package/dist/capability/capability.js +2 -0
- package/dist/capability/manifest.d.ts +2 -1
- package/dist/capability/manifest.d.ts.map +1 -1
- package/dist/capability/manifest.js +1 -1
- package/dist/capability/purge.d.ts +80 -0
- package/dist/capability/purge.d.ts.map +1 -0
- package/dist/capability/purge.js +76 -0
- package/dist/capability/settings.d.ts +86 -2
- package/dist/capability/settings.d.ts.map +1 -1
- package/dist/capability/settings.js +80 -1
- package/dist/cli/commandCatalog.generated.d.ts +238 -24
- package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
- package/dist/cli/commandCatalog.generated.js +251 -22
- package/dist/cli/commandFacts.d.ts +2 -1
- package/dist/cli/commandFacts.d.ts.map +1 -1
- package/dist/cli/commandFacts.js +2 -0
- package/dist/controlPlane/capability.d.ts.map +1 -1
- package/dist/controlPlane/capability.js +19 -2
- package/dist/controlPlane/context.d.ts +5 -0
- package/dist/controlPlane/context.d.ts.map +1 -1
- package/dist/controlPlane/context.js +3 -1
- package/dist/controlPlane/data/connection.d.ts +49 -12
- package/dist/controlPlane/data/connection.d.ts.map +1 -1
- package/dist/controlPlane/data/connection.js +56 -2
- package/dist/controlPlane/discovery/adminRoute.d.ts +29 -0
- package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
- package/dist/controlPlane/discovery/adminRoute.js +3 -0
- package/dist/controlPlane/error/errors.d.ts +4 -3
- package/dist/controlPlane/error/errors.d.ts.map +1 -1
- package/dist/controlPlane/http/cors.d.ts +4 -4
- package/dist/controlPlane/http/cors.js +4 -4
- package/dist/controlPlane/http/guard.d.ts +11 -0
- package/dist/controlPlane/http/guard.d.ts.map +1 -1
- package/dist/controlPlane/http/guard.js +17 -1
- package/dist/controlPlane/http/handlers.d.ts +139 -0
- package/dist/controlPlane/http/handlers.d.ts.map +1 -1
- package/dist/controlPlane/http/handlers.js +67 -4
- package/dist/controlPlane/http/readiness.d.ts +33 -0
- package/dist/controlPlane/http/readiness.d.ts.map +1 -0
- package/dist/controlPlane/http/readiness.js +39 -0
- package/dist/controlPlane/http/responses.d.ts +245 -0
- package/dist/controlPlane/http/responses.d.ts.map +1 -1
- package/dist/controlPlane/http/responses.js +101 -4
- package/dist/controlPlane/http/routes.d.ts +2 -0
- package/dist/controlPlane/http/routes.d.ts.map +1 -1
- package/dist/controlPlane/http/routes.js +31 -3
- package/dist/controlPlane/http/verify.d.ts.map +1 -1
- package/dist/controlPlane/http/verify.js +2 -1
- package/dist/controlPlane/migrationState/migrationState.d.ts +102 -0
- package/dist/controlPlane/migrationState/migrationState.d.ts.map +1 -0
- package/dist/controlPlane/migrationState/migrationState.js +142 -0
- package/dist/controlPlane/migrations/0003_connection_principal.d.ts +21 -0
- package/dist/controlPlane/migrations/0003_connection_principal.d.ts.map +1 -0
- package/dist/controlPlane/migrations/0003_connection_principal.js +33 -0
- package/dist/controlPlane/scope/scope.d.ts +23 -2
- package/dist/controlPlane/scope/scope.d.ts.map +1 -1
- package/dist/controlPlane/scope/scope.js +24 -3
- package/dist/createBackend.d.ts.map +1 -1
- package/dist/createBackend.js +9 -2
- package/dist/createEntrypoint.d.ts.map +1 -1
- package/dist/createEntrypoint.js +5 -3
- package/dist/crypto/envelope.d.ts +86 -0
- package/dist/crypto/envelope.d.ts.map +1 -0
- package/dist/crypto/envelope.js +129 -0
- package/dist/crypto/fieldKeys.d.ts +41 -0
- package/dist/crypto/fieldKeys.d.ts.map +1 -0
- package/dist/crypto/fieldKeys.js +56 -0
- package/dist/data/codecs.d.ts +23 -10
- package/dist/data/codecs.d.ts.map +1 -1
- package/dist/data/codecs.js +50 -36
- package/dist/entitlement/allowance.d.ts +25 -0
- package/dist/entitlement/allowance.d.ts.map +1 -0
- package/dist/entitlement/allowance.js +80 -0
- package/dist/entitlement/entitlement.d.ts +41 -0
- package/dist/entitlement/entitlement.d.ts.map +1 -1
- package/dist/entitlement/meter.d.ts +57 -0
- package/dist/entitlement/meter.d.ts.map +1 -0
- package/dist/entitlement/meter.js +13 -0
- package/dist/error/extend.d.ts +6 -5
- package/dist/error/extend.d.ts.map +1 -1
- package/dist/error/http.d.ts +168 -0
- package/dist/error/http.d.ts.map +1 -1
- package/dist/error/payload.d.ts +336 -0
- package/dist/error/payload.d.ts.map +1 -1
- package/dist/error/payload.js +116 -4
- package/dist/error/pithyError.d.ts +18 -7
- package/dist/error/pithyError.d.ts.map +1 -1
- package/dist/error/pithyError.js +6 -0
- package/dist/http/authContext.d.ts +7 -0
- package/dist/http/authContext.d.ts.map +1 -1
- package/dist/http/authContext.js +8 -1
- package/dist/http/scopeGates.d.ts +20 -0
- package/dist/http/scopeGates.d.ts.map +1 -0
- package/dist/http/scopeGates.js +36 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/logger/logger.d.ts +12 -5
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/record.d.ts +98 -0
- package/dist/logger/record.d.ts.map +1 -1
- package/dist/migrations/capabilitySets.d.ts +23 -0
- package/dist/migrations/capabilitySets.d.ts.map +1 -0
- package/dist/migrations/capabilitySets.js +74 -0
- package/dist/migrations/registry.d.ts +26 -0
- package/dist/migrations/registry.d.ts.map +1 -1
- package/dist/migrations/registry.js +38 -7
- package/dist/migrations/state.d.ts +60 -0
- package/dist/migrations/state.d.ts.map +1 -0
- package/dist/migrations/state.js +46 -0
- package/dist/naming/feature.d.ts +27 -5
- package/dist/naming/feature.d.ts.map +1 -1
- package/dist/naming/feature.js +36 -6
- package/dist/naming/limits.d.ts +47 -3
- package/dist/naming/limits.d.ts.map +1 -1
- package/dist/naming/limits.js +47 -3
- package/dist/naming/provisionScope.d.ts +6 -0
- package/dist/naming/provisionScope.d.ts.map +1 -1
- package/dist/naming/provisionScope.js +8 -3
- package/dist/naming/resourceNames.d.ts +21 -2
- package/dist/naming/resourceNames.d.ts.map +1 -1
- package/dist/naming/resourceNames.js +21 -3
- package/dist/privacy/blindIndex.d.ts +27 -0
- package/dist/privacy/blindIndex.d.ts.map +1 -0
- package/dist/privacy/blindIndex.js +57 -0
- package/dist/privacy/classes.d.ts +142 -0
- package/dist/privacy/classes.d.ts.map +1 -0
- package/dist/privacy/classes.js +209 -0
- package/dist/privacy/classify.d.ts +336 -0
- package/dist/privacy/classify.d.ts.map +1 -0
- package/dist/privacy/classify.js +1024 -0
- package/dist/privacy/encrypted.d.ts +210 -0
- package/dist/privacy/encrypted.d.ts.map +1 -0
- package/dist/privacy/encrypted.js +350 -0
- package/dist/privacy/encryptedColumns.d.ts +64 -0
- package/dist/privacy/encryptedColumns.d.ts.map +1 -0
- package/dist/privacy/encryptedColumns.js +134 -0
- package/dist/privacy/fieldRotation.d.ts +34 -0
- package/dist/privacy/fieldRotation.d.ts.map +1 -0
- package/dist/privacy/fieldRotation.js +186 -0
- package/dist/privacy/inventory.d.ts +12 -0
- package/dist/privacy/inventory.d.ts.map +1 -0
- package/dist/privacy/inventory.js +154 -0
- package/dist/privacy/posture.d.ts +114 -0
- package/dist/privacy/posture.d.ts.map +1 -0
- package/dist/privacy/posture.js +136 -0
- package/dist/privacy/redact.d.ts +7 -0
- package/dist/privacy/redact.d.ts.map +1 -0
- package/dist/privacy/redact.js +404 -0
- package/dist/privacy/report.d.ts +58 -0
- package/dist/privacy/report.d.ts.map +1 -0
- package/dist/privacy/report.js +15 -0
- package/dist/privacy/retirement.d.ts +68 -0
- package/dist/privacy/retirement.d.ts.map +1 -0
- package/dist/privacy/retirement.js +72 -0
- package/dist/privacy/sealed.d.ts +87 -0
- package/dist/privacy/sealed.d.ts.map +1 -0
- package/dist/privacy/sealed.js +221 -0
- package/dist/privacy/types.d.ts +559 -0
- package/dist/privacy/types.d.ts.map +1 -0
- package/dist/privacy/types.js +2 -0
- package/dist/seed/writeD1.d.ts +7 -1
- package/dist/seed/writeD1.d.ts.map +1 -1
- package/dist/seed/writeD1.js +9 -3
- package/dist/seed/writeKv.d.ts +2 -0
- package/dist/seed/writeKv.d.ts.map +1 -1
- package/dist/seed/writeKv.js +6 -2
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/worker/serviceLoopback.d.ts +36 -0
- package/dist/worker/serviceLoopback.d.ts.map +1 -0
- package/dist/worker/serviceLoopback.js +74 -0
- package/dist/workflow/faults.d.ts +12 -1
- package/dist/workflow/faults.d.ts.map +1 -1
- package/dist/workflow/faults.js +22 -15
- package/package.json +2 -2
- package/src/access/access.ts +298 -0
- package/src/access/acting.ts +59 -0
- package/src/access/policyCheck.ts +77 -0
- package/src/access/usage.ts +141 -0
- package/src/access/visibility.ts +109 -0
- package/src/audit/auditEvent.ts +4 -0
- package/src/audit/recorder.ts +10 -2
- package/src/capability/bindings.ts +159 -13
- package/src/capability/capability.ts +114 -29
- package/src/capability/purge.ts +124 -0
- package/src/capability/settings.ts +146 -2
- package/src/cli/commandCatalog.generated.ts +271 -22
- package/src/cli/commandFacts.ts +2 -0
- package/src/controlPlane/capability.ts +36 -1
- package/src/controlPlane/context.ts +4 -0
- package/src/controlPlane/data/connection.ts +90 -0
- package/src/controlPlane/discovery/adminRoute.ts +11 -0
- package/src/controlPlane/error/errors.ts +4 -3
- package/src/controlPlane/http/cors.ts +4 -4
- package/src/controlPlane/http/guard.ts +21 -0
- package/src/controlPlane/http/handlers.ts +98 -4
- package/src/controlPlane/http/readiness.ts +50 -0
- package/src/controlPlane/http/responses.ts +240 -2
- package/src/controlPlane/http/routes.ts +55 -1
- package/src/controlPlane/http/verify.ts +2 -0
- package/src/controlPlane/migrationState/migrationState.ts +200 -0
- package/src/controlPlane/migrations/0003_connection_principal.ts +37 -0
- package/src/controlPlane/scope/scope.ts +25 -2
- package/src/controlPlane/token/base64url.ts +1 -1
- package/src/createBackend.ts +28 -6
- package/src/createEntrypoint.ts +9 -3
- package/src/crypto/envelope.ts +196 -0
- package/src/crypto/fieldKeys.ts +76 -0
- package/src/data/codecs.ts +69 -37
- package/src/entitlement/allowance.ts +85 -0
- package/src/entitlement/entitlement.ts +38 -0
- package/src/entitlement/meter.ts +69 -0
- package/src/error/extend.ts +6 -5
- package/src/error/payload.ts +253 -4
- package/src/error/pithyError.ts +21 -7
- package/src/http/authContext.ts +13 -1
- package/src/http/scopeGates.ts +34 -0
- package/src/index.ts +2 -0
- package/src/logger/logger.ts +12 -5
- package/src/migrations/capabilitySets.ts +87 -0
- package/src/migrations/registry.ts +46 -2
- package/src/migrations/state.ts +94 -0
- package/src/naming/feature.ts +46 -7
- package/src/naming/limits.ts +35 -3
- package/src/naming/provisionScope.ts +19 -1
- package/src/naming/resourceNames.ts +52 -5
- package/src/privacy/blindIndex.ts +81 -0
- package/src/privacy/classes.ts +277 -0
- package/src/privacy/classify.ts +1447 -0
- package/src/privacy/encrypted.ts +513 -0
- package/src/privacy/encryptedColumns.ts +185 -0
- package/src/privacy/fieldRotation.ts +234 -0
- package/src/privacy/inventory.ts +147 -0
- package/src/privacy/posture.ts +205 -0
- package/src/privacy/redact.ts +423 -0
- package/src/privacy/report.ts +64 -0
- package/src/privacy/retirement.ts +102 -0
- package/src/privacy/sealed.ts +323 -0
- package/src/privacy/types.ts +804 -0
- package/src/seed/writeD1.ts +13 -4
- package/src/seed/writeKv.ts +6 -2
- package/src/version.generated.ts +1 -1
- package/src/worker/serviceLoopback.ts +105 -0
- package/src/workflow/faults.ts +51 -24
package/src/error/payload.ts
CHANGED
|
@@ -181,6 +181,38 @@ const AuthSessionNotFreshPublic = z
|
|
|
181
181
|
})
|
|
182
182
|
.describe("The session is authentic but the holder authenticated too long ago to attach a provider (403).");
|
|
183
183
|
|
|
184
|
+
const AuthAccountSuspendedPublic = z
|
|
185
|
+
.object({
|
|
186
|
+
code: z
|
|
187
|
+
.literal("auth/account_suspended")
|
|
188
|
+
.describe(
|
|
189
|
+
"The account this credential belongs to is suspended, so the request is refused whatever it carries. An operator suspended it on purpose, with a reason, and the reason is the `message`. When the suspension has an end, `params.suspendedUntil` names it as ISO-8601; a suspension past its end is no longer one, and nothing has to clear it. A separate code from `auth/forbidden` so a client can render the screen that says what happened rather than a generic refusal.",
|
|
190
|
+
),
|
|
191
|
+
status: z
|
|
192
|
+
.literal(403)
|
|
193
|
+
.describe(
|
|
194
|
+
"Forbidden — the credential is genuine and the account behind it may not act. Not 401: signing in again cannot help, and a client that read it as an expired token would loop.",
|
|
195
|
+
),
|
|
196
|
+
...publicFields,
|
|
197
|
+
})
|
|
198
|
+
.describe("The account is suspended (403).");
|
|
199
|
+
|
|
200
|
+
const AuthAccountDeletedPublic = z
|
|
201
|
+
.object({
|
|
202
|
+
code: z
|
|
203
|
+
.literal("auth/account_deleted")
|
|
204
|
+
.describe(
|
|
205
|
+
"The account this credential belongs to is deleted and inside its purge window: it cannot sign in or act, and it can still be restored until the window closes. After the purge there is no account, and the credential is simply invalid.",
|
|
206
|
+
),
|
|
207
|
+
status: z
|
|
208
|
+
.literal(403)
|
|
209
|
+
.describe(
|
|
210
|
+
"Forbidden — the account exists and may not act. Not 404, because until the purge runs it can come back, and not 401, because signing in again cannot help.",
|
|
211
|
+
),
|
|
212
|
+
...publicFields,
|
|
213
|
+
})
|
|
214
|
+
.describe("The account is deleted and awaiting its purge (403).");
|
|
215
|
+
|
|
184
216
|
const NotFoundPublic = z
|
|
185
217
|
.object({
|
|
186
218
|
code: z.literal("core/not_found").describe("The requested resource does not exist."),
|
|
@@ -501,6 +533,28 @@ const TurnstileConfigPublic = z
|
|
|
501
533
|
})
|
|
502
534
|
.describe("The Turnstile middleware is misconfigured (500).");
|
|
503
535
|
|
|
536
|
+
const TurnstileAnalyticsWindowPublic = z
|
|
537
|
+
.object({
|
|
538
|
+
code: z
|
|
539
|
+
.literal("turnstile/analytics_window")
|
|
540
|
+
.describe(
|
|
541
|
+
"The requested challenge window reaches past what Cloudflare keeps, or is wider than one read may ask for.",
|
|
542
|
+
),
|
|
543
|
+
status: z.literal(400).describe("Bad Request — narrow the window or move it later."),
|
|
544
|
+
...publicFields,
|
|
545
|
+
})
|
|
546
|
+
.describe("A Turnstile-analytics window outside Cloudflare's retention or range (400).");
|
|
547
|
+
|
|
548
|
+
const TurnstileAnalyticsUnavailablePublic = z
|
|
549
|
+
.object({
|
|
550
|
+
code: z
|
|
551
|
+
.literal("turnstile/analytics_unavailable")
|
|
552
|
+
.describe("Cloudflare does not serve Turnstile analytics to this account, so no outcome can be read."),
|
|
553
|
+
status: z.literal(409).describe("Conflict — the account's configuration rules the read out."),
|
|
554
|
+
...publicFields,
|
|
555
|
+
})
|
|
556
|
+
.describe("Turnstile analytics are not served to this account (409).");
|
|
557
|
+
|
|
504
558
|
// --- @pithy-sh/audit: audit-trail recorder codes ---
|
|
505
559
|
|
|
506
560
|
const AuditInvalidEventPublic = z
|
|
@@ -535,6 +589,18 @@ const MediaNotFoundPublic = z
|
|
|
535
589
|
})
|
|
536
590
|
.describe("A requested media record does not exist (404).");
|
|
537
591
|
|
|
592
|
+
const MediaForbiddenPublic = z
|
|
593
|
+
.object({
|
|
594
|
+
code: z
|
|
595
|
+
.literal("media/forbidden")
|
|
596
|
+
.describe(
|
|
597
|
+
"The caller may read the media record but not change it: only its owner, acting for the record's organization, or a role holding `media:manage` there may edit, delete or re-scope it.",
|
|
598
|
+
),
|
|
599
|
+
status: z.literal(403).describe("Forbidden."),
|
|
600
|
+
...publicFields,
|
|
601
|
+
})
|
|
602
|
+
.describe("The caller may read the record but not change it (403).");
|
|
603
|
+
|
|
538
604
|
const MediaUnsupportedPublic = z
|
|
539
605
|
.object({
|
|
540
606
|
code: z
|
|
@@ -565,6 +631,30 @@ const MediaEnrichmentFailedPublic = z
|
|
|
565
631
|
})
|
|
566
632
|
.describe("A media AI-enrichment step failed (500).");
|
|
567
633
|
|
|
634
|
+
const MediaAnalyticsWindowPublic = z
|
|
635
|
+
.object({
|
|
636
|
+
code: z
|
|
637
|
+
.literal("media/analytics_window")
|
|
638
|
+
.describe(
|
|
639
|
+
"The requested delivery window reaches past what Cloudflare keeps, or is wider than one read may ask for.",
|
|
640
|
+
),
|
|
641
|
+
status: z.literal(400).describe("Bad Request — narrow the window or move it later."),
|
|
642
|
+
...publicFields,
|
|
643
|
+
})
|
|
644
|
+
.describe("A delivery-analytics window outside Cloudflare's retention or range (400).");
|
|
645
|
+
|
|
646
|
+
const MediaAnalyticsUnavailablePublic = z
|
|
647
|
+
.object({
|
|
648
|
+
code: z
|
|
649
|
+
.literal("media/analytics_unavailable")
|
|
650
|
+
.describe(
|
|
651
|
+
"Cloudflare does not serve the delivery analytics this read needs to this account — a Customer Metadata Boundary outside the US carries none of them.",
|
|
652
|
+
),
|
|
653
|
+
status: z.literal(409).describe("Conflict — the account's configuration rules the read out."),
|
|
654
|
+
...publicFields,
|
|
655
|
+
})
|
|
656
|
+
.describe("Delivery analytics are not served to this account (409).");
|
|
657
|
+
|
|
568
658
|
// --- @pithy-sh/leaderboard: optional ranking capability codes ---
|
|
569
659
|
|
|
570
660
|
const LeaderboardBoardNotFoundPublic = z
|
|
@@ -931,7 +1021,7 @@ const StorageNotFoundPublic = z
|
|
|
931
1021
|
.object({
|
|
932
1022
|
code: z
|
|
933
1023
|
.literal("storage/not_found")
|
|
934
|
-
.describe("No object row matches, or the object exists but
|
|
1024
|
+
.describe("No object row matches, or the object exists but this caller may not read it."),
|
|
935
1025
|
status: z.literal(404).describe("Not Found."),
|
|
936
1026
|
...publicFields,
|
|
937
1027
|
})
|
|
@@ -939,18 +1029,22 @@ const StorageNotFoundPublic = z
|
|
|
939
1029
|
|
|
940
1030
|
const StorageForbiddenPublic = z
|
|
941
1031
|
.object({
|
|
942
|
-
code: z
|
|
1032
|
+
code: z
|
|
1033
|
+
.literal("storage/forbidden")
|
|
1034
|
+
.describe(
|
|
1035
|
+
"The caller may read the object but not change it: only its owner, acting for the object's organization, or a role holding `storage:manage` there may.",
|
|
1036
|
+
),
|
|
943
1037
|
status: z.literal(403).describe("Forbidden."),
|
|
944
1038
|
...publicFields,
|
|
945
1039
|
})
|
|
946
|
-
.describe("The caller
|
|
1040
|
+
.describe("The caller may read the object but not change it (403).");
|
|
947
1041
|
|
|
948
1042
|
const StorageQuotaExceededPublic = z
|
|
949
1043
|
.object({
|
|
950
1044
|
code: z
|
|
951
1045
|
.literal("storage/quota_exceeded")
|
|
952
1046
|
.describe(
|
|
953
|
-
"The declared upload size would push the owner past
|
|
1047
|
+
"The declared upload size would push the owner past the configured `bytesPerOwner` quota. Checked at init against pending and stored rows together, so concurrent inits cannot race past the limit. Where the `storageBytes` meter is enforced, the same refusal is `payments/quota_exceeded` (402).",
|
|
954
1048
|
),
|
|
955
1049
|
status: z.literal(413).describe("Content Too Large."),
|
|
956
1050
|
...publicFields,
|
|
@@ -1257,6 +1351,30 @@ const PaymentsSubscriptionChangeRefusedPublic = z
|
|
|
1257
1351
|
})
|
|
1258
1352
|
.describe("A subscription's state forbids the change asked of it (409).");
|
|
1259
1353
|
|
|
1354
|
+
const PaymentsOverrideStalePublic = z
|
|
1355
|
+
.object({
|
|
1356
|
+
code: z
|
|
1357
|
+
.literal("payments/override_stale")
|
|
1358
|
+
.describe(
|
|
1359
|
+
"A control-plane override write named a version behind the subject's override row. Two operators share one customer: both opened the same allowance, and the second would silently overwrite the first while the first believed they had succeeded. So the write is refused and nothing is written. 409, because the request is well-formed and the same request against the version it now holds would be accepted — the caller re-reads, and tries again.",
|
|
1360
|
+
),
|
|
1361
|
+
status: z.literal(409).describe("Conflict — the override changed since the caller read it."),
|
|
1362
|
+
...publicFields,
|
|
1363
|
+
})
|
|
1364
|
+
.describe("An override write named a stale version (409).");
|
|
1365
|
+
|
|
1366
|
+
const PaymentsQuotaExceededPublic = z
|
|
1367
|
+
.object({
|
|
1368
|
+
code: z
|
|
1369
|
+
.literal("payments/quota_exceeded")
|
|
1370
|
+
.describe(
|
|
1371
|
+
"An enforced kit meter — emails enqueued, bytes stored, media items held — would pass the subject's effective quota for the period. Nothing was written. 402 Payment Required, because it is the honest status: the plan does not allow more and the remedy is a purchase, a bigger tier, or a number pinned for this subject from the control plane. A meter refuses and never bills — the kit does not sell overage.",
|
|
1372
|
+
),
|
|
1373
|
+
status: z.literal(402).describe("Payment Required — the plan's allowance for this period is used up."),
|
|
1374
|
+
...publicFields,
|
|
1375
|
+
})
|
|
1376
|
+
.describe("An enforced meter is at its quota for this period (402).");
|
|
1377
|
+
|
|
1260
1378
|
// --- @pithy-sh/core: the `control-plane` verification strategy ---
|
|
1261
1379
|
//
|
|
1262
1380
|
// NOT Cloudflare's control plane. Everywhere else in this repo "control plane" means the Cloudflare
|
|
@@ -1407,6 +1525,68 @@ const OrganizationNominationInvalidPublic = z
|
|
|
1407
1525
|
})
|
|
1408
1526
|
.describe("No standing ownership offer this caller may accept (400).");
|
|
1409
1527
|
|
|
1528
|
+
// --- @pithy-sh/artifacts: Git repositories, ownership, grants and short-lived tokens ---
|
|
1529
|
+
|
|
1530
|
+
const ArtifactsNotFoundPublic = z
|
|
1531
|
+
.object({
|
|
1532
|
+
code: z
|
|
1533
|
+
.literal("artifacts/not_found")
|
|
1534
|
+
.describe(
|
|
1535
|
+
"No such repository, **or** one the caller holds no grant on and does not own. One code for both, byte for byte: a repository's name is in its clone URL, and a refusal that told the two apart would hand it over. Also what the credential route answers for a repository with remote access off. Which it was survives in `detail`, which the HTTP codec strips.",
|
|
1536
|
+
),
|
|
1537
|
+
status: z.literal(404).describe("Not Found."),
|
|
1538
|
+
...publicFields,
|
|
1539
|
+
})
|
|
1540
|
+
.describe("No such repository, or none this caller may see — the same answer for both (404).");
|
|
1541
|
+
|
|
1542
|
+
const ArtifactsForbiddenPublic = z
|
|
1543
|
+
.object({
|
|
1544
|
+
code: z
|
|
1545
|
+
.literal("artifacts/forbidden")
|
|
1546
|
+
.describe(
|
|
1547
|
+
"The caller can see the repository and may not do this to it: a grant holder asking for what only an owner may do, or a member of an organization whose role lacks the configured `managePower`. A 403 rather than the 404 above, because by then the caller already knows the repository exists.",
|
|
1548
|
+
),
|
|
1549
|
+
status: z.literal(403).describe("Forbidden."),
|
|
1550
|
+
...publicFields,
|
|
1551
|
+
})
|
|
1552
|
+
.describe("The caller may see this repository and may not do this (403).");
|
|
1553
|
+
|
|
1554
|
+
const ArtifactsInvalidNamePublic = z
|
|
1555
|
+
.object({
|
|
1556
|
+
code: z
|
|
1557
|
+
.literal("artifacts/invalid_name")
|
|
1558
|
+
.describe(
|
|
1559
|
+
"A supplied repository name is outside Cloudflare's charset or the 2-to-63 cap. Refused and never shortened: a name somebody chose is in every clone URL, and a silently different one is a repository nobody can find.",
|
|
1560
|
+
),
|
|
1561
|
+
status: z.literal(400).describe("Bad Request."),
|
|
1562
|
+
...publicFields,
|
|
1563
|
+
})
|
|
1564
|
+
.describe("A repository name breaks the naming rule (400).");
|
|
1565
|
+
|
|
1566
|
+
const ArtifactsNameTakenPublic = z
|
|
1567
|
+
.object({
|
|
1568
|
+
code: z
|
|
1569
|
+
.literal("artifacts/name_taken")
|
|
1570
|
+
.describe(
|
|
1571
|
+
"The owner already holds a repository with that display name, or a supplied physical name is already in the namespace. The second happens only where the adopter supplies names, so uniqueness is theirs.",
|
|
1572
|
+
),
|
|
1573
|
+
status: z.literal(409).describe("Conflict."),
|
|
1574
|
+
...publicFields,
|
|
1575
|
+
})
|
|
1576
|
+
.describe("That repository name is already in use (409).");
|
|
1577
|
+
|
|
1578
|
+
const ArtifactsInvalidConfigPublic = z
|
|
1579
|
+
.object({
|
|
1580
|
+
code: z
|
|
1581
|
+
.literal("artifacts/invalid_config")
|
|
1582
|
+
.describe(
|
|
1583
|
+
"The capability's configuration cannot be honored: a `tokenTtlSeconds` above the ceiling, or `organization` composed with no `managePower` named. Raised at boot, so a mistake fails on deploy rather than at the first mint.",
|
|
1584
|
+
),
|
|
1585
|
+
status: z.literal(400).describe("Bad Request — the configuration is malformed."),
|
|
1586
|
+
...publicFields,
|
|
1587
|
+
})
|
|
1588
|
+
.describe("The artifacts configuration is invalid (400).");
|
|
1589
|
+
|
|
1410
1590
|
// --- @pithy-sh/support: inbound inbox, taxonomy, and classification codes ---
|
|
1411
1591
|
|
|
1412
1592
|
const SupportNotFoundPublic = z
|
|
@@ -1610,6 +1790,8 @@ export const KitPublicErrorPayload = z
|
|
|
1610
1790
|
ForbiddenPublic,
|
|
1611
1791
|
AuthProviderUnavailablePublic,
|
|
1612
1792
|
AuthSessionNotFreshPublic,
|
|
1793
|
+
AuthAccountSuspendedPublic,
|
|
1794
|
+
AuthAccountDeletedPublic,
|
|
1613
1795
|
NotFoundPublic,
|
|
1614
1796
|
ConflictPublic,
|
|
1615
1797
|
RateLimitPublic,
|
|
@@ -1639,12 +1821,17 @@ export const KitPublicErrorPayload = z
|
|
|
1639
1821
|
TurnstileMissingTokenPublic,
|
|
1640
1822
|
TurnstileFailedPublic,
|
|
1641
1823
|
TurnstileConfigPublic,
|
|
1824
|
+
TurnstileAnalyticsWindowPublic,
|
|
1825
|
+
TurnstileAnalyticsUnavailablePublic,
|
|
1642
1826
|
AuditInvalidEventPublic,
|
|
1643
1827
|
AuditWriteFailedPublic,
|
|
1644
1828
|
MediaNotFoundPublic,
|
|
1829
|
+
MediaForbiddenPublic,
|
|
1645
1830
|
MediaUnsupportedPublic,
|
|
1646
1831
|
MediaStorageFailedPublic,
|
|
1647
1832
|
MediaEnrichmentFailedPublic,
|
|
1833
|
+
MediaAnalyticsWindowPublic,
|
|
1834
|
+
MediaAnalyticsUnavailablePublic,
|
|
1648
1835
|
LeaderboardBoardNotFoundPublic,
|
|
1649
1836
|
LeaderboardEntryNotFoundPublic,
|
|
1650
1837
|
LeaderboardScoreRejectedPublic,
|
|
@@ -1707,6 +1894,8 @@ export const KitPublicErrorPayload = z
|
|
|
1707
1894
|
PaymentsEntitlementNotInCatalogPublic,
|
|
1708
1895
|
PaymentsSubjectUnresolvedPublic,
|
|
1709
1896
|
PaymentsSubscriptionChangeRefusedPublic,
|
|
1897
|
+
PaymentsOverrideStalePublic,
|
|
1898
|
+
PaymentsQuotaExceededPublic,
|
|
1710
1899
|
ControlPlaneNotConnectedPublic,
|
|
1711
1900
|
ControlPlaneInvalidCredentialPublic,
|
|
1712
1901
|
ControlPlaneInsufficientScopePublic,
|
|
@@ -1719,6 +1908,11 @@ export const KitPublicErrorPayload = z
|
|
|
1719
1908
|
OrganizationLastAdministratorPublic,
|
|
1720
1909
|
OrganizationSlugTakenPublic,
|
|
1721
1910
|
OrganizationNominationInvalidPublic,
|
|
1911
|
+
ArtifactsNotFoundPublic,
|
|
1912
|
+
ArtifactsForbiddenPublic,
|
|
1913
|
+
ArtifactsInvalidNamePublic,
|
|
1914
|
+
ArtifactsNameTakenPublic,
|
|
1915
|
+
ArtifactsInvalidConfigPublic,
|
|
1722
1916
|
SupportNotFoundPublic,
|
|
1723
1917
|
SupportInvalidCategoryPublic,
|
|
1724
1918
|
SupportUnparseableMessagePublic,
|
|
@@ -1754,6 +1948,12 @@ const AuthProviderUnavailable = AuthProviderUnavailablePublic.extend(internalFie
|
|
|
1754
1948
|
const AuthSessionNotFresh = AuthSessionNotFreshPublic.extend(internalFields).describe(
|
|
1755
1949
|
AuthSessionNotFreshPublic.description ?? "",
|
|
1756
1950
|
);
|
|
1951
|
+
const AuthAccountSuspended = AuthAccountSuspendedPublic.extend(internalFields).describe(
|
|
1952
|
+
AuthAccountSuspendedPublic.description ?? "",
|
|
1953
|
+
);
|
|
1954
|
+
const AuthAccountDeleted = AuthAccountDeletedPublic.extend(internalFields).describe(
|
|
1955
|
+
AuthAccountDeletedPublic.description ?? "",
|
|
1956
|
+
);
|
|
1757
1957
|
const NotFound = NotFoundPublic.extend(internalFields).describe(NotFoundPublic.description ?? "");
|
|
1758
1958
|
const Conflict = ConflictPublic.extend(internalFields).describe(ConflictPublic.description ?? "");
|
|
1759
1959
|
const RateLimit = RateLimitPublic.extend(internalFields).describe(RateLimitPublic.description ?? "");
|
|
@@ -1815,6 +2015,12 @@ const TurnstileMissingToken = TurnstileMissingTokenPublic.extend(internalFields)
|
|
|
1815
2015
|
);
|
|
1816
2016
|
const TurnstileFailed = TurnstileFailedPublic.extend(internalFields).describe(TurnstileFailedPublic.description ?? "");
|
|
1817
2017
|
const TurnstileConfig = TurnstileConfigPublic.extend(internalFields).describe(TurnstileConfigPublic.description ?? "");
|
|
2018
|
+
const TurnstileAnalyticsWindow = TurnstileAnalyticsWindowPublic.extend(internalFields).describe(
|
|
2019
|
+
TurnstileAnalyticsWindowPublic.description ?? "",
|
|
2020
|
+
);
|
|
2021
|
+
const TurnstileAnalyticsUnavailable = TurnstileAnalyticsUnavailablePublic.extend(internalFields).describe(
|
|
2022
|
+
TurnstileAnalyticsUnavailablePublic.description ?? "",
|
|
2023
|
+
);
|
|
1818
2024
|
const AuditInvalidEvent = AuditInvalidEventPublic.extend(internalFields).describe(
|
|
1819
2025
|
AuditInvalidEventPublic.description ?? "",
|
|
1820
2026
|
);
|
|
@@ -1822,6 +2028,7 @@ const AuditWriteFailed = AuditWriteFailedPublic.extend(internalFields).describe(
|
|
|
1822
2028
|
AuditWriteFailedPublic.description ?? "",
|
|
1823
2029
|
);
|
|
1824
2030
|
const MediaNotFound = MediaNotFoundPublic.extend(internalFields).describe(MediaNotFoundPublic.description ?? "");
|
|
2031
|
+
const MediaForbidden = MediaForbiddenPublic.extend(internalFields).describe(MediaForbiddenPublic.description ?? "");
|
|
1825
2032
|
const MediaUnsupported = MediaUnsupportedPublic.extend(internalFields).describe(
|
|
1826
2033
|
MediaUnsupportedPublic.description ?? "",
|
|
1827
2034
|
);
|
|
@@ -1831,6 +2038,12 @@ const MediaStorageFailed = MediaStorageFailedPublic.extend(internalFields).descr
|
|
|
1831
2038
|
const MediaEnrichmentFailed = MediaEnrichmentFailedPublic.extend(internalFields).describe(
|
|
1832
2039
|
MediaEnrichmentFailedPublic.description ?? "",
|
|
1833
2040
|
);
|
|
2041
|
+
const MediaAnalyticsWindow = MediaAnalyticsWindowPublic.extend(internalFields).describe(
|
|
2042
|
+
MediaAnalyticsWindowPublic.description ?? "",
|
|
2043
|
+
);
|
|
2044
|
+
const MediaAnalyticsUnavailable = MediaAnalyticsUnavailablePublic.extend(internalFields).describe(
|
|
2045
|
+
MediaAnalyticsUnavailablePublic.description ?? "",
|
|
2046
|
+
);
|
|
1834
2047
|
const LeaderboardBoardNotFound = LeaderboardBoardNotFoundPublic.extend(internalFields).describe(
|
|
1835
2048
|
LeaderboardBoardNotFoundPublic.description ?? "",
|
|
1836
2049
|
);
|
|
@@ -2016,6 +2229,12 @@ const PaymentsSubjectUnresolved = PaymentsSubjectUnresolvedPublic.extend(interna
|
|
|
2016
2229
|
const PaymentsSubscriptionChangeRefused = PaymentsSubscriptionChangeRefusedPublic.extend(internalFields).describe(
|
|
2017
2230
|
PaymentsSubscriptionChangeRefusedPublic.description ?? "",
|
|
2018
2231
|
);
|
|
2232
|
+
const PaymentsOverrideStale = PaymentsOverrideStalePublic.extend(internalFields).describe(
|
|
2233
|
+
PaymentsOverrideStalePublic.description ?? "",
|
|
2234
|
+
);
|
|
2235
|
+
const PaymentsQuotaExceeded = PaymentsQuotaExceededPublic.extend(internalFields).describe(
|
|
2236
|
+
PaymentsQuotaExceededPublic.description ?? "",
|
|
2237
|
+
);
|
|
2019
2238
|
const ControlPlaneNotConnected = ControlPlaneNotConnectedPublic.extend(internalFields).describe(
|
|
2020
2239
|
ControlPlaneNotConnectedPublic.description ?? "",
|
|
2021
2240
|
);
|
|
@@ -2086,6 +2305,22 @@ const OrganizationNominationInvalid = OrganizationNominationInvalidPublic.extend
|
|
|
2086
2305
|
OrganizationNominationInvalidPublic.description ?? "",
|
|
2087
2306
|
);
|
|
2088
2307
|
|
|
2308
|
+
const ArtifactsNotFound = ArtifactsNotFoundPublic.extend(internalFields).describe(
|
|
2309
|
+
ArtifactsNotFoundPublic.description ?? "",
|
|
2310
|
+
);
|
|
2311
|
+
const ArtifactsForbidden = ArtifactsForbiddenPublic.extend(internalFields).describe(
|
|
2312
|
+
ArtifactsForbiddenPublic.description ?? "",
|
|
2313
|
+
);
|
|
2314
|
+
const ArtifactsInvalidName = ArtifactsInvalidNamePublic.extend(internalFields).describe(
|
|
2315
|
+
ArtifactsInvalidNamePublic.description ?? "",
|
|
2316
|
+
);
|
|
2317
|
+
const ArtifactsNameTaken = ArtifactsNameTakenPublic.extend(internalFields).describe(
|
|
2318
|
+
ArtifactsNameTakenPublic.description ?? "",
|
|
2319
|
+
);
|
|
2320
|
+
const ArtifactsInvalidConfig = ArtifactsInvalidConfigPublic.extend(internalFields).describe(
|
|
2321
|
+
ArtifactsInvalidConfigPublic.description ?? "",
|
|
2322
|
+
);
|
|
2323
|
+
|
|
2089
2324
|
const SupportNotFound = SupportNotFoundPublic.extend(internalFields).describe(SupportNotFoundPublic.description ?? "");
|
|
2090
2325
|
const SupportInvalidCategory = SupportInvalidCategoryPublic.extend(internalFields).describe(
|
|
2091
2326
|
SupportInvalidCategoryPublic.description ?? "",
|
|
@@ -2116,6 +2351,8 @@ export const KitErrorPayload = z
|
|
|
2116
2351
|
Forbidden,
|
|
2117
2352
|
AuthProviderUnavailable,
|
|
2118
2353
|
AuthSessionNotFresh,
|
|
2354
|
+
AuthAccountSuspended,
|
|
2355
|
+
AuthAccountDeleted,
|
|
2119
2356
|
NotFound,
|
|
2120
2357
|
Conflict,
|
|
2121
2358
|
RateLimit,
|
|
@@ -2145,12 +2382,17 @@ export const KitErrorPayload = z
|
|
|
2145
2382
|
TurnstileMissingToken,
|
|
2146
2383
|
TurnstileFailed,
|
|
2147
2384
|
TurnstileConfig,
|
|
2385
|
+
TurnstileAnalyticsWindow,
|
|
2386
|
+
TurnstileAnalyticsUnavailable,
|
|
2148
2387
|
AuditInvalidEvent,
|
|
2149
2388
|
AuditWriteFailed,
|
|
2150
2389
|
MediaNotFound,
|
|
2390
|
+
MediaForbidden,
|
|
2151
2391
|
MediaUnsupported,
|
|
2152
2392
|
MediaStorageFailed,
|
|
2153
2393
|
MediaEnrichmentFailed,
|
|
2394
|
+
MediaAnalyticsWindow,
|
|
2395
|
+
MediaAnalyticsUnavailable,
|
|
2154
2396
|
LeaderboardBoardNotFound,
|
|
2155
2397
|
LeaderboardEntryNotFound,
|
|
2156
2398
|
LeaderboardScoreRejected,
|
|
@@ -2213,6 +2455,8 @@ export const KitErrorPayload = z
|
|
|
2213
2455
|
PaymentsEntitlementNotInCatalog,
|
|
2214
2456
|
PaymentsSubjectUnresolved,
|
|
2215
2457
|
PaymentsSubscriptionChangeRefused,
|
|
2458
|
+
PaymentsOverrideStale,
|
|
2459
|
+
PaymentsQuotaExceeded,
|
|
2216
2460
|
ControlPlaneNotConnected,
|
|
2217
2461
|
ControlPlaneInvalidCredential,
|
|
2218
2462
|
ControlPlaneInsufficientScope,
|
|
@@ -2225,6 +2469,11 @@ export const KitErrorPayload = z
|
|
|
2225
2469
|
OrganizationLastAdministrator,
|
|
2226
2470
|
OrganizationSlugTaken,
|
|
2227
2471
|
OrganizationNominationInvalid,
|
|
2472
|
+
ArtifactsNotFound,
|
|
2473
|
+
ArtifactsForbidden,
|
|
2474
|
+
ArtifactsInvalidName,
|
|
2475
|
+
ArtifactsNameTaken,
|
|
2476
|
+
ArtifactsInvalidConfig,
|
|
2228
2477
|
SupportNotFound,
|
|
2229
2478
|
SupportInvalidCategory,
|
|
2230
2479
|
SupportUnparseableMessage,
|
package/src/error/pithyError.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import type { z } from "zod";
|
|
5
5
|
import type { MessageParams } from "../i18n/catalog";
|
|
6
|
+
import type { UnmarkedString } from "../privacy/types";
|
|
6
7
|
import type { ValidationIssue } from "./payload";
|
|
7
8
|
import { ErrorPayload } from "./payload";
|
|
8
9
|
|
|
@@ -18,7 +19,20 @@ import { ErrorPayload } from "./payload";
|
|
|
18
19
|
export class PithyError extends Error {
|
|
19
20
|
readonly payload: ErrorPayload;
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
/**
|
|
23
|
+
* `message`, `action` and `detail` are each an {@link UnmarkedString} here and on every subclass's arguments
|
|
24
|
+
* (#132): a value marked with a privacy class is refused at compile time, because all three ride into every log
|
|
25
|
+
* line and audit row that carries this error. A template literal is a plain `string`, so interpolation is not caught — see
|
|
26
|
+
* `privacy/types.ts` for exactly what is.
|
|
27
|
+
*/
|
|
28
|
+
constructor(
|
|
29
|
+
payload: ErrorPayload & {
|
|
30
|
+
message: UnmarkedString;
|
|
31
|
+
action?: UnmarkedString | undefined;
|
|
32
|
+
detail?: UnmarkedString | undefined;
|
|
33
|
+
},
|
|
34
|
+
options?: { cause?: unknown },
|
|
35
|
+
) {
|
|
22
36
|
const parsed = ErrorPayload.parse(payload);
|
|
23
37
|
super(parsed.message, options);
|
|
24
38
|
this.payload = parsed;
|
|
@@ -28,12 +42,12 @@ export class PithyError extends Error {
|
|
|
28
42
|
|
|
29
43
|
/** Variable parts every subclass accepts; the `code`/`status` are fixed by the subclass. */
|
|
30
44
|
interface ErrorArgs {
|
|
31
|
-
/** Override the public, safe-to-expose message. Defaults to a per-code summary. */
|
|
32
|
-
message?:
|
|
33
|
-
/** A remediation hint (CLI action line). */
|
|
34
|
-
action?:
|
|
35
|
-
/** Internal context for logs + audit. Never serialized to clients. */
|
|
36
|
-
detail?:
|
|
45
|
+
/** Override the public, safe-to-expose message. Defaults to a per-code summary. Never a privacy-marked value. */
|
|
46
|
+
message?: UnmarkedString;
|
|
47
|
+
/** A remediation hint (CLI action line). Never a privacy-marked value. */
|
|
48
|
+
action?: UnmarkedString;
|
|
49
|
+
/** Internal context for logs + audit. Never serialized to clients, and never a privacy-marked value (#132). */
|
|
50
|
+
detail?: UnmarkedString;
|
|
37
51
|
/**
|
|
38
52
|
* Values a translating client interpolates into its own wording for this code. Client-facing, so —
|
|
39
53
|
* unlike `action` and `detail` — these cross the boundary with `message`. The `code` is the catalog
|
package/src/http/authContext.ts
CHANGED
|
@@ -15,12 +15,24 @@ import { Locale } from "../i18n/locale";
|
|
|
15
15
|
* stored choice outrank their device's `Accept-Language`. That link read `c.var.locale` at first,
|
|
16
16
|
* which nothing ever wrote, so it silently contributed nothing and the chain degraded to
|
|
17
17
|
* `param → cookie → header → default` while the docs said otherwise.
|
|
18
|
+
*
|
|
19
|
+
* **`scopes` answers one question: did this request come from the adopter's own trusted backend, or from a
|
|
20
|
+
* device?** The name reads like a grant to the user, and it is not one. Tenancy roles went to `c.var.acting`
|
|
21
|
+
* with `@pithy-sh/organization`; management access is the control plane's, with its own credential and its
|
|
22
|
+
* own scope type. What is left is an attestation about the caller, which `@pithy-sh/auth`'s `resolveScopes`
|
|
23
|
+
* makes on every request from something the request proves — a service binding, a verified client
|
|
24
|
+
* certificate, a short-lived token bound to this origin — and never from anything it merely says (#560).
|
|
18
25
|
*/
|
|
19
26
|
export const AuthContext = z
|
|
20
27
|
.object({
|
|
21
28
|
userId: z.string().describe("ID of the authenticated user (populated by @pithy-sh/auth)."),
|
|
22
29
|
sessionId: z.string().describe("ID of the active session this request belongs to."),
|
|
23
|
-
scopes: z
|
|
30
|
+
scopes: z
|
|
31
|
+
.array(z.string())
|
|
32
|
+
.default([])
|
|
33
|
+
.describe(
|
|
34
|
+
"Attestations about the caller, not permissions granted to the user: what this request's caller was established to be — the adopter's own trusted backend, say — by the `resolveScopes` seam on `@pithy-sh/auth`, resolved per request and never cached. Empty when no resolver is configured. Tenancy roles are not here; they are `c.var.acting`.",
|
|
35
|
+
),
|
|
24
36
|
locale: Locale.nullish().describe(
|
|
25
37
|
"The reader's own stored language, from `pithy_auth_users.locale`; null or absent when they have never chosen.",
|
|
26
38
|
),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import { Hono } from "hono";
|
|
5
|
+
import type { PithyHonoEnv, ScopeGate } from "../capability/capability";
|
|
6
|
+
import { routeSpecOf } from "./routeSpec";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* **A capability's scope gates, read off its own routes rather than listed beside them (#560).**
|
|
10
|
+
*
|
|
11
|
+
* `Capability.scopeGates` is what `pithy doctor` names when nothing composed fills `AuthContext.scopes`.
|
|
12
|
+
* A list written out by hand next to the routes is a second copy of something the routes already say: every
|
|
13
|
+
* gated route carries `describeRoute({ scopes })`, because that is how the OpenAPI contract learns it. So the
|
|
14
|
+
* registrar is run against a throwaway app and the specs are read back — the same walk `openApiDocument`
|
|
15
|
+
* makes over a composed app, and a route whose scope changes changes what this returns with it.
|
|
16
|
+
*
|
|
17
|
+
* **Only `bearer` and `session` routes.** Those are the strategies `requireAuth()` takes, so a scope on one
|
|
18
|
+
* is an `AuthContext` scope. A `control-plane` route also names scopes, and they are a different credential
|
|
19
|
+
* entirely — a signed management token with its own type, never `c.var.auth` — so naming one here would tell
|
|
20
|
+
* an adopter to configure a resolver that could never open it.
|
|
21
|
+
*/
|
|
22
|
+
export function scopeGatesOf(register: (app: Hono<PithyHonoEnv>) => void): ScopeGate[] {
|
|
23
|
+
const app = new Hono<PithyHonoEnv>();
|
|
24
|
+
register(app);
|
|
25
|
+
const gates: ScopeGate[] = [];
|
|
26
|
+
for (const route of app.routes) {
|
|
27
|
+
const spec = routeSpecOf(route.handler);
|
|
28
|
+
if (!spec?.scopes || spec.scopes.length === 0) continue;
|
|
29
|
+
const verification = [spec.verification].flat();
|
|
30
|
+
if (!verification.some((strategy) => strategy === "bearer" || strategy === "session")) continue;
|
|
31
|
+
for (const scope of spec.scopes) gates.push({ scope, route: `${route.method} ${route.path}` });
|
|
32
|
+
}
|
|
33
|
+
return gates;
|
|
34
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -101,6 +101,8 @@ export {
|
|
|
101
101
|
ControlPlaneScope,
|
|
102
102
|
KEYS_ROTATE_SCOPE,
|
|
103
103
|
MANIFEST_READ_SCOPE,
|
|
104
|
+
MIGRATIONS_READ_SCOPE,
|
|
105
|
+
PRIVACY_READ_SCOPE,
|
|
104
106
|
SEAM_SCOPES,
|
|
105
107
|
} from "./controlPlane/scope/scope";
|
|
106
108
|
export { exportPublicJwk, type MintControlPlaneToken, mintControlPlaneToken } from "./controlPlane/token/mint";
|
package/src/logger/logger.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import type { ErrorPayload } from "../error/payload";
|
|
5
5
|
import { PithyError } from "../error/pithyError";
|
|
6
|
+
import type { Loggable, UnmarkedString } from "../privacy/types";
|
|
6
7
|
import { LOG_LEVEL_ORDER, type LogFields, type LogLevel, type LogRecord } from "./record";
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -15,22 +16,28 @@ import { LOG_LEVEL_ORDER, type LogFields, type LogLevel, type LogRecord } from "
|
|
|
15
16
|
* The reserved `error` field on any call may carry a `PithyError` or `ErrorPayload` — the engine lifts
|
|
16
17
|
* it into the record's `error`, `detail` and all. A log is internal; this is the inverse of the HTTP
|
|
17
18
|
* codec, which strips `detail`. The logger must never be wired to a client-facing surface.
|
|
19
|
+
*
|
|
20
|
+
* **A value marked with a privacy class does not compile here** (#132). Every method takes `F & Loggable<F>`,
|
|
21
|
+
* so a field holding a `pii()`/`phi()`/`secret()`/`pci()` value — the value itself, or one inside an object or
|
|
22
|
+
* an array, to the depth `privacy/types.ts` states — is refused at that key, and `redact(Schema, value)` is the
|
|
23
|
+
* way through. The message and a child's name take `UnmarkedString`, so `log.info(user.email)` is refused too. `LogFields` the schema stays permissive: a record that reaches a sink at runtime still parses,
|
|
24
|
+
* because a brand is a type and a parsed log line has none.
|
|
18
25
|
*/
|
|
19
26
|
export interface Logger {
|
|
20
27
|
/** Dev-time diagnostics. Dropped unless the logger's level is `debug`. */
|
|
21
|
-
debug(msg:
|
|
28
|
+
debug<F extends LogFields = LogFields>(msg: UnmarkedString, fields?: F & Loggable<F>): void;
|
|
22
29
|
/** Normal operational events. */
|
|
23
|
-
info(msg:
|
|
30
|
+
info<F extends LogFields = LogFields>(msg: UnmarkedString, fields?: F & Loggable<F>): void;
|
|
24
31
|
/** A recoverable problem worth attention. */
|
|
25
|
-
warn(msg:
|
|
32
|
+
warn<F extends LogFields = LogFields>(msg: UnmarkedString, fields?: F & Loggable<F>): void;
|
|
26
33
|
/** A failure that was observed. Pass `{ error }` (a PithyError) to carry its full payload. */
|
|
27
|
-
error(msg:
|
|
34
|
+
error<F extends LogFields = LogFields>(msg: UnmarkedString, fields?: F & Loggable<F>): void;
|
|
28
35
|
/**
|
|
29
36
|
* A namespaced sub-logger. `name` composes as `parent:child`; bound `fields` merge into every record
|
|
30
37
|
* the child emits. Omit `name` (pass `undefined`) to bind fields only — how request-correlation
|
|
31
38
|
* attaches `request`/`method`/`path` without renaming the logger.
|
|
32
39
|
*/
|
|
33
|
-
child(name?:
|
|
40
|
+
child<F extends LogFields = LogFields>(name?: UnmarkedString, fields?: F & Loggable<F>): Logger;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/** A sink receives a finished {@link LogRecord}. Adapters supply one; the engine builds records and calls it. */
|