Skip to content

feat: Phase 29 Infrastructure Hardening#383

Merged
FSM1 merged 26 commits into
mainfrom
feat/phase-29-infra-hardening
Mar 28, 2026
Merged

feat: Phase 29 Infrastructure Hardening#383
FSM1 merged 26 commits into
mainfrom
feat/phase-29-infra-hardening

Conversation

@FSM1

@FSM1 FSM1 commented Mar 28, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 29: Infrastructure Hardening
Goal: Orphaned IPNS records are cleaned up on deletion, test login endpoint is hardened for staging
Status: Verified ✓

Added batch IPNS unenrollment API endpoint, wired automatic unenrollment into all SDK delete paths, and added Grafana monitoring for the test login endpoint on staging.

Note: This PR includes Phase 28 commits (structured logger, unpin fixes, any cast cleanup, POC archive) as its base. Phase 28 PR #382 should be merged first.

Changes

Plan 29-01: Batch IPNS Unenroll API Endpoint

Created POST /ipns/unenroll on IpnsController with BatchUnenrollIpnsDto (validates array of IPNS names, max 200). IpnsService.unenrollBatch() delegates to existing RepublishService.unenrollIpns(). API client regenerated to expose ipnsControllerUnenrollBatch.

Key files:

  • apps/api/src/ipns/dto/unenroll.dto.ts (new)
  • apps/api/src/ipns/ipns.controller.ts (new route)
  • apps/api/src/ipns/ipns.service.ts (new method)
  • packages/api-client/src/generated/ (regenerated)

Plan 29-02: SDK IPNS Unenrollment on Delete

Added fireAndForgetUnenroll() and collectSubtreeIpnsNames() to CipherBoxClient. Wired into 4 deletion methods: deleteItem, deleteToBin, permanentDelete, emptyBin. Cleaned up 3 legacy TODO comments in folder.service.ts and delete.service.ts.

Key files:

  • packages/sdk/src/client.ts (fireAndForgetUnenroll, collectSubtreeIpnsNames)
  • apps/web/src/services/folder.service.ts (TODO cleanup)
  • apps/web/src/services/delete.service.ts (TODO cleanup)

Plan 29-03: Test Login Monitoring & Kubo Verification

Created Grafana alert rule (test-login-rate.json) firing when cipherbox_auth_logins_total{method="test"} exceeds 100/hour on staging. Verified existing production guard (NODE_ENV check + timingSafeEqual) and Kubo 127.0.0.1 binding.

Key files:

  • docker/grafana/alerts/test-login-rate.json (new)

Verification

  • IPNS unenrollment fires on file/folder deletion via SDK
  • Batch unenrollment works for folder deletes (collectSubtreeIpnsNames recursive walk)
  • POST /auth/test-login unreachable when NODE_ENV=production (existing unit tests verify)
  • Grafana alert rule created for staging test login rate monitoring
  • Kubo API bound to 127.0.0.1 in staging Docker compose (verified)

