Skip to content

feat(endpoints): add inodes introspection endpoint - #1273

Open
Molter73 wants to merge 2 commits into
mainfrom
mauro/feat/inodes-introspection
Open

feat(endpoints): add inodes introspection endpoint#1273
Molter73 wants to merge 2 commits into
mainfrom
mauro/feat/inodes-introspection

Conversation

@Molter73

@Molter73 Molter73 commented Jul 23, 2026

Copy link
Copy Markdown
Member

Description

Adds an "/inodes" endpoint that returns the contents of the userspace inode map for inspection. This will be useful in tests that need to look into the internal state after operations that don't emit events or during development to validate what is currently being tracked.

The new endpoint is disabled by default and can be enabled via the FACT_ENDPOINT_INTROSPECTION env var or the --introspection CLI arg, that should gate any future introspection endpoints we decide to add in the future. This configuration value is not hotreloadable, it can only be set at start up, this is meant to make it harder for the endpoint to come online on production environments.

Fixes #1079

Checklist

  • Patch has a change log entry OR does not need one.
  • Investigated and inspected CI test results
  • Updated documentation accordingly

Automated testing

  • Added unit tests
  • Added integration tests
  • Added regression tests

If any of these don't apply, please comment below.

Testing Performed

The responses are formatted with jq for better visibility, the endpoint returns minified JSON.

Introspection endpoint enabled:

{
  "39:228583982": "/etc/sensitive-files/seq",
  "39:200442630": "/etc/sensitive-files/my-dir/something",
  "39:227290637": "/etc/sensitive-files/private/super-private/something",
  "39:196873539": "/etc/sensitive-files/test",
  "39:194573419": "/etc/sensitive-files/my-dir/nested/something",
  "39:194396170": "/etc/sensitive-files/my-dir",
  "39:228288758": "/etc/sensitive-files/mount",
  "39:217415897": "/etc/sensitive-files/other",
  "39:228583961": "/etc/sensitive-files/sequence",
  "39:202489561": "/etc/sensitive-files/something",
  "39:194573388": "/etc/sensitive-files/my-dir/nested",
  "39:227290085": "/etc/sensitive-files/private",
  "39:227290591": "/etc/sensitive-files/private/super-private",
  "39:227290457": "/etc/sensitive-files/private/something",
  "39:126382982": "/etc/sensitive-files"
}

Introspection endpoint disabled, metrics endpoint enabled:

curl: (22) The requested URL returned error: 503

No endpoint enabled:

curl: (7) Failed to connect to localhost port 9000 after 0 ms: Could not connect to server

Summary by CodeRabbit

  • New Features
    • Added an optional endpoint introspection setting, configurable through the CLI or FACT_ENDPOINT_INTROSPECTION.
    • Added GET /inodes to return the host’s inode-to-path mappings as JSON.
    • Introspection is disabled by default and returns an unavailable response when disabled.
  • Documentation
    • Updated the changelog to include the new inode introspection endpoint.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Inode introspection

Layer / File(s) Summary
Introspection configuration and validation
fact/src/config/mod.rs, fact/src/config/tests.rs, fact/src/config/reloader/tests.rs
Adds endpoint introspection configuration, CLI and environment-variable parsing, default behavior, update propagation, and test coverage.
Introspection channel wiring
fact/src/lib.rs
Creates the request channel and passes its sender and receiver between the endpoint server and HostScanner.
Host-scanner inode serialization
fact/src/host_scanner.rs
Serializes inode-to-path mappings using "{dev}:{inode}" JSON keys and handles on-demand introspection requests.
Inodes endpoint and response handling
fact/src/endpoints.rs, CHANGELOG.md
Adds GET /inodes, gates endpoint activation on introspection, handles request failures, and records the feature in the changelog.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant EndpointServer
  participant HostScanner
  Client->>EndpointServer: GET /inodes
  EndpointServer->>HostScanner: Send oneshot response channel
  HostScanner->>HostScanner: Serialize current inode map
  HostScanner-->>EndpointServer: Return JSON result
  EndpointServer-->>Client: 200 application/json response
Loading

Possibly related PRs

  • stackrox/fact#1293: Refactors reloader configuration storage and endpoint update propagation touched by this PR’s reloader expectations.

Suggested reviewers: stringy

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested /inodes introspection path and the supporting plumbing described in #1079.
Out of Scope Changes check ✅ Passed The changed files are all directly related to endpoint introspection, config wiring, tests, or changelog updates.
Docstring Coverage ✅ Passed Docstring coverage is 88.46% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title clearly summarizes the main change: adding an inodes introspection endpoint.
Description check ✅ Passed The PR description covers the change summary, checklist, and testing performed, and is mostly complete.
✨ 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 mauro/feat/inodes-introspection

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

@codecov-commenter

