diff --git a/zddc/internal/convert/convert.go b/zddc/internal/convert/convert.go index 702bd76..5a9782d 100644 --- a/zddc/internal/convert/convert.go +++ b/zddc/internal/convert/convert.go @@ -210,10 +210,17 @@ func ToPDF(ctx context.Context, source []byte, m Metadata) ([]byte, error) { // required because the container drops CAP_SYS_ADMIN; the threat // model is "malicious markdown drives chromium RCE", contained by // --network=none + --cap-drop=ALL + --read-only + tmpfs. + // + // --disable-dev-shm-usage: without this, chromium tries to allocate + // shared memory under /dev/shm, which our --read-only container + // can't write to. The flag tells chromium to fall back to /tmp, + // which is a writable tmpfs (sized in runner.go). Standard fix for + // chromium-in-container; required by every CI/headless setup. cmd := []string{ "--headless", "--disable-gpu", "--no-sandbox", + "--disable-dev-shm-usage", "--user-data-dir=/tmp/chrome", "--no-pdf-header-footer", "--virtual-time-budget=10000", diff --git a/zddc/internal/convert/runner.go b/zddc/internal/convert/runner.go index 2fdb53e..550129b 100644 --- a/zddc/internal/convert/runner.go +++ b/zddc/internal/convert/runner.go @@ -244,7 +244,11 @@ func (cr *containerRunner) Run(ctx context.Context, image string, stdin []byte, args = append(args, "--network=none", "--read-only", - "--tmpfs=/tmp:size=128m,exec", + // /tmp must be large enough to host chromium's shared-memory + // fallback (--disable-dev-shm-usage redirects /dev/shm writes + // here) plus the user-data-dir. 256 MiB is plenty for the + // HTML→PDF flow; pandoc itself uses almost none. + "--tmpfs=/tmp:size=256m,exec", "--tmpfs=/run:size=4m", fmt.Sprintf("--memory=%dm", memMiB), fmt.Sprintf("--cpus=%s", cpus),