@runeditrun/rer 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +154 -2
- package/bin/rer.mjs +3 -0
- package/cli/README.md +243 -0
- package/cli/adapter.mjs +10 -0
- package/cli/ai-local.mjs +65 -0
- package/cli/archive-path.mjs +52 -0
- package/cli/archive-vite.mjs +55 -0
- package/cli/archive.mjs +8 -0
- package/cli/auth-copy.mjs +80 -0
- package/cli/bounded.mjs +70 -0
- package/cli/cache-doctor.mjs +63 -0
- package/cli/container-transfer.mjs +35 -0
- package/cli/container.mjs +62 -0
- package/cli/contract.mjs +25 -0
- package/cli/dependencies.mjs +45 -0
- package/cli/dependency-approval.mjs +33 -0
- package/cli/dependency-doctor.mjs +34 -0
- package/cli/dependency-input.mjs +43 -0
- package/cli/deploy-config.mjs +118 -0
- package/cli/deploy.mjs +37 -0
- package/cli/dev-identity.mjs +80 -0
- package/cli/errors.mjs +14 -0
- package/cli/export.mjs +42 -0
- package/cli/font-cache.mjs +29 -0
- package/cli/hosted-output.mjs +110 -0
- package/cli/independent.mjs +29 -0
- package/cli/inspect.mjs +174 -0
- package/cli/jsonc.mjs +111 -0
- package/cli/lifecycle.mjs +148 -0
- package/cli/local.mjs +100 -0
- package/cli/main.mjs +57 -0
- package/cli/managed-server.mjs +143 -0
- package/cli/node-runtime-cache.mjs +68 -0
- package/cli/node-runtime-discovery.mjs +56 -0
- package/cli/node-runtime-download.mjs +59 -0
- package/cli/node-runtime.mjs +50 -0
- package/cli/output.mjs +22 -0
- package/cli/parse.mjs +77 -0
- package/cli/port.mjs +52 -0
- package/cli/portability-worker.mjs +56 -0
- package/cli/portability.mjs +66 -0
- package/cli/preview-cache.mjs +99 -0
- package/cli/project.mjs +48 -0
- package/cli/release.mjs +24 -0
- package/cli/runtime-front-door.mjs +46 -0
- package/cli/scheduled.mjs +81 -0
- package/cli/server-activity.mjs +12 -0
- package/cli/server-proxy.mjs +93 -0
- package/cli/server-registry.mjs +177 -0
- package/cli/server-resources.mjs +22 -0
- package/cli/settings.mjs +15 -0
- package/cli/setup-answers.mjs +29 -0
- package/cli/setup-input.mjs +113 -0
- package/cli/shared.mjs +7 -0
- package/cli/transfers.mjs +33 -0
- package/cli/version.mjs +36 -0
- package/composition/git-source.mjs +111 -0
- package/edition-authoring/files.mjs +81 -0
- package/edition-authoring/manifest.mjs +65 -0
- package/global/README.md +155 -0
- package/global/checkout.mjs +72 -0
- package/global/cli/command.mjs +13 -0
- package/global/cli/install.mjs +75 -0
- package/global/cli/layout.mjs +45 -0
- package/global/cli/status.mjs +54 -0
- package/global/cli/uninstall.mjs +36 -0
- package/global/delegate.mjs +13 -0
- package/global/launch/capacity.mjs +72 -0
- package/global/launch/dev.mjs +129 -0
- package/global/launch/facts.mjs +93 -0
- package/global/launch/failure.mjs +5 -0
- package/global/launch/local-defaults.mjs +4 -0
- package/global/launch/prepare.mjs +61 -0
- package/global/launch/render.mjs +44 -0
- package/global/launch/state.mjs +97 -0
- package/global/launch/steps.mjs +81 -0
- package/global/launch/worker.mjs +43 -0
- package/global/list.mjs +59 -0
- package/global/main.mjs +53 -0
- package/global/package-info.mjs +38 -0
- package/global/registry.mjs +24 -0
- package/global/rer.mjs +29 -0
- package/global/update/archive.mjs +89 -0
- package/global/update/command.mjs +90 -0
- package/global/update/latest.mjs +6 -0
- package/global/update/notice.mjs +58 -0
- package/global/update/registry.mjs +47 -0
- package/global/version.mjs +29 -0
- package/install.sh +144 -0
- package/modules/ai/LICENSE +21 -0
- package/modules/ai/README.md +90 -0
- package/modules/ai/client.mjs +71 -0
- package/modules/ai/config.mjs +39 -0
- package/modules/ai/dev/index.d.mts +6 -0
- package/modules/ai/dev/index.mjs +64 -0
- package/modules/ai/dev/outbox.mjs +33 -0
- package/modules/ai/errors.mjs +28 -0
- package/modules/ai/fake/index.d.mts +3 -0
- package/modules/ai/fake/index.mjs +82 -0
- package/modules/ai/index.d.mts +10 -0
- package/modules/ai/index.mjs +3 -0
- package/modules/ai/package.json +6 -0
- package/modules/container/README.md +156 -0
- package/modules/container/adapters/d1.mjs +34 -0
- package/modules/container/adapters/egress.mjs +40 -0
- package/modules/container/adapters/jobs.mjs +47 -0
- package/modules/container/adapters/objects.mjs +84 -0
- package/modules/container/adapters/retained.mjs +14 -0
- package/modules/container/adapters/sqlite-error.mjs +7 -0
- package/modules/container/adapters/sqlite-worker.mjs +26 -0
- package/modules/container/assets.mjs +14 -0
- package/modules/container/bindings.mjs +1 -0
- package/modules/container/boundary.mjs +38 -0
- package/modules/container/build.mjs +65 -0
- package/modules/container/deploy-input.mjs +17 -0
- package/modules/container/entrypoint.sh +5 -0
- package/modules/container/host.mjs +43 -0
- package/modules/container/ingress.mjs +23 -0
- package/modules/container/lock.mjs +11 -0
- package/modules/container/migrations.mjs +16 -0
- package/modules/container/node-engine.mjs +39 -0
- package/modules/container/portability-schema.mjs +14 -0
- package/modules/container/remote/deploy.py +76 -0
- package/modules/container/remote/files.py +275 -0
- package/modules/container/remote/resources.py +157 -0
- package/modules/container/runner.mjs +10 -0
- package/modules/container/ssh-plan.mjs +44 -0
- package/modules/container/ssh.mjs +63 -0
- package/modules/container/transfer.mjs +27 -0
- package/modules/container/worker-engine.mjs +32 -0
- package/modules/dependencies/README.md +211 -0
- package/modules/dependencies/approval.mjs +51 -0
- package/modules/dependencies/auth.mjs +40 -0
- package/modules/dependencies/authority.mjs +14 -0
- package/modules/dependencies/crypto.mjs +13 -0
- package/modules/dependencies/index.d.mts +23 -0
- package/modules/dependencies/index.mjs +113 -0
- package/modules/dependencies/logout.mjs +42 -0
- package/modules/dependencies/native.mjs +9 -0
- package/modules/dependencies/protocol.mjs +23 -0
- package/modules/dependencies/schema/approval-attempt.sql +3 -0
- package/modules/dependencies/schema/dependencies.sql +15 -0
- package/modules/dependencies/store.mjs +19 -0
- package/modules/deploy-kit/README.md +308 -0
- package/modules/deploy-kit/artifact.mjs +47 -0
- package/modules/deploy-kit/build-modules.mjs +21 -0
- package/modules/deploy-kit/check.mjs +13 -0
- package/modules/deploy-kit/cli.mjs +50 -0
- package/modules/deploy-kit/container-config.mjs +8 -0
- package/modules/deploy-kit/default-stage.mjs +14 -0
- package/modules/deploy-kit/deploy.mjs +80 -0
- package/modules/deploy-kit/health-request.mjs +86 -0
- package/modules/deploy-kit/health.mjs +99 -0
- package/modules/deploy-kit/index.mjs +12 -0
- package/modules/deploy-kit/local-only.mjs +10 -0
- package/modules/deploy-kit/migrations.mjs +48 -0
- package/modules/deploy-kit/owner-cli.mjs +63 -0
- package/modules/deploy-kit/owner.mjs +297 -0
- package/modules/deploy-kit/prepare.mjs +62 -0
- package/modules/deploy-kit/provider.mjs +99 -0
- package/modules/deploy-kit/queue-consumer.mjs +10 -0
- package/modules/deploy-kit/receipt.mjs +16 -0
- package/modules/deploy-kit/render.mjs +47 -0
- package/modules/deploy-kit/resources.mjs +106 -0
- package/modules/deploy-kit/service-grants.mjs +25 -0
- package/modules/deploy-kit/sql-safety.mjs +94 -0
- package/modules/deploy-kit/sql-tokens.mjs +49 -0
- package/modules/deploy-kit/teardown.mjs +100 -0
- package/modules/deploy-kit/templates.mjs +31 -0
- package/modules/deploy-kit/validation.mjs +58 -0
- package/modules/deploy-kit/worker.mjs +105 -0
- package/modules/erasure/CONTRACT.md +90 -0
- package/modules/erasure/README.md +221 -0
- package/modules/erasure/audit.mjs +23 -0
- package/modules/erasure/blobs.mjs +45 -0
- package/modules/erasure/cascade.mjs +91 -0
- package/modules/erasure/completion.mjs +62 -0
- package/modules/erasure/declaration.mjs +51 -0
- package/modules/erasure/fence.mjs +19 -0
- package/modules/erasure/index.d.mts +62 -0
- package/modules/erasure/index.mjs +85 -0
- package/modules/erasure/order.mjs +56 -0
- package/modules/erasure/plan.mjs +121 -0
- package/modules/erasure/rate.mjs +57 -0
- package/modules/erasure/receipt.mjs +32 -0
- package/modules/erasure/root.mjs +21 -0
- package/modules/erasure/schema/erasure-rates.sql +5 -0
- package/modules/erasure/schema/erasure.sql +27 -0
- package/modules/erasure/scope.mjs +29 -0
- package/modules/erasure/sql.mjs +23 -0
- package/modules/erasure/verification.mjs +69 -0
- package/modules/integrations/core/CONTRACT.md +50 -0
- package/modules/integrations/core/README.md +27 -0
- package/modules/integrations/core/crypto.ts +19 -0
- package/modules/integrations/core/database.ts +21 -0
- package/modules/integrations/core/delivery.ts +97 -0
- package/modules/integrations/core/destination.ts +13 -0
- package/modules/integrations/core/index.ts +7 -0
- package/modules/integrations/core/node-transport.ts +72 -0
- package/modules/integrations/core/public-address.ts +57 -0
- package/modules/integrations/core/repository.ts +74 -0
- package/modules/integrations/core/schema.sql +36 -0
- package/modules/integrations/core/sqlite.ts +20 -0
- package/modules/integrations/core/timestamped-body-signature.ts +38 -0
- package/modules/integrations/core/tokens.ts +71 -0
- package/modules/integrations/core/types.ts +52 -0
- package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
- package/modules/integrations/core/webhook.ts +73 -0
- package/modules/job-queue/README.md +173 -0
- package/modules/job-queue/cli.mjs +64 -0
- package/modules/job-queue/command.mjs +64 -0
- package/modules/job-queue/config.mjs +48 -0
- package/modules/job-queue/identity.mjs +48 -0
- package/modules/job-queue/index.mjs +135 -0
- package/modules/job-queue/membership.mjs +30 -0
- package/modules/job-queue/module.json +10 -0
- package/modules/job-queue/policy.mjs +29 -0
- package/modules/job-queue/processes.mjs +80 -0
- package/modules/job-queue/resources.mjs +37 -0
- package/modules/job-queue/storage.mjs +64 -0
- package/modules/job-queue/supervisor.mjs +30 -0
- package/modules/job-queue/waiting.mjs +16 -0
- package/modules/mail/LICENSE +21 -0
- package/modules/mail/README.md +336 -0
- package/modules/mail/doctor.mjs +81 -0
- package/modules/mail/index.mjs +8 -0
- package/modules/mail/live-proof/README.md +113 -0
- package/modules/mail/live-proof/run.mjs +55 -0
- package/modules/mail/package.json +19 -0
- package/modules/mail/pnpm-lock.yaml +537 -0
- package/modules/mail/src/adapters/cloudflare/README.md +84 -0
- package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
- package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
- package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
- package/modules/mail/src/adapters/messaging/README.md +92 -0
- package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
- package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
- package/modules/mail/src/adapters/messaging/index.mjs +61 -0
- package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
- package/modules/mail/src/adapters/messaging/request.mjs +52 -0
- package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
- package/modules/mail/src/adapters/resend/README.md +120 -0
- package/modules/mail/src/adapters/resend/index.mjs +22 -0
- package/modules/mail/src/adapters/resend/response.mjs +54 -0
- package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
- package/modules/mail/src/adapters/resend/size.mjs +99 -0
- package/modules/mail/src/adapters/sendgrid/README.md +75 -0
- package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
- package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
- package/modules/mail/src/core/errors.mjs +9 -0
- package/modules/mail/src/core/transport.mjs +33 -0
- package/modules/mail/src/core/validation.mjs +172 -0
- package/modules/mail/src/development/index.d.mts +37 -0
- package/modules/mail/src/development/index.mjs +35 -0
- package/modules/mail/src/development/outbox.mjs +78 -0
- package/modules/mail/src/development/schema.sql +9 -0
- package/modules/mail/src/development/viewer-html.mjs +42 -0
- package/modules/mail/src/development/viewer.mjs +65 -0
- package/modules/mail/src/doctor/README.md +107 -0
- package/modules/mail/src/doctor/common.mjs +64 -0
- package/modules/mail/src/doctor/dns.mjs +102 -0
- package/modules/mail/src/doctor/index.mjs +28 -0
- package/modules/mail/src/doctor/probe.mjs +66 -0
- package/modules/mail/src/doctor/receive.mjs +81 -0
- package/modules/mail/src/doctor/records.mjs +45 -0
- package/modules/mail/src/events/README.md +268 -0
- package/modules/mail/src/events/d1-schema.sql +25 -0
- package/modules/mail/src/events/d1-storage.mjs +76 -0
- package/modules/mail/src/events/index.mjs +3 -0
- package/modules/mail/src/events/intake.mjs +201 -0
- package/modules/mail/src/events/storage.mjs +52 -0
- package/modules/mail/src/events/verify.mjs +92 -0
- package/modules/mail/src/inbound/README.md +137 -0
- package/modules/mail/src/inbound/bytes.mjs +91 -0
- package/modules/mail/src/inbound/headers.mjs +143 -0
- package/modules/mail/src/inbound/index.mjs +29 -0
- package/modules/mail/src/inbound/mime.mjs +36 -0
- package/modules/mail/src/inbound/multipart.mjs +27 -0
- package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
- package/modules/mail/src/inbound/resend.mjs +54 -0
- package/modules/mail/src/inbound/sendgrid.mjs +60 -0
- package/modules/mail/src/sender.mjs +33 -0
- package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
- package/modules/mail/src/sendgrid-email/index.mjs +86 -0
- package/modules/mail/src/unsubscribe/index.mjs +142 -0
- package/modules/operator-auth/README.md +599 -0
- package/modules/operator-auth/accounts/adapter.mjs +41 -0
- package/modules/operator-auth/accounts/admin.mjs +159 -0
- package/modules/operator-auth/accounts/credentials.mjs +27 -0
- package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
- package/modules/operator-auth/accounts/index.d.mts +9 -0
- package/modules/operator-auth/accounts/index.mjs +9 -0
- package/modules/operator-auth/accounts/native.d.mts +38 -0
- package/modules/operator-auth/accounts/native.mjs +171 -0
- package/modules/operator-auth/accounts/oa.mjs +90 -0
- package/modules/operator-auth/accounts/provider-control.mjs +50 -0
- package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
- package/modules/operator-auth/accounts/roles.mjs +26 -0
- package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
- package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
- package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
- package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
- package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
- package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
- package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
- package/modules/operator-auth/accounts/settings-page.mjs +4 -0
- package/modules/operator-auth/accounts/store.mjs +246 -0
- package/modules/operator-auth/branding.mjs +66 -0
- package/modules/operator-auth/config.mjs +35 -0
- package/modules/operator-auth/conformance/README.md +81 -0
- package/modules/operator-auth/conformance/index.d.mts +43 -0
- package/modules/operator-auth/conformance/index.mjs +300 -0
- package/modules/operator-auth/conformance/presentation.mjs +57 -0
- package/modules/operator-auth/crypto.mjs +17 -0
- package/modules/operator-auth/dev/badge.mjs +62 -0
- package/modules/operator-auth/dev/index.d.mts +14 -0
- package/modules/operator-auth/dev/index.mjs +137 -0
- package/modules/operator-auth/dev/personas.mjs +17 -0
- package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
- package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
- package/modules/operator-auth/dev-admission.d.mts +4 -0
- package/modules/operator-auth/dev-admission.mjs +10 -0
- package/modules/operator-auth/dev-gate.mjs +86 -0
- package/modules/operator-auth/docs/README.md +78 -0
- package/modules/operator-auth/docs/native-accounts.md +313 -0
- package/modules/operator-auth/docs/tanstack-start.md +218 -0
- package/modules/operator-auth/docs/vite-spa.md +144 -0
- package/modules/operator-auth/errors.mjs +9 -0
- package/modules/operator-auth/http.mjs +42 -0
- package/modules/operator-auth/identity/admission.mjs +105 -0
- package/modules/operator-auth/identity/after-confirm.mjs +18 -0
- package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
- package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
- package/modules/operator-auth/identity/email-link/index.mjs +128 -0
- package/modules/operator-auth/identity/email-link/message.mjs +16 -0
- package/modules/operator-auth/identity/index.d.mts +7 -0
- package/modules/operator-auth/identity/index.mjs +5 -0
- package/modules/operator-auth/identity/oidc/README.md +171 -0
- package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
- package/modules/operator-auth/identity/oidc/config.mjs +102 -0
- package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
- package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
- package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
- package/modules/operator-auth/identity/oidc/index.mjs +287 -0
- package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
- package/modules/operator-auth/identity/oidc/network.mjs +93 -0
- package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
- package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
- package/modules/operator-auth/identity/oidc/store.mjs +68 -0
- package/modules/operator-auth/identity/principal.mjs +28 -0
- package/modules/operator-auth/index.d.mts +223 -0
- package/modules/operator-auth/index.mjs +11 -0
- package/modules/operator-auth/input.mjs +55 -0
- package/modules/operator-auth/local-cli.mjs +16 -0
- package/modules/operator-auth/page.mjs +34 -0
- package/modules/operator-auth/policy.mjs +20 -0
- package/modules/operator-auth/routes.mjs +91 -0
- package/modules/operator-auth/service.mjs +152 -0
- package/modules/operator-auth/signin/index.mjs +67 -0
- package/modules/owner-claim/README.md +137 -0
- package/modules/owner-claim/idp.mjs +40 -0
- package/modules/owner-claim/index.d.mts +27 -0
- package/modules/owner-claim/index.mjs +39 -0
- package/modules/owner-claim/page.mjs +11 -0
- package/modules/owner-claim/schema/idp.sql +4 -0
- package/modules/owner-claim/schema/owner-claim.sql +18 -0
- package/modules/owner-claim/transaction.mjs +87 -0
- package/modules/owner-claim/verify.mjs +80 -0
- package/modules/portability/README.md +444 -0
- package/modules/portability/bridge.mjs +102 -0
- package/modules/portability/catalogue.mjs +118 -0
- package/modules/portability/cli-options.mjs +38 -0
- package/modules/portability/cli.mjs +56 -0
- package/modules/portability/copies.mjs +68 -0
- package/modules/portability/dev-data.mjs +32 -0
- package/modules/portability/diff.mjs +33 -0
- package/modules/portability/export.mjs +129 -0
- package/modules/portability/files.mjs +65 -0
- package/modules/portability/format.mjs +122 -0
- package/modules/portability/generated.mjs +60 -0
- package/modules/portability/hash.mjs +79 -0
- package/modules/portability/http.mjs +42 -0
- package/modules/portability/import.mjs +185 -0
- package/modules/portability/index.d.mts +58 -0
- package/modules/portability/index.mjs +22 -0
- package/modules/portability/journal.mjs +49 -0
- package/modules/portability/legacy-files.mjs +43 -0
- package/modules/portability/legacy-safety.mjs +27 -0
- package/modules/portability/lines.mjs +36 -0
- package/modules/portability/migration-history.mjs +18 -0
- package/modules/portability/migration-statements.mjs +23 -0
- package/modules/portability/payloads.mjs +52 -0
- package/modules/portability/provider-data.mjs +25 -0
- package/modules/portability/restore-safety.mjs +30 -0
- package/modules/portability/schema/portability.sql +25 -0
- package/modules/portability/storage.mjs +49 -0
- package/modules/release/README.md +212 -0
- package/modules/release/catalogue.mjs +79 -0
- package/modules/release/export.mjs +51 -0
- package/modules/release/produce.mjs +136 -0
- package/modules/release/public-child-transport-state.cjs +29 -0
- package/modules/release/public-child.mjs +71 -0
- package/modules/release/public-git.mjs +21 -0
- package/modules/release/publication-scan.mjs +22 -0
- package/modules/release/publish-handoff.mjs +37 -0
- package/modules/release/source.mjs +71 -0
- package/modules/release/test-summary.mjs +50 -0
- package/onboarding/README.md +163 -0
- package/onboarding/activity.mjs +31 -0
- package/onboarding/adapter-auth.mjs +88 -0
- package/onboarding/adapter-hosting.mjs +35 -0
- package/onboarding/adapter-labels.mjs +13 -0
- package/onboarding/adapter-settings.mjs +129 -0
- package/onboarding/adapter.mjs +238 -0
- package/onboarding/agent-handoff.mjs +9 -0
- package/onboarding/app-processes.mjs +21 -0
- package/onboarding/archive-output.d.mts +3 -0
- package/onboarding/archive-output.mjs +54 -0
- package/onboarding/command-custody.mjs +24 -0
- package/onboarding/command-recovery.mjs +8 -0
- package/onboarding/commands.mjs +166 -0
- package/onboarding/dependency-approval.mjs +36 -0
- package/onboarding/dev-identity.mjs +23 -0
- package/onboarding/email.mjs +24 -0
- package/onboarding/failures.mjs +42 -0
- package/onboarding/input-limits.mjs +7 -0
- package/onboarding/installer-inputs.mjs +24 -0
- package/onboarding/launcher.mjs +34 -0
- package/onboarding/local/build-files.mjs +64 -0
- package/onboarding/local/build.mjs +49 -0
- package/onboarding/local/environment.mjs +30 -0
- package/onboarding/local/files.mjs +35 -0
- package/onboarding/local/operation.mjs +166 -0
- package/onboarding/local/owner.mjs +34 -0
- package/onboarding/local/process-table.mjs +114 -0
- package/onboarding/local/recovery.mjs +79 -0
- package/onboarding/local/state.mjs +31 -0
- package/onboarding/metadata.mjs +7 -0
- package/onboarding/operator-install.mjs +45 -0
- package/onboarding/operator-installer.mjs +39 -0
- package/onboarding/operator-prerequisites.mjs +27 -0
- package/onboarding/package.json +6 -0
- package/onboarding/prefill.mjs +16 -0
- package/onboarding/private-files.mjs +97 -0
- package/onboarding/private.mjs +35 -0
- package/onboarding/process-cleanup.mjs +134 -0
- package/onboarding/process-diagnostics.mjs +91 -0
- package/onboarding/process-scope.mjs +118 -0
- package/onboarding/process-table.mjs +118 -0
- package/onboarding/process-tree.mjs +36 -0
- package/onboarding/production-modules.mjs +22 -0
- package/onboarding/production-options.mjs +40 -0
- package/onboarding/production-plan.mjs +8 -0
- package/onboarding/production-runtime.mjs +30 -0
- package/onboarding/project-vite.mjs +14 -0
- package/onboarding/providers.mjs +289 -0
- package/onboarding/queue.mjs +53 -0
- package/onboarding/queued-entry.mjs +34 -0
- package/onboarding/readiness.mjs +59 -0
- package/onboarding/redaction-jwt.mjs +32 -0
- package/onboarding/redaction.mjs +298 -0
- package/onboarding/schema/SETUP.schema.json +514 -0
- package/onboarding/schema/dependencies.mjs +35 -0
- package/onboarding/schema/json-schema.mjs +202 -0
- package/onboarding/schema/setup-validation.mjs +325 -0
- package/onboarding/secrets.mjs +21 -0
- package/onboarding/server.mjs +188 -0
- package/onboarding/setup-manifest.mjs +116 -0
- package/onboarding/state.mjs +46 -0
- package/onboarding/theme-files.mjs +37 -0
- package/onboarding/theme.mjs +118 -0
- package/onboarding/ui/activity.js +98 -0
- package/onboarding/ui/address-client.mjs +33 -0
- package/onboarding/ui/app.js +377 -0
- package/onboarding/ui/index.html +1 -0
- package/onboarding/ui/model-core.mjs +127 -0
- package/onboarding/ui/model.mjs +5 -0
- package/onboarding/ui/style.css +63 -0
- package/onboarding/ui/theme.css +37 -0
- package/onboarding/ui/workspace-art.js +30 -0
- package/onboarding/wrangler-auth.mjs +43 -0
- package/operations/README.md +246 -0
- package/operations/operations.mjs +168 -0
- package/operations/project-operations.mjs +157 -0
- package/package.json +11 -4
- package/scripts/publish-check/README.md +72 -0
- package/scripts/publish-check/checksum_maps.py +52 -0
- package/scripts/publish-check/cli.py +34 -0
- package/scripts/publish-check/command_arguments.py +51 -0
- package/scripts/publish-check/command_shell_quotes.py +41 -0
- package/scripts/publish-check/command_source_quotes.py +120 -0
- package/scripts/publish-check/content_identities.py +250 -0
- package/scripts/publish-check/copy_identities.py +41 -0
- package/scripts/publish-check/der.py +61 -0
- package/scripts/publish-check/destination.py +117 -0
- package/scripts/publish-check/env_values.py +74 -0
- package/scripts/publish-check/font_licence.py +23 -0
- package/scripts/publish-check/git_history.py +115 -0
- package/scripts/publish-check/github_refs.py +217 -0
- package/scripts/publish-check/gitleaks.py +72 -0
- package/scripts/publish-check/gitleaks.toml +40 -0
- package/scripts/publish-check/history_scan.py +176 -0
- package/scripts/publish-check/hit_location.py +18 -0
- package/scripts/publish-check/hygiene_config.py +42 -0
- package/scripts/publish-check/hygiene_text.py +11 -0
- package/scripts/publish-check/identity-recipe.mjs +54 -0
- package/scripts/publish-check/json_identity.py +79 -0
- package/scripts/publish-check/license-exceptions.json +11 -0
- package/scripts/publish-check/licenses.py +49 -0
- package/scripts/publish-check/policy.py +18 -0
- package/scripts/publish-check/project_identity.py +89 -0
- package/scripts/publish-check/provenance.py +91 -0
- package/scripts/publish-check/provenance_pins.py +33 -0
- package/scripts/publish-check/public_base.py +63 -0
- package/scripts/publish-check/public_hygiene.py +80 -0
- package/scripts/publish-check/publication-config.json +12 -0
- package/scripts/publish-check/publication_config.py +34 -0
- package/scripts/publish-check/report.py +52 -0
- package/scripts/publish-check/repository_commands.py +75 -0
- package/scripts/publish-check/repository_intent.py +52 -0
- package/scripts/publish-check/reviewed.py +41 -0
- package/scripts/publish-check/source_hashes.mjs +6 -0
- package/scripts/publish-check/tree_scan.py +88 -0
- package/scripts/publish-check/trust_bundle.py +87 -0
- package/scripts/publish-check.mjs +58 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { nativeDependencyAuthority } from './native.mjs';
|
|
2
|
+
import { createIdpOwnerClaimHandler } from '../owner-claim/idp.mjs';
|
|
3
|
+
import { createOidcProviders } from '../operator-auth/identity/oidc/index.mjs';
|
|
4
|
+
import { dependencyStore } from './store.mjs';
|
|
5
|
+
import { backchannelLogout } from './logout.mjs';
|
|
6
|
+
// Resolve the persisted server key at use, so Worker instances never cache a removed link.
|
|
7
|
+
export function linkedProviders(options) {
|
|
8
|
+
const {db,config,staticProviders,native}=options;
|
|
9
|
+
if(native)nativeDependencyAuthority(native);
|
|
10
|
+
if(config.dependencies && (staticProviders?.ids.includes('auth') || staticProviders?.callbackPaths?.['/auth/oidc/auth/callback'])) throw new Error('dependency_provider_conflict');
|
|
11
|
+
async function current() {
|
|
12
|
+
if (!config.dependencies) return staticProviders;
|
|
13
|
+
const store=dependencyStore(db),link=await store.link();
|
|
14
|
+
if(!link || link.needs_relink) return staticProviders;
|
|
15
|
+
const row=await store.first("SELECT private_key FROM rer_dependency_keys WHERE interface='oidc-provider@1'");
|
|
16
|
+
if(!row?.private_key) return null;
|
|
17
|
+
const unclaimed=native?()=>native.unclaimed():async()=>!await db.prepare('SELECT 1 FROM oa_operators LIMIT 1').first();
|
|
18
|
+
return createOidcProviders({...options, enabled:async()=>await options.enabled() || await unclaimed(), claim:async(request,token,expectedPin)=>{
|
|
19
|
+
if(!await unclaimed())return null;
|
|
20
|
+
if(native)return native.claimOwner?native.claimOwner({request,token,expectedPin}):null;
|
|
21
|
+
return createIdpOwnerClaimHandler({expectedPin,db,authConfig:config,clientIp:()=>options.clientIp(request),clock:options.clock,fetch:options.fetch})(new Request(config.origin+'/auth/owner-claim',{method:'POST',headers:{Host:new URL(config.origin).host,Origin:config.origin,'Content-Type':'application/json'},body:JSON.stringify({token})}));
|
|
22
|
+
}, claimScope: unclaimed, config:{origin:config.origin,local:config.local,transactionSecret:config.OIDC_TRANSACTION_SECRET ?? config.SESSION_SECRET+'-oidc',providers:[{
|
|
23
|
+
id:'auth',preset:'auth',issuer:link.issuer,clientId:link.client_id,privateKey:JSON.parse(row.private_key),
|
|
24
|
+
}]}});
|
|
25
|
+
}
|
|
26
|
+
if(!config.dependencies) return staticProviders;
|
|
27
|
+
return {
|
|
28
|
+
ids:[...new Set(['auth',...(staticProviders?.ids??[])])],
|
|
29
|
+
callbackPaths:{...staticProviders?.callbackPaths,'/auth/oidc/auth/callback':'auth'},
|
|
30
|
+
async begin(...args){const p=args[1]!=='auth'?staticProviders:await current();return p?p.begin(...args):Response.json({error:'provider_unlinked'},{status:403});},
|
|
31
|
+
async callback(...args){const p=args[1]!=='auth'?staticProviders:await current();return p?p.callback(...args):Response.json({error:'provider_unlinked'},{status:403});},
|
|
32
|
+
async logout(request){return backchannelLogout({...options,request});},
|
|
33
|
+
async list(){const p=await current();return p===staticProviders?(p?.list()??[]):[...await staticProviders?.list()??[],...await p?.list()??[]];},
|
|
34
|
+
async links(){const p=await current();const links=[...await staticProviders?.links()??[],...(p!==staticProviders?await p?.links()??[]:[])];return [...new Map(links.map(link=>[link.id,link])).values()];},
|
|
35
|
+
// The Auth dependency is linked and unlinked through its own approval routes;
|
|
36
|
+
// a static provider keeps its own pin controls.
|
|
37
|
+
async relink(id){if(id==='auth')throw new Error('Use the dependency link with fresh local owner proof');return staticProviders.relink(id);},
|
|
38
|
+
async unlink(id){if(id==='auth')throw new Error('Use the dependency unlink with fresh local owner proof');return staticProviders.unlink(id);},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { providerPinFence } from '../operator-auth/accounts/provider-pin.mjs';
|
|
2
|
+
// Repeat local owner authority inside the mutation transaction.
|
|
3
|
+
export function ownerFence(actor, now) {
|
|
4
|
+
const pin=providerPinFence(actor.providerPin);
|
|
5
|
+
return {sql:`${pin.sql} AND EXISTS(SELECT 1 FROM oa_sessions s JOIN oa_operators o ON o.id=s.operator_id
|
|
6
|
+
WHERE s.id=? AND o.id=? AND o.version=? AND s.operator_version=o.version AND s.revoked_at IS NULL AND s.expires_at>?
|
|
7
|
+
AND o.status='active' AND o.id=(SELECT p.id FROM oa_operators p LEFT JOIN oa_bootstrap b ON b.claim_id=p.id WHERE p.role='owner' AND p.status='active' ORDER BY b.claim_id IS NULL,p.created_at,p.id LIMIT 1))`,args:[...pin.args,actor.id,actor.operator.id,actor.operator.version,now]};
|
|
8
|
+
}
|
|
9
|
+
export function pinStatement(db, clientId, metadata, now) {
|
|
10
|
+
return db.prepare(`INSERT INTO oa_identity_providers(provider_id,preset,status,issuer,client_id,jwks_uri,authorization_endpoint,token_endpoint,algorithms,token_auth_method,response_iss,interface_version,linked_at)
|
|
11
|
+
VALUES('auth','auth','linked',?,?,?,?,?,?,'private_key_jwt',?,?,?) ON CONFLICT(provider_id) DO UPDATE SET
|
|
12
|
+
preset='auth',status='linked',issuer=excluded.issuer,client_id=excluded.client_id,jwks_uri=excluded.jwks_uri,authorization_endpoint=excluded.authorization_endpoint,token_endpoint=excluded.token_endpoint,algorithms=excluded.algorithms,token_auth_method=excluded.token_auth_method,response_iss=excluded.response_iss,interface_version=excluded.interface_version,linked_at=MAX(oa_identity_providers.linked_at+1,excluded.linked_at),pending_issuer=NULL,pending_client_id=NULL,pending_preset=NULL,pending_at=NULL,jwks=NULL,jwks_fetched_at=NULL`)
|
|
13
|
+
.bind(metadata.issuer,clientId,metadata.jwks,metadata.authorization,metadata.token,JSON.stringify(metadata.algorithms),metadata.responseIssuer?1:0,metadata.interfaceVersion,now);
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const encode = bytes => btoa(String.fromCharCode(...bytes)).replaceAll('+','-').replaceAll('/','_').replace(/=+$/,'');
|
|
2
|
+
const json = value => encode(new TextEncoder().encode(JSON.stringify(value)));
|
|
3
|
+
export async function keyPair() {
|
|
4
|
+
const pair = await crypto.subtle.generateKey({name:'ECDSA',namedCurve:'P-256'},true,['sign','verify']);
|
|
5
|
+
return {...await crypto.subtle.exportKey('jwk',pair.privateKey),kid:crypto.randomUUID(),alg:'ES256',use:'sig'};
|
|
6
|
+
}
|
|
7
|
+
export function publicKey(key) { return {kty:key.kty,crv:key.crv,x:key.x,y:key.y,kid:key.kid,alg:'ES256',use:'sig',key_ops:['verify']}; }
|
|
8
|
+
export async function assertion(key, claims) {
|
|
9
|
+
const encoded = `${json({alg:'ES256',typ:'JWT',kid:key.kid})}.${json(claims)}`;
|
|
10
|
+
const imported = await crypto.subtle.importKey('jwk',{...key,key_ops:['sign']},{name:'ECDSA',namedCurve:'P-256'},false,['sign']);
|
|
11
|
+
return `${encoded}.${encode(new Uint8Array(await crypto.subtle.sign({name:'ECDSA',hash:'SHA-256'},imported,new TextEncoder().encode(encoded))))}`;
|
|
12
|
+
}
|
|
13
|
+
export async function digest(value) { return encode(new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(value)))); }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Database, OperatorAuth } from '../operator-auth/index.mjs';
|
|
2
|
+
export function createDependencies<R extends string>(options: { db: Database; origin: string; name: string; provider?: string; local?: boolean; auth: OperatorAuth<R>; claimOwner?:(token:string)=>Promise<Response|null>; clock?: () => number; fetch?: typeof fetch; provides?:string[]; interfaces?: {interface:string;optional?:boolean;provider?:string;paths:string[]}[] }): {document():Promise<unknown>;handle(request:Request):Promise<Response|null>};
|
|
3
|
+
export const INTERFACE:string;
|
|
4
|
+
export const CALLBACK:string;
|
|
5
|
+
export const PATHS:string[];
|
|
6
|
+
export interface SqlFence {sql: string; args: unknown[]}
|
|
7
|
+
/** Snapshot captured before provider I/O; equality is repeated by shared code. */
|
|
8
|
+
export interface DependencyApproval {
|
|
9
|
+
interface: string; issuer: string; callbacks: string; attempt_id: string;
|
|
10
|
+
approved_by: string; approved_session: string; approved_version: number; expires_at: number;
|
|
11
|
+
}
|
|
12
|
+
export interface NativeDependencyAuthority<R extends string = string> {
|
|
13
|
+
unclaimed(): Promise<boolean>;
|
|
14
|
+
ownerFence(actor: import('../operator-auth/index.mjs').Session<R>, now: number): SqlFence | Promise<SqlFence>;
|
|
15
|
+
/** Repeat native current-owner/session/version/proof and provider policy/pin authority. Shared code fences the exact pending attempt and actor snapshot. */
|
|
16
|
+
approvalFence(input: {pending: DependencyApproval; contract: string; issuer: string; callbacks: string; now: number}): SqlFence | Promise<SqlFence>;
|
|
17
|
+
/** Ordinary owner approval must never enable identity from a stale first-setup flag. */
|
|
18
|
+
linkedStatements(input: {pending: DependencyApproval; contract: string; old: Record<string, unknown> | null; issuer: string; now: number}): import('../operator-auth/index.mjs').Statement[] | Promise<import('../operator-auth/index.mjs').Statement[]>;
|
|
19
|
+
unlinkStatements(input: {contract: string; actor: import('../operator-auth/index.mjs').Session<R>; now: number}): import('../operator-auth/index.mjs').Statement[] | Promise<import('../operator-auth/index.mjs').Statement[]>;
|
|
20
|
+
/** Atomic replay claim + pinned link/current binding checks + native session revocation/fence bump. */
|
|
21
|
+
consumeLogout(input: {claims: Record<string, unknown>; link: Record<string, unknown>; now: number; fence: boolean}): Promise<boolean>;
|
|
22
|
+
claimOwner?(input: {request: Request; token: string; expectedPin: {id: string; issuer: string; clientId: string; linkedAt: number}}): Promise<Response | null>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { nativeDependencyAuthority } from './native.mjs';
|
|
2
|
+
import { approveSetup, approveOwner, approvalFence, callbacksFor } from './approval.mjs';
|
|
3
|
+
import { dependencyStore } from './store.mjs';
|
|
4
|
+
import { publicKey, assertion, digest } from './crypto.mjs';
|
|
5
|
+
import { CALLBACK, INTERFACE, PATHS, NETWORK, insist, originOf, providerMetadata } from './protocol.mjs';
|
|
6
|
+
import { readJson } from '../operator-auth/identity/oidc/network.mjs';
|
|
7
|
+
import { readInput } from '../operator-auth/input.mjs';
|
|
8
|
+
import { ownerFence, pinStatement } from './authority.mjs';
|
|
9
|
+
export { INTERFACE, CALLBACK, PATHS } from './protocol.mjs';
|
|
10
|
+
export function createDependencies({db,origin,name,provider,interfaces=[{interface:INTERFACE,provider,paths:PATHS}],provides=[],local=false,auth,claimOwner,clock=Date.now,fetch:fetcher=globalThis.fetch}) {
|
|
11
|
+
originOf(origin,local);insist(Array.isArray(provides) && new Set(provides).size===provides.length && provides.every(value=>/^[a-z][a-z0-9-]*@[1-9][0-9]*$/.test(value)));
|
|
12
|
+
insist(Array.isArray(interfaces) && new Set(interfaces.map(d=>d.interface)).size===interfaces.length && interfaces.every(d=>/^[a-z][a-z0-9-]*@[1-9][0-9]*$/.test(d.interface) && Array.isArray(d.paths) && new Set(d.paths).size===d.paths.length && d.paths.every(p=>typeof p==='string' && /^\/[A-Za-z0-9_~/-]+$/.test(p) && !p.startsWith('//'))));
|
|
13
|
+
const store=dependencyStore(db);
|
|
14
|
+
const native=auth?.native?nativeDependencyAuthority(auth.accounts.dependencies):null;
|
|
15
|
+
async function owner(request,action) {
|
|
16
|
+
const actor=await auth.requireStepUp(request,action), current=await auth.accounts.owner();
|
|
17
|
+
insist(actor.operator.id===current?.id,'owner_required',403);return actor;
|
|
18
|
+
}
|
|
19
|
+
async function document() {
|
|
20
|
+
const installation=await store.installation(interfaces[0]?.interface??INTERFACE);
|
|
21
|
+
const linked=await Promise.all(interfaces.map(d=>store.link(d.interface)));
|
|
22
|
+
return {name,version:1,origin,sector_id:installation.sector_id,dependencies:interfaces.map((d,i)=>({provider:linked[i]?.issuer??d.provider??null,interface:d.interface,optional:d.optional??true,cardinality:1,paths:d.paths.map(p=>origin+p),jwks_uri:origin+'/.well-known/rer-link/jwks/'+d.interface})),provides,
|
|
23
|
+
claim:interfaces.some(d=>d.interface===INTERFACE) && (native?await native.unclaimed():!await db.prepare('SELECT 1 FROM oa_operators LIMIT 1').first()),callback:origin+CALLBACK,jwks_uri:origin+'/.well-known/rer-link/jwks'};
|
|
24
|
+
}
|
|
25
|
+
async function callback(request) {
|
|
26
|
+
const data=await readInput(request);
|
|
27
|
+
insist(Object.keys(data).every(k=>['issuer','code','interface'].includes(k)) && typeof data.code==='string' && data.code.length>0 && data.code.length<=4096);
|
|
28
|
+
const contract=data.interface??INTERFACE,declared=interfaces.find(d=>d.interface===contract);insist(declared,'undeclared_interface',403);
|
|
29
|
+
const old=await store.link(contract),pending=await store.first('SELECT * FROM rer_dependency_pending WHERE interface=?',contract);
|
|
30
|
+
const callbacks=callbacksFor(origin,declared);
|
|
31
|
+
insist(pending?.attempt_id && pending.expires_at>clock() && pending.callbacks===callbacks,'local_owner_proof_required',403);
|
|
32
|
+
const expected=pending.issuer;
|
|
33
|
+
insist(data.issuer===expected,'untrusted_link_issuer',403);
|
|
34
|
+
const pendingPin=native?null:await store.first('SELECT provider_id AS id,issuer,client_id AS clientId,linked_at AS linkedAt FROM oa_provider_session_pins WHERE session_id=?',pending.approved_session);
|
|
35
|
+
const initialApproval=await approvalFence(pending,contract,expected,callbacks,clock(),pendingPin,native);
|
|
36
|
+
insist(await store.first(`SELECT 1 WHERE ${initialApproval.sql}`,...initialApproval.args),'local_owner_proof_required',403);
|
|
37
|
+
const metadata=await providerMetadata(expected,fetcher,contract),installation=await store.installation(contract);
|
|
38
|
+
const codeDigest=await digest(expected+'\0'+data.code);
|
|
39
|
+
const consumed=await store.run('INSERT INTO rer_dependency_codes(digest,created_at) VALUES(?,?) ON CONFLICT DO NOTHING',codeDigest,clock());
|
|
40
|
+
insist(consumed.meta.changes===1,'link_code_used',409);
|
|
41
|
+
const now=Math.floor(clock()/1000), proof=await assertion(installation.key,{iss:origin,sub:origin,aud:metadata.link.redeem,iat:now,exp:now+60,jti:crypto.randomUUID(),code:data.code,interface:contract,sector_id:installation.sector_id,callback:origin+CALLBACK});
|
|
42
|
+
const result=await readJson(fetcher,metadata.link.redeem,NETWORK,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({code:data.code,interface:contract,origin,callback:origin+CALLBACK,client_assertion_type:'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',client_assertion:proof})});
|
|
43
|
+
insist(result.issuer===expected && result.interface===contract && result.sector_id===installation.sector_id && result.origin===origin && result.callback===origin+CALLBACK && typeof result.client_id==='string' && result.client_id.length>0 && result.client_id.length<=512,'invalid_link_grant',403);
|
|
44
|
+
if(old && old.issuer===expected) insist(result.client_id===old.client_id && ['previous-key','provider-owner'].includes(result.continuity),'link_continuity_required',403);
|
|
45
|
+
// A provider move never edits identity bindings. Only a new, proven-email sign-in
|
|
46
|
+
// can establish a binding at the new issuer; existing principals remain immutable.
|
|
47
|
+
const unchanged=old?{sql:'EXISTS(SELECT 1 FROM rer_dependency_link WHERE interface=? AND issuer=? AND client_id=? AND metadata=? AND needs_relink=? AND linked_at=?)',args:[contract,old.issuer,old.client_id,JSON.stringify(old.metadata),old.needs_relink,old.linked_at]}:{sql:'NOT EXISTS(SELECT 1 FROM rer_dependency_link WHERE interface=?)',args:[contract]};
|
|
48
|
+
const approval=await approvalFence(pending,contract,expected,callbacks,clock(),pendingPin,native);
|
|
49
|
+
await db.batch([
|
|
50
|
+
db.prepare(`UPDATE rer_dependency_codes SET created_at=CASE WHEN EXISTS(SELECT 1 FROM rer_dependency_keys WHERE interface=? AND json_extract(private_key,'$.kid')=?) AND ${unchanged.sql} AND ${approval.sql} THEN created_at ELSE -1 END WHERE digest=?`).bind(contract,installation.key.kid,...unchanged.args,...approval.args,codeDigest),
|
|
51
|
+
db.prepare(`INSERT INTO rer_dependency_link(interface,issuer,client_id,metadata,needs_relink,linked_at) VALUES(?,?,?,?,0,?) ON CONFLICT(interface) DO UPDATE SET issuer=excluded.issuer,client_id=excluded.client_id,metadata=excluded.metadata,needs_relink=0,linked_at=MAX(rer_dependency_link.linked_at+1,excluded.linked_at)`).bind(contract,expected,result.client_id,JSON.stringify(metadata),clock()),
|
|
52
|
+
...(native?await native.linkedStatements({pending,contract,old,issuer:expected,now:clock()}):[
|
|
53
|
+
...(contract===INTERFACE?[db.prepare("INSERT INTO oa_auth_audit(id,actor_id,action,value,created_at) SELECT ?,claim_id,'idpSignIn','true',? FROM oa_bootstrap WHERE EXISTS(SELECT 1 FROM oa_auth_settings WHERE singleton=1 AND first_setup=1 AND idp_sign_in=0) AND EXISTS(SELECT 1 FROM oa_setup_links WHERE interface='oidc-provider@1' AND attempt_id=? AND completed=0 AND owner_id=claim_id)").bind(crypto.randomUUID(),clock(),pending.attempt_id)]:[]),
|
|
54
|
+
...(contract===INTERFACE?[db.prepare("UPDATE oa_auth_settings SET idp_sign_in=CASE WHEN first_setup=1 AND EXISTS(SELECT 1 FROM oa_setup_links WHERE interface='oidc-provider@1' AND attempt_id=? AND completed=0 AND owner_id=(SELECT claim_id FROM oa_bootstrap)) THEN 1 ELSE idp_sign_in END,first_setup=0 WHERE singleton=1").bind(pending.attempt_id)]:[]),
|
|
55
|
+
db.prepare('UPDATE oa_setup_links SET completed=1 WHERE interface=? AND attempt_id=?').bind(contract,pending.attempt_id),
|
|
56
|
+
]),
|
|
57
|
+
db.prepare('DELETE FROM rer_dependency_pending WHERE interface=?').bind(contract),
|
|
58
|
+
...(contract===INTERFACE?[pinStatement(db,result.client_id,metadata,clock())]:[]),
|
|
59
|
+
...(!native && contract===INTERFACE && old?[db.prepare("UPDATE oa_sessions SET revoked_at=COALESCE(revoked_at,?) WHERE id IN (SELECT p.session_id FROM oa_provider_sessions p LEFT JOIN oa_provider_session_pins pin ON pin.session_id=p.session_id WHERE pin.provider_id='auth' OR (p.issuer=? AND (? OR pin.session_id IS NULL)))").bind(clock(),old.issuer,old.issuer!==expected?1:0)]:[]),
|
|
60
|
+
...(!native && contract===INTERFACE && old && old.issuer!==expected?[db.prepare("DELETE FROM oa_domain_rules WHERE provider_id='auth'")]:[]),
|
|
61
|
+
]);
|
|
62
|
+
if(result.install_owner!==undefined) {
|
|
63
|
+
insist(contract===INTERFACE && typeof result.install_owner==='string' && typeof claimOwner==='function','owner_claim_unavailable',403);
|
|
64
|
+
const claimed=await claimOwner(result.install_owner);
|
|
65
|
+
if(claimed?.status!==303){const detail=await claimed?.json().catch(()=>null);insist(false,typeof detail?.error==='string' && /^[a-z_]+$/.test(detail.error)?detail.error:'owner_claim_refused',claimed?.status??403);}
|
|
66
|
+
}
|
|
67
|
+
return Response.json({linked:true,interface:contract});
|
|
68
|
+
}
|
|
69
|
+
async function unlink(request) {
|
|
70
|
+
const actor=await owner(request,'providers:unlink'),input=await readInput(request),contract=input.interface??INTERFACE,link=await store.link(contract);
|
|
71
|
+
insist(interfaces.some(d=>d.interface===contract));
|
|
72
|
+
const key=await store.first('SELECT private_key FROM rer_dependency_keys WHERE interface=?',contract),fence=native?await native.ownerFence(actor,clock()):ownerFence(actor,clock()),guard=crypto.randomUUID();
|
|
73
|
+
await db.batch([
|
|
74
|
+
db.prepare(`INSERT INTO rer_dependency_codes(digest,created_at) SELECT ?,CASE WHEN ${fence.sql} THEN ? ELSE -1 END`).bind(guard,...fence.args,clock()),
|
|
75
|
+
db.prepare('DELETE FROM rer_dependency_keys WHERE interface=?').bind(contract),
|
|
76
|
+
...(native?await native.unlinkStatements({contract,actor,now:clock()}):[
|
|
77
|
+
...(contract===INTERFACE?[db.prepare("UPDATE oa_sessions SET revoked_at=COALESCE(revoked_at,?) WHERE id IN (SELECT p.session_id FROM oa_provider_sessions p LEFT JOIN oa_provider_session_pins pin ON pin.session_id=p.session_id WHERE pin.provider_id='auth' OR p.issuer=(SELECT issuer FROM rer_dependency_link WHERE interface=?))").bind(clock(),contract)]:[]),
|
|
78
|
+
...(contract===INTERFACE?[db.prepare("DELETE FROM oa_domain_rules WHERE provider_id='auth'")]:[]),
|
|
79
|
+
]),
|
|
80
|
+
...(contract===INTERFACE?[db.prepare("UPDATE oa_identity_providers SET status='unlinked' WHERE provider_id='auth'")]:[]),
|
|
81
|
+
db.prepare('DELETE FROM rer_dependency_pending WHERE interface=?').bind(contract),
|
|
82
|
+
db.prepare('DELETE FROM rer_dependency_link WHERE interface=?').bind(contract),
|
|
83
|
+
...(!native?[db.prepare('DELETE FROM oa_setup_links WHERE interface=?').bind(contract),
|
|
84
|
+
db.prepare("INSERT INTO oa_auth_audit VALUES(?,?,'provider:unlink',?,?)").bind(crypto.randomUUID(),actor.operator.id,contract,clock())]:[]),
|
|
85
|
+
]);
|
|
86
|
+
// Local removal is already complete. The provider may be offline.
|
|
87
|
+
if(link && key) {try {await readJson(fetcher,link.metadata.link.deregister,NETWORK,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({client_id:link.client_id,origin,interface:contract,client_assertion_type:'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',client_assertion:await assertion(JSON.parse(key.private_key),{iss:link.client_id,sub:link.client_id,aud:link.metadata.link.deregister,iat:Math.floor(clock()/1000),exp:Math.floor(clock()/1000)+60,jti:crypto.randomUUID()})})});}catch{}}
|
|
88
|
+
return Response.json({unlinked:true});
|
|
89
|
+
}
|
|
90
|
+
return {document,async handle(request) {
|
|
91
|
+
const u=new URL(request.url),path=u.pathname;
|
|
92
|
+
const jwksContract=path.startsWith('/.well-known/rer-link/jwks/')?path.slice('/.well-known/rer-link/jwks/'.length):null;
|
|
93
|
+
if(!jwksContract && !['/.well-known/rer-dependencies','/.well-known/rer-link/jwks',CALLBACK,'/auth/dependencies/relink','/auth/dependencies/setup','/auth/dependencies/unlink','/auth/dependencies/status'].includes(path))return null;
|
|
94
|
+
try {
|
|
95
|
+
insist(u.origin===origin && (!request.headers.has('host') || request.headers.get('host')===u.host) && !u.search && !u.hash,'invalid_link_origin',403);
|
|
96
|
+
if(path==='/.well-known/rer-dependencies' && request.method==='GET')return Response.json(await document());
|
|
97
|
+
if((path==='/.well-known/rer-link/jwks' || jwksContract) && request.method==='GET') {const contract=jwksContract??interfaces[0]?.interface??INTERFACE;insist(interfaces.some(d=>d.interface===contract));return Response.json({keys:[publicKey((await store.installation(contract)).key)]});}
|
|
98
|
+
if(path==='/auth/dependencies/status' && request.method==='GET') {const link=await store.link(),links=(await db.prepare('SELECT interface,issuer,needs_relink FROM rer_dependency_link').all()).results;return Response.json({linked:!!link && !link.needs_relink,needsRelink:!!link?.needs_relink,links:links.map(r=>({interface:r.interface,issuer:r.issuer,linked:!r.needs_relink}))});}
|
|
99
|
+
insist(request.method==='POST','method_not_allowed',405);
|
|
100
|
+
if(path===CALLBACK)return await callback(request);
|
|
101
|
+
insist(request.headers.get('origin')===origin,'invalid_link_origin',403);
|
|
102
|
+
if(path==='/auth/dependencies/unlink')return await unlink(request);
|
|
103
|
+
if(path==='/auth/dependencies/relink' || path==='/auth/dependencies/setup') {
|
|
104
|
+
const input=await readInput(request),contract=input.interface??INTERFACE;originOf(input.issuer);const declared=interfaces.find(d=>d.interface===contract);insist(declared);
|
|
105
|
+
const callbacks=callbacksFor(origin,declared);
|
|
106
|
+
if(path==='/auth/dependencies/setup') await approveSetup({db,auth,request,input,contract,callbacks,clock});
|
|
107
|
+
else await approveOwner({store,actor:await owner(request,'providers:relink'),input,contract,callbacks,clock,native});
|
|
108
|
+
return Response.json({pending:true});
|
|
109
|
+
}
|
|
110
|
+
insist(false,'not_found',404);
|
|
111
|
+
}catch(e){return Response.json({error:e.code??'link_unavailable'},{status:e.status??503});}
|
|
112
|
+
}};
|
|
113
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { dependencyStore } from './store.mjs';
|
|
2
|
+
import { parseIdToken, verifySignature } from '../operator-auth/identity/oidc/jwt.mjs';
|
|
3
|
+
import { readJson } from '../operator-auth/identity/oidc/network.mjs';
|
|
4
|
+
import { readInput } from '../operator-auth/input.mjs';
|
|
5
|
+
import { insist, NETWORK } from './protocol.mjs';
|
|
6
|
+
export const LOGOUT_EVENT='http://schemas.openid.net/event/backchannel-logout';
|
|
7
|
+
export const FENCE_EVENT='https://runeditrun.com/events/identity-fence';
|
|
8
|
+
export async function backchannelLogout({db,config,request,clock=Date.now,fetch:fetcher=globalThis.fetch,native}) {
|
|
9
|
+
try {
|
|
10
|
+
const url=new URL(request.url);
|
|
11
|
+
insist(url.origin===config.origin && url.pathname==='/auth/oidc/auth/logout' && !url.search && request.method==='POST');
|
|
12
|
+
const data=await readInput(request);insist(typeof data.logout_token==='string');
|
|
13
|
+
const link=await dependencyStore(db).link();insist(link && !link.needs_relink,'provider_unlinked',403);
|
|
14
|
+
const pin={issuer:link.issuer,clientId:link.client_id,algorithms:link.metadata.algorithms};
|
|
15
|
+
const parsed=parseIdToken(data.logout_token,pin, 'logout+jwt'),c=parsed.claims;
|
|
16
|
+
await verifySignature(parsed,await readJson(fetcher,link.metadata.jwks,NETWORK));
|
|
17
|
+
const now=clock()/1000;
|
|
18
|
+
insist(c.iss===pin.issuer && (c.aud===pin.clientId || Array.isArray(c.aud) && c.aud.length===1 && c.aud[0]===pin.clientId));
|
|
19
|
+
insist(Number.isSafeInteger(c.iat) && c.iat<=now+30 && c.iat>=now-86400 && Number.isSafeInteger(c.exp) && c.exp>now && c.exp>c.iat && c.exp-c.iat<=86400);
|
|
20
|
+
insist(c.nbf===undefined || Number.isSafeInteger(c.nbf) && c.nbf<=now+30);
|
|
21
|
+
insist(!Object.hasOwn(c,'nonce') && typeof c.jti==='string' && c.jti.trim().length>0 && c.jti.length<=256);
|
|
22
|
+
insist(c.events && typeof c.events==='object' && !Array.isArray(c.events));
|
|
23
|
+
const events=Object.keys(c.events);insist(events.length>0 && events.every(k=>[LOGOUT_EVENT,FENCE_EVENT].includes(k) && c.events[k] && typeof c.events[k]==='object' && !Array.isArray(c.events[k]) && Object.keys(c.events[k]).length===0));
|
|
24
|
+
for(const key of ['sub','sid']) if(Object.hasOwn(c,key)) insist(typeof c[key]==='string' && c[key].length>0 && c[key].length<=255);
|
|
25
|
+
insist(c.sub || c.sid);
|
|
26
|
+
if(native) {
|
|
27
|
+
const consumed=await native.consumeLogout({claims:c,link,now:clock(),fence:events.includes(FENCE_EVENT)});
|
|
28
|
+
insist(consumed,'logout_already_used',409);
|
|
29
|
+
return new Response(null,{status:204});
|
|
30
|
+
}
|
|
31
|
+
const rows=(await db.prepare('SELECT DISTINCT subject FROM oa_provider_sessions WHERE issuer=? AND (? IS NULL OR sid=?)').bind(c.iss,c.sid??null,c.sid??null).all()).results;
|
|
32
|
+
if(c.sid) insist(rows.length>0 && (!c.sub || rows.every(r=>r.subject===c.sub)));
|
|
33
|
+
const subject=c.sub??(rows.length===1?rows[0].subject:null);insist(subject);
|
|
34
|
+
const person=await db.prepare('SELECT operator_id FROM oa_external_identities WHERE issuer=? AND subject=?').bind(c.iss,subject).first();insist(person);
|
|
35
|
+
const statements=[db.prepare(`INSERT INTO oa_logout_events(issuer,jti,consumed_at) VALUES(?,?,CASE WHEN EXISTS(SELECT 1 FROM rer_dependency_link WHERE interface='oidc-provider@1' AND issuer=? AND client_id=? AND metadata=? AND needs_relink=0 AND linked_at=?) AND EXISTS(SELECT 1 FROM oa_external_identities WHERE issuer=? AND subject=? AND operator_id=?) THEN ? ELSE -1 END)`).bind(c.iss,c.jti,link.issuer,link.client_id,JSON.stringify(link.metadata),link.linked_at,c.iss,subject,person.operator_id,clock())];
|
|
36
|
+
if(events.includes(FENCE_EVENT)) statements.push(db.prepare('UPDATE oa_operators SET version=version+1 WHERE id=?').bind(person.operator_id));
|
|
37
|
+
statements.push(db.prepare(`UPDATE oa_sessions SET revoked_at=COALESCE(revoked_at,?) WHERE id IN
|
|
38
|
+
(SELECT session_id FROM oa_provider_sessions WHERE issuer=? AND subject=? AND (? IS NULL OR sid=?))`).bind(clock(),c.iss,subject,c.sid??null,c.sid??null));
|
|
39
|
+
try { await db.batch(statements); } catch { insist(false,'logout_already_used',409); }
|
|
40
|
+
return new Response(null,{status:204});
|
|
41
|
+
} catch(error) { return Response.json({error:error.code??'invalid_logout'},{status:error.status??400}); }
|
|
42
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AuthError } from '../operator-auth/errors.mjs';
|
|
2
|
+
// These are D1 transaction fragments, not a second linking protocol. Each
|
|
3
|
+
// fragment uses only the adopter's tenant/account/session tables. The common
|
|
4
|
+
// protocol adds key/link/code/pin guards and commits everything in one batch.
|
|
5
|
+
export function nativeDependencyAuthority(value) {
|
|
6
|
+
const methods = ['unclaimed', 'ownerFence', 'approvalFence', 'linkedStatements', 'unlinkStatements', 'consumeLogout'];
|
|
7
|
+
if (!value || typeof value !== 'object' || methods.some(k => typeof value[k] !== 'function') || value.claimOwner !== undefined && typeof value.claimOwner !== 'function') throw new AuthError('native_dependency_capability_required', 503);
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readJson, discover } from '../operator-auth/identity/oidc/network.mjs';
|
|
2
|
+
import { PRESETS, MINIMUM_PROVIDER_VERSION } from '../operator-auth/identity/oidc/presets.mjs';
|
|
3
|
+
export const INTERFACE = 'oidc-provider@1';
|
|
4
|
+
export const CALLBACK = '/.well-known/rer-link/callback';
|
|
5
|
+
export const PATHS = ['/auth/oidc/auth/callback','/auth/oidc/auth/logout'];
|
|
6
|
+
export const NETWORK = { timeoutMs:5000 };
|
|
7
|
+
export function insist(value, code = 'invalid_link', status = 400) { if (!value) throw Object.assign(new Error(code),{code,status}); }
|
|
8
|
+
export function originOf(value, local = false) {
|
|
9
|
+
let u;try { u=new URL(value); } catch { insist(false); }
|
|
10
|
+
insist(u.origin===value && !u.username && !u.password && (u.protocol==='https:' || local && u.protocol==='http:' && ['localhost','127.0.0.1','[::1]'].includes(u.hostname)));
|
|
11
|
+
return u.origin;
|
|
12
|
+
}
|
|
13
|
+
export async function providerMetadata(issuer, fetcher, contract = INTERFACE) {
|
|
14
|
+
originOf(issuer);
|
|
15
|
+
const p={...PRESETS.auth,id:'auth',issuer,endpointOrigins:[issuer],tokenAuthMethod:'private_key_jwt',keyAlgorithm:'ES256',minimumVersion:MINIMUM_PROVIDER_VERSION};
|
|
16
|
+
const oidc=contract===INTERFACE?await discover(fetcher,p,NETWORK):{};
|
|
17
|
+
const link=await readJson(fetcher,`${issuer}/.well-known/rer-provider`,NETWORK);
|
|
18
|
+
insist(link.issuer===issuer && (link.interface===contract || Array.isArray(link.provides) && new Set(link.provides).size===link.provides.length && link.provides.every(i=>typeof i==='string' && /^[a-z][a-z0-9-]*@[1-9][0-9]*$/.test(i)) && link.provides.includes(contract)) && link.version===1,'unsupported_link_interface',502);
|
|
19
|
+
for (const name of ['initiate','redeem','deregister']) {
|
|
20
|
+
const u=new URL(link[name]);insist(u.origin===issuer && u.protocol==='https:' && !u.search && !u.hash && !u.username && !u.password,'untrusted_link_endpoint',502);
|
|
21
|
+
}
|
|
22
|
+
return {...oidc,link};
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS rer_dependency_installation (
|
|
2
|
+
singleton INTEGER PRIMARY KEY CHECK(singleton=1), sector_id TEXT NOT NULL UNIQUE
|
|
3
|
+
);
|
|
4
|
+
CREATE TABLE IF NOT EXISTS rer_dependency_link (
|
|
5
|
+
interface TEXT PRIMARY KEY, issuer TEXT NOT NULL, client_id TEXT NOT NULL, metadata TEXT NOT NULL,
|
|
6
|
+
needs_relink INTEGER NOT NULL DEFAULT 0, linked_at INTEGER NOT NULL
|
|
7
|
+
);
|
|
8
|
+
CREATE TABLE IF NOT EXISTS rer_dependency_pending (
|
|
9
|
+
interface TEXT PRIMARY KEY, issuer TEXT NOT NULL, approved_by TEXT NOT NULL, expires_at INTEGER NOT NULL, approved_session TEXT NOT NULL, approved_version INTEGER NOT NULL, callbacks TEXT NOT NULL
|
|
10
|
+
);
|
|
11
|
+
CREATE TABLE IF NOT EXISTS rer_dependency_codes (
|
|
12
|
+
digest TEXT PRIMARY KEY, created_at INTEGER NOT NULL CHECK(created_at>=0)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE TABLE IF NOT EXISTS rer_dependency_keys (interface TEXT PRIMARY KEY, private_key TEXT NOT NULL);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { keyPair } from './crypto.mjs';
|
|
2
|
+
export function dependencyStore(db) {
|
|
3
|
+
const first=(sql,...args)=>db.prepare(sql).bind(...args).first();
|
|
4
|
+
const run=(sql,...args)=>db.prepare(sql).bind(...args).run();
|
|
5
|
+
return {
|
|
6
|
+
first,run,
|
|
7
|
+
async installation(contract = 'oidc-provider@1') {
|
|
8
|
+
await run('INSERT INTO rer_dependency_installation(singleton,sector_id) VALUES(1,?) ON CONFLICT DO NOTHING',crypto.randomUUID());
|
|
9
|
+
const row=await first('SELECT * FROM rer_dependency_installation WHERE singleton=1');
|
|
10
|
+
let key=await first('SELECT private_key FROM rer_dependency_keys WHERE interface=?',contract);
|
|
11
|
+
if(!key) {
|
|
12
|
+
await run('INSERT INTO rer_dependency_keys(interface,private_key) VALUES(?,?) ON CONFLICT DO NOTHING',contract,JSON.stringify(await keyPair()));
|
|
13
|
+
key=await first('SELECT private_key FROM rer_dependency_keys WHERE interface=?',contract);
|
|
14
|
+
}
|
|
15
|
+
return {...row,key:JSON.parse(key.private_key)};
|
|
16
|
+
},
|
|
17
|
+
async link(contract = 'oidc-provider@1') { const row=await first("SELECT * FROM rer_dependency_link WHERE interface=?",contract);return row?{...row,metadata:JSON.parse(row.metadata)}:null; },
|
|
18
|
+
};
|
|
19
|
+
}
|