codecov-commenter commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 5.71429% with 66 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.42%. Comparing base (09d7ddc) to head (91987cb).

Files with missing lines Patch % Lines
fact/src/host_scanner.rs 0.00% 27 Missing ⚠️
fact/src/endpoints.rs 0.00% 26 Missing ⚠️
fact/src/lib.rs 0.00% 10 Missing ⚠️
fact/src/config/mod.rs 57.14% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1273      +/-   ##
==========================================
- Coverage   35.97%   35.42%   -0.56%     
==========================================
  Files          22       22              
  Lines        3180     3241      +61     
  Branches     3180     3241      +61     
==========================================
+ Hits         1144     1148       +4     
- Misses       2033     2088      +55     
- Partials        3        5       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fact/src/config/tests.rs`:
- Around line 300-325: Update the configuration tests to assert
EndpointConfig::introspection() directly rather than relying only on FactConfig
equality. In fact/src/config/tests.rs:300-325, compare the parsed effective
value in the parsing loop; at 1512-1565, assert the updated value and add an
introspection: false update input; at 2409-2421, assert the environment-derived
value; and at 2644-2657, assert the environment-overrides-YAML value.

In `@fact/src/endpoints.rs`:
- Around line 158-172: Update the host-scanner request flow around
host_scanner_intro to use try_send, returning HTTP 503 when the channel is
saturated or unavailable instead of awaiting indefinitely. Wrap rx.await in a
bounded timeout and return an appropriate 503 response when the scanner does not
respond before it expires, while preserving the existing success and
scanner-error responses.
🪄 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.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 82d61e0c-05b2-4af2-a25b-96743e92d407

📥 Commits

Reviewing files that changed from the base of the PR and between 922750c and 46754dc.

📒 Files selected for processing (5)
  • fact/src/config/mod.rs
  • fact/src/config/tests.rs
  • fact/src/endpoints.rs
  • fact/src/host_scanner.rs
  • fact/src/lib.rs

Comment thread fact/src/config/tests.rs Outdated
Comment thread fact/src/endpoints.rs
@Molter73
Molter73 force-pushed the mauro/feat/inodes-introspection branch from 262673f to a142db9 Compare July 23, 2026 15:20

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fact/src/config/reloader.rs`:
- Around line 189-192: Update the endpoint reload branch around
Reloader::endpoint_should_reload so it applies only reloadable endpoint fields
and preserves the existing old.introspection value, rather than replacing old
with new.endpoint.clone(). Add or extend regression coverage for an
introspection-only change followed by a reloadable endpoint change, ensuring
introspection remains unchanged until restart.
🪄 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.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 0163c873-533a-44c7-a1be-af9dac47f696

📥 Commits

Reviewing files that changed from the base of the PR and between 46754dc and a142db9.

📒 Files selected for processing (2)
  • fact/src/config/mod.rs
  • fact/src/config/reloader.rs

Comment thread fact/src/config/reloader.rs Outdated
@Molter73
Molter73 marked this pull request as ready for review July 23, 2026 16:29
@Molter73
Molter73 requested a review from a team as a code owner July 23, 2026 16:29

@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.

♻️ Duplicate comments (1)
fact/src/config/reloader.rs (1)

177-180: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Clone new.endpoint before using struct-update syntax.

Line 179 moves EndpointConfig out of &FactConfig; this does not compile because EndpointConfig is Clone, not Copy.

Proposed fix
                 *old = EndpointConfig {
                     introspection: old.introspection,
-                    ..new.endpoint
+                    ..new.endpoint.clone()
                 };
#!/usr/bin/env bash
set -euo pipefail

