Every page served inside a KXCO Meridian data room is watermarked on the server, for the one person reading it, at the moment they ask for it. The stored file is never sent. What leaves is an image composited against that reader's identity, so a page that turns up somewhere it should not can be traced to whoever opened it. This is a note about the week we spent getting that mark out of the reader's way without making it useless, and the delivery rebuild that turned out to be the bigger problem.
01Two jobs, one page
The mark has two layers, and they are doing different work.
The first is a dense grid of small rotated text repeating a per-reader code. That code is what makes a leaked page attributable. It is deliberately hard to remove by cropping, because it is everywhere.
The second is a sparse set of large diagonal lines carrying the issuer, the reader's address, their IP and the timestamp. Three of them per page. That layer is for a human looking at a leak and asking who and when.
Both are composited server side on every request. Nothing about the mark exists in the stored document, so there is no clean copy to accidentally serve and no version of the file that can be shared without it.
02The test that decides
The mark is not judged by eye. It is judged by an automated test that simulates the realistic attack: somebody points a phone at a monitor and sends the photograph on.
The test takes a clean page and a marked page, puts both through what a camera and a messaging app would do to them, and measures what survives. That means downscaling to forty per cent, pushing the contrast the way any recipient would, and re-encoding as a low quality JPEG. Then it measures coverage.
Coverage is the number that matters, and it is worth being precise about why. The obvious measure is the mean difference between the clean page and the marked one. That is the wrong measure, and it took a while to see it. A document page is mostly white space, so ink on a few per cent of the pixels averages down into noise and the number reports "faint" no matter what the mark actually does. Coverage instead counts the proportion of pixels shifted by more than six grey levels. It asks the question you actually care about: is there ink here that somebody could read.
The floor is one per cent after the photograph treatment. Below that, we do not consider the page attributable, and the build fails.
03Why fainter failed
A recipient told us the mark was making the body text hard to read. The obvious response is to lighten it, so that is what we tried first.
The fine grid ran at a fill opacity of 0.13 with a 0.10 white stroke. We took it to 0.09 and 0.06. The page looked better immediately. The photograph test came back at 0.45 per cent against a floor of 1. We split the difference at 0.11 and 0.08 and got 0.68 per cent. Still failing.
That is the finding, and it is not intuitive, but "just make it darker" is not the lesson either. An earlier round of tuning on the same test had already produced a result that looks backwards: raising the fill while lowering the white stroke behind it made the mark harder to recover, not easier. Fill 0.075 with a 0.06 stroke left 0.91 per cent surviving; fill 0.11 with a 0.05 stroke left 0.36.
The reason is what the contrast push does. It maps each pixel to roughly 1.35 times its value minus 28, so on a light document the background saturates to pure white, and a faint dark glyph saturates along with it, leaving nothing to measure. The white halo drawn behind each character is what survives that, because it moves pixels in the opposite direction. The halo is load-bearing forensics rather than decoration, and fill and stroke have to move together.
04Density is what a reader feels
The lever that worked was the other one. What a reader experiences as clutter is not how dark each tile is, it is how many tiles cross a line of text.
The grid was repeating every 190 by 110 pixels, which puts two tiles across a typical line of body copy. Widening it to 230 by 135 puts one there instead, cutting the tile count to about two thirds without touching the ink at all. Coverage stayed comfortably above the floor because the tiles that remain are just as dark.
We went further than that and it broke: 270 by 160 dropped under the floor, and 240 by 140 passed at 1.06 per cent, which is too thin a margin to hold across different fonts and page designs.
Then the better idea. If intensity is what the camera needs and density is what the reader resents, move the forensic load onto the layer with fewer, larger marks. We took the fine grid down to 0.09 and 0.06, the setting that had failed on its own, and pushed the three large identity lines up from 0.07 to 0.14. Coverage after the photograph treatment came back at 1.19 per cent, the healthiest margin of anything we tried, with the layer the reader actually notices at its lightest.
Three big lines survive a phone camera better, per unit of ink, than hundreds of small tiles do. That is the whole lesson.
05The delivery problem underneath
While measuring the mark we found the larger problem. Readers had been telling us pages arrived slowly and sometimes never arrived at all, with no error and no indication anything was still working.
Pages are stored as 150 dpi PNGs. A landscape slide is 3000 by 1688 pixels and between two and a half and five megabytes. Every single request was fetching that file from object storage, decoding it, compositing the watermark, re-encoding it as PNG and sending the whole thing. On a single core machine, at the throughput we actually observed, that is four to five seconds a page. Put two or three people in the room at once and pages queue behind each other until some of them never land.
Serving JPEG instead of PNG, at quality 85 with full chroma sampling and capped at 2000 pixels wide, took a real slide from 4.79 MB to 0.45 MB and encoded it in 133 milliseconds. The reader was never entitled to the exact stored bytes, so lossy is not a compromise here, it is the correct choice.
One measurement worth passing on: we tried mozjpeg for the extra compression and dropped it. On the same page it took 690 milliseconds against 87 for libjpeg-turbo, a factor of five in time for about fifteen per cent in size. On a machine that is compositing a watermark for every reader on every page, that trade is backwards.
06The trap that took the deck down
This one cost us twenty minutes of production and is worth publishing because the failure mode is silent until it is not.
The obvious way to write the pipeline is to composite the overlay and then resize the result. That is what we wrote. It is wrong, because the image library applies the resize before the composite regardless of the order the calls appear in. So a 3000 pixel overlay was being laid onto a page that had already become 2000 pixels wide, and it threw.
Every page wider than the cap failed. Every landscape deck page returned an error while the portrait documents in the same room worked perfectly, which is exactly the shape of bug that survives a test suite: the synthetic page in our watermark test was 1275 pixels wide, under the cap, so it never exercised the path.
The fix is to scale first as its own explicit step and build the overlay at the scaled size. The lasting fix is that the test now includes a 3000 pixel page and asserts it comes back scaled, correctly proportioned and still marked.
07Where it landed
Two more changes finished it.
Each page now has a served copy stored alongside the original: scaled once at render time and kept as a high quality JPEG, so a read never fetches or decodes the multi-megabyte source. Pages rendered before that existed build their copy on first read, so the second reader is already on the fast path. Measured on the server, a page went from roughly two seconds to 0.22 seconds warm and 0.69 cold.
The viewer needed work too. It had been requesting every page within its prefetch window simultaneously, and a page that timed out or errored had no handler at all: no retry, no message, a blank card for ever. That is what "some never arrive" actually was. It now loads three at a time, gives each a twenty second deadline with two retries and backoff, says which page it is loading, and if a page still fails it says so and offers to try again.
The mark did not weaken through any of this. Page coverage sits above four per cent, every corner and the centre carry it, and the photograph test passes with the widest margin it has had.
08What to copy
Four things generalise beyond our stack.
Measure coverage, not mean difference. On a mostly white page the mean will tell you a good mark is faint. Count the pixels that moved.
Write the adversarial test before you tune anything. Ours simulates a phone photograph of a screen because that is the realistic attack, and it is the only reason we did not ship a watermark that looked fine and attributed nothing. Every tuning decision in this note was settled by that test rather than by opinion.
Separate the levers. If a mark has a dense layer and a sparse layer, they are not interchangeable. Ink survives compression, and the white halo behind the glyph is part of that ink, not decoration. Density is what annoys readers. Knowing which lever does which lets you improve both at once instead of trading one against the other.
A silent failure is worse than a slow one. Most of the complaint we received was not really about speed. It was about a blank panel that never said whether it was still working. An honest "loading page 4" and a retry button would have absorbed most of it long before any of the pipeline work.
Meridian data rooms: kxco.ai/meridian · Talk to us: kxco.ai/contact · Security policy: kxco.ai/security. Figures in this note are measurements taken from KXCO production and pre-production systems in August 2026 on the hardware described, and are not benchmarks; your numbers will differ with image content, resolution and host. Client-side watermarking deters, it does not prevent: the forensic layer is the server-composited one, and a determined party with physical access to a screen can always photograph it, which is why the mark is designed to survive that rather than to stop it. KXCO is a software company; regulated services are operated by licensed institutions and KXCO never custodies assets. Nothing here is investment advice.