Before any AI, Redact has to do its job by hand. In this lesson we run the base template, look at how it’s put together, and use it to cover a document ourselves. Every later lesson adds AI on top of this, and this manual path has to keep working the whole way. Checkpoint: redact-01-starter

Why start without AI?

A suggestion is only useful if there’s something to do with it. So the first thing Redact needs is the part a person uses: upload a file, draw boxes, check the result, save it. When the AI arrives in lesson 06, it slots into a workflow that already works, instead of being the workflow. That also gives us a fallback for free. If the local model is slow, missing or wrong, the person can still finish the job by hand.

What “flattened” means

Covering text in a PDF editor usually just draws a black rectangle on top. The text underneath is still in the file: anyone can select it, copy it, or delete the rectangle. That’s how lawyers for Paul Manafort leaked a “redacted” court filing in 2019. Redact avoids that by never editing your file. It turns each page into a picture, paints the black boxes into the pixels, and saves that picture as a new file. There’s no text layer left to copy, and your original stays exactly as it was. More on this: About flattened copies.

1. Get the starter

If you did lesson 00, stop the app and the service with Ctrl+C in both terminals. You’re still inside redact-course/redact, so go up one folder and start your branch there:
Otherwise, clone it first:
You now have your own branch, my-redact, starting at the first lesson. Each later lesson adds its commit to this branch with git cherry-pick.

2. Install and start the app

The app lives in the redact folder of the course branch:
If you came from lesson 00, npm ci reinstalls the packages for the starter, which has no AI packages yet. When the terminal prints Ready, open localhost:3101. You’ll see What do you need to share safely?

3. Take a tour of the code

The whole workflow lives in src/features/redact/. Five files do the real work: file-policy.ts sets the limits.
Files over 25 MB or PDFs over 25 pages are refused up front, with a plain message, before anything heavy happens. Every page is drawn at most 2400 pixels on its long side, which keeps memory predictable. rasterize.ts makes every page a PNG. An image becomes one page; a PDF is drawn page by page with PDF.js, and the code checks the pages come back in order:
From here on, nothing touches the uploaded bytes again; everything works on these page images. workflow-domain.ts stores marks as percentages.
x, y, width and height are percentages of the page, not pixels. That’s why a mark stays in the same place whether the page is shown small on your screen or drawn full size for the copy. source is manual for now; in lesson 07, marks you accept from a suggestion become accepted. DocumentSurface.tsx turns your drag into those percentages:
safe-copy.ts turns them back into pixels and paints them. The conversion is a small function called markToPixelRect:
The left and top edges round down and the right and bottom round up, so a box never comes out a pixel short of what you drew. Then the page image is drawn and each box is filled black:
For a PNG upload the result is saved as a PNG. For a PDF, each flattened page is embedded as a picture in a brand-new PDF, so the copy has no text layer at all.

4. Cover a document by hand

Select Choose a file and pick public/test-files/redact/chat-private.png. It’s fictional test material. Choose Private, leave Additional direction empty, and select Prepare for review. On Original, drag across Maya Chen. A mark appears, and the same area turns black in Safe-share preview. Drag the mark to move it, and drag a corner to resize it. Do the same for the email, the address, the phone number, the account number and the reference. Then select Done, then Save copy.

5. Check the copy

Open the downloaded chat-private.redacted.png outside the browser. The six values you covered are solid black; the text around them is still readable. Open public/test-files/redact/chat-private.png too: it hasn’t changed.

6. Try a PDF

Select Redact another document and upload public/test-files/redact/statement-multipage.pdf. Add one mark on page 1, select Next →, add one on page 2, then Done and Save copy. Open statement-multipage.redacted.pdf. It has two pages, each with your mark. Try to select text in it: you can’t, because every page is now an image.

If something goes wrong

  • npm ci fails: check node --version is 22.17 or newer, then run it again.
  • The upload is refused: read the message; it’s one of the limits in file-policy.ts, such as “File must be 25 MB or smaller” or “Choose a PNG, JPEG, or PDF file”.
  • A locked PDF: Redact can’t open password-protected PDFs and says “Choose an unlocked PDF”; unlock it first.

7. Check your work

Stop the dev server with Ctrl+C, then run:
Each should finish without errors, and npm test reports Tests 33 passed (33).

So what just happened?

You ran a complete, manual privacy tool and saw the three rules it’s built on. The original file is never changed, every mark is placed by a person, and the output is a new flattened file. Those rules hold for the rest of the course.

Try it yourself

Upload public/test-files/redact/chat-private.jpg, cover only the account number, and save it. Open the result and zoom in on the edges of the black box: it covers the whole number, with no half-pixel gap. That’s the rounding in markToPixelRect at work.

Where to next

Next: Read a page with local OCR (lesson 02)