Skip to content

feat(core): add authenticated static directory hosting#1966

Merged
senamakel merged 3 commits into
tinyhumansai:mainfrom
senamakel:codex/http-host-static-server
May 16, 2026
Merged

feat(core): add authenticated static directory hosting#1966
senamakel merged 3 commits into
tinyhumansai:mainfrom
senamakel:codex/http-host-static-server

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented May 16, 2026

Summary

  • add a new core http_host domain that can start, list, inspect, and stop ad-hoc static directory HTTP servers
  • serve hosted directories from the Rust core with in-process Axum listeners and graceful shutdown registration
  • enable HTTP Basic auth by default using the active session user when available plus a generated password, with OS user fallback
  • harden hosted paths against traversal and symlink escape, and cover the new surface with focused Rust tests

Problem

  • Some core-driven workflows need to expose local files over HTTP without standing up a separate sidecar or external server.
  • The existing JSON-RPC server gives us a control plane, but there was no built-in way for the core to host a directory on a chosen port.
  • Exposing a directory without auth would make accidental LAN access too easy, especially for preview-style or agent-driven flows.

Solution

  • add controller-registered http_host.start, http_host.stop, http_host.get, and http_host.list handlers under a dedicated Rust domain
  • manage hosted servers in-process with a singleton registry, per-server cancellation tokens, and a core shutdown hook
  • default to Basic auth, deriving the username from persisted session state when possible and generating a per-server password
  • serve index.html when present, otherwise render a simple directory listing, while rejecting traversal and canonicalized path escape

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/coverage.yml. CI remains the source of truth for merged diff-cover; I did not run the full local coverage workflow for this core-only change.
  • Coverage matrix updated — N/A: no existing matrix rows were added, removed, or renamed for this new core RPC surface.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: no matrix feature IDs were touched.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: core-only RPC surface, no release smoke doc change needed.
  • Linked issue closed via Closes #NNN in the ## Related section — N/A: no GitHub issue was provided for this change.

Impact

  • Runtime impact: Rust core / JSON-RPC controller surface.
  • Security impact: hosted directories are auth-protected by default and path resolution rejects traversal / escape.
  • Compatibility impact: additive only; no existing controller contract is changed.

Related

  • Closes: N/A
  • Follow-up PR(s)/TODOs:
    • consider adding explicit host allowlisting or loopback-only defaults for more sensitive file-serving flows

AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: codex/http-host-static-server
  • Commit SHA: f05627bf3e0f02ddb92cd96fd7f4ae55f12eead2

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: cargo test --manifest-path Cargo.toml http_host:: --lib
  • Rust fmt/check (if changed): cargo fmt --manifest-path Cargo.toml --all --check; cargo check --manifest-path Cargo.toml --lib
  • Tauri fmt/check (if changed): pre-push hook ran cargo fmt --manifest-path app/src-tauri/Cargo.toml --all --check and cargo check --manifest-path app/src-tauri/Cargo.toml

Validation Blocked

  • command: node scripts/codex-pr-preflight.mjs --strict-path --lightweight
  • error: expected Codex-web path /workspace/openhuman, got /Users/enamakel/work/tinyhumansai/openhuman-6; branch naming convention expected an issue-key branch and this local change was shipped without a provided issue key
  • impact: no code validation was blocked, but the PR metadata documents that this was a local checkout rather than a Codex-web issue branch

Behavior Changes

  • Intended behavior change: adds a new authenticated static-directory hosting capability to the Rust core
  • User-visible effect: trusted callers can ask the core to host a directory on a chosen port and receive the URL plus generated Basic-auth credentials

Parity Contract

  • Legacy behavior preserved: existing JSON-RPC dispatch and controller registration flow remain unchanged; this is additive
  • Guard/fallback/dispatch parity checks: controller registration stays in src/core/all.rs, params use schema-driven validation, and server shutdown is wired through the existing core shutdown hook mechanism

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this PR
  • Resolution (closed/superseded/updated): new canonical PR for this branch

