Skip to content

Add EnsRainbowBeam app with endpoint for submitting labels#2015

Open
djstrong wants to merge 32 commits into
mainfrom
2003-add-new-app-with-endpoint-for-submitting-labels
Open

Add EnsRainbowBeam app with endpoint for submitting labels#2015
djstrong wants to merge 32 commits into
mainfrom
2003-add-new-app-with-endpoint-for-submitting-labels

Conversation

@djstrong
Copy link
Copy Markdown
Member

@djstrong djstrong commented Apr 29, 2026

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • Add EnsRainbowBeam (apps/ensrainbowbeam): Hono service with GET /health and POST /api/discover that labelhashes submitted label literals, classifies them against ENSNode’s Omnigraph labels index (unknown_in_index / healed_in_index / absent_from_index), skips unnormalizable labels, and emits one structured JSON line per submission to stdout (sink/storage deferred).
  • Extend ENSApi Omnigraph with Query.labels(by: LabelsByLabelHashesInput!): LabelHash scalar, stricter validation, deduped batch lookup (cap 100 distinct hashes per request), plus codegen/SDK updates (enssdk, ensrainbow-sdk).
  • Ship EnsRainbowBeamClient (@ensnode/ensrainbow-sdk/ensrainbowbeam-client), Docker/Compose (docker/services/ensrainbowbeam.yml), Terraform module (terraform/modules/ensrainbowbeam), broad CORS for browser use, and a Beam-it UI on docs/ensrainbow.io calling beam.ensrainbow.io.

Why

  • External callers need a small HTTP surface to submit label discoveries. Closes #2003.
  • Batch labels lookup in ENSApi keeps Beam’s per-request Omnigraph traffic bounded and typed end-to-end.
  • SDK + marketing-site UI make the endpoint usable from browsers and local dev immediately; JSONL stdout preserves a stable row shape for a future persistent/on-chain pipeline.

Testing

  • pnpm -F ensrainbowbeam test — 27 tests (labels.test.ts, submissions.test.ts: validation, classification, skipped unnormalized labels, Omnigraph timeout/error paths, JSONL logging).

Notes for Reviewer (Optional)

  • No persistence yet—stdout JSONL only; leaderboard/on-chain emission are follow-ups on Add new app with endpoint for submitting labels #2003.
  • Per-request cap: 100 raw labels; each may expand to up to 2 distinct LabelHashes (raw + normalized). Omnigraph chunking uses LABELS_BY_LABELHASH_MAX (100).
  • POST /api/discover body: { labels: string[], callerAddress: Address }; response includes per-label status, label, optional labelHash / normalizedLabel.
  • CORS is origin: "*" (same pattern as other public ENSNode HTTP services)—not an env allowlist.
  • Handler file is still named submissions.ts internally; public route is /api/discover.

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

…ation

- Introduced `apps/ens-labels-collector`, a Hono server that accepts label submissions via `POST /api/submissions`.
- Implemented classification of labels against ENSNode's Omnigraph index, emitting structured JSON logs for each submission.
- Added a new `Query.labels` field in ENSApi for batch lookup of labels by their hashes.
- Included necessary configurations, Dockerfile, and example environment variables for local development.
- Comprehensive tests for submission handling and classification logic.

