Repository navigation
feat(backend): WASM sandbox, BullMQ queues + DLQ, GraphQL subscriptions, WebAuthn core (BE-010, BE-006, BE-007, BE-008 partial) - #1735
Merged
ayomideadeniran merged 6 commits intoOct 22, 2026
Conversation
…y limits (BE-010) Runs untrusted contract bytecode in an isolated worker thread with an empty env, wasm-metering gas instrumentation (1,000,000 gas), a 50MB linear-memory ceiling and a 2s wall-clock backstop. Returns structured per-test results with gas usage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… dlq (BE-006) Critical/standard/low queues with 5 retries using exponential jitter backoff, per-user enqueue quotas, a cluster-wide Soroban RPC limiter for minting, a durable dead-letter queue with alerts, and admin inspect/replay/purge endpoints. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…events (BE-007) Adds exerciseStatusUpdated, courseProgressChanged and newGlobalAnnouncement over graphql-ws on the shared Apollo schema, with JWT auth on connect, per-subscription authorization, token-expiry disconnects and listener cleanup on close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt and routes Evaluation jobs run in the wasm sandbox and stream status through the exerciseStatusUpdated subscription. Mounts /evaluations and /admin/queues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…8, partial) Adds webauthn_credentials and webauthn_challenges tables, CBOR/COSE parsing, none/packed/fido-u2f attestation and assertion verification, 60s single-use challenges, and signature-counter clone detection. HTTP endpoints and tests are not included yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
||
| const router: ReturnType<typeof Router> = Router(); | ||
|
|
||
| router.use(authenticateToken as any); |
| * @route POST /api/v1/evaluations | ||
| * @desc Queue a contract submission for sandboxed evaluation | ||
| */ | ||
| router.post('/', authenticate, validateRequest(evaluationSubmissionSchema), async (req: Request, res: Response) => { |
| * @route GET /api/v1/evaluations/:jobId | ||
| * @desc Evaluation state and, once finished, the structured sandbox result | ||
| */ | ||
| router.get('/:jobId', authenticate, async (req: Request, res: Response) => { |
|
@Emeka000 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@emmzy4tech is attempting to deploy a commit to the Ayomide Adeniran's projects Team on Vercel. A member of the Team first needs to authorize it. |
Contributor
|
Thank you for your contribution! This PR is currently under review. If I find any incorrect implementation or issues, I will leave comments on what needs to be adjusted next. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1582
Closes #1578
Closes #1579
closes #1580
Summary
BE-010: Sandboxed WASM execution engine (#1582)
src/utils/wasmValidator.tschecks the module before anything runs: strict base64, 1MB size cap, structural validation, no function/table/global imports (so no fs/network/env reachable), memory max ≤ 50MB, a single memory only (no multi-memory bypass), and no exception-handling tag section (guests can't catch the out-of-gas trap).src/services/wasmExecutionSandbox.tsruns guest code in aworker_threadsWorker started withenv: {},execArgv: []and capped V8 heap/stack:wasm-metering; gas is mirrored into aSharedArrayBuffer, so the figure stays accurate even after a kill.memory.growpast it returns -1).passed/failed/error/skipped, actual vs expected, gas and duration per test) plus overall gas, peak memory and termination reason.src/workers/evaluationWorker.tsprocessescontract-evaluationjobs and streamsQUEUED → RUNNING → PASSED/FAILED/ERRORover the BE-007 subscription.POST /api/v1/evaluationsqueues a submission;GET /api/v1/evaluations/:jobIdreturns the result to its owner. Only this path gets a 1MB JSON body parser; the global 100kb default is unchanged.BE-006: BullMQ queues with concurrency controls and DLQ (#1578)
src/queues/queueManager.tsdefinestx-minting(critical),ipfs-pinning/contract-evaluation(standard) andemail-notifications(low). Queues are created lazily, so importing the module opens no Redis connections.UnrecoverableError, move to the DLQ.Retry-After.tx-mintinghas a cluster-wide Redis limiter (SOROBAN_RPC_MAX_SUBMISSIONS_PER_SEC, default 5/s) and bounded concurrency.src/workers/mintingWorker.tsalso pauses the whole queue on Soroban 429s without using up a retry. It treats the blockchain service's circuit-breakersuccess: falseas a failure.src/workers/dlqProcessor.tskeeps a durabledead-letterqueue (it has no consumer). It sends an alert per dead-lettered job (log, plusDLQ_ALERT_WEBHOOK_URLif set) and a throttled alert when depth reachesDLQ_ALERT_THRESHOLD./api/v1/admin/queues(admin only):GET /stats,GET /dlq,GET /dlq/:id,POST /dlq/:id/replay,POST /dlq/replay,DELETE /dlq/:id,POST /dlq/purge(requiresconfirm: true).npm run workers:queues(dev) /npm run start:workers(prod).BE-007: GraphQL subscriptions over graphql-ws (#1579)
exerciseStatusUpdated(studentId),courseProgressChanged(courseId)andnewGlobalAnnouncement, plus an admin-onlypublishGlobalAnnouncementmutation.updateLearningProgressnow publishescourseProgressChanged.src/graphqlWsServer.tsserves Apollo (HTTP) and graphql-ws on one shared executable schema:connectionParams; a missing, invalid, expired or revoked token closes with 4403.connection_inittime out.POST /publish/:roomendpoint is removed.src/graphql/pubsub.tsis a typed pub/sub with topics keyed per argument and a bounded buffer per subscriber. An optional Redis bridge carries events across processes, so worker publishes reach WS clients. Measured push latency in tests is well under 50ms.Authorizationheader.@graphql-tools/schemais now a direct dependency, pinned to the 10.0.33 already inpackage-lock.json.BE-008: Passkey/WebAuthn (#1580): partial, not ready to close
Included:
WebAuthnCredential/WebAuthnChallengeand migration20261021000000_webauthn_passkeys. The migration adds new tables only and leaves existing tables untouched.src/auth/webauthn.ts: strict CBOR decoding, authenticator-data parsing, COSE key import (ES256/384/512, EdDSA, RS256), and signature verification. Attestation formats:none,packed(self and x5c, including the AAGUID extension check) andfido-u2f.src/auth/passkeyService.ts: registration and authentication ceremonies.Still to do:
controllers/passkeyController.ts,routes/auth/passkey.routes.ts(token issuance) and tests for the passkey service. I'll follow up in this PR or a separate one.The legacy
/api/v1/passkeyroutes are left as they were. Their signature check is a stub (it accepts any non-zero 64-byte signature). They don't issue session tokens, but they should be retired once the new endpoints land.Testing
tsc --noEmit: clean, as it was onmain.jest(backend): 110 suites passed, 1,326 tests (main: 105 suites, 1,246 tests).wasmExecutionSandbox,wasmValidator,queueManager,queueWorkers,admin.queues.routes,graphqlSubscriptions.queueManagerincludes a real-Redis BullMQ integration test showing exactly 6 attempts, then the DLQ, then replay. It skips itself when Redis isn't reachable; CI provides Redis.graphqlSubscriptionsruns a real graphql-ws client against a real server.tests/readinessMonitor.test.tsfails intermittently on a pre-existing timing assertion (expected >= 10, received 9). It's unrelated to this PR and passes on rerun.npm run test:migrations) locally against Postgres. It runs in CI.New environment variables (all optional)
WASM_SANDBOX_MAX_CONCURRENCY,MINTING_WORKER_CONCURRENCY,SOROBAN_RPC_MAX_SUBMISSIONS_PER_SEC,*_USER_RATE_LIMIT_PER_MIN,DLQ_ALERT_WEBHOOK_URL,DLQ_ALERT_THRESHOLD,GRAPHQL_PUBSUB_REDIS_URL/GRAPHQL_PUBSUB_REDIS,WEBAUTHN_RP_ID,WEBAUTHN_RP_NAME,WEBAUTHN_ORIGINS,WEBAUTHN_USER_VERIFICATION,WEBAUTHN_ATTESTATION.