The first M1 (Viewer) increment: lift the Spike E RPC shape into src/ipc as a
Qt-free, unit-testable library implementing the §2.1 process split. The UI
process forks a sandboxed document process, hands it the document fd via
SCM_RIGHTS (§2.1: no FS access beyond handed fds), and talks Cap'n Proto
two-party RPC over a socketpair. The document process installs the seccomp
sandbox, receives the fd, and serves the DocumentProcess interface (open,
getPageCount, ping) backed by QPDF reading from the handed fd.
src/ipc/ (new, Qt-free so testable without QApplication):
- ipc.capnp: M1 protocol — DocumentProcess{open(fd via SCM_RIGHTS, pathHint)
-> OpenResult{pageCount, pdfVersion}; getPageCount; ping}. ADR-0004 trust
boundary: the UI validates every response; the doc bounds-checks requests.
- DocumentProcessServer: low-level wrapSocketFd + TwoPartyVatNetwork(SERVER)
+ makeRpcServer (the Spike E proven shape, NOT EzRpcServer which calls
accept() on a connected socketpair end). QPDF reads from the handed fd.
- DocumentProcessClient: the Qt<->KJ event-loop integration (§2.3) — runs the
KJ event loop on a dedicated worker thread (low-level makeRpcClient, not
EzRpcClient which uses a thread-local context that breaks cross-thread
destruction). Synchronous methods block the caller via std::promise.
- ProcessLauncher: fork + socketpair + send_fd + construct client.
- FdPassing: raw SCM_RIGHTS send/recv, orthogonal to the RPC transport.
- Sandbox: seccomp allow-list lifted from Spike E, +sendmsg/recvmsg (fd
passing), +fcntl/fstat/lseek/pread64 (QPDF/stdio on the handed fd).
src/app: wire File>Open and the command-line positional arg to launch the
document process off the GUI thread (QtConcurrent) and show the page count in
the status bar (QMetaObject::invokeMethod back to the GUI thread).
test/test_ipc: end-to-end test — launches the sandboxed document process,
opens a real PDF, verifies ping + pageCount + getPageCount. PASSES under the
seccomp sandbox; ASan-clean with sandbox disabled (ASan's pipe2 conflicts
with the allow-list, an ASan-only artefact). Registered as ctest ipc_end_to_end.
Both Spike E (raw protocol) and this (real RPC) now pass under the sandbox.
|
||
|---|---|---|
| .gitea/workflows | ||
| LICENSES | ||
| ci | ||
| cmake | ||
| docs | ||
| packaging | ||
| spike | ||
| src | ||
| test | ||
| .clang-format | ||
| .clang-tidy | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .pre-commit-config.yaml | ||
| CMakeLists.txt | ||
| CMakePresets.json | ||
| CODEOWNERS | ||
| CONTRIBUTING.md | ||
| README.md | ||
| REUSE.toml | ||
| SECURITY.md | ||
| TRADEMARK.md | ||
| vcpkg-configuration.json | ||
| vcpkg.json | ||
README.md
FreePDFEditor
A cross-platform native desktop PDF editor with a true WYSIWYG interface and full content editing: edit existing text with reflow, replace images, manipulate vector objects, plus annotations, forms, signatures, and page assembly.
- Stack: C++20, Qt 6.7 (Widgets shell, custom canvas), CMake, vcpkg.
- License: GPL-3.0-or-later (see
LICENSES/GPL-3.0-or-later.txt). Qt is dynamically linked under LGPL-3.0. - Targets: Windows 10+, macOS 12+ (Intel + Apple Silicon), Linux (X11/Wayland).
This repository contains the source and build infrastructure. The full engineering plan and
roadmap live in docs/plan.md; architecture decision records are under
docs/adr/.
Status
Pre-M0. This is the initial repository scaffold described in §14 of the plan: CMake + vcpkg skeleton, governance docs, CI matrix, packaging pipeline, an empty-window application shell, the pixel-diff harness scaffolding, and the Spike A (QPDF verbatim round-trip) harness. The feasibility spikes that gate the project run on top of this scaffolding.
Building
cmake --preset default
cmake --build --preset default
ctest --preset default
See CONTRIBUTING.md for the full build setup, gate matrix, and coding
standard.
Source control
All code is developed on the project Gitea instance at https://gitea.lm.je/ai-ad4/freepdfeditor
per docs/plan.md §13. See SECURITY.md for vulnerability reporting.