This feature addresses issue [#2003](#2003).
- Updated the maximum number of raw labels accepted per `POST /api/submissions` from 50 to 100.
- Adjusted the `LABELS_BY_HASHES_MAX` limit in the ENSApi from 100 to 200 to accommodate larger batch lookups.
- Modified tests to reflect the new limits for both submissions and label hash queries.
- Enhanced documentation to clarify the relationship between submission limits and hash expansions.
Copilot AI review requested due to automatic review settings April 29, 2026 19:41
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment May 18, 2026 0:06am
enskit-react-example.ensnode.io Ready Ready Preview, Comment May 18, 2026 0:06am
ensnode-enskit-react-example Ready Ready Preview, Comment May 18, 2026 0:06am
ensnode.io Ready Ready Preview, Comment May 18, 2026 0:06am
ensrainbow.io Ready Ready Preview, Comment May 18, 2026 0:06am

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 29, 2026

🦋 Changeset detected

Latest commit: 5b92ad7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 25 packages
Name Type
ensapi Minor
@ensnode/ensrainbow-sdk Minor
ensrainbowbeam Minor
enssdk Minor
ensindexer Minor
ensrainbow Minor
@docs/ensrainbow Minor
ensadmin Minor
@ensnode/enskit-react-example Patch
@ensnode/enssdk-example Patch
@ensnode/datasources Minor
@namehash/ens-referrals Minor
@ensnode/ensdb-sdk Minor
enskit Minor
@ensnode/ensnode-sdk Minor
@namehash/namehash-ui Minor
fallback-ensapi Minor
@ensnode/integration-test-env Minor
@docs/ensnode Minor
enscli Minor
ensskills Minor
@ensnode/ponder-sdk Minor
@ensnode/ponder-subgraph Minor
@ensnode/shared-configs Minor
@ensnode/ensindexer-perf-testing Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

New ens-labels-collector Hono service accepts POST /api/submissions, hashes and classifies submitted ENS labels via Omnigraph lookups, and emits one structured JSON line per submission to stdout. ENSApi adds Query.labels(by: LabelsByHashesInput!) for batch label lookup by hashes (deduped, capped at 200, omits missing).

Changes

Cohort / File(s) Summary
ens-labels-collector scaffold & metadata
/.changeset/ens-labels-collector-app.md, apps/ens-labels-collector/.env.local.example, apps/ens-labels-collector/Dockerfile, apps/ens-labels-collector/README.md, apps/ens-labels-collector/package.json, apps/ens-labels-collector/tsconfig.json, apps/ens-labels-collector/vitest.config.ts
New app scaffold and metadata: changeset, env example, Dockerfile, README, package manifest, TS and Vitest configs.
app bootstrap & server
apps/ens-labels-collector/src/app.ts, apps/ens-labels-collector/src/index.ts, apps/ens-labels-collector/src/config.ts
Hono app export with routes, global error handler, Zod-validated runtime config, server start/close, graceful shutdown and signal/exception handlers.
HTTP handlers & tests
apps/ens-labels-collector/src/handlers/health.ts, apps/ens-labels-collector/src/handlers/submissions.ts, apps/ens-labels-collector/src/handlers/submissions.test.ts
Adds GET /health and POST /api/submissions with Zod validation, address normalization, max-labels guard, request-id/timestamp, timed Omnigraph lookup using AbortSignal.any, classification, single JSON-line stdout emission; comprehensive tests including error paths and dedupe.
label processing & tests
apps/ens-labels-collector/src/lib/labels.ts, apps/ens-labels-collector/src/lib/labels.test.ts
Label hashing and optional normalization, deduped lookup-hash collection, unhealed-hit detection, classification into unknown_in_index/healed_in_index/absent_from_index; unit tests for hashing, normalization, dedupe, and classification logic.
Omnigraph client
apps/ens-labels-collector/src/lib/omnigraph-client.ts
Typed Omnigraph GraphQL query and lookupLabels against configured ENSNode URL; short-circuits empty input, forwards AbortSignal, surfaces GraphQL errors, returns label hits.
shared helpers
apps/ens-labels-collector/src/lib/error-response.ts
Standardized errorResponse helper mapping ZodError, Error, string, and other shapes to canonical JSON responses with inferred status and optional details.
ENSApi GraphQL additions & tests
apps/ensapi/src/omnigraph-api/schema/label.ts, apps/ensapi/src/omnigraph-api/schema/query.ts, apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts, apps/ensapi/src/omnigraph-api/yoga.ts
Adds LabelsByHashesInput and LABELS_BY_HASHES_MAX (200); new Query.labels(by: LabelsByHashesInput!): [LabelRef!]! resolver that validates batch size, dedups inputs, returns only present labels; integration tests for present/absent/dedup/over-limit cases; conditional Yoga error masking.
misc changeset/config
.changeset/config.json
Includes ens-labels-collector in changeset fixed list.
other runtime tweak
apps/ensrainbow/src/commands/entrypoint-command.ts
Gates readiness marker on buildDbConfig success and adds an additional shutdown/abort check after building DB config.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Collector as "ens-labels-collector\n(Hono)"
    participant Omnigraph as "ENSApi / Omnigraph\n(GraphQL)"
    participant DB as "Label Index DB"

    Client->>Collector: POST /api/submissions { labels, callerAddress }
    activate Collector
    Collector->>Collector: Validate input\nnormalize address, hash labels
    Collector->>Collector: Collect & dedupe lookup hashes
    Collector->>Omnigraph: Query labels(by: { hashes })
    activate Omnigraph
    Omnigraph->>DB: SELECT labels WHERE labelHash IN (...)
    DB-->>Omnigraph: [{ hash, interpreted }]
    Omnigraph-->>Collector: [{ hash, interpreted }]
    deactivate Omnigraph
    Collector->>Collector: Classify labels\n(healed/unknown/absent)
    Collector->>Collector: Emit single JSON log line to stdout
    Collector-->>Client: 200 { submittedAt, callerAddress, results }
    deactivate Collector
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hopping through hashes one by one,

I normalize, dedupe, then run,
I query the graph and log what I find,
One JSON line keeps each submission signed,
A rabbit’s cheer for labels neatly lined.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The PR title refers to 'EnsRainbowBeam app' but the changeset and all modifications focus on the 'ens-labels-collector' app, not EnsRainbowBeam. Update the title to accurately reflect the main change: 'Add ens-labels-collector app with endpoint for submitting labels' or similar phrasing that references the correct app name.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR follows the template structure with Summary, Why, Testing, and Notes sections; however, the pre-review checklist items are not marked complete despite requirements being met.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2003-add-new-app-with-endpoint-for-submitting-labels

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “labels collector” service and the supporting Omnigraph query for batched labelhash lookups, enabling external callers to submit raw labels and have them classified against ENSNode’s indexed label table.

Changes:

  • Add Query.labels(by: { hashes }) to ENSApi Omnigraph schema with a hard cap (200) and integration tests.
  • Add new app apps/ens-labels-collector (Hono) exposing POST /api/submissions + /health, with label hashing/normalization, Omnigraph lookups, and JSONL stdout logging.
  • Regenerate enssdk/omnigraph schema + introspection for the new query/input; update lockfile and add changeset.

Reviewed changes

Copilot reviewed 20 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds new app importer + lockfile refresh (including patched dependency hashes).
packages/enssdk/src/omnigraph/generated/schema.graphql Adds LabelsByHashesInput and Query.labels to generated schema.
packages/enssdk/src/omnigraph/generated/introspection.ts Updates generated introspection for typed Omnigraph client.
apps/ensapi/src/omnigraph-api/schema/query.ts Implements Query.labels resolver using Drizzle inArray with max-size enforcement.
apps/ensapi/src/omnigraph-api/schema/label.ts Adds LabelsByHashesInput and LABELS_BY_HASHES_MAX.
apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts Integration coverage for Query.labels.
apps/ens-labels-collector/vitest.config.ts Vitest project config for the new app.
apps/ens-labels-collector/tsconfig.json TS config for the new app.
apps/ens-labels-collector/src/lib/omnigraph-client.ts Typed Omnigraph query wrapper + memoized client.
apps/ens-labels-collector/src/lib/labels.ts Hashing + lookup-hash collection + classification logic.
apps/ens-labels-collector/src/lib/labels.test.ts Unit tests for hashing/classification helpers.
apps/ens-labels-collector/src/lib/error-response.ts Standardized { message, details? } error response helper.
apps/ens-labels-collector/src/index.ts Server startup + graceful shutdown wiring.
apps/ens-labels-collector/src/handlers/submissions.ts POST /api/submissions handler: validation, lookup, classification, logging.
apps/ens-labels-collector/src/handlers/submissions.test.ts Handler-level tests with mocked Omnigraph client.
apps/ens-labels-collector/src/handlers/health.ts /health handler.
apps/ens-labels-collector/src/config.ts Env config parsing + memoization.
apps/ens-labels-collector/src/app.ts Route wiring + notFound/onError handling.
apps/ens-labels-collector/package.json New app package manifest and scripts.
apps/ens-labels-collector/README.md App documentation + usage/config.
apps/ens-labels-collector/Dockerfile Containerization for the new service.
apps/ens-labels-collector/.env.local.example Local env template.
.changeset/ens-labels-collector-app.md Changeset for ens-labels-collector, ensapi, and enssdk.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +35
const closeServer = () =>
new Promise<void>((resolve, reject) =>
server.close((err) => {
if (err) reject(err);
else resolve();
}),
);

const gracefulShutdown = async () => {
try {
await closeServer();
process.exit(0);
} catch (error) {
console.error("[ens-labels-collector] shutdown error", error);
process.exit(1);
}
};
Comment on lines +43 to +58
export async function lookupLabels(hashes: readonly string[]): Promise<LabelHit[]> {
if (hashes.length === 0) return [];

const result = await getClient().omnigraph.query({
query: LabelsByHashes,
variables: { hashes: hashes as `0x${string}`[] },
});

if (result.errors && result.errors.length > 0) {
throw new Error(
`Omnigraph labels query returned errors: ${result.errors.map((e) => e.message).join("; ")}`,
);
}

return (result.data?.labels ?? []) as LabelHit[];
}
Comment on lines +86 to +92
it("rejects requests over the maximum allowed hash count", async () => {
// generate (LABELS_BY_HASHES_MAX + 1) distinct labelhashes deterministically
const hashes: LabelHash[] = [];
for (let i = 0; i <= 200; i++) {
const hex = i.toString(16).padStart(64, "0");
hashes.push(`0x${hex}` as LabelHash);
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ens-labels-collector/Dockerfile`:
- Around line 11-16: The runner stage in the Dockerfile runs as root; update the
runner stage to switch to a non-root user by creating or using an existing
unprivileged user (e.g., "node" or a custom uid/gid), ensure
ownership/permissions of WORKDIR (/app/apps/ens-labels-collector) are set to
that user, and add a USER instruction before the CMD so the container process
launched by CMD ["pnpm","start"] does not run as root; reference the runner
stage, WORKDIR, and CMD when making these changes.
- Around line 14-16: The Dockerfile lacks a HEALTHCHECK so orchestrators can't
detect runtime liveness/readiness; add a HEALTHCHECK instruction after the
EXPOSE 4444 (and before CMD ["pnpm","start"]) that probes the app's health
endpoint (e.g., HTTP GET to localhost:4444/health or the appropriate route) and
configure sensible options (--interval, --timeout, --start-period, --retries) so
failed probes mark the container unhealthy; ensure the probe command exits
non‑zero on failure so Docker/k8s receive correct health signals.

In `@apps/ens-labels-collector/src/app.ts`:
- Around line 15-18: The onError handler currently returns the raw error to
clients via errorResponse in app.onError; change it to log the full error
internally (using console.error or processLogger) but return a generic 500
response body (e.g., { error: "Internal Server Error" } or a minimal error id)
from errorResponse instead of the raw error object; keep the logging call for
app.onError((error, c) => ...) but remove/error-sanitize any exposure of error
when calling errorResponse so implementation details are not leaked to clients.

In `@apps/ens-labels-collector/src/config.ts`:
- Around line 4-8: The PORT schema currently uses Number.parseInt which allows
partial numeric strings like "4444abc"; update the transform for PORT in
config.ts to validate the raw string strictly (e.g. ensure it matches /^\d+$/)
before parsing and return the default 4444 when undefined, and otherwise parse
with Number.parseInt or Number(value); if the string fails the digits-only
check, throw or return a z.ZodError/invalid value so the .pipe(z.number()...)
never receives a truncated number—target the PORT zod schema's .transform
callback to implement this validation.

In `@apps/ens-labels-collector/src/handlers/submissions.test.ts`:
- Around line 33-38: The test suite creates a global console.log spy
(consoleSpy) but never restores it, which can leak a mocked console into other
tests; add an afterAll (or afterEach) teardown that calls
consoleSpy.mockRestore() to restore the original console.log; locate the
consoleSpy declaration and the existing beforeEach block in submissions.test.ts
and add afterAll(() => consoleSpy.mockRestore()) so the spy is removed when the
suite finishes.
- Around line 40-213: Tests in submissions.test.ts use an await-then-expect
pattern for async responses (e.g., calling await res.json() then asserting), and
the reviewer suggests optionally switching to Vitest's await
expect(promise).resolves.* pattern; to apply this refactor, locate the tests
that call app.request and then await res.json() (examples: the "classifies a
healed label correctly..." test, "classifies an unhealed label...", "normalizes
the callerAddress..." and others) and replace the manual await-and-then
assertions with direct promise assertions (e.g., use await
expect(res.json()).resolves.toMatchObject(...) or resolves.toEqual(...) where
appropriate), keeping status assertions either as expect(res.status).toBe(...)
or using await expect(Promise.resolve(res.status)).resolves.toBe(...); this is
optional—apply consistently across the file if you refactor.

In `@apps/ens-labels-collector/src/handlers/submissions.ts`:
- Line 110: The call to lookupLabels() is unbounded and can hang; wrap the
external Omnigraph lookup in a bounded timeout so the handler doesn't block
indefinitely—use a Promise.race (or an AbortController if lookupLabels accepts a
signal) to enforce a maximum wait (e.g., configurable constant) and handle
timeout by returning a clear error or fallback before assigning hits; update the
code around the lookupLabels call (where hits is awaited) to cancel/ignore the
lookup on timeout and log/propagate a timeout-specific error.

In `@apps/ens-labels-collector/src/index.ts`:
- Around line 27-35: The current gracefulShutdown function always exits with 0;
update it to accept an optional exitCode parameter (default 0) and pass that to
process.exit so callers can signal failure; then change the uncaughtException
handler (process.on('uncaughtException', ...)) to call gracefulShutdown(1) (or
invoke a dedicated fatalShutdown that logs the error and calls
gracefulShutdown(1)) so uncaught exceptions exit non‑zero while SIGINT/SIGTERM
continue to call gracefulShutdown() with the default 0. Ensure references to
gracefulShutdown and the uncaughtException handler are updated accordingly.

In `@apps/ens-labels-collector/src/lib/omnigraph-client.ts`:
- Around line 43-49: Change lookupLabels to accept readonly LabelHash[] instead
of readonly string[] and remove the unsafe cast to `0x${string}` in the query
variables; update the function signature for lookupLabels, remove the cast
currently applied to `hashes` when calling getClient().omnigraph.query with
LabelsByHashes, and pass the hashes array directly (ensuring LabelHash is
imported/available). Confirm callers (e.g., collectLookupHashes) already provide
LabelHash[] so no runtime changes are needed.

In `@apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts`:
- Around line 89-91: The test currently hardcodes 200 when building hashes to
trigger the overflow; import the exported constant LABELS_BY_HASHES_MAX and use
it to generate one more than the cap (e.g. loop to <= LABELS_BY_HASHES_MAX) so
the test reliably creates LABELS_BY_HASHES_MAX+1 hashes; add the import for
LABELS_BY_HASHES_MAX and replace the literal 200 in the loop in
label.integration.test.ts (the block that builds hashes) with that constant.
- Around line 34-84: Refactor each test to assert the async request using the
"await expect(...).resolves" pattern instead of awaiting the result then calling
expect: call request<LabelsByHashesResult>(LabelsByHashes, {...}) inside await
expect(...).resolves and use resolves.toHaveLength(), resolves.toEqual(),
resolves.toMatchObject() (for the healed label object) and
resolves.toMatchObject() or resolves.toHaveProperty() for specific fields; for
the dedupe/presence tests use
resolves.toHaveLength()/resolves.toHaveProperty('labels.0.hash',
ETH_LABEL_HASH), and for the "interpreted !== encodeLabelHash" case express the
inequality via a resolves matcher (e.g.,
resolves.toHaveProperty('labels.0.interpreted', expect.not...)) so all
assertions use the resolves chain against the promise returned by request
(references: LabelsByHashes, request, LabelsByHashesResult, ETH_LABEL_HASH,
ABSENT_LABEL_HASH, encodeLabelHash).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f8a4ec9f-6c70-4113-8141-54936f4f333c

📥 Commits

Reviewing files that changed from the base of the PR and between c29b4c5 and c57108e.

⛔ Files ignored due to path filters (3)
  • packages/enssdk/src/omnigraph/generated/introspection.ts is excluded by !**/generated/**
  • packages/enssdk/src/omnigraph/generated/schema.graphql is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • .changeset/ens-labels-collector-app.md
  • apps/ens-labels-collector/.env.local.example
  • apps/ens-labels-collector/Dockerfile
  • apps/ens-labels-collector/README.md
  • apps/ens-labels-collector/package.json
  • apps/ens-labels-collector/src/app.ts
  • apps/ens-labels-collector/src/config.ts
  • apps/ens-labels-collector/src/handlers/health.ts
  • apps/ens-labels-collector/src/handlers/submissions.test.ts
  • apps/ens-labels-collector/src/handlers/submissions.ts
  • apps/ens-labels-collector/src/index.ts
  • apps/ens-labels-collector/src/lib/error-response.ts
  • apps/ens-labels-collector/src/lib/labels.test.ts
  • apps/ens-labels-collector/src/lib/labels.ts
  • apps/ens-labels-collector/src/lib/omnigraph-client.ts
  • apps/ens-labels-collector/tsconfig.json
  • apps/ens-labels-collector/vitest.config.ts
  • apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts
  • apps/ensapi/src/omnigraph-api/schema/label.ts
  • apps/ensapi/src/omnigraph-api/schema/query.ts

Comment on lines +11 to +16
FROM deps AS runner
WORKDIR /app/apps/ens-labels-collector
ENV NODE_ENV=production
EXPOSE 4444

CMD ["pnpm", "start"]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Run the container as a non-root user.
The runner stage currently executes as root, which is an avoidable security risk.

🔧 Proposed fix
 FROM deps AS runner
 WORKDIR /app/apps/ens-labels-collector
 ENV NODE_ENV=production
+USER node
 EXPOSE 4444
 
 CMD ["pnpm", "start"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM deps AS runner
WORKDIR /app/apps/ens-labels-collector
ENV NODE_ENV=production
EXPOSE 4444
CMD ["pnpm", "start"]
FROM deps AS runner
WORKDIR /app/apps/ens-labels-collector
ENV NODE_ENV=production
USER node
EXPOSE 4444
CMD ["pnpm", "start"]
🧰 Tools
🪛 Checkov (3.2.525)

[low] 1-16: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-16: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ens-labels-collector/Dockerfile` around lines 11 - 16, The runner stage
in the Dockerfile runs as root; update the runner stage to switch to a non-root
user by creating or using an existing unprivileged user (e.g., "node" or a
custom uid/gid), ensure ownership/permissions of WORKDIR
(/app/apps/ens-labels-collector) are set to that user, and add a USER
instruction before the CMD so the container process launched by CMD
["pnpm","start"] does not run as root; reference the runner stage, WORKDIR, and
CMD when making these changes.

Comment on lines +14 to +16
EXPOSE 4444

CMD ["pnpm", "start"]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a HEALTHCHECK for runtime readiness/liveness.
Without it, orchestrators have weaker signal for unhealthy instances.

🔧 Proposed fix
 ENV NODE_ENV=production
 EXPOSE 4444
+HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
+  CMD node -e 'fetch("http://127.0.0.1:4444/health").then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))'
 
 CMD ["pnpm", "start"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EXPOSE 4444
CMD ["pnpm", "start"]
EXPOSE 4444
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e 'fetch("http://127.0.0.1:4444/health").then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))'
CMD ["pnpm", "start"]
🧰 Tools
🪛 Checkov (3.2.525)

[low] 1-16: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-16: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ens-labels-collector/Dockerfile` around lines 14 - 16, The Dockerfile
lacks a HEALTHCHECK so orchestrators can't detect runtime liveness/readiness;
add a HEALTHCHECK instruction after the EXPOSE 4444 (and before CMD
["pnpm","start"]) that probes the app's health endpoint (e.g., HTTP GET to
localhost:4444/health or the appropriate route) and configure sensible options
(--interval, --timeout, --start-period, --retries) so failed probes mark the
container unhealthy; ensure the probe command exits non‑zero on failure so
Docker/k8s receive correct health signals.

Comment thread apps/ensrainbowbeam/src/app.ts
Comment thread apps/ens-labels-collector/src/config.ts Outdated
Comment thread apps/ensrainbowbeam/src/handlers/submissions.test.ts
Comment thread apps/ens-labels-collector/src/handlers/submissions.ts Outdated
Comment thread apps/ens-labels-collector/src/index.ts Outdated
Comment thread apps/ens-labels-collector/src/lib/omnigraph-client.ts Outdated
Comment thread apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts
Comment thread apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts Outdated
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 29, 2026 19:58 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 29, 2026 19:58 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 29, 2026 19:58 Inactive
Comment thread apps/ens-labels-collector/src/index.ts Outdated
Comment thread apps/ens-labels-collector/src/config.ts Outdated
Comment thread apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts Outdated
Comment thread apps/ens-labels-collector/src/app.ts Outdated
Comment thread apps/ensrainbowbeam/src/lib/omnigraph-client.ts Outdated
Comment thread apps/ens-labels-collector/src/handlers/submissions.ts Outdated
…r submissions

- Updated error handling in the app to prevent leaking underlying error messages to clients, responding with a generic 500 status instead.
- Introduced a timeout mechanism for the `POST /api/submissions` endpoint to prevent stalled requests from holding resources indefinitely.
- Added a new utility function to handle promise timeouts, ensuring graceful degradation in case of delays during label lookups.
- Updated tests to include new error handling and timeout scenarios.
Copilot AI review requested due to automatic review settings April 29, 2026 20:41
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 29, 2026 20:41 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 29, 2026 20:41 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 29, 2026 20:41 Inactive
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new labels-submission collector app and extends ENSApi/Omnigraph to support batch label lookups by labelhash, enabling callers to classify submitted labels against the ENSNode index.

Changes:

  • Add apps/ens-labels-collector (Hono server) with POST /api/submissions + stdout JSONL sink and label classification logic.
  • Add Omnigraph Query.labels(by: LabelsByHashesInput!): [Label!]! with a 200-hash cap, plus integration tests.
  • Regenerate enssdk/omnigraph generated schema + introspection to expose the new query to typed clients.

Reviewed changes

Copilot reviewed 21 out of 24 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds new app importer deps + bumps esbuild snapshot used by toolchain.
packages/enssdk/src/omnigraph/generated/schema.graphql Adds LabelsByHashesInput + Query.labels to generated schema.
packages/enssdk/src/omnigraph/generated/introspection.ts Updates generated introspection to include new input + query field.
apps/ensapi/src/omnigraph-api/yoga.ts Configures masked GraphQL errors (prod) with dev passthrough behavior.
apps/ensapi/src/omnigraph-api/schema/query.ts Implements Query.labels resolver with dedupe + max hash cap.
apps/ensapi/src/omnigraph-api/schema/label.ts Adds LabelsByHashesInput + LABELS_BY_HASHES_MAX.
apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts Adds integration coverage for Query.labels behavior and limits.
apps/ens-labels-collector/vitest.config.ts Sets up unit-test project config for the new app.
apps/ens-labels-collector/tsconfig.json Adds TS config for the new app with @/* path alias.
apps/ens-labels-collector/src/lib/omnigraph-client.ts Adds typed Omnigraph client + cached client for label lookups.
apps/ens-labels-collector/src/lib/labels.ts Adds hashing + normalization + classification utilities.
apps/ens-labels-collector/src/lib/labels.test.ts Unit tests for label hashing/collection/classification utilities.
apps/ens-labels-collector/src/lib/error-response.ts Standardizes JSON error responses for the new service.
apps/ens-labels-collector/src/index.ts Adds server bootstrap + graceful shutdown handling.
apps/ens-labels-collector/src/handlers/submissions.ts Implements submissions endpoint schema validation, lookup, classification, timeout, and logging.
apps/ens-labels-collector/src/handlers/submissions.test.ts Unit tests for submissions handler validation, behavior, and logging.
apps/ens-labels-collector/src/handlers/health.ts Adds liveness endpoint handler.
apps/ens-labels-collector/src/config.ts Adds env config parsing/memoization for PORT + ENSNODE_URL.
apps/ens-labels-collector/src/app.ts Wires routes + notFound + error handling for the new app.
apps/ens-labels-collector/package.json Declares new workspace app package, scripts, and dependencies.
apps/ens-labels-collector/README.md Documents endpoints, classification logic, and configuration.
apps/ens-labels-collector/Dockerfile Provides container build/run recipe for the new app.
apps/ens-labels-collector/.env.local.example Adds local dev env template.
.changeset/ens-labels-collector-app.md Adds changeset for new app + Omnigraph API + regenerated SDK artifacts.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +34 to +42
* Races `promise` against a `setTimeout`-backed timeout, rejecting with `Error(message)` on
* expiry. The underlying promise is NOT cancelled (the Omnigraph SDK does not currently expose
* an `AbortSignal`); we simply stop waiting on it. The unhandled resolution is harmless.
*/
function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
let timer: ReturnType<typeof setTimeout> | undefined;
const timeout = new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error(message)), ms);
});
Comment thread .changeset/ens-labels-collector-app.md Outdated
Comment on lines +1 to +5
---
"ens-labels-collector": minor
"ensapi": minor
"enssdk": minor
---
Comment on lines +35 to +41
## Configuration

| Env var | Required | Description |
|---------|----------|-------------|
| `PORT` | no (default `4444`) | HTTP listen port. |
| `ENSNODE_URL` | yes | Base URL of an ENSNode (ENSApi) instance with Omnigraph at `/api/omnigraph`. |

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ens-labels-collector/package.json`:
- Line 23: You added the workspace dependency "@ensnode/ensnode-sdk" to
package.json but did not update the lockfile; run the package manager to
regenerate and commit the lockfile (e.g., run pnpm install to update
pnpm-lock.yaml) so the new workspace dependency is reflected, then include that
updated lockfile commit alongside the package.json change.

In `@apps/ens-labels-collector/src/handlers/submissions.ts`:
- Around line 34-37: The lookupLabels function currently races the Omnigraph SDK
call with withTimeout but does not pass an AbortSignal, so timed-out requests
keep running; update lookupLabels to accept an optional AbortSignal (or
QueryOptions) and pass it into the Omnigraph query call (the SDK supports
AbortSignal via QueryOptions), and change withTimeout usage to create an
AbortController whose signal is passed to the SDK and that is aborted when the
timeout fires; if lookupLabels is called with an existing signal, wire it to
abort the controller (or merge signals) so caller cancellations propagate;
finally update callers of lookupLabels (the HTTP request handler paths around
the earlier call sites) to supply the request's AbortSignal or forward
cancellation so in-flight requests are actually aborted on timeout or client
disconnect.

In `@apps/ensapi/src/omnigraph-api/yoga.ts`:
- Around line 19-28: Replace the custom masking block inside maskedErrors so it
uses Yoga's exported maskError function and the app's Pino logger: instead of
calling console.error and returning raw Error instances, call the Yoga maskError
fallback (maskError) with the error and log the error via the existing Pino
logger (logger) before returning maskError's result; ensure maskError is
imported from Yoga and the logger variable used is the same Pino instance the
app uses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a9de560f-e3b8-4c43-9c9b-f3068d0f74f5

📥 Commits

Reviewing files that changed from the base of the PR and between c57108e and 440023c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • apps/ens-labels-collector/package.json
  • apps/ens-labels-collector/src/app.ts
  • apps/ens-labels-collector/src/config.ts
  • apps/ens-labels-collector/src/handlers/submissions.test.ts
  • apps/ens-labels-collector/src/handlers/submissions.ts
  • apps/ens-labels-collector/src/index.ts
  • apps/ens-labels-collector/src/lib/omnigraph-client.ts
  • apps/ensapi/src/omnigraph-api/schema/label.integration.test.ts
  • apps/ensapi/src/omnigraph-api/yoga.ts

Comment thread apps/ensrainbowbeam/package.json
Comment thread apps/ens-labels-collector/src/handlers/submissions.ts Outdated
Comment thread apps/ensapi/src/omnigraph-api/yoga.ts
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io April 29, 2026 21:03 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io April 29, 2026 21:03 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensnode.io April 29, 2026 21:03 Inactive
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/ensapi/src/omnigraph-api/schema/query.ts Outdated
Comment thread apps/ensrainbowbeam/src/lib/omnigraph-client.ts Outdated
…ema` to transform labels into `LiteralLabel` and adjust `SubmissionResultItem` to use typed labels. Enhance tests to reflect these changes and ensure proper handling of raw and normalized labels.
@vercel vercel Bot temporarily deployed to Preview – ensnode.io May 4, 2026 10:47 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io May 4, 2026 10:47 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io May 4, 2026 10:48 Inactive
djstrong added 2 commits May 4, 2026 12:56
…or distinct `LabelHash` inputs, update error messages for clarity, and allow duplicate `LabelHashes` within the maximum distinct count. Adjust related tests and documentation to reflect these changes.
…dling for invalid inputs, ensuring clearer error messages. Modify tests to validate mixed-case hex digits and reject uppercase `0X` prefixes for `LabelHash` variables.
Copilot AI review requested due to automatic review settings May 4, 2026 10:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

djstrong added 2 commits May 4, 2026 20:55
…` and `discover()` methods, introduce `EnsRainbowBeamHttpError` for error handling, and ensure client-side validation aligns with server expectations. Update exports and configuration for new client integration.
…plement comprehensive unit tests covering validation logic, health checks, and discover functionality, ensuring proper error handling and response validation. Introduce `omnigraph-consts.ts` for server-side limits on label requests.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 47 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

djstrong added 2 commits May 5, 2026 14:07
…ent workflows for `ensrainbowbeam`, and enhance configuration with CORS origins. Update Terraform modules for deployment and add new environment variables. Enhance UI components for label submission and results display.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 55 out of 58 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread terraform/modules/ensrainbowbeam/main.tf
Comment thread docker/services/ensrainbowbeam.yml
Comment thread docs/ensrainbow.io/src/components/organisms/HealUnknownName.tsx Outdated
Comment thread terraform/main.tf
Comment thread terraform/modules/ensrainbowbeam/variables.tf
Comment thread terraform/modules/ensrainbowbeam/variables.tf
Comment thread docs/ensrainbow.io/src/components/organisms/HealUnknownName.tsx Outdated
…alized labels, enhancing the classification logic. Introduce new types for skipped labels and adjust related components to accommodate the changes. Update deployment workflow by removing outdated comments.
Comment thread docker/services/ensrainbowbeam.yml
Comment thread docker/services/ensrainbowbeam.yml
Comment thread docs/ensrainbow.io/src/components/organisms/HealUnknownName.tsx Outdated
Comment thread terraform/modules/ensrainbowbeam/main.tf
djstrong added 2 commits May 6, 2026 15:55
… `.env.local.example` and `README.md` to reflect the removal of `CORS_ORIGINS`. Refactor `config.ts` to eliminate CORS parsing and adjust `app.ts` to remove CORS middleware, streamlining the application setup.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 55 out of 58 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread apps/ensrainbowbeam/src/lib/labels.ts Outdated
Comment on lines +67 to +71
* True when a submitted label is already normalized under ENSIP-15.
*/
export function isProcessableLabel(rawLabel: LiteralLabel): boolean {
return isNormalizedLabel(rawLabel as unknown as Label);
}
Comment on lines +49 to +53
export type LabelClassification = HashedLabel & {
status: LabelStatus;
};

export type SkippedLabelClassification = {
@@ -11,7 +13,7 @@ LabelRef.implement({
hash: t.field({
description:
"The Label's LabelHash\n(@see https://ensnode.io/docs/reference/terminology#labels-labelhashes-labelhash-function)",
Comment thread docs/ensrainbow.io/src/components/organisms/HealUnknownName.tsx
Comment thread docs/ensrainbow.io/src/components/organisms/HealUnknownName.tsx
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 6, 2026

Greptile Summary

This PR introduces the EnsRainbowBeam app (apps/ensrainbowbeam): a Hono HTTP service exposing GET /health and POST /api/discover that normalizes submitted ENS labels under ENSIP-15, classifies them against the ENSNode Omnigraph labels index, and emits structured JSONL to stdout (persistence deferred). It also extends the ENSApi Omnigraph with a new Query.labels(by: LabelsByLabelHashesInput!) field and a LabelHash scalar, ships a typed EnsRainbowBeamClient in @ensnode/ensrainbow-sdk, and adds a "Beam-it" UI to docs/ensrainbow.io.

  • New EnsRainbowBeam app validates up to 100 labels per request, runs ENSIP-15 normalization (unnormalizable labels get skipped_unnormalized), deduplicates labelhashes, looks them up against Omnigraph with a 10 s timeout + client-disconnect cancel, and returns per-label unknown_in_index / healed_in_index / absent_from_index / skipped_unnormalized status while logging a JSONL line per request to stdout.
  • Omnigraph Query.labels performs server-side dedup, caps at 100 distinct LabelHashes per request, and uses createGraphQLError so validation messages survive Yoga's maskError; a new LabelHash scalar enforces 32-byte lowercase hex at parse time.
  • SDK + UI: EnsRainbowBeamClient wraps the HTTP surface with client-side validation and injectable fetch; the Astro/React Beam-it page lets browser users submit labels directly to beam.ensrainbow.io.

Confidence Score: 5/5

Safe to merge; the new service is well-isolated, stdout-only, and thoroughly tested.

All changed paths are new code with no side effects beyond stdout logging and read-only Omnigraph queries. The 27-test suite covers validation, classification, timeout/error paths, deduplication, and log format. The two findings are minor documentation and hardening notes that do not affect correctness.

apps/ensapi/src/omnigraph-api/schema/query.ts — the raw labelHashes input array has no upper bound before deduplication.

Important Files Changed

Filename Overview
apps/ensrainbowbeam/src/handlers/submissions.ts Core POST /api/discover handler: validates input with Zod, normalizes labels via ENSIP-15, dedupes hashes, calls Omnigraph with AbortSignal timeout/cancellation, classifies results, preserves original submission order, and emits a JSONL log line per request. Logic is correct and well-tested.
apps/ensrainbowbeam/src/lib/labels.ts Pure label-processing utilities: ENSIP-15 normalization, labelhashing, Omnigraph hit classification. Correctly handles unnormalizable labels (returns null), non-normalized labels (uppercase → normalized form with normalizedLabel populated), and deduplication. Well-covered by unit tests.
apps/ensrainbowbeam/src/lib/omnigraph-client.ts Wraps the typed gql.tada query against ENSNode Omnigraph; includes chunking logic and AbortSignal forwarding. One minor doc inaccuracy: "up to 200 distinct LabelHashes" should be 100 given current submission limits.
apps/ensapi/src/omnigraph-api/schema/query.ts Adds Query.labels resolver with dedup and LABELS_BY_LABELHASH_MAX enforcement; uses createGraphQLError so validation messages survive Yoga's maskError. The cap applies to distinct hashes only — no bound on the raw input array size before parsing.
apps/ensapi/src/omnigraph-api/schema/scalars.ts Adds LabelHash scalar with parseLabelHash validation and createGraphQLError on bad input; serialize is identity. Clean addition alongside existing scalars.
packages/ensrainbow-sdk/src/ensrainbowbeam-client.ts Typed HTTP client for EnsRainbowBeam with client-side validation (validateDiscoverParams), injectable fetch, EnsRainbowBeamHttpError with details extraction, and AbortSignal passthrough. Well-tested with 13 unit tests.
docs/ensrainbow.io/src/components/organisms/HealUnknownName.tsx Beam-it React UI with user-configurable beam URL, per-line label parsing, client-side validation, result table with StatusBadge, and full error handling. isSubmitting guard prevents concurrent submits.
apps/ensrainbowbeam/src/index.ts Hono/Node server entry point with deduplicated graceful shutdown (SIGINT, SIGTERM, uncaughtException). stdout kept clean for JSONL stream; operational logs go to stderr.
apps/ensapi/src/omnigraph-api/yoga.ts Adds dev-mode masked-error logging that prints the original error server-side while still forwarding the masked payload to clients; production keeps Yoga defaults. Graphql-armor plugins remain commented out.

Sequence Diagram

sequenceDiagram
    participant Browser as Browser / SDK Client
    participant Beam as EnsRainbowBeam<br/>(POST /api/discover)
    participant ENSIP15 as ENSIP-15 Normalizer
    participant Omnigraph as ENSNode Omnigraph<br/>(Query.labels)
    participant DB as ensIndexer label table

    Browser->>Beam: "POST /api/discover { labels[], callerAddress }"
    Beam->>Beam: "Zod validation (<=100 labels, valid EVM address)"
    loop Each label
        Beam->>ENSIP15: normalizeLabel(rawLabel)
        ENSIP15-->>Beam: normalized form OR null (skipped)
    end
    Beam->>Beam: collectLookupHashes (dedup)
    Beam->>Omnigraph: "Query.labels(by: { labelHashes }) with AbortSignal (10s timeout)"
    Omnigraph->>Omnigraph: parseValue LabelHash (normalize + validate)
    Omnigraph->>Omnigraph: "dedup + cap <=100 distinct hashes"
    Omnigraph->>DB: SELECT WHERE labelHash IN (...)
    DB-->>Omnigraph: matching Label rows
    Omnigraph-->>Beam: "[{ hash, interpreted }]"
    Beam->>Beam: "classifySubmissions -> unknown/healed/absent per label"
    Beam->>Beam: console.log(JSON.stringify(logLine)) [stdout JSONL]
    Beam-->>Browser: "{ callerAddress, results[] }"
Loading

Reviews (3): Last reviewed commit: "refactor(ensrainbowbeam): update LabelHi..." | Re-trigger Greptile

Comment on lines +67 to +70
process.on("uncaughtException", (error) => {
console.error("[ensrainbowbeam] uncaughtException", error);
void gracefulShutdown(1);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The process registers handlers for SIGINT, SIGTERM, and uncaughtException but not unhandledRejection. Node.js fires unhandledRejection for any Promise that rejects without a .catch() handler, and while Node 15+ terminates on it by default, it bypasses the graceful closeServer() path entirely, so in-flight connections won't be drained and the exit code may be 0 instead of 1.

Suggested change
process.on("uncaughtException", (error) => {
console.error("[ensrainbowbeam] uncaughtException", error);
void gracefulShutdown(1);
});
process.on("uncaughtException", (error) => {
console.error("[ensrainbowbeam] uncaughtException", error);
void gracefulShutdown(1);
});
process.on("unhandledRejection", (reason) => {
console.error("[ensrainbowbeam] unhandledRejection", reason);
void gracefulShutdown(1);
});

Comment thread apps/ensrainbowbeam/src/handlers/submissions.ts Outdated
Comment thread apps/ensrainbowbeam/src/lib/labels.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 55 out of 58 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add new app with endpoint for submitting labels

3 participants