Skip to content

daniel-alexander4/nib

Repository files navigation

Nib

License: AGPLv3 Go Platforms 100% local cgo-free

A complete PDF toolkit that runs entirely on your own machine. Fill any form, make scans searchable with on-device OCR in 41 languages, truly redact, compare revisions visually, sign and timestamp, convert to and from Office and PDF/A, pull tables out to spreadsheets, and reshape pages — all from a desktop-style app or a scriptable command line.

And it's yours: free and open source under the AGPL, a single self-contained binary with no installer, no account, no subscription, and no cloud. Nothing you open, type, or sign ever leaves your computer, and your documents and signing identity live in an encrypted vault only your SSH key can open.


How Nib compares

How Nib's feature set lines up against the three best-known PDF editors. Legend: ✅ built in · 🟡 partial or limited · ❌ not available.

Capability Nib Adobe Acrobat Pro Foxit PDF Editor PDF-XChange Editor
Fill forms — including flat & scanned
Detect fields, turn a scan into a fillable form
On-device OCR (41 languages)
True redaction + pattern / PII search-and-redact
Visual and text document compare 🟡
Edit existing text with paragraph reflow 🟡 (cover & replace)
Digital signature with your own certificate
RFC-3161 trusted timestamp
OpenTimestamps (Bitcoin) proof of when
Peer-to-peer co-signing, no server
PDF/A archival export
Office ↔ PDF conversion † 🟡
Table → spreadsheet (XLSX / ODS / CSV) 🟡
Merge, split, rotate, crop, N-up, Bates, page labels
AES-256 encryption
Scriptable command line / batch a folder 🟡
Runs 100% offline, no account, no telemetry 🟡
Free & open source (AGPLv3)
Single self-contained binary, cross-platform ‡
Price 🟢 Free 🔴 Subscription 🔴 Paid 🟡 Free tier

† Office conversion uses LibreOffice if it's installed — optional, detected at runtime, never bundled. ‡ One portable binary for Linux / macOS / Windows (PDF-XChange Editor is Windows-only).

Acrobat, Foxit and PDF-XChange are mature commercial editors that do plenty Nib doesn't aim to — full WYSIWYG content editing, prepress, cloud collaboration. The table is about the jobs Nib does cover, and where it works differently.

Choose Nib when you want to own your tools: work entirely offline with no account or subscription, script PDF jobs from the command line, keep every document and signature on your own machine, and stand on a transparent AGPLv3 codebase you can read and rebuild. Reach for a commercial editor when you need full WYSIWYG layout editing or prepress.


What you can do

Fill any PDF — even ones without form fields

Nib fills normal interactive forms (AcroForm) directly. For flat, scanned, or print-only forms with no fields, the Text tool lets you type anywhere on the page.

Smart field detection

Press Detect and Nib scans the page, then drops fillable widgets where they belong:

  • Blank lines → a text box above every fill-in rule, including the faint light-gray lines on modern forms.
  • Boxes → a field inside each empty box (boxes that already contain text are skipped).
  • Tables → one input per blank cell.
  • Checkboxes → click to check.
  • Circle-the-answer choicesY / N, option sets near a "(circle one)" note, pipe-separated lists on their own ($5 | $10 | $25), a labelled run of options (Type of Membership: Youth Teen Adult …), and repeats of a choice the "(circle one)" governs (every Male / Female on the page, not just the first).

It's a smart proposal, not magic — move, resize, retype, or ignore anything it suggests.

Turn a flat scan into a fillable form

Run Detect on a flat or scanned form, then File → Save as → Save as fillable form… to emit a real interactive AcroForm PDF: every detected text box and checkbox becomes a live, fillable field (with proper appearance streams, so it works in Adobe and any browser), dropped right onto the original page — the scan itself is untouched. Need a dropdown or a radio-button group? The Edit-tab Dropdown and Radio tools let you draw one and type its choices; they're authored as a real combobox / radio group. A radio group lays its buttons out to match the box you draw — a wide box runs them across, a tall box stacks them down. A quick step lets you name each field so the collected data is meaningful — Nib pre-fills each name from the field's own label on the page (e.g. a box beside "First Name:" is named first_name), which you can edit (focus a row to highlight that field). On an image-only scan there's no text to read, so fields stay field_N unless you've run OCR first. The opposite of flattening: instead of baking your answers in, you publish a blank form for others to fill.

Mail-merge a form from a spreadsheet

Got a fillable form and a spreadsheet of records? File → Fill from spreadsheet… picks a CSV and fills the form once per row, handing you a ZIP with one PDF per row. The CSV's first row is the form's field names (export them with File → Export → Form data (CSV) to see the exact names); checkbox fields take true/false/yes/1. It runs entirely on your machine — the same engine as the nib fill command line, just point-and-click. (The form needs real fillable fields; on a flat scan, run Save as fillable form… first.)

Exchange form data as XFDF

