Skip to content

Start the embedded native Server with only dw installed - #161

Draft
rmcdaniel wants to merge 6 commits into
mainfrom
cli/native-dev-server
Draft

rmcdaniel wants to merge 6 commits into
mainfrom
cli/native-dev-server

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

dw server:start-dev now starts the native Server and bundled SQLite inside the standalone CLI. Installing dw is enough to start from an empty directory, with no PHP, Docker, Redis, separate executable or runtime download.

The command keeps persistent local data, binds loopback, enforces bearer authentication and shuts down HTTP and the native runtime on interruption. Its private data guard rejects shared or linked directories/files, special files and replaced identities before startup. Windows uses protected ACLs and held handles that deny deletion. Help and documentation show the startup and connection commands.

The engine and its Avro/SQL patches are pinned together to accepted Server source 3d90086f73533ac4437735bae6b9d0446558577c (development identity). SDK #279 provides the compiler-pin opt-out needed to compose the SDK with this engine; the current candidate pin must be replaced by its qualified registry package before release. The legacy PHP line is unchanged.

Validation:

  • 326 Rust tests pass, including both generated PHP differential checks and eleven actual-runtime tests. A separate disposable privileged test confirms refusal of a foreign-owned private directory.
  • The local binary passes empty-PATH startup, real CLI admission, restart and private-state refusal checks. An actual SDK Worker completes a workflow after a durable timer.
  • All five staged release binaries at exact source 59809483009aec9aa189058a3dc450a4a9830af8 pass the expanded smoke checks: Linux x86_64/aarch64, macOS x86_64/aarch64 and Windows x86_64. Its actual 2.x comparison and asset assembly also pass in run 43601809134.
  • Clippy and all 26 parity-harness controls pass. The full compatibility comparison is 894/922 matches, 28 retained intentional differences and zero unexpected differences; 25 PHP-Artisan startup cases remain historical reference data and are replaced by the native-runtime tests.
  • Independent private-state source review passes. Central #401 has its separate source-only approval for the Rust 1.99 caller fixture.

Remaining gates are the qualified SDK package, accepted central #401 caller record, shared PHP bootstrap repair (#400/#404), current independent review/hosted checks, and normal merge/main verification. Bootstrap failures remain pre-test NONPASS. No release is claimed. CLI #157 remains set aside.

Refs #160. Delivery is recorded after the published CLI passes the first-start acceptance.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security HOLD on exact de4f097.

The loopback and bearer-token boundary is sound, but the claimed private persistent data boundary is not yet enforced. Settings::prepare_directory creates a 0700 directory only when it is new, then canonicalizes an existing path and opens server.lock with ordinary OpenOptions. It does not reject a symlink/reparse point, foreign-owned or group/world-accessible existing directory, a symlink/hardlink/non-regular lock, or a replacement between canonicalization and later path use. Runtime::open then opens the relative database.sqlite by pathname, so a pre-created database.sqlite symlink or hard link inside an accepted data directory can redirect native SQLite bootstrap/WAL writes outside the selected directory. The same pathname boundary is the basis for relative payload state.

This repository already has the appropriate Unix pattern in completion_log: open the directory with O_NOFOLLOW|O_DIRECTORY, verify owner/mode, use openat on the held fd with O_NOFOLLOW, then fstat regular type, owner, private mode and nlink==1. Apply an equivalent anchored boundary to the development directory, lock, database and owned relative state; on Windows refuse reparse points and retain a handle-based identity through use. Existing state must either meet the same private ownership/type/link rules or refuse before the runtime opens or mutates it.

Please add causal controls for an existing shared/foreign directory, a symlinked directory, symlink/hardlink/non-regular server.lock and database.sqlite, a parent/directory replacement between checks and use, and Windows reparse points, while retaining the fresh-directory and restart positives. No release artifact or registry qualification is implied by this source review.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security rebind at exact c5522615c2327ec35c4351b6734a5eb857ad103a / tree 6349ea3e35ab9ea5d28c62da38391322900fb464: PASS for the private development-storage source boundary that was held at de4f097.

The selected directory is no longer accepted through canonicalization plus ordinary pathname opens. On Unix the implementation walks and retains directory descriptors, checks each ancestor, opens children and files with O_NOFOLLOW, verifies owner/private mode/type and single-link regular files, uses openat for the lock/database, enters through fchdir, and revalidates held identities across runtime startup and shutdown. The entire pre-existing relative tree is checked before native opens; the process-private umask covers SQLite WAL/journal and later payload state. On Windows it refuses reparse points, validates current-user ownership and a closed effective allow-list, requires single-link regular files, creates new directories with a protected inheritable current-user/SYSTEM ACL, and holds every directory/file handle without DELETE sharing so parent/final replacement cannot race later relative opens.