rg -n -C 3 'derive\(.*(Clone|Copy)|struct EndpointConfig|\.{2}new\.endpoint' \
  fact/src/config/mod.rs fact/src/config/reloader.rs
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/config/reloader.rs` around lines 177 - 180, Update the
EndpointConfig struct update in the reloader logic to clone new.endpoint before
spreading it, since new is borrowed and EndpointConfig is not Copy. Preserve the
existing introspection override from old while ensuring the assignment does not
move out of the borrowed FactConfig.
🧹 Nitpick comments (1)
fact/src/config/reloader.rs (1)

499-505: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the watch notification state too. Add a per-case expected notification flag and check endpoint.has_changed().unwrap() after send_updates(); otherwise an unconditional send could still leave this test green.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/config/reloader.rs` around lines 499 - 505, Extend the test cases
iterated by the reloader test to include an expected notification flag, then
update the loop around Reloader::from and send_updates to assert
endpoint.has_changed().unwrap() against that per-case expectation. Preserve the
existing endpoint value assertion while ensuring each case verifies both state
and notification behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@fact/src/config/reloader.rs`:
- Around line 177-180: Update the EndpointConfig struct update in the reloader
logic to clone new.endpoint before spreading it, since new is borrowed and
EndpointConfig is not Copy. Preserve the existing introspection override from
old while ensuring the assignment does not move out of the borrowed FactConfig.

---

Nitpick comments:
In `@fact/src/config/reloader.rs`:
- Around line 499-505: Extend the test cases iterated by the reloader test to
include an expected notification flag, then update the loop around
Reloader::from and send_updates to assert endpoint.has_changed().unwrap()
against that per-case expectation. Preserve the existing endpoint value
assertion while ensuring each case verifies both state and notification
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: ed55f6e3-23b4-46a8-b9aa-fdc48ab9a94f

📥 Commits

Reviewing files that changed from the base of the PR and between a142db9 and 7e715d5.

📒 Files selected for processing (1)
  • fact/src/config/reloader.rs

@Molter73
Molter73 marked this pull request as draft July 27, 2026 11:29
Molter73 added 2 commits July 27, 2026 15:14
Adds an "/inodes" endpoint that returns the contents of the userspace
inode map for inspection. This will be useful in tests that need to look
into the internal state after operations that don't emit events or
during development to validate what is currently being tracked.

The new endpoint is disabled by default and can be enabled with the new
`endpoint.introspection` configuration value, that should gate any
future introspection endpoints we decide to add in the future. This
configuration value is not hotreloadable, it can only be set at start
up, this is meant to make it harder for the endpoint to come online on
production environments.
@Molter73
Molter73 force-pushed the mauro/feat/inodes-introspection branch from 61ce645 to 91987cb Compare July 27, 2026 13:15
@Molter73
Molter73 marked this pull request as ready for review July 27, 2026 13:16

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
fact/src/config/reloader/tests.rs (1)

343-353: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Format these Rust literals ([0,0,0,0], 8000) should use rustfmt spacing (([0, 0, 0, 0], 8000)); run make format on the Rust change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/config/reloader/tests.rs` around lines 343 - 353, Apply rustfmt
formatting to the Rust literals in the FactConfig and EndpointConfig test
fixtures, changing the IPv4 array spacing to the standard formatted style; run
make format for the Rust change.

Source: Coding guidelines

🧹 Nitpick comments (1)
fact/src/config/reloader/tests.rs (1)

245-273: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a regression test proving introspection is not hot-reloadable.

Both configurations currently use introspection: Some(true), so this test cannot detect accidental reloads. Add a case changing Some(false) to Some(true) with an expected None notification.

Proposed test
+generate_endpoint_test! {
+    test_reloader_endpoint_introspection_not_reloadable,
+    FactConfig {
+        endpoint: EndpointConfig {
+            introspection: Some(false),
+            ..Default::default()
+        },
+        ..Default::default()
+    },
+    FactConfig {
+        endpoint: EndpointConfig {
+            introspection: Some(true),
+            ..Default::default()
+        },
+        ..Default::default()
+    },
+    None
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/config/reloader/tests.rs` around lines 245 - 273, Update the
generated reloader test configuration around test_reloader_endpoint_no_change so
the endpoint introspection value differs between the initial and updated
FactConfig, changing from Some(false) to Some(true), while keeping the expected
notification as None to verify introspection is not hot-reloadable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fact/src/host_scanner.rs`:
- Around line 521-530: Update the introspection receive branch in the host
scanner loop so a None result from self.introspection.recv() exits the loop or
disables the closed-channel branch instead of continuing. Preserve request
handling and response sending for Some(req) values, preventing the select loop
from spinning after channel closure.

---

Outside diff comments:
In `@fact/src/config/reloader/tests.rs`:
- Around line 343-353: Apply rustfmt formatting to the Rust literals in the
FactConfig and EndpointConfig test fixtures, changing the IPv4 array spacing to
the standard formatted style; run make format for the Rust change.

---

Nitpick comments:
In `@fact/src/config/reloader/tests.rs`:
- Around line 245-273: Update the generated reloader test configuration around
test_reloader_endpoint_no_change so the endpoint introspection value differs
between the initial and updated FactConfig, changing from Some(false) to
Some(true), while keeping the expected notification as None to verify
introspection is not hot-reloadable.
🪄 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.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 7caa26f7-b7ea-40bf-a814-19d1f581e106

📥 Commits

Reviewing files that changed from the base of the PR and between 17b96c9 and 91987cb.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • fact/src/config/mod.rs
  • fact/src/config/reloader/tests.rs
  • fact/src/config/tests.rs
  • fact/src/endpoints.rs
  • fact/src/host_scanner.rs
  • fact/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • CHANGELOG.md
  • fact/src/lib.rs
  • fact/src/config/mod.rs
  • fact/src/endpoints.rs

Comment thread fact/src/host_scanner.rs
@Molter73
Molter73 enabled auto-merge (squash) July 27, 2026 15:33
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 introspection endpoints

2 participants