Trading form data with Acrobat or Foxit? Nib reads and writes XFDF, the XML form-data interchange format both speak. File → Export → Form data (XFDF) saves the open form's values; File → Import form data (XFDF)… fills the form from an .xfdf file and saves the result. On the command line it's nib export-xfdf IN -o OUT.xfdf and nib fill IN --data DATA.xfdf -o OUT. Hierarchical (dotted) field names are preserved as nested fields; like every Nib operation it runs entirely on your machine.

Convert to PDF/A for archiving

Need a document that archives will still open decades from now? File → Export → Archival PDF (PDF/A-2b)… converts the open document to a PDF/A-2b candidate: Nib embeds an sRGB output profile, writes the PDF/A identification, and removes active content and attachments. On the command line it's nib pdfa IN -o OUT.

Nib is honest about what it can and can't do here. The fast built-in converter is pure Go, but PDF/A requires every font embedded and device-independent colour, and Nib can't add a missing font or convert colour itself — so a document with non-embedded fonts, DeviceCMYK colour, or encryption is refused with the specific reason rather than turned into a file that falsely claims conformance.

For those harder documents, Nib can use Ghostscript if it's installed on your system — it re-embeds fonts and converts colour, the general conversion pure Go can't do. It's strictly optional: when Ghostscript is present the dialog offers a "Convert with Ghostscript" button (and the CLI a --gs flag); when it isn't, the built-in converter is used and the feature simply isn't offered. Ghostscript is detected at runtime and never bundled, so Nib stays a single pure-Go binary. (Note: that path runs Ghostscript over your PDF; it executes under Ghostscript's sandbox, but it is processing the document, so only enable it for files you'd open anyway.)

Either way, because no pure-Go PDF/A validator exists, Nib can't certify the result itself — it produces a candidate you should verify with veraPDF before relying on it for archival.

Open an office or Markdown document

File → Open & convert to PDF… opens a Markdown, Word, Excel, PowerPoint, or OpenDocument file (.md/.markdown, .docx/.doc/.odt/.rtf/.txt, .xlsx/.xls/.ods/.csv, .pptx/.ppt/.odp) by converting it to PDF — it then becomes the open document, ready to mark up, fill, sign, or save.

Markdown converts natively, in pure Go, with no external tool: headings, emphasis, lists, blockquotes, and code blocks render as crisp selectable text (Latin scripts; images and tables are skipped). For office formats, rendering office layout is something pure Go can't do, so Nib shells out to LibreOffice in headless mode; like Ghostscript it's strictly optional and detected at runtime (never bundled, so Nib stays a single cgo-free binary). When LibreOffice isn't installed the file picker narrows to Markdown, and the CLI verb reports it's missing:

nib office report.docx -o report.pdf
nib office notes.md -o notes.pdf