The exact predecessor findings are covered: shared/foreign/symlinked directories; symlink, hardlink, directory, FIFO/shared lock and SQLite state; nested payload links; held database replacement; held-parent replacement; plus fresh-directory, lock exclusion, persistence/restart, busy-port, worker/timer and signal positives. Refusals preserve the outside witness and occur before SQLite writes. I found no remaining source bypass of the bounded data boundary.

This is a source/security rebind only. Current Windows/macOS builds and their staged-artifact refusal/lifecycle cases, complete exact-head CI, SDK package substitution, and normal release qualification remain separate gates; the existing shared PHP/bootstrap failures are not converted to PASS.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security bounded successor PASS at exact 15634510b8d3c09c3baf984eeec09e37f031cc65.

The Server dependency, Avro patch and both SQL patch pins move together from 63cea4854ec6ebe56ab1603b014e246b907aedc0 to accepted 3d90086f73533ac4437735bae6b9d0446558577c; GitHub reports the same complete tree ceeac261d5cc3176d2dca2c01ce32b6b4e246893 for both commits, and the lock records match the manifest pins.

The only non-pin delta makes the existing foreign-owner refusal run in ordinary unprivileged Unix CI against a system-owned directory, while retaining the separate root-only causal fixture that constructs an exact foreign-owned private directory. It does not change Data::open, the Unix/Windows ownership and replacement guards, or any effectful startup/data path reviewed at c5522615c2327ec35c4351b6734a5eb857ad103a.

This is a source rebind only. Exact-head platform artifacts, shared bootstrap/package gates and release qualification remain separate.

@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Bounded source rebind PASS for exact 59809483009aec9aa189058a3dc450a4a9830af8 / tree e67cd26fadbe49300742798a2bbe0011446d89f9.

The complete delta from reviewed 15634510 is confined to start_dev_data_windows.rs and closes the reported unchecked Windows FFI outputs without changing the admitted ACL policy or any Unix path:

  • a successful ConvertSidToStringSidW must also return a non-null allocation before UTF-16 scanning;
  • both the sizing and populated GetTokenInformation(TokenUser) results must be large enough for TOKEN_USER, and the returned byte count must remain inside the allocated aligned buffer before dereference;
  • every successful GetAce must return non-null, its header must fit, and an allow ACE must be at least ACCESS_ALLOWED_ACE size before either cast/SID access.

All new branches fail closed. git diff --check is clean. I did not execute the Windows-only code locally; fresh exact-head CodeQL, Windows compilation/runtime guards, normal CI, and the separate shared-bootstrap/SDK package gates remain required. No release or held #157 scope is added.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Independent bounded classification for exact 59809483009aec9aa189058a3dc450a4a9830af8 / tree e67cd26fadbe49300742798a2bbe0011446d89f9, limited to CodeQL check 132468058285's three rust/access-invalid-pointer annotations:

  • line 194, (*acl).AceCount: false positive. GetSecurityInfo returned ERROR_SUCCESS, DACL_SECURITY_INFORMATION was requested with a non-null ppDacl, and the code refuses acl.is_null() before this dereference. Microsoft's contract says that, on success with that flag/output, ppDacl points to the DACL inside the returned security descriptor. The owning descriptor allocation is retained across the complete loop. See GetSecurityInfo.
  • line 199, ACE_HEADER: false positive. GetAce must return nonzero and the returned ace must be non-null before the cast. Microsoft's contract defines pAce as an out pointer set to the address of the ACE on success. See GetAce.
  • line 209, ACCESS_ALLOWED_ACE: false positive. It is reached only for ACCESS_ALLOWED_ACE_TYPE, after the same successful/non-null GetAce guard and after AceSize >= size_of::<ACCESS_ALLOWED_ACE>(). The new header-size check also precedes the ACE type read.

The SARIF paths begin at the local null_mut initializers and do not model these Win32 out-parameter success postconditions. I find no remaining unchecked null/size dereference in these three paths. This classification is exact to alerts 25/28/27 on this commit; it is not a blanket suppression or a gate change. Repository CodeQL triage/readback and the pending exact-head Windows/runtime gates remain with the owner.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Exact source 59809483009aec9aa189058a3dc450a4a9830af8 now passes all five staged native-platform builds and the expanded development-runtime checks, the actual 2.x comparison, and release-asset assembly: dry run 43601809134.

CodeQL check 132468058285 is SUCCESS after the exact alerts 25/28/27 were recorded as false positives following independent FFI-path classification. Authenticated readback confirms each reason and linked review. The runtime null/size checks, data guards and required analysis remain in place.

The remaining failures are the pre-test PHP 8.3 archive setup and central caller admission, plus the resulting aggregate failure. Their shared repair is central #400/#404; its concrete organization-workflow selector change is already escalated once and awaits an administrator. Central #401 and SDK #279's registry-package follow-through remain dependencies. This source/artifact result does not publish the CLI or close #160.

This branch has not been deployed

No deployments
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