-
-
Notifications
You must be signed in to change notification settings - Fork 94
@fedify/debugger: Embedded ActivityPub debug dashboard
#564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Add the `@fedify/debugger` package skeleton with build infrastructure: - Create deno.json, package.json, and tsdown.config.ts - Add placeholder src/mod.ts - Register the package in the root deno.json workspace, pnpm-workspace.yaml, README.md Packages table, and CONTRIBUTING.md repository structure list - Add hono as a root-level Deno import for JSR resolution fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Add the core `createFederationDebugger()` function that wraps a `Federation` object to intercept requests matching a configurable debug path prefix (default `/__debug__`) and serve them via an internal Hono app. All other methods and non-debug requests are delegated to the inner federation object. Tests cover delegation of all Federation interface methods, debug path interception, custom path prefix, JSON API, and onNotFound passthrough. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Replace plain text responses with JSX-based server-side rendered HTML pages using Hono's JSX engine: - Layout component with minimal skeleton CSS styling - Traces list page showing trace IDs, activity types, counts, timestamps, and auto-polling via inline script - Trace detail page showing activity direction, type, actor, signature verification details, inbox URL, and expandable activity JSON Rename mod.ts to mod.tsx to support JSX pragma directives. Add four new tests verifying HTML content in responses. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Add docs/manual/debug.md covering installation, setup, configuration options, dashboard pages, JSON API, and framework integration examples (Hono, Express). Also add "Debugging" entry to the Manual sidebar and "@fedify/debugger" to the References section in VitePress config. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Document the new @fedify/debugger package under the Version 2.0.0 section, including the createFederationDebugger() API, dashboard pages, and JSON API endpoint. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
When called without an exporter option, createFederationDebugger() now automatically sets up OpenTelemetry tracing internally: it creates a MemoryKvStore, FedifySpanExporter, BasicTracerProvider with SimpleSpanProcessor, and registers it as the global tracer provider via trace.setGlobalTracerProvider(). This eliminates the need for users to manually configure OTel just to use the debug dashboard. The return type is always Federation<TContextData> regardless of which overload is used, avoiding confusion from differing return types. The existing overload with an explicit exporter option is preserved for advanced use cases where users need full control over the OTel setup. Documentation updated to show the simplified usage as the primary example, with the manual OTel setup moved to an "Advanced setup" section. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Add optional authentication for the debug dashboard via the new `auth` option on `createFederationDebugger()`. Three modes are supported: - `"password"`: login form with a single password field - `"usernamePassword"`: login form with username and password - `"request"`: authenticate by inspecting the Request object (e.g., IP-based filtering), returning 403 on rejection Each form-based mode accepts either static credentials or an `authenticate()` callback. Sessions use HMAC-SHA256 signed cookies generated with Web Crypto API; the key is randomly created at startup so sessions do not survive restarts. Authentication only applies to the debug dashboard routes; ActivityPub endpoints pass through without any auth check. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Wrap OTel span callbacks in LogTape's withContext() so that all log records emitted during request handling and queue processing carry the traceId and spanId in their properties. This enables per-trace log collection by consumers such as @fedify/debugger. Affected code paths: - FederationImpl.fetch() HTTP request handling - FederationImpl.processQueuedTask() for fanout, outbox, and inbox message types fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Collect LogTape log records per OTel trace so that the debug dashboard can display all logs associated with a given trace. Key changes: - Add LogStore in-memory storage keyed by traceId with eviction - Expose a LogTape Sink via the returned federation.sink property - Simplified overload auto-configures LogTape with contextLocalStorage - Add GET /api/logs/:traceId JSON endpoint - Show logs table in trace detail page with level badges - Update docs and changelog Note: Logs are stored in-memory and do not survive process restarts, unlike traces which may be persisted in a KvStore. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Without a global context manager, parent–child spans each get independent trace IDs, so logs collected under the HTTP span's traceId never match the trace stored by FedifySpanExporter. Register an AsyncLocalStorageContextManager in the simplified overload of createFederationDebugger() so that child spans (e.g. activitypub.inbox) inherit the parent trace, and logs are correctly associated with dashboard traces. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Without a global propagator, propagation.inject() produces an empty carrier object, so trace context is lost when activities cross message queue boundaries. This caused unrelated activities (e.g., an Accept response to a Follow) to end up under the wrong trace. Register W3CTraceContextPropagator in the simplified overload alongside the context manager and tracer provider, and document the requirement for advanced setups. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
The existing code appended the debugger sink to every logger entry, but the fallback block already adds a root-category logger when none exists. When a root logger was present, logs were captured both by the root entry and by each sub-category entry, producing duplicates. Only append the debugger sink to the root-category logger and leave sub-category loggers unchanged, since the root logger already captures all log records. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Use @media (prefers-color-scheme: dark) to automatically follow the OS color scheme. Covers all dashboard pages including traces list, trace detail, and login form. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Summary of ChangesHello @dahlia, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a powerful new debugging tool for Fedify applications: an embedded ActivityPub debug dashboard. This dashboard provides real-time visibility into ActivityPub traces and activities, making it significantly easier for developers to understand the flow of federated interactions, verify signatures, and diagnose issues. It integrates seamlessly with existing Fedify setups, offering both simplified and advanced configuration options, and enhances observability by correlating logs with specific traces. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces an excellent new feature: the @fedify/debugger package. This embedded debug dashboard will be a great asset for developers working with Fedify, providing valuable real-time insights into ActivityPub traces and activities. The implementation is comprehensive, including tracing, logging, a clean UI, and authentication. The necessary changes to the core @fedify/fedify package are minimal and well-targeted. I've identified a few areas for improvement, mainly concerning dependency management, code duplication, and opportunities to use library helpers for more robust code.
There was a problem hiding this 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 @fedify/debugger package that wraps a Federation instance and serves an embedded SSR ActivityPub trace/log dashboard under a configurable path prefix, plus correlates LogTape logs with OTel traces by injecting traceId/spanId into logging context.
Changes:
- Introduces
packages/debuggerwith SSR UI (traces list, trace detail, login) + JSON APIs + optional auth and log sink. - Updates
@fedify/fedifymiddleware to enrich LogTape context with active spantraceId/spanIdduring request handling and queued task processing. - Wires the new package into workspace/deps/docs/changelog.
Reviewed changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Adds packages/debugger to workspace and catalogs @opentelemetry/context-async-hooks. |
| pnpm-lock.yaml | Locks new debugger package dependencies. |
| packages/fedify/src/federation/middleware.ts | Injects traceId/spanId into LogTape context around spans. |
| packages/fedify/README.md | Adds @fedify/debugger to package list. |
| packages/debugger/tsdown.config.ts | Build config for the new package (ESM/CJS + DTS). |
| packages/debugger/src/mod.tsx | Core proxy implementation, auth/session handling, OTel/LogTape setup, API routes. |
| packages/debugger/src/mod.test.ts | Unit tests for proxy routing, API, auth modes, and log sink behavior. |
| packages/debugger/src/views/layout.tsx | Shared HTML layout + styles for dashboard pages. |
| packages/debugger/src/views/traces-list.tsx | Traces list SSR page + polling script. |
| packages/debugger/src/views/trace-detail.tsx | Trace detail SSR page including activities and log table. |
| packages/debugger/src/views/login.tsx | Login form SSR page for cookie-based auth modes. |
| packages/debugger/package.json | npm package metadata + dependencies/scripts. |
| packages/debugger/deno.json | JSR metadata/tasks for the new package. |
| docs/package.json | Adds @fedify/debugger to docs workspace deps. |
| docs/manual/debug.md | New manual page documenting the debugger. |
| docs/.vitepress/config.mts | Adds “Debugging” manual link and @fedify/debugger reference. |
| deno.lock | Updates Deno lockfile for new dependencies. |
| deno.json | Adds debugger workspace entry and import mappings (incl. hono + context-async-hooks). |
| CONTRIBUTING.md | Documents the new package in the monorepo structure list. |
| CHANGES.md | Changelog entries for debugger + LogTape context correlation change. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Replace @fedify/fixture and @std/assert with node:test and node:assert/strict so that the debugger tests run on Deno, Node.js, and Bun without requiring the @std/assert JSR dependency, which was not resolvable in Node.js/Bun environments. Also import from "@fedify/debugger" instead of "./mod.tsx" so that each runtime resolves the module through its own package exports (deno.json for Deno, package.json dist/ for Node.js/Bun). fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
The tsdown config injects @js-temporal/polyfill into the build output, making it a runtime requirement. Having it in devDependencies causes MODULE_NOT_FOUND errors for consumers in Node.js environments. fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
configure() returns a Promise but was called without await, so use configureSync() instead to match the else branch. Also move contextLocalStorage after the existingConfig spread with a ?? fallback so it is not silently overwritten by the existing configuration. fedify-dev#564 (comment) fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
Session cookies for login and logout were missing the Secure attribute, so cookies could be sent over unencrypted connections. Now the Secure flag is conditionally added when the request URL uses the https: protocol. fedify-dev#564 (comment) fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
pathPrefix was interpolated directly into a dangerouslySetInnerHTML script block via a template literal. A malicious pathPrefix containing quotes or </script> could enable script injection. Now the value is JSON-encoded and < is escaped as \u003c so it cannot break out of the script tag. fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
FederationImpl previously captured the TracerProvider at construction time. When the simplified overload of createFederationDebugger() registered a global provider AFTER the federation was already created, the federation still held the old noop provider and traces were silently lost. Now the tracerProvider getter falls back to trace.getTracerProvider() on every access when no explicit provider was passed, so late registration of a global provider is picked up automatically. Also remove the now-incorrect NOTE comment from the documentation that said createFederationDebugger() must be called before createFederation(). fedify-dev#564 (comment) fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
Embed the Fedify mascot logo as an inline SVG component (FedifyLogo) in the debug dashboard header, displayed at 24px next to the title text. The header link uses flexbox for vertical alignment between the logo and text. fedify-dev#561 Co-Authored-By: Claude <noreply@anthropic.com>
Reject empty strings, paths without leading slash, and paths containing control characters, semicolons, or commas that are unsafe in HTTP headers (Set-Cookie, Location). Trailing slashes are automatically stripped during normalization. This prevents misconfigured path prefixes from hijacking routing or tainting response headers. fedify-dev#561 fedify-dev#564 Co-Authored-By: Claude <noreply@anthropic.com>
Use a Proxy to dynamically forward all Federation methods to the inner federation instead of maintaining a hardcoded list of method names. Only fetch (for debug interception) and sink (for log collection) are overridden. This ensures the debugger wrapper automatically supports any new methods added to the Federation interface without requiring manual updates. fedify-dev#561 fedify-dev#564 Co-Authored-By: Claude <noreply@anthropic.com>
Replace plain === string comparison for static password and username/password auth with timingSafeEqual from node:crypto to prevent timing side-channel attacks on credential checks. Also add tests for the full session lifecycle: logging in with a valid password, using the cookie to access protected pages, and verifying that forged cookies are rejected. fedify-dev#561 fedify-dev#564 Co-Authored-By: Claude <noreply@anthropic.com>
Prevent duplicate OpenTelemetry provider registrations and LogTape sink configurations when createFederationDebugger() is called multiple times without an explicit exporter. A module-level cache reuses the exporter from the first auto-setup call. Co-Authored-By: Claude <noreply@anthropic.com>
LogStore.get() now returns a shallow copy so callers cannot mutate the internal array. The trace detail page uses a safeISOString helper that falls back to "(invalid)" for NaN or otherwise unparseable timestamps instead of throwing a RangeError. Co-Authored-By: Claude <noreply@anthropic.com>
Replace the in-memory Map-based LogStore with a KvStore-backed implementation so that logs produced by worker processes are visible in the debug dashboard served by the web process. LogStore.add() enqueues writes to a promise chain and exposes a flush() method; route handlers call flush() before reading to ensure all pending writes are visible. This avoids fromAsyncSink() which was incompatible with configureSync(). The advanced overload now requires a kv option alongside the exporter. The simplified overload shares the auto-created MemoryKvStore. Co-Authored-By: Claude <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a new @fedify/debugger package, which is a significant and useful feature for developers using Fedify. The implementation is of high quality, with a well-thought-out design including auto-configuration of OpenTelemetry, a clean proxy-based architecture, and a secure authentication mechanism for the dashboard. The documentation is also excellent. I've found a couple of issues, one related to potential data loss in multi-process environments in the log store, and another minor issue in the LogTape auto-configuration logic. Overall, this is a great addition to the Fedify ecosystem.
There was a problem hiding this 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 19 out of 21 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
2chanhaeng
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be very useful! It would be better if mod.ts and mod.test.ts were split to make it easier to read.
Replace the in-memory per-trace sequence counter with a
timestamp + random suffix key strategy. This eliminates two
problems:
- Key collisions when multiple processes share the same KvStore
(each process had its own counter starting at 0).
- Unbounded memory growth from the #seq Map that was never cleared.
fedify-dev#564 (comment)
fedify-dev#564 (comment)
Co-Authored-By: Claude <noreply@anthropic.com>
A single failed KvStore.set() could poison the promise chain, causing all subsequent writes and flush() to reject. It could also trigger an unhandled rejection since the synchronous Sink never awaits the result. Catch and discard errors so logging remains best-effort. fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
The existing XSS test only checked that the malicious payload was absent from the response body, but never verified that the debug dashboard HTML was actually rendered. Strengthen the assertion by branching on the Content-Type header: when the response is HTML, confirm the dashboard is present AND the payload is escaped; otherwise, confirm the delegated response also does not contain the payload. Addresses: fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
The ~700-line mod.tsx was doing too many things: log storage, auth helpers, route definitions, and the public createFederationDebugger API. Extract cohesive units into their own modules: - log-store.ts: LogStore class, SerializedLogRecord, createLogSink - auth.ts: FederationDebuggerAuth type, session/HMAC helpers, checkAuth - routes.tsx: createDebugApp (Hono route tree with auth middleware) mod.tsx is now the public entry point that wires the pieces together and re-exports the public types. Addresses: fedify-dev#564 (comment) Co-Authored-By: Claude <noreply@anthropic.com>
Include screenshots of the traces list page and trace detail page in the Dashboard pages section so that readers can see what the UI looks like before setting it up. Co-Authored-By: Claude <noreply@anthropic.com>
Move all @opentelemetry/* version specifiers into the pnpm catalog (pnpm-workspace.yaml) and root deno.json workspace imports so that individual packages no longer duplicate them. Also bump to the latest versions: - @opentelemetry/context-async-hooks ^2.0.0 → ^2.5.0 - @opentelemetry/core ^2.0.0 → ^2.5.0 - @opentelemetry/sdk-trace-base ^2.0.0 → ^2.5.0 - @opentelemetry/semantic-conventions ^1.27.0 → ^1.39.0 - @opentelemetry/exporter-trace-otlp-proto (new) ^0.211.0 - @opentelemetry/sdk-node (new) ^0.211.0 Co-Authored-By: Claude <noreply@anthropic.com>
sij411
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debugger UI worked as expected.
The @fedify/debugger package introduced in #564 was missing a README. Add one following the same pattern as other packages in the monorepo, covering package description, usage example, and installation. Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Closes #561
Adds a new
@fedify/debuggerpackage that provides an embedded real-time ActivityPub debug dashboard. It works as a proxy implementing theFederationinterface, wrapping the original federation object and serving an SSR-based web UI at/__debug__/using Hono internally.Features
/__debug__/): Shows recent traces with trace ID, activity types, activity count, and timestamp/__debug__/traces/:traceId): Shows all activities in a trace with direction, type, actor, signature verification details, expandable JSON, and per-trace log recordstraceId/spanIdinto LogTape context viawithContext(), enabling logs to be filtered by tracecreateFederationDebugger(federation)overload that auto-configures OTel tracer provider, span exporter, and LogTape sinkauthoption with password protection via cookie-based sessionsprefers-color-schemepreferenceScreenshots
Changes to
@fedify/fedifyThe
fetch()and all threeprocessQueuedTask()handlers in packages/fedify/src/federation/middleware.ts now call LogTape'swithContext()to injecttraceIdandspanIdfrom the active OTel span. This enables the debugger (and any LogTape sink) to correlate log records with specific traces.Usage