Each conversion runs in its own temporary directory with an isolated LibreOffice profile and a timeout. As with Ghostscript, LibreOffice interprets the document (a large surface, and office files can carry macros — headless LibreOffice doesn't auto-run them at the default security level), so only convert files you'd open anyway. Fidelity is LibreOffice's: complex documents may not convert pixel-perfectly.

Circles & pills for multiple choice

Choosing an option marks it the way a person would: a circle around a single letter (or Y/N), or a pill around a whole word — baked cleanly into the PDF on save.

One-click stamps

Quick-stamps for the things you reach for most — today's date, "Approved", and a checkmark. Drop one on, drag to place, resize to fit.

Signing flags — sign, date, initial, name, title, company

Filling a form with the same fields on every page? Run Detect to find the blanks, then in the Flags tab pick Sign, Date, Initial, Name, Title, or Company and click a blank to flag it — the flag snaps to that line (or click anywhere to place one freehand). Then click each flag to fill it and Nib jumps to the next one: a date flag stamps today's date; a sign/initial flag drops your signature or initials, picked from the Library once and reused; and a name/title/company flag fills from your autofill profile (add the value once under Edit → Edit autofill profile and every such flag reuses it). Each fill is sized to fit its flag. These place a visible signature for filling out the form; the cryptographic signature is still the separate Finalize & sign step.

Send it to someone else to sign (like DocuSign, without the cloud). Plant the flags, then click Signing marks completed to lock the document: flag placement and every editing tool switch off and the flags freeze, so the layout can't drift before it goes out (toggle Edit marks again if you still need to change it). Click Save for signing… and email the saved file. The flags travel inside that one PDF — no sidecar to lose — so when the recipient opens it in Nib it opens locked in signing mode: they can fill but not edit. A banner offers Start, walks them flag-to-flag with Next field, and ends with Mark complete & sign — one step that flattens the filled document and applies the recipient's own tamper-evident certification signature, saved as <doc>.signed.pdf. A Finish & sign button stays available the whole time, so they can complete even if they filled a flag from the Library or left one blank. Send the file as-is: printing it or re-exporting it through another app strips the flags.

Skip email entirely — send it Nib-to-Nib. Instead of mailing the file, Collaborate → Originate → Send a document to a peer… hands it straight to a pinned peer over the same encrypted, no-cloud channel co-signing uses (both of you online; they pick Receive a document… first). Received files save into ~/nib — a flagged document waiting for you under to-sign/, a finished signature under signed/ — so the round trip is: you send the flagged file, they fill and Mark complete & sign, they send it back, and the signed copy lands in your ~/nib/signed/. Each hop needs both peers online; nothing is stored on a server in between.

Signatures & images, stored securely

  • Draw your signature on a pad; it's saved as a clean transparent PNG, so it sits on the line instead of inside a white box.
  • Upload a photographed or scanned signature and Nib knocks the white paper background out to transparency for you — preview it, tune the threshold, and it sits on the page instead of inside a white box. (Add logos and other images the same way; uncheck the box to keep an image's background as-is.)
  • Everything lives in an encrypted image library inside your vault. Click to place, then drag and resize anywhere.

Annotate

Highlight text, draw freehand, add free text boxes, and drag a border — a colored outline with no fill — anywhere on a page; works on flat PDFs too. Highlights and borders are any color: pick one from the swatch row (your last five colors stay one click away) or open the picker for a new shade. A border's thickness is yours to set, in points. Draw Shapes — a line, an arrow, a rectangle, or an ellipse (rectangles and ellipses can be filled) — in any colour and thickness; they bake into the page so they show in every viewer. Drop a Note to leave a comment — a sticky note you place, type into, and drag; on save it becomes a real clickable sticky-note annotation (an icon whose popup shows your text) that any PDF viewer can read.

Edit existing text

Edit → Edit text, then drag a box over baked-in text. Nib covers it with a fill sampled from the background and drops an editable box prefilled in the original's size, colour, and closest font (serif / sans / mono, bold, italic) — so a fix reads like an edit, not a patch. The page stays sharp and vector. The original text stays underneath (it's a visual edit) until you press Remove originals, which flattens just the edited pages so the old text is gone for good — or until you flatten / finalize the whole document.

OCR — make a scan searchable

Got a scanned PDF that's just images? Edit → OCR reads the text on every page and adds an invisible text layer underneath the scan, so the page still looks exactly the same but the text is now selectable, copyable, and findable (and shows up in Find). The OCR runs entirely on your machine — the recognition engine is built into Nib (no install, no cloud, nothing leaves your computer) — so it works offline like everything else. Pick the scan's language from the dropdown next to the button (English, French, German, Spanish, Italian, Czech, Dutch, Hungarian, Polish, Portuguese, Romanian, Swedish, Turkish, Vietnamese, Russian, Ukrainian, Bulgarian, Serbian, Macedonian, Belarusian, Greek, Thai, Hindi, Bengali, Marathi, Nepali, Sanskrit, Tamil, Telugu, Kannada, Malayalam, Gujarati, Punjabi, Arabic, Persian, Urdu, Hebrew, Chinese (Simplified and Traditional), Japanese, Korean) for best accuracy; full Unicode comes through either way (accents, quotes, dashes, and non-Latin scripts — including right-to-left Arabic and Hebrew, which stay searchable in logical order, and CJK). A quality selector next to it trades speed for accuracy: Fast (200 DPI) is the quick default; Best (300 DPI) renders the pages larger so small or faint text reads more reliably — a bit slower, worth it when accuracy matters. The OCR'd document also gets its language tag set (PDF /Lang) so a screen reader announces it in the right voice. It's undoable, too.

Real redaction

Draw redaction boxes and press Apply. Nib re-renders those pages flat so the content underneath is actually gone — not just hidden behind a black rectangle. (Verified: a redacted page exposes no hidden text or form field.)

Don't want to hunt for every occurrence by hand? Redact text… finds them for you: type a word or phrase, and/or tick a built-in pattern — SSN, email, phone, card number — and Nib marks every match in the document as a redaction box. Review the boxes (remove any you don't want), then press Apply to flatten them for real. It reads the text layer, so it works on any text-based PDF — and on a scan once you've run OCR. Matches split across the page's text runs are still caught; matches that wrap across a line break are not (rare for the patterns).

Compare two versions

Wondering what changed between draft v3 and v4 of a contract? File → Compare… lets you pick a second PDF and compare it against the open document three ways, switchable from the toolbar at the top of the dialog:

  • Text — diffs the two text layers word-by-word: removed text struck through in red, additions in green, inline. Tells you what changed. Works on any text-based PDF (a scan with no text shows nothing — run OCR on it first, which makes it diffable). Reading order follows each document's text stream, so it's most reliable comparing two versions produced by the same tool.
  • Side-by-side — renders a page of each document next to each other.
  • Differences — paints a per-pixel difference map: regions that changed are highlighted in red, with the percentage of the page that differs. Tells you where it changed — and because it compares rendered pixels, not text, it works on scanned PDFs too. Pages that differ in size between the two documents are shown side by side instead (normalise page sizes first for a difference map).

