Skip to content

data-plane: serve the Native file mutations other than writes - #1373

Merged
NancyKwongPerplexity merged 1 commit into
mainfrom
data-plane-native-files-mutations
Nov 4, 2026
Merged

NancyKwongPerplexity merged 1 commit into
mainfrom
data-plane-native-files-mutations

Conversation

@NancyKwongPerplexity

@NancyKwongPerplexity NancyKwongPerplexity commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds the Native file mutations other than writes to the data plane's files routes, behind the same SPACE_DATA_PLANE_NATIVE_FILES=serve setting as #1372: files:mkdir, files:remove, files:rename, files:changes_enable, files:changes_disable and files:modified.

Each request follows space-api's handler, in its order:

  1. Authorization from the headers (AuthorizeNativeFileRequest).
  2. The JSON body. The tracking calls read it as space-api's Option<Json<_>> does, so no Content-Type means no body.
  3. The Idempotency-Key check, right after the body, with space-api's messages.
  4. The field checks.
  5. For mkdir, remove and rename, the request's fingerprint, computed with the shared pplx-space-native-files functions and sent with PrepareNativeFileCall. space-api binds the key before it wakes or reaches the sandbox.
  6. The sandbox call on the granted route.

What follows from that:

  • Changed request: a different request under a bound key gets space-api's 409 idempotency_conflict and never reaches the sandbox.
  • Rename key: a rename carries the guest key space-api derives in its grant, so a retry on either server is replayed by the same key. A grant without one is answered with a 500 and is not sent.
  • Change tracking: it checks its key but neither binds nor sends it, as on space-api. files:modified takes no key.
  • Reconnect: every call reconnects once through a fresh preparation when the node-api drains, and not after a closed connection, as space-api's spaced client does for these unary calls.
  • No blind retry: failures map through the shared files mapping. For mkdir, remove and tracking, which the sandbox runs without a key, a failure that may have applied the change is not retryable.

Write and write_batch are the next PR.

Validation

At this head (2947d368), on main at 0271415a:

  • Rebased onto data-plane: serve Native files:stat, files:list and files:read #1372's new head with no conflicts; the commit is unchanged (git range-diff reports it identical).
  • cargo fmt --all -- --check, scripts/generate-env-contract.sh --check, scripts/check-rust-error-policy.sh and scripts/source_guards/env_test_hook_boundary.py pass.
  • cargo clippy --locked --all-targets --all-features -p pplx-space-data-plane-server -- -D warnings passes.
  • cargo check --workspace --all-targets --locked passes.
  • cargo arch against main's baseline, renames and ARCHITECTURE.toml: 0 required findings and 112 advisory, the same findings as main.
  • cargo nextest run --config 'profile.test.package.pplx-space-api.opt-level=0' --all-features --all-targets, one package at a time, with Postgres and Redis from the nextest setup scripts and SPACE_CI_REQUIRE_TEST_DEPS=1: pplx-space-data-plane-server 405 passed; pplx-space-data-plane-svc 7 passed; pplx-space-api 2189 passed, 1 skipped; pplx-space-native-files 2 passed.

The scripted API in these tests binds keys as space-api's handler does: it keeps the first fingerprint per key and refuses a different one with the 409, and derives the guest key with the shared guest_key. Each new test was mutation-checked: I applied the change, rebuilt, saw the test fail, and reverted.

