Files
vdm/docs/08-porting.md
T
samiandClaude Opus 5 facd851824
CI / clang-format (push) Canceled after 0s
CI / testserver (push) Canceled after 0s
CI / bootstrap-script (push) Canceled after 0s
CI / bootstrap-script-2604 (push) Canceled after 0s
CI / extension-lint (push) Canceled after 0s
CI / build (clang) (push) Canceled after 0s
CI / build (gcc) (push) Canceled after 0s
CI / sanitizers (dev) (push) Canceled after 0s
CI / sanitizers (tsan) (push) Canceled after 0s
CI / clang-tidy (push) Canceled after 0s
CI / conformance (push) Canceled after 0s
CI / nightly-integration (push) Canceled after 0s
CI / gui-dod (push) Canceled after 0s
CI / gui-dod-nightly (push) Canceled after 0s
docs: define the cross-platform porting strategy (ADR 0020, macOS first)
The tree is Ubuntu-first, but the Linux-specific surface is about ten files and
already sits at the bottom of the I/O and RPC stacks. Rather than fork the repo
or scatter ifdefs, platform differences go behind narrow seams with one backend
directory per OS, selected centrally in cmake/platform.cmake.

Linux stays the reference implementation and its behaviour does not change: the
owning lanes (CORE, DAEMON) move today's Linux code behind the seams on Ubuntu
first, with the suite still green, before any macOS code exists. A new PORT lane
owns only the per-OS backends, so macOS work never writes shared logic and
CLAUDE.md's lane rule stays intact.

Adds ADR 0020 (the decision and phases), docs/08-porting.md (seam list, API
mapping for macOS and Windows, dependency table, verification gates) and
docs/agents/AGENT-PORT.md (the lane brief), plus the PORT row in the ownership
table.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-15 17:36:27 +04:00

5.2 KiB

08 — Porting guide (macOS first, Windows later)

Read docs/adr/0020-cross-platform-strategy.md first; this file is the concrete inventory it refers to. Rule: Linux behaviour never changes. #ifdef never appears in logic.

Directory layout

Platform backends live beside the code they serve, one directory per OS:

core/src/io/platform/linux/file_ops.cpp        core/src/io/platform/macos/file_ops.cpp
core/src/io/platform/file_ops.hpp              # the seam — no #ifdef, no OS types
daemon/src/rpc/platform/linux/{wakeup,peercred,instance_lock,runtime_dir}.cpp
daemon/src/rpc/platform/macos/{...}.cpp
daemon/src/rpc/platform/*.hpp                  # the seams
cmake/platform.cmake                           # VELOX_OS_* + source selection

cmake/platform.cmake sets exactly one of VELOX_OS_LINUX, VELOX_OS_MACOS, VELOX_OS_WINDOWS, and exposes velox_platform_sources(<target> <dir>) which adds <dir>/platform/<os>/*.cpp. Selection happens there and nowhere else.

The seams

Five interfaces cover the whole port. Signatures are indicative, not binding — the owning lane settles them in Phase 0.

Seam Interface Why
File preallocation Result<void> preallocate(int fd, uint64_t bytes) posix_fallocate is Linux/glibc
Cache advice void advise_dontneed(int fd, uint64_t off, uint64_t len) posix_fadvise has no macOS equivalent
Durable flush Result<void> flush_durable(int fd) fdatasync vs F_FULLFSYNC
Loop wakeup class Wakeup { int pollfd(); void signal(); void drain(); } eventfd is Linux-only
Peer identity Result<PeerId> peer_of(int fd) SO_PEERCRED vs LOCAL_PEERCRED
Single instance Result<Lock> acquire(string_view runtime_dir) abstract sockets are Linux-only
Runtime dir string runtime_dir(), string data_dir() XDG vs ~/Library

API mapping

Linux (today) macOS Windows (later) Note
posix_fallocate(fd,0,n) fcntl(F_PREALLOCATE) then ftruncate(n) SetFileValidData / SetEndOfFile macOS needs the ftruncate; F_PREALLOCATE alone does not set size. Fall back to ftruncate on failure, exactly as the Linux path already does for EOPNOTSUPP
posix_fadvise(DONTNEED) no equivalent — no-op FILE_FLAG_NO_BUFFERING Do not substitute F_NOCACHE: it changes caching for the whole descriptor, not a written range. A no-op is honest; record it
fdatasync(fd) fcntl(fd, F_FULLFSYNC), fall back to fsync FlushFileBuffers fsync on macOS does not guarantee the drive flushed. .veloxpart resume integrity depends on this — use F_FULLFSYNC
pwrite same WriteFile + OVERLAPPED POSIX, no work
O_NOFOLLOW same FILE_FLAG_OPEN_REPARSE_POINT POSIX, no work
eventfd self-pipe (pipe2/`O_NONBLOCK O_CLOEXEC`) event object
timerfd poll() timeout computed from the next deadline waitable timer Simplest port: the loop already has a deadline set
SO_PEERCRED + struct ucred getpeereid(fd,&uid,&gid) named-pipe token Same-UID check is the security property; keep it
abstract socket \0velox-daemon-<hash> socket file in the runtime dir + `flock(LOCK_EX LOCK_NB)` named mutex
$XDG_RUNTIME_DIR $TMPDIR (per-user, already private) %LOCALAPPDATA% macOS has no XDG runtime dir
$XDG_DATA_HOME ~/Library/Application Support/Velox %APPDATA%
libsecret / Secret Service Keychain (Security.framework) DPAPI / Credential Manager Behind the credential-store seam. CLAUDE.md §4 still applies: never SQLite, never logs
systemd user units launchd plist (~/Library/LaunchAgents) Service/Task Scheduler Phase 3

Build dependencies

Ubuntu macOS (Homebrew)
qt6-base-dev, qt6-svg-dev, qt6-tools-dev qt@6
libcurl4-openssl-dev system libcurl, or curl
libsqlite3-dev system sqlite, or sqlite
libssl-dev openssl@3 (set OPENSSL_ROOT_DIR)
libsecret-1-dev none — Keychain is in the SDK
nlohmann-json3-dev nlohmann-json
nodejs, npm node

The root CMakeLists.txt currently does pkg_check_modules(LIBSECRET REQUIRED ...) unconditionally. That must become Linux-only, or macOS cannot configure at all. It is the single hard blocker for a first macOS build.

What does not change

contracts/ and both generated clients, tools/mockd, tests/conformance, tools/testserver, the whole extension, and every hostile-mode expectation. If a port tempts you to change a fixture or a schema, stop — that is a contract change and it goes through PROTO.

Verification gates

  • Phase 0 done: Ubuntu suite still 57/57, and git diff shows only moves behind seams.
  • Phase 1 done: veloxd, velox, libveloxcore build on macOS; core unit tests pass.
  • Phase 2 done: conformance (mockd and live veloxd) and the engine hostile-mode matrix pass on macOS; one real download completes with a matching SHA-256.
  • Phase 3 done: .dmg or Homebrew formula installs and runs on a clean machine.