In the visual modes Nib auto-aligns the pages: it matches each page of one document to its counterpart in the other, so if a version inserted or deleted a page the comparison stays lined up instead of every later page reading as changed. The outer ‹ › then step through the matched pairs, an added or removed page is shown on its own with a banner, and the toolbar notes how many pages were added/removed. A page that simply changed position is recognised as a move — its banner says where it went (and where it came from) rather than reporting it as one page deleted and another added, so a reorder reads as a reorder. For text-based PDFs alignment is instant — it matches on the page text. For scanned documents (or a scan compared against a digital original), where there's no text to match on, Nib instead renders every page and aligns on a visual fingerprint, so two scanned revisions line up too; lightly-edited pages stay paired (the difference map then shows what changed) while a genuinely new or removed page becomes a gap. That render pass shows brief progress; uncheck Auto-align at any time to page the two sides manually. Like everything else it runs entirely on your machine — the second PDF never leaves your computer.

Scan for hidden content

Secure → Scan for hidden content lists what's lurking in a PDF that you can't see on the page: auto-run hooks (OpenAction, additional actions), JavaScript, risky link/widget actions (launch a program, submit a form, open a URL), embedded files, optional-content layers, XMP metadata, and the document's identifying properties (author, title, creator…). Then remove it four ways, strongest fidelity-preserving first:

  • Strip active content — neutralises every auto-run hook, script and risky action while keeping the page text and layout intact.
  • Strip identifying metadata — clears the document properties (author, title, creator, subject, keywords), deletes the XMP metadata, and regenerates the document's tracking identifier, leaving the visible content untouched. (pdfcpu re-stamps a generic producer and the current date on write, so the file names Nib, not you.)
  • Remove files & media — deletes only embedded files and media annotations, leaving all other interactivity untouched.
  • Flatten to images — the guaranteed-inert floor: turns every page into an image so nothing active can remain (selectable text is lost).

If a strip can't produce a sound document it's reported and your open document is left untouched, so you can step down to the next method safely. Any removal produces a new, unsigned copy — save it to keep the cleaned version.

Add password protection

Secure → Add password protection… saves a separate AES-256 encrypted copy that needs the password to open (the same password opens and owns the file). You type it twice — Nib can't recover a forgotten one. Your open document is left unprotected and editable; the protection is a standalone export, never combined with signing (encrypting rewrites the file, so the copy won't carry a signature). Headless: nib encrypt IN -o OUT --password-file FILE (or $NIB_PDF_PASSWORD).

Remove password protection

Open a password-protected PDF and Nib asks for the password, then unlocks the working copy so you can view and edit it — saving keeps it unprotected. For a PDF that opens but blocks editing/printing/copying (owner-password restrictions), Secure → Remove password protection… strips those flags. Both produce a plain, unrestricted document, the same as qpdf --decrypt. This is for a document you can already open or are authorized to edit — Nib only tries the password you type and never guesses or recovers one. Unlocking rewrites the file, so an existing digital signature won't survive — and because an encrypted document can't be inspected until it's unlocked, Nib tells you right after if unlocking invalidated a signature it turned out to carry.

Attachments

Secure → Attachments lists the files embedded inside a PDF. Extract any one to save it out, or Attach a file… to embed a new one (a source file, a README, anything). Adding a file replaces the open document with a new, unsigned copy — save it to keep the attachment.

Sign & finalize — tamper-evident

Finalize & sign seals the document with a certification signature from an identity kept in your vault and bakes in a visible watermark — a preset like DRAFT, CONFIDENTIAL, FINALIZED, COPY, or VOID (or your own text), with adjustable opacity, colour, size, and angle and a live preview — optionally with a trusted RFC-3161 timestamp. Any later edit breaks the signature — that's the point. Export your public certificate so others can verify it's you.

Sign with your own certificate. By default Finalize uses Nib's self-signed identity (integrity, not third-party trust). If you have a CA-issued credential, import it under ⚙ → Identity & peers → Signing certificate (a PKCS#12 .p12/.pfx file + its passphrase); then Finalize offers a Sign as choice and signs with that certificate and its chain, so a verifier who trusts the issuing CA sees a trusted signature. The certificate is used only for solo Finalize — your Nib identity and pinned peers are untouched, and co-signing always uses the Nib identity. You enter the .p12 passphrase at each signing; the key is never stored unencrypted. (Hardware tokens / PIV / system keystores aren't supported — they'd require cgo, and Nib ships as one pure-Go static binary.)

Every PDF you open also shows a signature badge: untampered, modified, or unsigned. Click details for the full picture — every signer (not just the first), and whether each signing time is backed by an independent timestamp authority or merely stated by the signer.

Timestamp with OpenTimestamps — prove when

Timestamp (OpenTimestamps) creates a small .ots proof that anchors your document's hash to the Bitcoin blockchain, so anyone can later confirm the exact file existed, unaltered, by that time — with no certificate, no account, and no trust in Nib. Only a SHA-256 hash of the document is sent to the public OpenTimestamps calendar servers; the document itself never leaves your machine. The .ots is a sidecar — it never touches the PDF, so it can't disturb a signature — keep it alongside that exact file. The proof becomes fully verifiable a few hours after the next Bitcoin block confirms it. It proves when a document existed, not who wrote or signed it — that's what signing and co-signing are for.