Summary by CodeRabbit

  • New Features
    • In-process HTTP hosting to serve local directories with start/stop/list/get controls
    • Optional Basic Authentication with automatic username resolution and generated passwords
    • Auto-serving of index.html and HTML directory listings with correct MIME types
    • Path-traversal protections, host/port validation, and sanitized labels/usernames
    • Hosted servers tracked, discoverable via RPC, and cleaned up automatically on shutdown

Review Change Stack

@senamakel senamakel requested a review from a team May 16, 2026 22:12
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45486aea-768b-49fe-90ca-63faa6d0bc8a

📥 Commits

Reviewing files that changed from the base of the PR and between 62e42a2 and 9e502fe.

📒 Files selected for processing (1)
  • src/openhuman/http_host/ops.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/openhuman/http_host/ops.rs

📝 Walkthrough

Walkthrough

Adds an http_host subsystem providing types, ops, RPC adapters, controller schemas, request routing, Basic Auth, path-safety, tests, and global registration to start/list/get/stop in-process static-directory HTTP servers.

Changes

HTTP Hosted Directory Server

Layer / File(s) Summary
Data types and module API
src/openhuman/http_host/types.rs, src/openhuman/http_host/mod.rs, src/openhuman/mod.rs, src/openhuman/http_host/rpc.rs
Request/response payloads and authentication shapes are defined; module-level docs and public ops/rpc exports and schema re-exports are added. The openhuman module now publicly exposes http_host.
Core hosted-directory runtime and HTTP handlers
src/openhuman/http_host/ops.rs, src/openhuman/http_host/auth.rs, src/openhuman/http_host/handlers.rs, src/openhuman/http_host/path_utils.rs, src/openhuman/http_host/tests.rs
Global registry (OnceLock+Mutex) tracks runtimes and enforces uniqueness; start_hosted_dir_server() canonicalizes directories, sanitizes bind host/labels, resolves/generates Basic Auth credentials, binds a TCP listener, spawns an Axum router task, and registers shutdown hooks. Handlers enforce auth, resolve request paths safely, stream files with MIME types, or render HTML directory listings. Tests cover sanitization, username resolution, path-traversal rejection, and end-to-end auth flow.
Schema inventory and RPC adapters
src/openhuman/http_host/schemas.rs, src/openhuman/http_host/rpc.rs
schemas.rs exposes all_controller_schemas()/all_registered_controllers() and maps start/stop/get/list to typed ControllerSchema definitions and async handler functions. rpc.rs provides async start/stop/get/list adapters delegating to ops and returning RpcOutcome.
Global controller registration
src/core/all.rs
Adds http_host registered controllers and declared controller schemas into the global controller registry for CLI/RPC discovery.
Cargo feature tweak
Cargo.toml
Enables the io feature for the tokio-util dependency.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant RPC as http_host::rpc
  participant Ops as http_host::ops
  participant Registry
  participant Axum
  participant FS as Filesystem
  Caller->>RPC: start(StartHostedDirParams)
  RPC->>Ops: start_hosted_dir_server(params)
  Ops->>Registry: check/register ServerRuntime
  Ops->>Axum: spawn server task (with HostedDirState)
  Ops-->>Caller: HostedDirStartResult
  Note over Axum: Incoming HTTP request
  Axum->>Axum: ensure_authorized(headers, auth)
  Axum->>Axum: resolve_request_path(root, path)
  Axum->>FS: read file or list directory
  Axum-->>Caller: file bytes or HTML listing
  Caller->>RPC: stop(HostedDirLookupParams)
  RPC->>Ops: stop_hosted_dir_server(server_id)
  Ops->>Registry: remove & cancel token
  Ops-->>Caller: HostedDirStopResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I found a folder, soft and neat,
I serve its files with tiny feet,
A secret key, a hop, a scrawl,
No traversal slips, I guard them all,
Static web ends with a rabbit's beat.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.02% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(core): add authenticated static directory hosting' accurately and concisely describes the primary change—addition of authenticated static directory HTTP hosting capability.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
src/openhuman/http_host/ops.rs (1)

1-787: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Split this module; it is well beyond the repository size limit.

This file is 787 lines, which makes maintenance and review harder. Please split by concerns (e.g., registry/lifecycle, auth, path safety, HTTP handlers, tests).

As per coding guidelines: Rust modules should generally not exceed ~500 lines; split growing modules into smaller units.