Key Decisions

  • Kubo port 5001 access control deferred — Docker 127.0.0.1 binding is sufficient for staging
  • Test login endpoint NOT removed from staging — E2E tests depend on it
  • SDK layer is the integration point (not legacy folder.service.ts)
  • Fire-and-forget pattern for unenrollment (don't block deletion UX)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Batch IPNS unenroll endpoint (up to 200 names) and SDK fire‑and‑forget unenrollment on deletions
    • Grafana alert for elevated staging "test login" activity
  • Improvements

    • Automatic IPNS cleanup integrated into delete flows (files/folders/bin)
    • Consistent logging improvements and error handling across web, SDK, and API
  • Documentation / Planning

    • Project planning updated: Phases 28 & 29 marked complete (2026-03-28)

FSM1 and others added 14 commits March 28, 2026 04:22
Create lib/logger.ts with level-filtered output (debug/info/warn/error).
In production, only warn and error are emitted. Replace all 124 raw
console.* calls across 28 web app source files with logger equivalents.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All .catch with empty callbacks on IPFS unpin calls and AudioContext
close calls now log warnings via the structured logger so failures
are visible instead of silently swallowed. Also fixes the one
occurrence in packages/sdk/src/client.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all `as any` casts in production web code with typed
alternatives. Polyfill globalThis augmentations use proper
declare-global blocks, the debug error log uses a typed Window
interface, and the Zustand debug store uses a typed window property.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove 00-Preliminary-R&D/poc/ which was superseded by the production
implementation. The POC code is preserved in git history. Add
ARCHIVED.md documenting what was removed and where to find it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create plans, summaries, and verification for Phase 28. All 4 success
criteria verified: structured logger, visible unpin failures, no
as-any casts, POC archived. Update ROADMAP.md and STATE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exposes the existing RepublishService.unenrollIpns() via a new REST
endpoint. Accepts up to 200 IPNS names per call with validation.
Scoped to authenticated user via JWT guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fires when POST /auth/test-login exceeds 100 calls/hour on staging.
Verified: production guard (NODE_ENV + timingSafeEqual) and Kubo
port 5001 bound to 127.0.0.1 in staging compose are already in place.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add fireAndForgetUnenroll() and collectSubtreeIpnsNames() to
CipherBoxClient. deleteItem, deleteToBin, permanentDelete, and
emptyBin now batch-unenroll orphaned IPNS records from the TEE
republish schedule via POST /ipns/unenroll.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
IPNS unenrollment is now handled by SDK's fireAndForgetUnenroll().
Removes orphaned-record warnings and deferred-work comments from
folder.service.ts and delete.service.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-existing implicit-any on line 1165 caught by DTS build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 3 plans executed, verification passed on all 4 success criteria.
ROADMAP.md, STATE.md updated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 28, 2026

Copy link
Copy Markdown

Walkthrough

Marks Phases 28 and 29 complete (2026-03-28) and implements Phase 29 work: a batch IPNS unenroll REST API plus DTOs, SDK fire-and-forget batch unenroll integration into delete flows, structured logger replacements in web services, and a Grafana test-login alert.

Changes

Cohort / File(s) Summary
Planning & Roadmap
\.planning/ROADMAP.md, \.planning/STATE.md, \.planning/config.json, .planning/phases/29-infrastructure-hardening/...
Updates project state (Phase 28 & 29 marked complete 2026-03-28), adds Phase 29 plans/summaries/context/discussion/verification and config newline change.
IPNS API DTOs & Exports
apps/api/src/ipns/dto/unenroll.dto.ts, apps/api/src/ipns/dto/index.ts
Adds BatchUnenrollIpnsDto (1–200 IPNS names, validation) and BatchUnenrollIpnsResponseDto; re-exports from DTO barrel.
IPNS Service & Controller
apps/api/src/ipns/ipns.service.ts, apps/api/src/ipns/ipns.controller.ts
Adds IpnsService.unenrollBatch(userId, ipnsNames) using concurrent allSettled with per-item logging and non-abort semantics, plus authenticated/throttled POST /ipns/unenroll controller returning totals.
Republish Service
apps/api/src/republish/republish.service.ts
unenrollIpns now returns Promise<number> and normalizes result.affected to affected ?? 0, returning the affected count.
API Client & Generated Models
packages/api-client/src/generated/..., packages/api-client/src/models/index.ts
Regenerated API client exposing ipnsControllerUnenrollBatch and exported generated DTO/model types for batch unenroll.
SDK Delete Flow Integration
packages/sdk/src/client.ts
Adds helpers to collect IPNS names (including recursive subtree when loaded), implements chunked fire-and-forget unenroll calls (BATCH_SIZE=200) wired into delete flows (deleteItem, permanentDelete, emptyBin; skip deleteToBin), and narrows catch typing to err: unknown.
Web Service Logging Standardization
apps/web/src/services/folder.service.ts, apps/web/src/services/delete.service.ts, apps/web/src/components/file-browser/FileBrowser.tsx, apps/web/src/lib/web3auth/hooks.ts
Replaces console.* with logger.* in multiple spots, updates comments/TODOs to reflect SDK-handled unenroll, and removes a debug log field.
Infrastructure & Monitoring
docker/grafana/alerts/test-login-rate.json
Adds Grafana alert rule "Test Login Rate High" evaluating increase(cipherbox_auth_logins_total{method="test"}[1h]) > 100 with 5m for clause and OK noData/execErr states.
Tests & CI & Scripts
apps/api/src/ipns/ipns.service.spec.ts, tests/sdk-e2e/..., .github/workflows/ci.yml, scripts/check-api-client.sh, apps/api/jest.config.js
Adds unit tests for unenrollBatch, a new E2E test verifying SDK unenroll-on-delete, switches CI openapi comparison to semantic JSON diff, improves generated-file detection in precheck script, and relaxes ipns controller function coverage threshold.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Client
    participant SDK as SDK Client
    participant API as IPNS Controller
    participant Service as IPNS Service
    participant Republish as Republish Service

    User->>SDK: deleteItem(item)
    SDK->>SDK: collectRemovedItemIpnsNames(item)
    Note over SDK: extract fileMetaIpnsName(s)
    SDK->>SDK: chunk & fireAndForgetUnenroll(chunks)
    SDK->>API: POST /ipns/unenroll (chunk)
    activate API
    API->>Service: unenrollBatch(userId, chunk)
    activate Service
    Service->>Republish: unenrollIpns(userId, name) per item
    Republish-->>Service: success/failure (per-item)
    Service-->>API: { totalUnenrolled: N }
    deactivate Service
    API-->>SDK: BatchUnenrollIpnsResponseDto
    deactivate API
    SDK->>SDK: log failures (fire-and-forget)
    SDK-->>User: delete flow completes
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: Phase 29 Infrastructure Hardening' is clearly related to and accurately summarizes the main objectives of the changeset, which centers on implementing Phase 29 infrastructure hardening features.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase-29-infra-hardening

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.

@FSM1 FSM1 requested a review from Copilot March 28, 2026 04:01
@FSM1 FSM1 changed the title Phase 29: Infrastructure Hardening feat: Phase 29 Infrastructure Hardening Mar 28, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

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.

Pull request overview

Phase 29 infrastructure hardening to reduce operational buildup of orphaned IPNS republish enrollments and add staging monitoring around the test-login endpoint (plus Phase 28 base commits: structured logging/type hygiene + POC archival).

Changes:

  • Added POST /ipns/unenroll batch endpoint (max 200) and regenerated @cipherbox/api-client bindings.
  • Wired SDK delete flows to fire-and-forget batch unenrollment (including recursive subtree collection for loaded folders).
  • Added a Grafana alert for staging test-login rate; replaced remaining web console.* usage with structured logger and tightened types for debug globals/polyfills.

Reviewed changes

Copilot reviewed 71 out of 74 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/sdk/src/client.ts Adds fire-and-forget IPNS unenroll + subtree collection; hooks into delete flows; improves unpin error visibility
packages/sdk/src/bin/index.ts Bin ops plumbing returns removed item for unenroll wiring
apps/api/src/ipns/dto/unenroll.dto.ts Adds batch unenroll DTO + response DTO with validation
apps/api/src/ipns/dto/index.ts Re-exports new unenroll DTOs
apps/api/src/ipns/ipns.controller.ts Adds POST /ipns/unenroll route with throttling + swagger docs
apps/api/src/ipns/ipns.service.ts Implements unenrollBatch() delegating to RepublishService
packages/api-client/openapi.json Updates OpenAPI spec to include unenroll endpoint and formatting updates
packages/api-client/src/generated/ipns/ipns.ts Regenerates client with ipnsControllerUnenrollBatch
packages/api-client/src/models/batchUnenrollIpnsDto.ts Adds generated unenroll request model
packages/api-client/src/models/batchUnenrollIpnsResponseDto.ts Adds generated unenroll response model
packages/api-client/src/models/index.ts Exports newly generated unenroll models
docker/grafana/alerts/test-login-rate.json Adds staging alert for elevated test-login usage
apps/web/src/lib/logger.ts Introduces structured, level-filtered logger for the web app
apps/web/src/services/upload.service.ts Switches error logging to structured logger
apps/web/src/services/share.service.ts Switches warn logging to structured logger
apps/web/src/services/search-index.service.ts Switches warn logging to structured logger
apps/web/src/services/ipns.service.ts Switches warn logging to structured logger
apps/web/src/services/folder.service.ts Removes legacy TODOs; points TEE unenroll responsibility to SDK path
apps/web/src/services/device-registry.service.ts Switches error logging to structured logger
apps/web/src/services/delete.service.ts Removes legacy TODO; switches error logging to structured logger
apps/web/src/services/bin.service.ts Switches logging to structured logger; makes unpin failures visible
apps/web/src/hooks/useSharedNavigation.ts Switches error logging to structured logger
apps/web/src/hooks/useSearch.ts Switches warn/error logging to structured logger
apps/web/src/hooks/useFolderNavigation.ts Switches error logging to structured logger
apps/web/src/hooks/useFileVersions.ts Switches warn logging to structured logger; makes unpin failures visible
apps/web/src/hooks/useFileOperations.ts Switches warn logging to structured logger; makes unpin failures visible
apps/web/src/hooks/useFileDownload.ts Switches error logging to structured logger
apps/web/src/hooks/useDropUpload.ts Switches warn logging to structured logger; makes cleanup failures visible
apps/web/src/hooks/useBin.ts Switches error logging to structured logger
apps/web/src/hooks/useAuth.ts Switches log/warn/error logging to structured logger
apps/web/src/components/settings/ConnectionTest.tsx Switches warn logging to structured logger
apps/web/src/components/file-browser/FileBrowser.tsx Switches error logging to structured logger
apps/web/src/components/file-browser/BinBrowser.tsx Switches error logging to structured logger
apps/web/src/components/file-browser/ShareDialog.tsx Switches error logging to structured logger
apps/web/src/components/file-browser/InviteLinkTab.tsx Switches error logging to structured logger
apps/web/src/components/file-browser/ReplaceFileDialog.tsx Switches error logging to structured logger; makes unpin failures visible
apps/web/src/components/file-browser/PdfPreviewDialog.tsx Switches error logging to structured logger
apps/web/src/components/file-browser/AudioPlayerDialog.tsx Switches warn logging to structured logger; makes AudioContext close failures visible
apps/web/src/lib/sw-registration.ts Switches warn/error logging to structured logger
apps/web/src/lib/crypto/key-wrapping.ts Switches error logging to structured logger
apps/web/src/lib/web3auth/hooks.ts Switches console logging to structured logger for CoreKit debug traces/errors
apps/web/src/lib/web3auth/core-kit-provider.tsx Switches initialization error logging to structured logger
apps/web/src/stores/folder.store.ts Adds typed window.__ZUSTAND_FOLDER_STORE__ for E2E
apps/web/src/main.tsx Types DEV-only error capture globals; avoids any casts
apps/web/src/polyfills.ts Uses typed declare global shims for process/Buffer
00-Preliminary-R&D/ARCHIVED.md Documents archival of legacy PoC content
00-Preliminary-R&D/poc/.env.example Deleted legacy PoC artifact
00-Preliminary-R&D/poc/README.md Deleted legacy PoC artifact
00-Preliminary-R&D/poc/package.json Deleted legacy PoC artifact
00-Preliminary-R&D/poc/scripts/gen-private-key.ts Deleted legacy PoC artifact
00-Preliminary-R&D/poc/src/index.ts Deleted legacy PoC artifact
00-Preliminary-R&D/poc/state/state.json Deleted legacy PoC artifact
00-Preliminary-R&D/poc/tsconfig.json Deleted legacy PoC artifact
.planning/config.json Minor formatting fix
.planning/STATE.md Updates planning state to reflect phase completion/progression
.planning/ROADMAP.md Marks Phase 28/29 as complete and records plan breakdown
.planning/phases/28-code-hygiene-logging/28-01-PLAN.md Adds Phase 28 plan doc
.planning/phases/28-code-hygiene-logging/28-01-SUMMARY.md Adds Phase 28 summary doc
.planning/phases/28-code-hygiene-logging/28-02-PLAN.md Adds Phase 28 plan doc
.planning/phases/28-code-hygiene-logging/28-02-SUMMARY.md Adds Phase 28 summary doc
.planning/phases/28-code-hygiene-logging/28-03-PLAN.md Adds Phase 28 plan doc
.planning/phases/28-code-hygiene-logging/28-03-SUMMARY.md Adds Phase 28 summary doc
.planning/phases/28-code-hygiene-logging/28-04-PLAN.md Adds Phase 28 plan doc
.planning/phases/28-code-hygiene-logging/28-04-SUMMARY.md Adds Phase 28 summary doc
.planning/phases/28-code-hygiene-logging/28-VERIFICATION.md Adds Phase 28 verification doc
.planning/phases/29-infrastructure-hardening/29-01-PLAN.md Adds Phase 29 plan doc
.planning/phases/29-infrastructure-hardening/29-01-SUMMARY.md Adds Phase 29 summary doc
.planning/phases/29-infrastructure-hardening/29-02-PLAN.md Adds Phase 29 plan doc
.planning/phases/29-infrastructure-hardening/29-02-SUMMARY.md Adds Phase 29 summary doc
.planning/phases/29-infrastructure-hardening/29-03-PLAN.md Adds Phase 29 plan doc
.planning/phases/29-infrastructure-hardening/29-03-SUMMARY.md Adds Phase 29 summary doc
.planning/phases/29-infrastructure-hardening/29-CONTEXT.md Adds Phase 29 context doc
.planning/phases/29-infrastructure-hardening/29-DISCUSSION-LOG.md Adds Phase 29 discussion log
.planning/phases/29-infrastructure-hardening/29-VERIFICATION.md Adds Phase 29 verification doc

Comment thread packages/sdk/src/client.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (7)
apps/web/src/components/file-browser/FileBrowser.tsx (1)

450-452: Avoid duplicate download-failure logging at this layer.

downloadFromIpns already logs failures before rethrowing. Logging again here creates duplicate error events.

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

In `@apps/web/src/components/file-browser/FileBrowser.tsx` around lines 450 - 452,
The catch block in the FileBrowser component is duplicating error logs because
downloadFromIpns already logs and rethrows failures; remove the logger.error
call from the catch and simply rethrow the caught error (or `throw err`) so only
downloadFromIpns handles logging; locate the catch that calls
logger.error('Download failed:', err) within the FileBrowser component and
delete that logging line, leaving the rethrow behavior intact.
apps/web/src/hooks/useBin.ts (1)

59-61: Include the caught error object for non-blocking purge failures.

Current logging drops the actual exception, which makes triage harder in staging/prod.

🔧 Proposed fix
-      }).catch(() => {
-        logger.error('[useBin] Auto-purge failed (non-blocking)');
+      }).catch((err) => {
+        logger.error('[useBin] Auto-purge failed (non-blocking):', err);
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/hooks/useBin.ts` around lines 59 - 61, In useBin's auto-purge
promise catch block (the .catch in apps/web/src/hooks/useBin.ts), stop
swallowing the error and pass the caught error into logger.error so the
exception details are recorded; change the arrow to accept an error parameter
(e.g., .catch((err) => { logger.error('[useBin] Auto-purge failed
(non-blocking)', err); })) or include it as metadata (e.g.,
logger.error('[useBin] Auto-purge failed (non-blocking)', { err })) so the
actual exception is logged for triage.
apps/web/src/components/file-browser/BinBrowser.tsx (1)

214-214: Consider including the caught error object in bin failure logs.

These catch blocks currently log only static messages, which makes root-cause diagnosis harder in production incidents.

♻️ Suggested improvement
-      } catch {
-        logger.error('[Bin] Restore failed');
+      } catch (err) {
+        logger.error('[Bin] Restore failed:', err);
       }

Apply the same pattern to the other updated catch blocks in this file.

Also applies to: 235-235, 263-263, 281-281, 301-301

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

In `@apps/web/src/components/file-browser/BinBrowser.tsx` at line 214, The catch
blocks in BinBrowser (file BinBrowser.tsx) that currently call logger.error with
static messages (e.g., the '[Bin] Restore failed' call and the other catches at
the indicated spots) should be changed to include the caught error object and
context (e.g., logger.error('[Bin] Restore failed', { error }) or include
error.message / error.stack) so that the actual exception details are logged;
update each catch in the component (the restore handler and the catch blocks
around the other operations at the other indicated lines) to pass the caught
error variable into logger.error along with the existing message.
apps/web/src/services/bin.service.ts (2)

718-720: Consider emitting a metric for unpin failures, not only console warnings.

These warnings improve local visibility, but they still won’t be centrally alertable without telemetry. Adding a lightweight counter (e.g., bin_unpin_failures_total) would make regressions detectable.

Also applies to: 751-753, 831-833, 849-851

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

In `@apps/web/src/services/bin.service.ts` around lines 718 - 720, The
unpinFromIpfs error handlers currently only call logger.warn; modify each catch
for unpinFromIpfs (calls to unpinFromIpfs where errors are handled with
logger.warn) to also emit/increment a telemetry metric (e.g.,
bin_unpin_failures_total) so failures are centrally observable; use the existing
metrics/telemetry client in this module (or add a lightweight counter wrapper)
and include useful labels such as cid and error type/message when incrementing
so alerts and dashboards can filter by failure cause.

271-273: Include the caught error in non-blocking publish-failure logs.

Line 272 and Line 337 currently emit static messages only, which drops root-cause context.

♻️ Proposed fix
-  } catch {
-    logger.error('[Bin] addToBin publish failed (non-blocking)');
+  } catch (err) {
+    logger.error('[Bin] addToBin publish failed (non-blocking):', err);
   }

...

-  } catch {
-    logger.error('[Bin] addManyToBin publish failed (non-blocking)');
+  } catch (err) {
+    logger.error('[Bin] addManyToBin publish failed (non-blocking):', err);
   }

Also applies to: 336-338

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

In `@apps/web/src/services/bin.service.ts` around lines 271 - 273, The catch
blocks in addToBin (and the similar non-blocking publish catch later) swallow
the exception because they use an empty catch; update those catch clauses to
capture the error (e.g., catch (err)) and pass the error object into
logger.error so the log includes the error stack/message along with the existing
context string like "[Bin] addToBin publish failed (non-blocking)"; ensure you
reference both catch sites in bin.service.ts so both logs include the caught
error details.
apps/api/src/ipns/ipns.controller.ts (1)

146-150: Add a dedicated unenroll metric for endpoint observability.

publish/resolve already emit metrics; adding an unenroll counter would help capacity tracking and alerting for this new hardening path.

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

In `@apps/api/src/ipns/ipns.controller.ts` around lines 146 - 150, Add a dedicated
unenroll metric emission after the call to this.ipnsService.unenrollBatch:
emit/increment a counter (e.g., "ipns_unenroll_total" or "unenroll_counter")
with the number of unenrolled items (use result.totalUnenrolled) and any
relevant labels (e.g., method="unenrollBatch" or tenant/user id) following the
pattern used by existing publish/resolve metrics; if the controller doesn't yet
have a metrics client, inject/obtain the same metrics service used elsewhere
(e.g., metricsService or promClient wrapper) and call its increment/observe
method after unenrollBatch returns before constructing the response.
packages/sdk/src/client.ts (1)

986-1008: Minor: Folder subtree IPNS names not collected in permanentDelete.

Unlike deleteItem and deleteToBin which call collectSubtreeIpnsNames() for folders, permanentDelete only unenrolls the top-level folderEntry.ipnsName (line 1005). Nested folder/file IPNS names within the deleted folder aren't explicitly unenrolled.

This is likely acceptable because:

  1. Bin entries don't store full nested children metadata needed for recursion
  2. Per existing learnings, orphaned IPNS records naturally expire from the DHT after 48 hours when the TEE republisher stops processing them

Consider adding a brief comment noting that nested IPNS names rely on natural DHT expiry, for clarity.

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

In `@packages/sdk/src/client.ts` around lines 986 - 1008, permanentDeleteFromBin
currently only collects the top-level folderEntry.ipnsName for unenrollment (see
use of entry, folderEntry and fireAndForgetUnenroll) while
deleteItem/deleteToBin use collectSubtreeIpnsNames; update the permanent delete
flow by adding a concise code comment in the method handling permanent deletion
(around the block that captures entry and calls binOps.permanentDeleteFromBin
and fireAndForgetUnenroll) that explains nested folder/file IPNS names are not
being collected here and instead rely on natural DHT expiry after the TEE
republisher stops, referencing the existing behavior in collectSubtreeIpnsNames
and the reason why recursive collection isn’t done for bin entries.
🤖 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/api/src/ipns/ipns.service.ts`:
- Around line 302-304: The loop over republishService.unenrollIpns currently
increments the unenrolled counter unconditionally, overcounting when no rows
were deleted; update RepublishService.unenrollIpns to return an explicit result
(e.g., number of affected rows or a boolean) and change the callers in
ipns.service.ts (the call to republishService.unenrollIpns and the unenrolled
variable) to only increment unenrolled when the returned affected count > 0 (or
true), or alternatively rename the field (e.g., processedAttempts) everywhere if
you prefer to report attempts instead of actual deletions; ensure all other call
sites and the response payload are updated to use the new return value or field
name.

In `@apps/web/src/components/file-browser/FileBrowser.tsx`:
- Around line 549-550: The error log in FileBrowser.tsx currently emits
plaintext filenames via logger.error(`Download failed for ${file.name}:`, err);
— remove the plaintext file.name from logs and replace it with a non-sensitive
identifier (eg. file.id or a client-side encrypted/hashed name) or a redacted
placeholder; update the download error handling code path (where logger.error is
called) to compute/send the encrypted/hash token for the file name (perform
client-side encryption/hashing of file.name before any logging or transmission)
and log that token plus the error object instead of file.name.

In `@apps/web/src/lib/web3auth/hooks.ts`:
- Around line 159-162: The debug call in loginWithJWT currently logs a raw
persistent user identifier via logger.debug([...], { verifier:
loginParams.verifier, verifierId: userId }), which leaks PII; change this to
avoid logging the raw userId by either removing verifierId from the metadata or
replacing it with a non-reversible fingerprint (e.g., call a hashing/redaction
helper like hashIdentifier(userId) or redactIdentifier(userId) before passing it
to logger.debug). Update the call site in loginWithJWT to use the helper (or
omit verifierId) so logs no longer contain the plain userId while retaining
useful debug context (e.g., hashedVerifierId).

In `@docker/grafana/alerts/test-login-rate.json`:
- Line 26: The alert query is filtering cipherbox_auth_logins_total by
method="test" but the instrumented counter in
apps/api/src/metrics/metrics.service.ts only emits method="web3auth", and
noDataState is set to "OK" which masks missing data; either update the alert
expression to use method="web3auth" (modify the expr that references
cipherbox_auth_logins_total{method="test"}) or add instrumentation to emit
method="test" on test-login success paths in metrics.service.ts, and change
noDataState from "OK" to a non-masking state (e.g., "ALERT"/appropriate non-OK
value) so missing series surfaces as an operational alert.

In `@packages/sdk/src/client.ts`:
- Around line 1163-1165: Prettier is failing due to inline formatting of the
promise rejection handler; reformat the sdkCore.unpinFromIpfs call so .catch is
on its own line and the arrow callback uses proper parentheses/line breaks
(refer to sdkCore.unpinFromIpfs and relayResult.cid) — e.g., put .catch(...) on
a new line and break the arrow function body across lines so the file passes
Prettier/CI.

---

Nitpick comments:
In `@apps/api/src/ipns/ipns.controller.ts`:
- Around line 146-150: Add a dedicated unenroll metric emission after the call
to this.ipnsService.unenrollBatch: emit/increment a counter (e.g.,
"ipns_unenroll_total" or "unenroll_counter") with the number of unenrolled items
(use result.totalUnenrolled) and any relevant labels (e.g.,
method="unenrollBatch" or tenant/user id) following the pattern used by existing
publish/resolve metrics; if the controller doesn't yet have a metrics client,
inject/obtain the same metrics service used elsewhere (e.g., metricsService or
promClient wrapper) and call its increment/observe method after unenrollBatch
returns before constructing the response.

In `@apps/web/src/components/file-browser/BinBrowser.tsx`:
- Line 214: The catch blocks in BinBrowser (file BinBrowser.tsx) that currently
call logger.error with static messages (e.g., the '[Bin] Restore failed' call
and the other catches at the indicated spots) should be changed to include the
caught error object and context (e.g., logger.error('[Bin] Restore failed', {
error }) or include error.message / error.stack) so that the actual exception
details are logged; update each catch in the component (the restore handler and
the catch blocks around the other operations at the other indicated lines) to
pass the caught error variable into logger.error along with the existing
message.

In `@apps/web/src/components/file-browser/FileBrowser.tsx`:
- Around line 450-452: The catch block in the FileBrowser component is
duplicating error logs because downloadFromIpns already logs and rethrows
failures; remove the logger.error call from the catch and simply rethrow the
caught error (or `throw err`) so only downloadFromIpns handles logging; locate
the catch that calls logger.error('Download failed:', err) within the
FileBrowser component and delete that logging line, leaving the rethrow behavior
intact.

In `@apps/web/src/hooks/useBin.ts`:
- Around line 59-61: In useBin's auto-purge promise catch block (the .catch in
apps/web/src/hooks/useBin.ts), stop swallowing the error and pass the caught
error into logger.error so the exception details are recorded; change the arrow
to accept an error parameter (e.g., .catch((err) => { logger.error('[useBin]
Auto-purge failed (non-blocking)', err); })) or include it as metadata (e.g.,
logger.error('[useBin] Auto-purge failed (non-blocking)', { err })) so the
actual exception is logged for triage.

In `@apps/web/src/services/bin.service.ts`:
- Around line 718-720: The unpinFromIpfs error handlers currently only call
logger.warn; modify each catch for unpinFromIpfs (calls to unpinFromIpfs where
errors are handled with logger.warn) to also emit/increment a telemetry metric
(e.g., bin_unpin_failures_total) so failures are centrally observable; use the
existing metrics/telemetry client in this module (or add a lightweight counter
wrapper) and include useful labels such as cid and error type/message when
incrementing so alerts and dashboards can filter by failure cause.
- Around line 271-273: The catch blocks in addToBin (and the similar
non-blocking publish catch later) swallow the exception because they use an
empty catch; update those catch clauses to capture the error (e.g., catch (err))
and pass the error object into logger.error so the log includes the error
stack/message along with the existing context string like "[Bin] addToBin
publish failed (non-blocking)"; ensure you reference both catch sites in
bin.service.ts so both logs include the caught error details.

In `@packages/sdk/src/client.ts`:
- Around line 986-1008: permanentDeleteFromBin currently only collects the
top-level folderEntry.ipnsName for unenrollment (see use of entry, folderEntry
and fireAndForgetUnenroll) while deleteItem/deleteToBin use
collectSubtreeIpnsNames; update the permanent delete flow by adding a concise
code comment in the method handling permanent deletion (around the block that
captures entry and calls binOps.permanentDeleteFromBin and
fireAndForgetUnenroll) that explains nested folder/file IPNS names are not being
collected here and instead rely on natural DHT expiry after the TEE republisher
stops, referencing the existing behavior in collectSubtreeIpnsNames and the
reason why recursive collection isn’t done for bin entries.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a046a123-9655-4c87-86aa-046299a464f9

📥 Commits

Reviewing files that changed from the base of the PR and between e990dc7 and 4eb19ad.

⛔ Files ignored due to path filters (6)
  • 00-Preliminary-R&D/poc/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • packages/api-client/openapi.json is excluded by !packages/api-client/**
  • packages/api-client/src/generated/ipns/ipns.ts is excluded by !**/generated/**, !**/generated/**, !packages/api-client/**
  • packages/api-client/src/models/batchUnenrollIpnsDto.ts is excluded by !packages/api-client/**
  • packages/api-client/src/models/batchUnenrollIpnsResponseDto.ts is excluded by !packages/api-client/**
  • packages/api-client/src/models/index.ts is excluded by !packages/api-client/**
📒 Files selected for processing (68)
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/config.json
  • .planning/phases/28-code-hygiene-logging/28-01-PLAN.md
  • .planning/phases/28-code-hygiene-logging/28-01-SUMMARY.md
  • .planning/phases/28-code-hygiene-logging/28-02-PLAN.md
  • .planning/phases/28-code-hygiene-logging/28-02-SUMMARY.md
  • .planning/phases/28-code-hygiene-logging/28-03-PLAN.md
  • .planning/phases/28-code-hygiene-logging/28-03-SUMMARY.md
  • .planning/phases/28-code-hygiene-logging/28-04-PLAN.md
  • .planning/phases/28-code-hygiene-logging/28-04-SUMMARY.md
  • .planning/phases/28-code-hygiene-logging/28-VERIFICATION.md
  • .planning/phases/29-infrastructure-hardening/29-01-PLAN.md
  • .planning/phases/29-infrastructure-hardening/29-01-SUMMARY.md
  • .planning/phases/29-infrastructure-hardening/29-02-PLAN.md
  • .planning/phases/29-infrastructure-hardening/29-02-SUMMARY.md
  • .planning/phases/29-infrastructure-hardening/29-03-PLAN.md
  • .planning/phases/29-infrastructure-hardening/29-03-SUMMARY.md
  • .planning/phases/29-infrastructure-hardening/29-CONTEXT.md
  • .planning/phases/29-infrastructure-hardening/29-DISCUSSION-LOG.md
  • .planning/phases/29-infrastructure-hardening/29-VERIFICATION.md
  • 00-Preliminary-R&D/ARCHIVED.md
  • 00-Preliminary-R&D/poc/.env.example
  • 00-Preliminary-R&D/poc/README.md
  • 00-Preliminary-R&D/poc/package.json
  • 00-Preliminary-R&D/poc/scripts/gen-private-key.ts
  • 00-Preliminary-R&D/poc/src/index.ts
  • 00-Preliminary-R&D/poc/state/state.json
  • 00-Preliminary-R&D/poc/tsconfig.json
  • apps/api/src/ipns/dto/index.ts
  • apps/api/src/ipns/dto/unenroll.dto.ts
  • apps/api/src/ipns/ipns.controller.ts
  • apps/api/src/ipns/ipns.service.ts
  • apps/web/src/components/file-browser/AudioPlayerDialog.tsx
  • apps/web/src/components/file-browser/BinBrowser.tsx
  • apps/web/src/components/file-browser/FileBrowser.tsx
  • apps/web/src/components/file-browser/InviteLinkTab.tsx
  • apps/web/src/components/file-browser/PdfPreviewDialog.tsx
  • apps/web/src/components/file-browser/ReplaceFileDialog.tsx
  • apps/web/src/components/file-browser/ShareDialog.tsx
  • apps/web/src/components/settings/ConnectionTest.tsx
  • apps/web/src/hooks/useAuth.ts
  • apps/web/src/hooks/useBin.ts
  • apps/web/src/hooks/useDropUpload.ts
  • apps/web/src/hooks/useFileDownload.ts
  • apps/web/src/hooks/useFileOperations.ts
  • apps/web/src/hooks/useFileVersions.ts
  • apps/web/src/hooks/useFolderNavigation.ts
  • apps/web/src/hooks/useSearch.ts
  • apps/web/src/hooks/useSharedNavigation.ts
  • apps/web/src/lib/crypto/key-wrapping.ts
  • apps/web/src/lib/logger.ts
  • apps/web/src/lib/sw-registration.ts
  • apps/web/src/lib/web3auth/core-kit-provider.tsx
  • apps/web/src/lib/web3auth/hooks.ts
  • apps/web/src/main.tsx
  • apps/web/src/polyfills.ts
  • apps/web/src/services/bin.service.ts
  • apps/web/src/services/delete.service.ts
  • apps/web/src/services/device-registry.service.ts
  • apps/web/src/services/folder.service.ts
  • apps/web/src/services/ipns.service.ts
  • apps/web/src/services/search-index.service.ts
  • apps/web/src/services/share.service.ts
  • apps/web/src/services/upload.service.ts
  • apps/web/src/stores/folder.store.ts
  • docker/grafana/alerts/test-login-rate.json
  • packages/sdk/src/client.ts
💤 Files with no reviewable changes (7)
  • 00-Preliminary-R&D/poc/scripts/gen-private-key.ts
  • 00-Preliminary-R&D/poc/tsconfig.json
  • 00-Preliminary-R&D/poc/state/state.json
  • 00-Preliminary-R&D/poc/package.json
  • 00-Preliminary-R&D/poc/.env.example
  • 00-Preliminary-R&D/poc/README.md
  • 00-Preliminary-R&D/poc/src/index.ts

Comment thread apps/api/src/ipns/ipns.service.ts Outdated
Comment thread apps/web/src/components/file-browser/FileBrowser.tsx Outdated
Comment thread apps/web/src/lib/web3auth/hooks.ts
Comment thread docker/grafana/alerts/test-login-rate.json
Comment thread packages/sdk/src/client.ts Outdated
FSM1 and others added 4 commits March 28, 2026 05:21
- Add 200-name chunking in fireAndForgetUnenroll (prevents silent 400 errors)
- Extract collectRemovedItemIpnsNames and collectBinEntryIpnsNames helpers
- Fix missing subtree collection in permanentDelete/emptyBin (was only
  unenrolling folder, not nested files)
- Use accumulator pattern in collectSubtreeIpnsNames (avoids temp arrays)
- Parallelize unenrollBatch with Promise.allSettled (was sequential await)
- Reduce Grafana alert maxDataPoints from 43200 to 100

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 29 added unenrollBatch endpoint whose NestJS decorator branches
are unreachable in unit tests (same pattern as other controllers).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove file.name from batch download error log (user metadata)
- Remove userId/verifierId from CoreKit debug log (PII)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve conflicts from Phase 28 squash merge (#382):
- Take main's logger module prefixes for all web app files
- Keep Phase 29 TODO cleanup in folder.service.ts
- Keep Phase 29 SDK unenrollment additions + prettier fix in client.ts
- Re-apply privacy fix (remove file.name from batch download log)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 26 out of 28 changed files in this pull request and generated 6 comments.

Comment thread packages/sdk/src/client.ts
Comment thread packages/sdk/src/client.ts Outdated
Comment thread packages/sdk/src/client.ts
Comment thread apps/api/src/ipns/ipns.controller.ts
Comment thread apps/api/src/ipns/ipns.controller.ts
Comment thread apps/api/src/ipns/dto/unenroll.dto.ts Outdated
@codecov

codecov Bot commented Mar 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 52.56410% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.56%. Comparing base (9827f49) to head (2bd4b5e).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/sdk/src/client.ts 51.66% 29 Missing ⚠️
apps/api/src/ipns/ipns.controller.ts 20.00% 2 Missing and 2 partials ⚠️
apps/api/src/republish/republish.service.ts 0.00% 3 Missing ⚠️
apps/api/src/ipns/ipns.service.ts 90.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #383      +/-   ##
==========================================
+ Coverage   60.62%   70.56%   +9.93%     
==========================================
  Files         128      107      -21     
  Lines        9526     6631    -2895     
  Branches      925      938      +13     
==========================================
- Hits         5775     4679    -1096     
+ Misses       3540     1738    -1802     
- Partials      211      214       +3     
Flag Coverage Δ
api 83.86% <52.56%> (-0.46%) ⬇️
api-client 83.86% <52.56%> (-0.46%) ⬇️
core 83.86% <52.56%> (-0.46%) ⬇️
crypto 83.86% <52.56%> (-0.46%) ⬇️
desktop 14.98% <ø> (-16.23%) ⬇️
sdk 83.86% <52.56%> (-0.46%) ⬇️
sdk-core 83.86% <52.56%> (-0.46%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
apps/api/src/ipns/ipns.service.ts 88.50% <90.00%> (+0.09%) ⬆️
apps/api/src/republish/republish.service.ts 88.54% <0.00%> (-1.38%) ⬇️
apps/api/src/ipns/ipns.controller.ts 65.78% <20.00%> (-6.94%) ⬇️
packages/sdk/src/client.ts 76.33% <51.66%> (-2.10%) ⬇️

... and 34 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

FSM1 and others added 5 commits March 28, 2026 05:39
Prettier and lint-staged produce different trailing newline behavior
for openapi.json, causing spurious CI failures on the generated
client verification step.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…align MaxLength

- Remove IPNS unenrollment from deleteToBin (soft delete preserves items
  for restore; unenroll only on permanentDelete/emptyBin)
- Add @httpcode(200) to unenroll endpoint (NestJS @post defaults to 201)
- Align MaxLength from 70 to 76 to match resolve DTO
- Fix check-api-client.sh to skip already-up-to-date generated files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…iffs

CI prettier and local prettier produce different array formatting
(single-line vs multi-line for single-element arrays). Compare the
parsed JSON instead of raw text to avoid false failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover all paths: full success, partial failure, total failure, empty
array. Brings ipns.service.ts patch coverage from 0% to covered.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies the full unenroll pipeline: upload file → delete (triggers
fireAndForgetUnenroll) → call unenroll endpoint directly to confirm
it's reachable and idempotent. Exercises SDK client, API endpoint,
and RepublishService in a real E2E context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 30 out of 32 changed files in this pull request and generated 3 comments.

Comment thread docker/grafana/alerts/test-login-rate.json Outdated
Comment thread apps/api/src/ipns/ipns.service.ts
Comment thread tests/sdk-e2e/src/suites/ipns-consistency.test.ts Outdated
…nfig, cleaner test

- Return affected count from unenrollIpns() so totalUnenrolled reflects
  actual DB deletions, not just resolved promises
- Align maxDataPoints to 43200 in test-login-rate alert (matches other alerts)
- Replace private field access in E2E test with dedicated axios instance
- Tighten unenroll assertion to expect 0 (already cleaned up by deleteItem)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/api/src/ipns/ipns.service.spec.ts (1)

1317-1363: Add one edge-case test for fulfilled no-op unenroll (affected = 0).

Current cases cover success/failure flow well, but not the fulfilled-zero path that drives totalUnenrolled semantics.

✅ Suggested test addition
 describe('unenrollBatch', () => {
@@
   it('should handle empty array', async () => {
     const result = await service.unenrollBatch('user-1', []);
     expect(result.totalUnenrolled).toBe(0);
   });
+
+  it('should not increment totalUnenrolled when unenroll returns 0', async () => {
+    const republishService = getRepublishMock();
+    republishService.unenrollIpns.mockResolvedValueOnce(0);
+
+    const result = await service.unenrollBatch('user-1', ['already-unenrolled']);
+
+    expect(result.totalUnenrolled).toBe(0);
+    expect(republishService.unenrollIpns).toHaveBeenCalledTimes(1);
+  });
 });

As per coding guidelines: **/*.test.ts: Focus on test coverage, edge cases, and test quality. Ensure tests are meaningful and not just for coverage metrics.

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

In `@apps/api/src/ipns/ipns.service.spec.ts` around lines 1317 - 1363, Add an
edge-case unit test in the unenrollBatch suite to cover when
republishService.unenrollIpns fulfills with affected = 0 (a no-op) so
totalUnenrolled isn't incremented; mock (service as unknown as Record<string,
unknown>).republishService.unenrollIpns to resolve to 0 for one or more names,
call service.unenrollBatch('user-1', [...]) including that name, and assert
result.totalUnenrolled reflects only successful (affected > 0) unenrolls and
that unenrollIpns was invoked the expected number of times.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/check-api-client.sh`:
- Around line 35-42: The pre-commit check loop over GENERATED_FILES currently
skips files when `git diff --quiet "$file"` reports no unstaged changes, which
lets staged API source changes bypass required regenerated client updates;
remove the conditional that calls `git diff --quiet` (and its surrounding
comment/continue) so that the script always enforces that each file in
GENERATED_FILES is present in STAGED_FILES (i.e., always require the generated
client to be staged when corresponding sources are staged); update the loop
handling around MISSING_GENERATED, GENERATED_FILES, and STAGED_FILES accordingly
to ensure missing generated files are flagged regardless of unstaged diffs.

---

Nitpick comments:
In `@apps/api/src/ipns/ipns.service.spec.ts`:
- Around line 1317-1363: Add an edge-case unit test in the unenrollBatch suite
to cover when republishService.unenrollIpns fulfills with affected = 0 (a no-op)
so totalUnenrolled isn't incremented; mock (service as unknown as Record<string,
unknown>).republishService.unenrollIpns to resolve to 0 for one or more names,
call service.unenrollBatch('user-1', [...]) including that name, and assert
result.totalUnenrolled reflects only successful (affected > 0) unenrolls and
that unenrollIpns was invoked the expected number of times.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b016087a-5bef-4ed3-b7d9-e84c31eec7d0

📥 Commits

Reviewing files that changed from the base of the PR and between dd8fc1b and 2bd4b5e.

📒 Files selected for processing (10)
  • .github/workflows/ci.yml
  • apps/api/src/ipns/dto/unenroll.dto.ts
  • apps/api/src/ipns/ipns.controller.ts
  • apps/api/src/ipns/ipns.service.spec.ts
  • apps/api/src/ipns/ipns.service.ts
  • apps/api/src/republish/republish.service.ts
  • docker/grafana/alerts/test-login-rate.json
  • packages/sdk/src/client.ts
  • scripts/check-api-client.sh
  • tests/sdk-e2e/src/suites/ipns-consistency.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • docker/grafana/alerts/test-login-rate.json
  • apps/api/src/ipns/ipns.controller.ts
  • apps/api/src/ipns/dto/unenroll.dto.ts
  • packages/sdk/src/client.ts

Comment thread scripts/check-api-client.sh
@FSM1 FSM1 merged commit a209337 into main Mar 28, 2026
26 checks passed
@FSM1 FSM1 deleted the feat/phase-29-infra-hardening branch March 28, 2026 18:40
This was referenced Mar 28, 2026
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.

2 participants