Verify a timestamp checks an .ots against the open document right inside Nib: it confirms the proof is for that exact file and reports the Bitcoin block time it was anchored at. Only a public block height is looked up over the internet — never the document or its hash — and the block is confirmed against three public block explorers run by independent operators, at least two of which must agree, so no single explorer can spoof a result (point it at your own Esplora endpoint to verify trustlessly). If the proof was still pending (stamped but not yet anchored when you last saved it), verifying it once a Bitcoin block has confirmed it lets you save the now-complete proof — a self-contained .ots that no longer needs any calendar server to verify, ever. You can also verify with any other OpenTimestamps tool (e.g. opentimestamps.org) — the proof is standard.

Co-sign with a peer

Two people can sign the same document, each attesting — in a visible block and a cryptographically-signed reason — that they accept the other's identity. Nib pins that identity by its key fingerprint, which you compare once over a channel you both trust (read it aloud on a call, or paste it across a secure chat) under Identity & peers; every fingerprint has a Copy button for that comparison.

There are two ways to exchange the document:

  • Pass the file — you co-sign, then send the PDF to the other person (email, USB, Signal); they co-sign and send it back. Nothing but the file moves, and the result verifies on its own, with no server in between.
  • Live, over an encrypted channel — co-sign in real time without passing a file. One person arms to receive (Collaborate → Receive → Receive a live co-signature…), the other dials in (Collaborate → Originate → Co-sign live with a peer…). The connection is mutually authenticated TLS, pinned to each other's identity key: an unpinned peer is dropped at the handshake, before any document bytes are exchanged. The receiver reviews the exact document and accepts or declines — nothing is signed without that consent — and the session tears down after a single exchange.

Reachability for live sessions. Nib runs no relay, rendezvous, or NAT-traversal infrastructure of its own (that would mean a server, and Nib has none). The dialing peer reaches the receiver's armed listener directly, so the receiver makes their chosen host:port reachable one of two ways:

  • Port-forward the chosen port on their router to their machine, or
  • Share a private network you both already trust — a VPN such as Tailscale or WireGuard — and bind / dial the address it hands you.

If the receiver is behind CGNAT (common on mobile and some ISPs), port-forwarding won't work — use the VPN path. The security model doesn't depend on how you reach each other: the pinned-key handshake holds over any transport.

Pages & export

Combine PDFs… (File tab) assembles several documents into one: add the files, arrange them with ↑ ↓, and they merge top-to-bottom into a new document — then reorder individual pages across them by dragging thumbnails. It works even with nothing open, and the result is a new, unsigned document (Save As to keep it).

Changed your mind? ↶ Undo / ↷ Redo (Ctrl+Z / Ctrl+Shift+Z, on the Edit tab) step back and forth through document operations — rotate, delete, reorder, crop, split, page numbers, outline and metadata edits — for the open document. The same Ctrl+Z also undoes overlays you place — a stamp, border, shape, note, cover-edit, or sign/date/initial flag — and dragging or resizing one, so one keystroke walks back your most recent change whichever kind it was. History clears when you open another file or run a content-destroying step (redaction or flatten can't be undone). While you're drawing pdf.js annotations (text boxes, highlights, ink) those keep pdf.js's own Ctrl+Z; typing in a field uses your browser's normal undo.

Rotate, delete, append, and reorder pages — drag a page's thumbnail in the sidebar to move it where you want. Rotate every page at once with Rotate all ↺ / ↻ on the Edit tab, or hover a thumbnail to rotate (either direction) or delete a single page. Shift- or Ctrl/Cmd-click thumbnails to select several at once, then rotate, delete, or move the whole selection to the front or back (⤒ / ⤓) from the bar above the thumbnails — or drag a selected thumbnail to slide the whole group to any spot, keeping its order. Extract pages… saves a range (type 1-3, 5) as a new PDF without touching the open document, and Insert blank page drops a fresh page — matching its neighbour's size — after the current one. Duplicate page drops a copy of the current page right after it, and Insert PDF… splices another PDF in before the current page (before page 1 to prepend a cover; use + Append PDF for the end). Page numbers… stamps a running number onto every page at the corner you choose — add a prefix and a zero-pad width for Bates numbering (e.g. ABC + width 6 → ABC000001), or tick "of N" for classic "Page 3 of 10". Page labels… sets the document's logical page numbers — the ones a viewer shows in its page box and thumbnails, as distinct from ink stamped on the page — so front matter can read i, ii, iii while the body reads 1, 2, 3. Add a range per section (from which page, what style — decimal, upper/lower Roman, upper/lower letters, or a prefix-only label — and where its count starts); pages before the first range carry no label. Going the other way, N-up… combines several pages onto each sheet (2-up, 4-up, up to 16) for printing or handouts, in reading order, with an optional border — each sheet keeps the document's page size. Got a document whose pages are all different sizes? Normalize sizes (Edit tab) resizes every page to the document's most common size, scaling each page's content to fit and centring it — a one-click way to make a mixed-size scan or merge uniform. It keeps each page's orientation (a landscape page stays landscape) and the text stays live and selectable. Crop… trims the margins away — draw a box around the part to keep and every page (or just the current one) is cut down to it. The box is taken as a proportion of the page, so a document with mixed page sizes keeps the same relative region on each. It's a re-crop, not a re-render, so quality is untouched; the trimmed-off content is hidden behind the smaller page, not destroyed — use Flatten or Redact to remove it for good. Got a scanned 2-up or 4-up sheet? Split page… (on the Edit tab) cuts the current page into a grid of separate pages — pick the columns and rows, preview where the cuts land, and optionally resize each piece to a full page. Not a clean grid? Split by box lets you split a page by hand — drag a rectangle around each region you want, then Apply box split and the page is replaced by those regions, each as its own page. The new pages all come out the same size (the largest region's, smaller ones centred and padded), so the output is uniform. It's a re-crop, not a re-render, so every piece keeps its original quality.

Edit the outline — the Outline panel (sidebar, File tab) lists a PDF's bookmarks; Edit outline… opens an editor to author them: add a bookmark for any page, rename, delete, and indent to nest (chapters → sections). Bookmarks stay in page order and jump to the top of their page; saving replaces the document's outline.

Split by bookmarks (File → Export) turns one bookmarked PDF into a folder of separate files — one per top-level bookmark, named from the bookmark with an optional prefix. Point it at a scored orchestration and get one PDF per instrument/part in seconds; pick the destination folder, and the open document is left untouched. No bookmarks? Split into files by page range (File → Export) divides the page sequence instead — every N pages, or custom ranges like 1-3, 4-8, 9-10 where each range becomes its own file — into a folder, the open document untouched.

Flatten to a guaranteed-flat PDF, or export pages as PNGs (single or ZIP) and form data as JSON / CSV. Save back over the original, or as a flattened or editable copy. Print the current document — fills, signatures, and all — straight from the File tab through your browser's print dialog.

Reduce file size (File → Save as) shrinks a PDF two ways and shows the before→after size before you save. Optimize is lossless — it strips redundant data and keeps selectable text, but mostly helps bloated files (little effect on scans). Compress re-renders pages as JPEG images at a chosen quality: big savings on scans, but it flattens the document, so selectable text and search are lost — best for scanned/image-heavy PDFs.

Pull the contents out (File → Export): Document text (.txt) dumps the document's text layer to a plain-text file, and Embedded images (ZIP) bundles the pictures inside the PDF into a zip — JPEGs come out as-is, other images are re-encoded as PNG/TIFF. Text extraction reads the existing text layer, so a scanned (image-only) page has nothing to give and contributes nothing (there's no OCR), and complex multi-column layouts may not preserve reading order.

This page's table → spreadsheet (File → Export → This page's table (XLSX), (CSV), or (ODS)) clusters the current page's text into rows and columns and saves it as an Excel .xlsx, an OpenDocument .ods, or a .csv. It's a best-effort extraction of grid-style tables — merged cells, multi-line cells, and irregular layouts may come out wrong, so review the result. Like the text export it reads the text layer (OCR a scanned page first), and like everything else the spreadsheet is built on your machine (no office-suite dependency — Nib writes the minimal file format itself).

Find in the document

Type in the Find box (or press Ctrl/Cmd+F) to highlight every match. Step through them with the ‹ › buttons or Enter / Shift+Enter, and the readout next to the box shows which match you're on out of the total (3/12).

Keyboard shortcuts

  • PageUp / PageDown — previous / next page; Home / End — first / last page.
  • Ctrl/Cmd + + / − / 0 — zoom in / out / fit to width.
  • Ctrl/Cmd + S save, +O open, +F find, +B toggle the sidebar.

Navigation keys stand down while you're typing in a field or a dialog is open, so they never get in the way of editing.

Choose your layout

Pick how the commands are presented from ⚙ Settings → Layout: the classic Menus (File / Edit / View), a compact Toolbar of dropdowns and icons, or Both. The choice is saved in your vault. (Defaults to Menus.)

Dark or light

Tap the sun/moon button in the top-right to switch between the dark (Catppuccin Mocha) and light (Catppuccin Latte) themes. The choice is saved in your vault. (Defaults to dark.)