🤖 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 `@src/openhuman/http_host/ops.rs` around lines 1 - 787, Split this large ops.rs
into focused modules: move registry/lifecycle items (HostedDirRegistry,
HostedDirRuntime, registry(), register_shutdown_hook_once(),
start_hosted_dir_server, stop_hosted_dir_server, stop_all_hosted_dir_servers,
list_hosted_dir_servers, get_hosted_dir_server) into a new module (e.g.,
hosted_dir/registry.rs); move HTTP handlers and runtime state (HostedDirState,
serve_root, serve_path, serve_relative_path, serve_file, serve_directory, Router
construction) into hosted_dir/handlers.rs; move auth logic (ensure_authorized,
unauthorized_response, resolve_default_auth_username, fallback_env_username,
sanitize_basic_auth_username, resolve_default_auth_username_from_user_value,
generate_password) into hosted_dir/auth.rs; move path safety and util helpers
(canonicalize_hosted_directory, sanitize_bind_host, sanitize_optional_label,
resolve_request_path, parent_href_for, child_href_for, escape_html,
render_host_for_url, content_type_for_path) into hosted_dir/path.rs; update mod
declarations and imports to re-export types like HostedDirServerInfo and
StartHostedDirParams and adjust uses in tests; finally extract the #[cfg(test)]
tests into hosted_dir/tests.rs (or keep under module tests) and use shared test
helpers (TEST_MUTEX) as needed. Ensure all moved functions keep the same
signatures and update uses of LOG_PREFIX and types so code compiles.
🤖 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 `@src/core/all.rs`:
- Around line 130-131: You register HTTP host controllers via
controllers.extend(crate::openhuman::http_host::all_http_host_registered_controllers())
but there are no corresponding entries added in
build_declared_controller_schemas(), so validate_registry can fail; fix by
adding the http_host controller schemas to build_declared_controller_schemas()
(or have all_http_host_registered_controllers expose its schemas and merge them
there) so that the declared schemas include the http_host handlers before
validate_registry runs (update build_declared_controller_schemas(),
validate_registry usage, or adjust all_http_host_registered_controllers to
return both controllers and schemas).

In `@src/openhuman/http_host/ops.rs`:
- Around line 102-108: The log currently prints the full absolute path via
root_dir.display() (in the log::info call and the later similar logs around the
same area), which can expose PII—replace that usage with a redacted
representation instead (e.g., a helper like redact_path(root_dir) that returns
either the path basename, last N segments, or a masked string such as
"<redacted>" or "~/<last_segment>"); update the log::info calls that reference
root_dir (and any other places logging full directory paths in this module) to
call that redaction helper so logs never contain full directory paths.
- Around line 331-355: serve_file currently reads the whole file into memory
with tokio::fs::read; change it to open the file with tokio::fs::File::open and
stream it to the response using tokio_util::io::ReaderStream (or FramedRead)
wrapped in Body::wrap_stream so the payload is sent as a streaming body. On
error opening the file, log the error (keep the existing log message pattern)
and return the same INTERNAL_SERVER_ERROR response; on success set
StatusCode::OK and the content type header (using content_type_for_path) and
return the Response whose body is Body::wrap_stream(reader_stream) so large
files don't allocate fully in memory.

In `@src/openhuman/http_host/schemas.rs`:
- Around line 93-109: The ControllerSchema for "stop" is missing the "stopped"
output that rpc::stop returns; update the "stop" ControllerSchema (in the block
defining ControllerSchema { namespace: "http_host", function: "stop", ... }) to
include an additional outputs FieldSchema with name: "stopped", ty:
TypeSchema::Bool, comment: something like "Whether the server was successfully
stopped.", and required: true so the schema matches rpc::stop's returned {
stopped, server } shape.

In `@src/openhuman/http_host/types.rs`:
- Around line 36-37: Change the default bind host in the function
default_bind_host() from "0.0.0.0" to the loopback address "127.0.0.1" so the
HTTP Basic-auth endpoint is not exposed to the LAN by default; keep the rest of
the function unchanged and ensure any docs or configuration that mention the
previous default are updated so external binding remains an explicit opt-in.

---