Test What it proves Mutation that makes it fail
mutations_answer_through_the_granted_route_in_the_apis_shapes All six answer in space-api's shapes. The sandbox gets space-api's guest requests (exist_ok, missing_ok, roots and globs, window) for the granted sandbox, with the route token. mkdir, remove and rename are prepared with the fingerprint the shared function computes for their body, the others with none. The rename carries the guest key derived from the bound key and that fingerprint fingerprint over another request; send the client's key instead of the grant's; build files:modified for the placeholder sandbox; require a JSON body for changes_disable
a_changed_request_under_a_bound_key_never_reaches_the_sandbox A replay is prepared with the same fingerprint and reaches the sandbox with the same guest key. A changed rename, mkdir or remove is refused with the 409 and never reaches it. A rename granted without a guest key is a 500 and is not sent. A request without its key is never prepared prepare a rename or a mkdir without its fingerprint; drop the mkdir key check; send a rename without the grant's key
each_mutation_reconnects_only_when_the_node_api_drains For each of the six: a drain reconnects once, with the same fingerprint on both preparations. A closed connection does not reconnect. The 503 for it is retryable only for rename and modified reconnect a rename on a closed connection; map unkeyed mutations like keyed ones
each_failure_answers_as_the_api_answers_it (from #1372, 9 new rows) Missing and invalid keys; the key checked after the body (a 422 wins); tracking enable with enabled: false; two modified filters; a non-RFC3339 since; a mkdir failure that may have applied (not retryable); a mkdir refused before start (retryable, Retry-After: 2); a rename failure (retryable) drop the key check; check the key before the body; drop the tracking and filter checks; map unkeyed mutations like keyed ones

Not validated here: a real space-api and runtime. The Kind run at PR 11's head covers that.

Severity and risk

Severity: does not apply (new, off by default).

Risk:

  • With the setting off: nothing changes.
  • With serve: mutations answered by the data plane bind the same public_idempotency_records rows as space-api, so a retry may land on either server. A wrong fingerprint encoding would turn cross-server retries into 409s. The tests pin the encodings to the shared functions.
  • Rollback: set the setting to off, or revert this PR.

Stack

This is PR 7 of 12 and is based on data-plane-native-files-read (#1372). Merge order:

  1. native-files: share the file-request fingerprints and guest key #1362
  2. native-files: share Native file request handling #1363
  3. public-api-http: make Native error rendering usable outside space-api #1364
  4. api: serve native file access RPCs on the activation listener #1365
  5. auth: let the data-plane identity call the native file access RPCs #1371
  6. data-plane: serve Native files:stat, files:list and files:read #1372
  7. this PR
  8. data-plane-native-files-write
  9. data-plane-native-files-drain
  10. kind-native-files-data-plane
  11. smoke-native-files-data-plane
  12. docs-native-files-data-plane

Not covered

  • Writes: until the next PR lands, serve does not answer files:write or files:write_batch. Route only the operations it answers to the data plane.
  • Reconnect re-binding: a reconnect prepares again, which renews the same binding with the same fingerprint, where space-api's own reconnect does not bind again. The outcome is the same.

AI assistance: Claude Code drafted the code, tests, and this description; the author reviewed them.

@nikhilbirmiwal-pplx nikhilbirmiwal-pplx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Checked mkdir/remove/rename/changes_enable/changes_disable/modified against the space-api handlers: fingerprints are built from the same parsed path/flag/user inputs; the API binds them (and derives the rename guest key) in PrepareNativeFileCall via the forwarded Idempotency-Key; a drain-reconnect re-prepares with the same fingerprint and resends the same guest key. Error mapping (unprotected vs file_call_error) and operation budgets match the API's spaced client. No blocking issues.

@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the data-plane-native-files-read branch from 52a2c5e to 5d6bfac Compare November 3, 2026 19:40
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the data-plane-native-files-mutations branch from 21f33ba to 2947d36 Compare November 3, 2026 19:40
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the data-plane-native-files-read branch from 5d6bfac to 7af744f Compare November 3, 2026 22:27
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the data-plane-native-files-mutations branch from 2947d36 to 09529af Compare November 3, 2026 22:28
Base automatically changed from data-plane-native-files-read to main November 3, 2026 23:10
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Explain Change →

Automatic approval withheld

Assessment Current risk Maximum for approval
Merge risk Moderate Low

Why: The new file-mutation routes change how requests reach sandbox workers. No specific failure is established, but the change warrants moderate merge risk under the repository’s guidance.

Commit: 50c6608046a9.

No actionable comments were generated in the recent review.

Walkthrough

The native file API now routes mkdir, remove, rename, change-tracking, and modified-file requests to sandbox workers. The handlers validate request bodies and idempotency keys, bind fingerprints for keyed mutations, and use operation-specific sandbox budgets. Grants can include a parsed guest replay key. Tests cover request validation, idempotency conflicts, sandbox failures, and draining-node reconnection.

Possibly related PRs

  • perplexityai/SPACE#1317: Implements Windows guest filesystem operations for mkdir, remove, and rename, including keyed replay and conflict handling.
Merge Risk: Moderate · up to 50c66

The new file-mutation routes change how requests reach sandbox workers. No specific failure is established, but the change warrants moderate merge risk under the repository’s guidance.

Security architecture risk: Low · up to 50c66

The additional mutation routes preserve sandbox authorization and request-binding controls. Rename retains its replay identity, and automatic retries of unkeyed mutations are restricted to requests rejected before execution. No introduced security defect was established, but deployment and recovery coverage is not complete.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — An admitted caller can mutate paths and tracking state within the granted sandbox using the established guest-user contract. The inspected boundary constrains the target through organization and sandbox ownership checks; the new request body does not select an independent tenant or runtime endpoint.

Trust Boundaries and Controls

  • observed — Authorization remains with the API rather than moving into data-plane body processing. Both initial authorization and preparation enter the API ownership decision, and draining recovery prepares again before another privileged call. Key binding occurs before runtime access, while rename requires the API-derived replay key.

Resilience and Maintainability Implications

  • observed — The existing tracking owner uses service-owned serialized updates so caller cancellation cannot reorder acknowledgements. Watch setup publishes under a state lock, rolls back partially registered consumers on failure, and uses a generation fence so disable supersedes pending setup. These mechanisms remain unchanged for the additional callers.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

Failed Failed checks: 2 inconclusive

Check name Status Explanation Resolution
Description Follows The Pr Template Inconclusive The available description excerpt contains substantive ## Summary and ## Validation sections. The excerpt is explicitly truncated before the remaining content, so the presence and real content of … Provide the complete PR description, including the text after the truncated validation table, so the ## Severity and risk and AI assistance: requirements can be checked.
Public-Safe Content Inconclusive The title and reviewed diff contain no credentials, private keys, internal URLs or hostnames, cloud identifiers, workstation paths, logs, or incident details. The supplied PR description is truncated,… Provide the complete PR description or rerun the check with the full description available.
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title starts with the lowercase area "data-plane:" and a lowercase summary. It accurately describes serving Native file mutations other than writes and has no trailing period, ticket ID, or intern…
Bug Fixes Name A Regression Test Passed PASS. The PR adds previously unserved Native file mutation routes; it does not fix an existing application defect. The Validation section also states that each new test was mutation-checked and names …
Weakened Safeguards Are Justified Passed No safeguard was weakened. The PR adds mutation tests and assertions, with no changes to CI, lint, guard, allowlist, baseline, or nextest configuration. The only production deletion moves rejected f…
Full details: Title check

Explanation

The title starts with the lowercase area "data-plane:" and a lowercase summary. It accurately describes serving Native file mutations other than writes and has no trailing period, ticket ID, or internal link.

Full details: Bug Fixes Name A Regression Test

Explanation

PASS. The PR adds previously unserved Native file mutation routes; it does not fix an existing application defect. The Validation section also states that each new test was mutation-checked and names tests such as mutations_answer_through_the_granted_route_in_the_apis_shapes and a_changed_request_under_a_bound_key_never_reaches_the_sandbox. The diff adds these tests under crates/.

Full details: Weakened Safeguards Are Justified

Explanation

No safeguard was weakened. The PR adds mutation tests and assertions, with no changes to CI, lint, guard, allowlist, baseline, or nextest configuration. The only production deletion moves rejected from native_files/read.rs to the parent module without changing its implementation; existing read handlers still use it. The fake worker refactor preserves the existing Stat path and adds scripted mutation replies.

Full details: Description Follows The Pr Template

Explanation

The available description excerpt contains substantive ## Summary and ## Validation sections. The excerpt is explicitly truncated before the remaining content, so the presence and real content of ## Severity and risk and the AI assistance: line cannot be verified. The full PR description is not available in the checkout.

Resolution

Provide the complete PR description, including the text after the truncated validation table, so the ## Severity and risk and AI assistance: requirements can be checked.

Full details: Public-Safe Content

Explanation

The title and reviewed diff contain no credentials, private keys, internal URLs or hostnames, cloud identifiers, workstation paths, logs, or incident details. The supplied PR description is truncated, and no complete description is available in the checkout or Git metadata. This check also covers the description, so the omitted text cannot be verified.

Resolution

Provide the complete PR description or rerun the check with the full description available.

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

Recent review info
Automatic approval policy

Commit: 50c6608046a9.

Setting Effective value Source
Automatic approvals Enabled Global configuration
Merge risk Low Override 2
Security risk Not set Global configuration

Matching overrides (numbered as in the resolved configuration):

  • Override 2 — any author; paths: **, !ui/**; merge risk: Low.

Any matching disabled override withholds approval, regardless of risk. Otherwise, the strictest explicitly set limit wins for each score. Limits omitted by all matching overrides inherit the global configuration.

Not set means no limit was configured after inheritance. Use @coderabbitai configuration to inspect the resolved settings and their sources.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between bbcd248 and 50c6608.

Files selected for processing: 7
  • crates/l40_api/data/plane/server/src/native_files.rs
  • crates/l40_api/data/plane/server/src/native_files/call.rs
  • crates/l40_api/data/plane/server/src/native_files/mutations.rs
  • crates/l40_api/data/plane/server/src/native_files/read.rs
  • crates/l40_api/data/plane/server/src/native_files/tests.rs
  • crates/l40_api/data/plane/server/src/native_files/tests/fakes.rs
  • crates/l40_api/data/plane/server/src/native_files/tests/mutations.rs

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the data-plane-native-files-mutations branch from 09529af to bbcd248 Compare November 3, 2026 23:17
Add files:mkdir, files:remove, files:rename, files:changes_enable,
files:changes_disable and files:modified to the data plane's Native
files routes, in the steps and order of space-api's handlers:
- the JSON body (Option<Json> for the tracking calls);
- the Idempotency-Key check;
- the field checks;
- for mkdir, remove and rename, the shared pplx-space-native-files
  fingerprint, sent with PrepareNativeFileCall so space-api binds the
  key before anything reaches the sandbox.

A changed request under a bound key is refused with space-api's 409 and
never reaches the sandbox. The rename carries the guest key space-api
derives in its grant; a grant without one is a 500 and is not sent.
Change tracking checks its key but neither binds nor sends it, as on
space-api.

Every call reconnects once through a fresh preparation when the node-api
drains, and not after a closed connection, as space-api's spaced client
does. A failure that may have applied an unkeyed mutation (mkdir,
remove, tracking) does not invite a retry.
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the data-plane-native-files-mutations branch from bbcd248 to 50c6608 Compare November 4, 2026 19:03
@NancyKwongPerplexity
NancyKwongPerplexity added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit 45e963a Nov 4, 2026
41 checks passed
@NancyKwongPerplexity
NancyKwongPerplexity deleted the data-plane-native-files-mutations branch November 4, 2026 19:32
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