Private by design

  • Binds 127.0.0.1 only — never reachable from the network; writes are guarded by a per-process CSRF token and a loopback-origin check. The one deliberate exception is a live co-signing session you arm yourself: while armed, Nib opens a single routable listener that accepts only the one peer whose key you pinned, and tears it down after one exchange (see Co-sign with a peer).
  • Your image library, signing identity, autofill profile, and recent files live in one AES-256-GCM vault, encrypted at rest.
  • The vault is sealed to your SSH key: it unlocks at startup with no password. Authorize more than one key to use it across machines, and back it up / restore it fully encrypted. (Lose every authorized key and the vault is unrecoverable — by design.)
  • Passphrase-protected SSH keys are supported. If your unlock key is encrypted with a passphrase, Nib prompts for it at startup and decrypts the key in memory — the key file stays encrypted on disk, so a stolen disk plus key file still can't open the vault without the passphrase. (An unencrypted key keeps the no-prompt startup.) ssh-agent is not used: the vault is unlocked by decrypting to the key, an operation agents don't perform.

Get started

Run from source

go build -o nib ./cmd/nib
./nib [file.pdf]

The whole UI is embedded in the binary — nothing to fetch at runtime. Nib opens its window in your installed Chrome / Edge / Brave / Chromium (app mode), or falls back to a normal browser tab.

The first run opens a short intro explaining what the SSH key protects, then a one-time setup where you either use an SSH key you already have or have Nib create one for you (at a path you can change — works the same on Linux, macOS, and Windows, no key needed up front). That key is what unlocks your vault, so keep it safe and back it up. You can authorize or create more keys later from the ⚙ menu → Manage authorized keys….

Install on Debian / Ubuntu

./install.sh

Builds Nib for your machine, packages a .deb, installs it, and adds Nib to your applications menu (under Office). Run it again any time to upgrade in place.

Other platforms

./build.sh

Cross-compiles a static, cgo-free binary for Linux, macOS, and Windows (amd64 + arm64) into dist/, plus Linux .deb packages. On macOS and Windows you run the binary directly — it's fully self-contained.

A Makefile wraps these: make dist regenerates the third-party notices and runs the cross-compile/package; make install does the same for a local install; make notices regenerates THIRD-PARTY-NOTICES.md on its own.

Staying up to date

A version pill at the top always shows the installed version, colored by update status: yellow — status unknown (no check has run yet, or the startup check is turned off); green — you're on the latest release; red — a newer release exists. At startup Nib asks GitHub for its latest release version and colors the pill accordingly. Clicking the pill checks right now — even with the startup check off — and, when a newer release exists, offers to download the build matching your OS and architecture (a .deb for a package install, otherwise the raw binary).

This is the only call Nib makes on its own, and it's a version query — no document data, no telemetry: your documents never leave your computer. Turn the startup check off from ⚙ Settings → Check for updates on startup (saved in your vault), or set NIB_NO_UPDATE_CHECK=1 to force it off regardless (clicking the version pill still checks either way). Nib only notifies and downloads — it never installs or replaces itself; you apply the update the way you installed (apt / install.sh, or by swapping the binary).

Options

Variable Effect
NIB_ADDR Pin a fixed loopback address (e.g. 127.0.0.1:8791) instead of a random port. Must be loopback (127.0.0.1, localhost, or ::1) — a non-loopback address is refused at startup.
NIB_NO_BROWSER Don't open a window — just serve and log the URL (headless / remote).
NIB_NO_UPDATE_CHECK Disable the automatic startup update check (clicking the version pill still checks).

Command line

Beyond the desktop app, nib runs a handful of operations headlessly — no window, no server — so you can script them or batch a folder. Anything that isn't a known command (a PDF path, or nothing) still opens the app as usual.

Command What it does
nib timestamp FILE… Write an OpenTimestamps proof (FILE.ots) for each file.
nib timestamp --verify FILE… Check each file against its FILE.ots proof.
nib verify [--json] FILE… Report each file's signature integrity.
nib optimize IN -o OUT Losslessly shrink a PDF (or -w FILE… to rewrite in place).
nib merge IN… -o OUT Concatenate PDFs, in order, into one.
nib sanitize IN -o OUT Strip identifying metadata and active content — JavaScript, auto-actions, embedded files (or -w FILE…).
nib sign IN -o OUT --cert ID.p12 Certify a PDF with an imported .p12 identity.
nib rotate IN -o OUT --deg N Rotate pages by 90/180/270° (--pages 1-3,5 to limit; or -w FILE…).
nib pages IN -o OUT --keep SEL Keep/reorder pages (--keep 1-3,5) or delete them (--remove 2,4).
nib split IN --out-dir DIR … Burst into one file per chunk (--every N), range (--ranges 1-3,4-8), or --bookmarks.
nib encrypt IN -o OUT Add AES-256 password protection (--password-file FILE or $NIB_PDF_PASSWORD, required; an already-encrypted PDF is reported, not re-encrypted).
nib decrypt IN -o OUT Remove password protection / owner restrictions (--password-file FILE or $NIB_PDF_PASSWORD; already-plain PDFs pass through).
nib nup IN -o OUT --n N Place N pages per sheet — 2/4/6/9/16… (--border for outlines).
nib normalize IN -o OUT Resize every page to the document's most common page size — make a mixed-size PDF uniform (content scaled to fit, centred; orientation kept).
nib pdfa IN -o OUT Convert to a PDF/A-2b archival candidate (embed sRGB OutputIntent + PDF/A XMP, strip active content). Refuses documents with non-embedded fonts or encryption. Verify the result with veraPDF — Nib can't certify conformance itself.
nib pagenum IN -o OUT Stamp running page numbers or Bates numbering (--prefix ABC --pad 6 --position br --total). --continuous (-w | --out-dir DIR) FILE… threads one counter across a whole file set (multi-file Bates production).
nib pagelabels IN -o OUT Set logical page labels — one --range PAGE:STYLE[:START[:PREFIX]] per section (STYLE = decimal/roman-lower/roman-upper/alpha-lower/alpha-upper/none), e.g. --range 1:roman-lower --range 5:decimal.
nib fill IN --data D Fill a form: a JSON or XFDF record (--data x.json|.xfdf -o OUT, the inverse of Export form data) or a CSV mail-merge (--data rows.csv --out-dir DIR — header row = field names, one filled PDF per row; --name-col COL names each output). Filling removes any existing signature.
nib export-xfdf IN -o OUT Export a form's field data as XFDF — the XML interchange format Acrobat and Foxit read and write (the inverse of nib fill --data x.xfdf).
nib attachments IN [--json] List embedded files; --extract NAME -o OUT pulls one out, --add FILE -o OUT embeds one.
nib outline IN [--json] List the document's bookmark outline (indented by level, or JSON).
nib watch DIR --do OP Run timestamp/optimize/sanitize on each PDF added to DIR, until interrupted.
nib version Print the version.

Commands that produce a PDF write it to -o/--out; timestamp writes a sidecar .ots beside each input and verify prints a report. Flags may go before or after the file arguments. The exit status is non-zero on failure — verify returns 2 when a signature is invalid or absent — so a check drops straight into a script:

nib verify contract.pdf && echo "signature intact"
for f in *.pdf; do nib timestamp "$f"; done
nib sanitize -w *.pdf          # scrub a whole folder in place
nib optimize in.pdf -o - | nib sanitize - -o out.pdf   # compose in a pipeline

For optimize, merge, sanitize, sign, rotate, pages, encrypt, decrypt, nup, pagenum, pagelabels, and fill (JSON mode), a filename of - reads a PDF from stdin, and -o - writes the result to stdout (refused when stdout is a terminal), so the commands chain together.

optimize and sanitize take -w/--in-place to rewrite each file given instead of writing a single -o output — the batch form for a folder. Each rewrite is atomic (written through a temp file and renamed over the original, so a failure never corrupts it) and preserves the file's permissions.

nib sign reads the certificate passphrase from --password-file FILE, the NIB_P12_PASSWORD environment variable, or — when run in a terminal with neither set — a no-echo prompt, so it's never on the command line where other processes could see it. Add --tsa URL to fix the signing time with an RFC3161 timestamp authority.

nib watch DIR --do timestamp|optimize|sanitize runs that operation on each PDF dropped into DIR and keeps running until you stop it (Ctrl-C) — the "process my inbox" / scheduled-job workflow. It polls (no background file-watching dependency), waits for each file to finish copying before acting, and handles each file once. timestamp writes a .ots sidecar; optimize/sanitize rewrite in place. Run nib <command> -h for a command's own flags.


How it works

Nib is a small Go server that embeds the entire single-page UI and the pdf.js engine. Your browser renders and fills the PDF; pdfcpu stamps, flattens, and redacts; pdfsign signs and verifies. Pure Go, no cgo, permissive dependencies only — so it builds to one portable binary per platform.

cmd/nib          entry point — run a headless command, or bind loopback and open the window
internal/cli     headless subcommands (timestamp, verify, optimize, merge, sanitize, sign)
internal/server  HTTP API + embedded UI, loopback-only guard
internal/vault   encrypted store (AES-256-GCM, sealed to your SSH key)
internal/pdfops  pdfcpu stamping / flattening / redaction
internal/sign    signing + signature verification
web/             the single-page UI and the vendored pdf.js engine (embedded)

License

Nib is free software under the GNU Affero General Public License v3.0 — see LICENSE. Copyright © 2026 Daniel Alexander.

Distributed as-is, with no warranty of any kind, to the extent permitted by law (AGPLv3 §§15–16). You may use, study, share, and modify it under the AGPL; derivative works must also be released under the AGPL. Because Nib is AGPL, if you run a modified version to provide a network service, you must offer that version's complete source to its users (AGPLv3 §13).

Nib also incorporates third-party software (Go modules and the vendored pdf.js engine), all under AGPLv3-compatible permissive licenses (BSD, MIT, Apache-2.0). Their required copyright and license notices are collected in THIRD-PARTY-NOTICES.md, regenerated with build/gen-notices.sh.

⚙ Settings & Help → About Nib… shows these in-app — a plain-English account of what a Nib signature does and doesn't prove, plus the licence and third-party notices read straight from the shipped files.

About

Fill, sign, flatten & redact PDFs entirely on your own machine — a single-binary local web app. No cloud, no accounts, no telemetry.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors