Part of epic #40. Depends on #5 (pack subcommand).
Goal
Produce a bundled macOS binary that is notarisable, not just
ad-hoc signable. Mach-O is the awkward case: appending bytes after
__LINKEDIT invalidates a signature. Solution: pre-allocate a
placeholder segment at link time, overwrite it with the archive at
pack time, then re-sign.
Scope
Link-time placeholder
Top-level CMakeLists.txt (macOS branch only):
- Add
FLAPI_RESERVED_BUNDLE_MIB CMake option, default 16.
- Generate a
<build>/_flapi_bundle_placeholder.bin file of N MiB of
zeros.
- Link the
flapi executable with
-Wl,-sectcreate,__FLAPI,__bundle,<path/to/placeholder.bin>.
- Section must be placed before
__LINKEDIT (linker default is
usually fine; assert in CI via otool -l | grep -A1 __LINKEDIT).
Pack-time overwrite
In src/pack.cpp (the macOS branch):
- Detect Mach-O magic on the output file.
- Locate the
__FLAPI/__bundle section (parse load commands).
- If
archive_size > section_size: print a clear error
("Bundle is N MiB; reserved segment is M MiB. Rebuild flapi with
-DFLAPI_RESERVED_BUNDLE_MIB=N+slack."), exit non-zero.
- Otherwise: overwrite the segment in place, zero-pad the slack so
the EOCD still self-locates relative to segment EOF (the EOCD
goes inside the segment, not at file EOF).
- Invoke
codesign --force --sign "$CODESIGN_IDENTITY" <out>. Falls
back to --sign - (ad-hoc) when the env var is unset.
Legacy --macos-append flag
- Explicit opt-in to the trailing-append flow (the
Linux/Windows behaviour) on macOS.
- Ad-hoc signing only; warn that the result is not notarisable.
- Kept for parity with the spike and for users who don't need
notarisation.
Red tests (test/integration/test_self_packaging_macos.py, skipped on non-Darwin)
- Reserved-segment pack succeeds →
codesign --verify <out> passes.
- Bundled binary still self-locates the archive (EOCD inside the
__FLAPI/__bundle section is unchanged).
- Oversized input → non-zero exit with the corrective message.
--macos-append mode still works (signature allowed to be
warning-only).
Green criteria
- All red tests pass on macos-latest.
otool -l <flapi> shows __FLAPI/__bundle exists and is sized
per FLAPI_RESERVED_BUNDLE_MIB.
codesign --verify --deep --strict <flapi-prod> exits 0 on the
reserved-segment path.
- Linux and Windows builds unaffected.
Files
- Modified:
CMakeLists.txt, src/pack.cpp, src/main.cpp (flag
wiring)
- New:
test/integration/test_self_packaging_macos.py
References
- Spike notes on the macOS trade-off — section "Cross-OS reality" in
the spike README.
- The reserved-segment approach is industry-standard (AppImage,
PyInstaller, Wails all use variants).
Part of epic #40. Depends on #5 (pack subcommand).
Goal
Produce a bundled macOS binary that is notarisable, not just
ad-hoc signable. Mach-O is the awkward case: appending bytes after
__LINKEDITinvalidates a signature. Solution: pre-allocate aplaceholder segment at link time, overwrite it with the archive at
pack time, then re-sign.
Scope
Link-time placeholder
Top-level
CMakeLists.txt(macOS branch only):FLAPI_RESERVED_BUNDLE_MIBCMake option, default16.<build>/_flapi_bundle_placeholder.binfile of N MiB ofzeros.
flapiexecutable with-Wl,-sectcreate,__FLAPI,__bundle,<path/to/placeholder.bin>.__LINKEDIT(linker default isusually fine; assert in CI via
otool -l | grep -A1 __LINKEDIT).Pack-time overwrite
In
src/pack.cpp(the macOS branch):__FLAPI/__bundlesection (parse load commands).archive_size > section_size: print a clear error("Bundle is N MiB; reserved segment is M MiB. Rebuild flapi with
-DFLAPI_RESERVED_BUNDLE_MIB=N+slack."), exit non-zero.the EOCD still self-locates relative to segment EOF (the EOCD
goes inside the segment, not at file EOF).
codesign --force --sign "$CODESIGN_IDENTITY" <out>. Fallsback to
--sign -(ad-hoc) when the env var is unset.Legacy
--macos-appendflagLinux/Windows behaviour) on macOS.
notarisation.
Red tests (
test/integration/test_self_packaging_macos.py, skipped on non-Darwin)codesign --verify <out>passes.__FLAPI/__bundlesection is unchanged).--macos-appendmode still works (signature allowed to bewarning-only).
Green criteria
otool -l <flapi>shows__FLAPI/__bundleexists and is sizedper
FLAPI_RESERVED_BUNDLE_MIB.codesign --verify --deep --strict <flapi-prod>exits 0 on thereserved-segment path.
Files
CMakeLists.txt,src/pack.cpp,src/main.cpp(flagwiring)
test/integration/test_self_packaging_macos.pyReferences
the spike README.
PyInstaller, Wails all use variants).