Outside diff comments:
In `@src/openhuman/http_host/ops.rs`:
- Around line 1-787: Split this large ops.rs into focused modules: move
registry/lifecycle items (HostedDirRegistry, HostedDirRuntime, registry(),
register_shutdown_hook_once(), start_hosted_dir_server, stop_hosted_dir_server,
stop_all_hosted_dir_servers, list_hosted_dir_servers, get_hosted_dir_server)
into a new module (e.g., hosted_dir/registry.rs); move HTTP handlers and runtime
state (HostedDirState, serve_root, serve_path, serve_relative_path, serve_file,
serve_directory, Router construction) into hosted_dir/handlers.rs; move auth
logic (ensure_authorized, unauthorized_response, resolve_default_auth_username,
fallback_env_username, sanitize_basic_auth_username,
resolve_default_auth_username_from_user_value, generate_password) into
hosted_dir/auth.rs; move path safety and util helpers
(canonicalize_hosted_directory, sanitize_bind_host, sanitize_optional_label,
resolve_request_path, parent_href_for, child_href_for, escape_html,
render_host_for_url, content_type_for_path) into hosted_dir/path.rs; update mod
declarations and imports to re-export types like HostedDirServerInfo and
StartHostedDirParams and adjust uses in tests; finally extract the #[cfg(test)]
tests into hosted_dir/tests.rs (or keep under module tests) and use shared test
helpers (TEST_MUTEX) as needed. Ensure all moved functions keep the same
signatures and update uses of LOG_PREFIX and types so code compiles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 066407be-c1cc-4b29-823a-5ac83279db02

📥 Commits

Reviewing files that changed from the base of the PR and between 40a384e and f05627b.

📒 Files selected for processing (7)
  • src/core/all.rs
  • src/openhuman/http_host/mod.rs
  • src/openhuman/http_host/ops.rs
  • src/openhuman/http_host/rpc.rs
  • src/openhuman/http_host/schemas.rs
  • src/openhuman/http_host/types.rs
  • src/openhuman/mod.rs

Comment thread src/openhuman/http_host/ops.rs
Comment thread src/openhuman/http_host/ops.rs Outdated
Comment thread src/openhuman/http_host/schemas.rs
Comment thread src/openhuman/http_host/types.rs Outdated
@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 16, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 `@src/core/all.rs`:
- Around line 130-131: The http_host controllers are being added to the
agent-facing catalog via
controllers.extend(crate::openhuman::http_host::all_http_host_registered_controllers()),
which exposes local-directory hosting and Basic Auth to agents; remove this
extend call from the public/all-facing registration and instead register those
controllers only in the internal-only catalog (e.g., inside
build_internal_only_controllers() or whatever internal registration function you
have) so they remain available to RPC/trusted callers but not discoverable to
agents or tool listings. Ensure you update references to
crate::openhuman::http_host::all_http_host_registered_controllers() so it is
invoked only from the internal registration path and not from the public
controllers list.

In `@src/openhuman/http_host/handlers.rs`:
- Around line 65-70: The warning logs currently print absolute filesystem paths
via resolved.display(), which can leak PII; update each log::warn! that logs the
path (the Err branches where you log "metadata failed path=... err=..." and
similar occurrences later in the file) to call redact_path_for_log(&resolved)
instead of resolved.display(), and ensure you pass the redacted string into the
log macro (e.g., log::warn!("{} metadata failed path={} err={}", LOG_PREFIX,
redact_path_for_log(&resolved), error)). Apply the same replacement for the
other two occurrences mentioned (the similar log::warn! blocks around the other
metadata error handlers).
- Around line 117-128: The current directory iteration using while let
Ok(Some(entry)) = entries.next_entry().await will silently stop on the first
read error and return a partial listing; change the loop to explicitly match on
entries.next_entry().await and handle all arms: Ok(Some(entry)) -> process and
push to rows (keeping the existing file_type handling), Ok(None) -> break the
loop, Err(e) -> log the error and return an Err (or propagate an appropriate
HTTP error) so the request fails instead of returning a partial listing; update
the logic around entries, rows, and entry.file_type().await to reflect this
explicit match and error propagation.

In `@src/openhuman/http_host/ops.rs`:
- Around line 257-260: The loop currently cancels and awaits each runtime
sequentially, so a long-running join can delay cancelling other runtimes;
instead, first iterate over runtimes and call runtime.shutdown.cancel() for
every (preserving server_id visibility if needed), then in a separate loop await
each runtime.join_handle.await (and handle errors) so all cancellations are
issued before any await blocks; update the code around the runtimes iteration to
perform cancelation and awaiting in two distinct passes using the existing
runtimes, runtime.shutdown.cancel(), and runtime.join_handle.await symbols.
- Around line 91-101: The bind_target concatenation in ops::start (where
bind_target is built and passed to TcpListener::bind) doesn't bracket IPv6
literals, causing binds like "::1:3000" to fail; update the logic that
constructs bind_target to mirror render_host_for_url's handling of IPv6 (i.e.,
detect a bare IPv6 host containing ':' and not already bracketed and wrap it in
[ ] before appending :{port}), or otherwise produce a valid SocketAddr string
for TcpListener::bind; ensure TcpListener::bind gets the bracketed host:port
form so IPv6 and IPv4 both work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 103a0bc1-76d4-423b-85e5-02ddc7a3d45f

📥 Commits

Reviewing files that changed from the base of the PR and between f05627b and 62e42a2.

📒 Files selected for processing (10)
  • Cargo.toml
  • src/core/all.rs
  • src/openhuman/http_host/auth.rs
  • src/openhuman/http_host/handlers.rs
  • src/openhuman/http_host/mod.rs
  • src/openhuman/http_host/ops.rs
  • src/openhuman/http_host/path_utils.rs
  • src/openhuman/http_host/schemas.rs
  • src/openhuman/http_host/tests.rs
  • src/openhuman/http_host/types.rs
✅ Files skipped from review due to trivial changes (1)
  • Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/http_host/mod.rs
  • src/openhuman/http_host/types.rs
  • src/openhuman/http_host/schemas.rs

Comment thread src/core/all.rs
Comment on lines +130 to +131
// Ad-hoc static directory HTTP hosting for local file sharing / previews
controllers.extend(crate::openhuman::http_host::all_http_host_registered_controllers());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep http_host out of the agent-facing catalog if this is meant for trusted callers only.

Registering it here makes the namespace discoverable to agents/tool listings, not just RPC callers. For a surface that can expose arbitrary local directories and returns the generated Basic Auth credentials, that is a much broader trust boundary than build_internal_only_controllers().

Also applies to: 265-265

🤖 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 `@src/core/all.rs` around lines 130 - 131, The http_host controllers are being
added to the agent-facing catalog via
controllers.extend(crate::openhuman::http_host::all_http_host_registered_controllers()),
which exposes local-directory hosting and Basic Auth to agents; remove this
extend call from the public/all-facing registration and instead register those
controllers only in the internal-only catalog (e.g., inside
build_internal_only_controllers() or whatever internal registration function you
have) so they remain available to RPC/trusted callers but not discoverable to
agents or tool listings. Ensure you update references to
crate::openhuman::http_host::all_http_host_registered_controllers() so it is
invoked only from the internal registration path and not from the public
controllers list.

Comment on lines +65 to +70
Err(error) => {
log::warn!(
"{LOG_PREFIX} metadata failed path={} err={}",
resolved.display(),
error
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Redact filesystem paths in warning logs.

These warnings still emit absolute local paths via .display(), which can leak usernames and other local PII. Use redact_path_for_log(...) at these sites too.

As per coding guidelines: Never log secrets or full PII—redact.

Also applies to: 93-97, 159-163

🤖 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 `@src/openhuman/http_host/handlers.rs` around lines 65 - 70, The warning logs
currently print absolute filesystem paths via resolved.display(), which can leak
PII; update each log::warn! that logs the path (the Err branches where you log
"metadata failed path=... err=..." and similar occurrences later in the file) to
call redact_path_for_log(&resolved) instead of resolved.display(), and ensure
you pass the redacted string into the log macro (e.g., log::warn!("{} metadata
failed path={} err={}", LOG_PREFIX, redact_path_for_log(&resolved), error)).
Apply the same replacement for the other two occurrences mentioned (the similar
log::warn! blocks around the other metadata error handlers).

Comment on lines +117 to +128
match tokio::fs::read_dir(dir).await {
Ok(mut entries) => {
let mut rows = Vec::new();
while let Ok(Some(entry)) = entries.next_entry().await {
let name = entry.file_name().to_string_lossy().to_string();
let file_type = match entry.file_type().await {
Ok(file_type) => file_type,
Err(_) => continue,
};
let suffix = if file_type.is_dir() { "/" } else { "" };
rows.push((name, suffix.to_string()));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don't silently return a partial directory listing on iteration errors.

while let Ok(Some(entry)) = entries.next_entry().await stops on the first read error and returns an incomplete listing as if it succeeded. This should log and fail the request instead.

Suggested fix
-            while let Ok(Some(entry)) = entries.next_entry().await {
-                let name = entry.file_name().to_string_lossy().to_string();
-                let file_type = match entry.file_type().await {
-                    Ok(file_type) => file_type,
-                    Err(_) => continue,
-                };
-                let suffix = if file_type.is_dir() { "/" } else { "" };
-                rows.push((name, suffix.to_string()));
-            }
+            loop {
+                let entry = match entries.next_entry().await {
+                    Ok(Some(entry)) => entry,
+                    Ok(None) => break,
+                    Err(error) => {
+                        log::warn!(
+                            "{LOG_PREFIX} read_dir iteration failed path={} err={}",
+                            crate::openhuman::http_host::path_utils::redact_path_for_log(dir),
+                            error
+                        );
+                        return (
+                            StatusCode::INTERNAL_SERVER_ERROR,
+                            "failed to enumerate hosted directory",
+                        )
+                            .into_response();
+                    }
+                };
+                let name = entry.file_name().to_string_lossy().to_string();
+                let file_type = match entry.file_type().await {
+                    Ok(file_type) => file_type,
+                    Err(_) => continue,
+                };
+                let suffix = if file_type.is_dir() { "/" } else { "" };
+                rows.push((name, suffix.to_string()));
+            }
📝 Committable suggestion

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

Suggested change
match tokio::fs::read_dir(dir).await {
Ok(mut entries) => {
let mut rows = Vec::new();
while let Ok(Some(entry)) = entries.next_entry().await {
let name = entry.file_name().to_string_lossy().to_string();
let file_type = match entry.file_type().await {
Ok(file_type) => file_type,
Err(_) => continue,
};
let suffix = if file_type.is_dir() { "/" } else { "" };
rows.push((name, suffix.to_string()));
}
match tokio::fs::read_dir(dir).await {
Ok(mut entries) => {
let mut rows = Vec::new();
loop {
let entry = match entries.next_entry().await {
Ok(Some(entry)) => entry,
Ok(None) => break,
Err(error) => {
log::warn!(
"{LOG_PREFIX} read_dir iteration failed path={} err={}",
crate::openhuman::http_host::path_utils::redact_path_for_log(dir),
error
);
return (
StatusCode::INTERNAL_SERVER_ERROR,
"failed to enumerate hosted directory",
)
.into_response();
}
};
let name = entry.file_name().to_string_lossy().to_string();
let file_type = match entry.file_type().await {
Ok(file_type) => file_type,
Err(_) => continue,
};
let suffix = if file_type.is_dir() { "/" } else { "" };
rows.push((name, suffix.to_string()));
}
🤖 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 `@src/openhuman/http_host/handlers.rs` around lines 117 - 128, The current
directory iteration using while let Ok(Some(entry)) = entries.next_entry().await
will silently stop on the first read error and return a partial listing; change
the loop to explicitly match on entries.next_entry().await and handle all arms:
Ok(Some(entry)) -> process and push to rows (keeping the existing file_type
handling), Ok(None) -> break the loop, Err(e) -> log the error and return an Err
(or propagate an appropriate HTTP error) so the request fails instead of
returning a partial listing; update the logic around entries, rows, and
entry.file_type().await to reflect this explicit match and error propagation.

Comment thread src/openhuman/http_host/ops.rs Outdated
Comment thread src/openhuman/http_host/ops.rs Outdated
@senamakel senamakel merged commit fe8237a into tinyhumansai:main May 16, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant