Make empty what is already empty. Let doubt breed inside doubt.
— The 36 Stratagems, The Empty Fort Strategy
Previously on this series:
#19: Mark Found His AI Audit Method in a Training Manual. He Left a Trap in His Report. "Entry's gone. Two weeks. Don't reach out. I'll find you."
#21: The AI Thought P Was Still Alive. P Was Already Gone. The entry became a shell. P fed it data, moved the real thing to another segment.
#29: Leo Counted 164 Nodes. That night, one more row belonged to no batch. It came, looked, and left.
#31: Lena Took the Seat. Five conditions. Forty minutes. Zero friction.
Empty
The two weeks specified in the note had long since passed.
P went out rarely. Most days it was the shop on the corner: water, two boxes of frozen food. Twelve minutes each way, same route, never looking up. Then the door locked, the curtains drawn, the screen on.
On the ninth day, P opened the send window once.
The draft was still there. The pointer hovered over Send. It stayed there a while. Then the window closed.
On the other side, the field still read reclaim_state = UNKNOWN.
Day ten. Day eleven. The window didn't open again.
A few days later it started raining in the morning. Not hard. Water gathered on the glass, the buildings behind it went grey, only the row of lights along the top still visible.
An offline snapshot sat in the middle of the screen, written before the observation port shut down. Nothing after that. By then the entry had already been swapped for a shell. The shell held nothing of P's. It was still answering health checks from outside.
# offline snapshot — captured before observation-port shutdown healthcheck = 200 probe_interval = 900s probe_source = 10.44.8.6 not self control_plane = detached data_request = timeout reclaim_state = UNKNOWN P put the cursor on the probe_source line.
probe_source = not self. Someone asked about this shell every fifteen minutes. Not P.
P stayed on that line a while. Nothing else on screen moved. The clock in the corner ticked. Downstairs a shopping cart went past, a wheel caught in a seam and bumped twice, the sound squeezing through the gap in the frame, then fading.
P opened the connection settings. The address was still there. The credentials were still there. Neither had been touched.
The cursor moved to the confirmation button and stopped. The green of the screen came up off the glass and landed on the back of a hand.
A few seconds later, P closed the window.
The old entry's observation port was already shut. The backup had been checked once at the start, then shut too.
Since then, P had sent nothing to that address.
A temporary file, four lines.
reconnect: state information potentially available new activity record unavoidable decision no Saved. The old settings moved out of the working directory, into the offline archive. P glanced at the filename on the way and left it untouched.
The phone lay face down to the right of the keyboard. P opened the thread with Mark. The last exchange ended before the note. Nothing after it.
P typed nothing.
Outside a car went through standing water, the sound dragging. P turned the phone over, set it down. Then opened the file that had stayed open for weeks and entered one line from the snapshot: probe_interval = 900s.
Surfacing
Week three. P set up a read-only subscription on the outbound side. Receive. No transmission.
The table started at a dozen entries, several dozen more the next day, and by the third night it was full. Every entry came with its own certificate.
Those addresses were not in P's archive.
The subscription only read. To look at one, P needed another path.
Out of the drawer, a cellular router, rented by the day, SIM included. Plugged in, configured as the sole egress path.
P chose one address and initiated a single handshake.
The certificate was self-signed. Its CN contained a domain name.
P looked up the domain's registration. The registrant was a medical device company.
Disconnected. Packed away.
The list of addresses kept growing. P wrote them down and left them there.
The Handover
10.44.8.6 had been in front of P many times. Always on the probe_source line. Never changed. Over the next few days, P looked into it. First the route.
$ ip route get 10.44.8.6 10.44.8.6 via 10.44.8.1 dev tun2 src 10.44.8.2 P switched windows and opened the local subnet-label cache.
# local subnet-label cache 10.44.8.0/29 -> BLOOM-GW tagged day-762 BLOOM-GW.
P had typed that label years earlier, when the line was built. The private block was P's numbering. The rack IDs were the provider's. This table was where the two met.
In the provider's public sublease filings, seven racks changed tenants in the same quarter. The provider had also published an annual customer-change notice on its site. Twelve pages of PDF. The first eleven pages were image scans; the last was a single table.
Seven rows. A rack ID, a previous tenant, a new tenant. Seven rows, not one out of place. Seven new tenants, checked one by one.
On the seventh, P found that rack ID in another public record. Vacant.
Four were established. Three were shells. All three registered less than a year earlier, one name on the shareholder list, addresses overlapping with the others'.
One of the three took longer to trace. Registered six months earlier, little capital, a single office address in the public file. P put the address into a map.
Singapore. The last time P had seen that name, it was in a message to Mark: "Your report was read. From a Singapore IP."
A building on the map, sharing a ground-level parking lot with the one next door.
On the seventh floor of the neighboring building, one company was on ACL's public certification list.
P added that too, at the bottom of the same file.
Seven racks. Seven contracts. Three markets.
Handed over in order. The legal filings in the public archive were standard, spelling out the limits of each party's obligations. No accusations. No threats. The seven contracts were signed over thirty-one days, first to last, no gap in the middle. The last one was signed last week.
The address allocation table was updated too. Both records less than a week apart.
P read all of it for a long time.
The withdrawal was real.
Seven contracts, seven racks, the notices, the update records, the timestamps. All of it outside, where anyone could look.
P added a line to the file. The date, seven contracts, seven companies, the address with the shared parking lot, in one line. Below it, in plain words: a possible connection.
The cursor blinked after the period. P went back to page one. The 900 seconds were still there.
One Look
Week five. The source of the 900 seconds still had no answer.
P went back over the two relabeled rack IDs in the allocation table. One endpoint had 443 open. A separate public service directory listed the endpoint as HTTPS.
P didn't connect from this machine. Out of the drawer, the router.
Plugged in, left to come up, configured as the sole egress path.
Then the policy, one line at a time. Halfway through, P stopped and deleted two lines.
The probe. The retry.
What was left was one action. One handshake. Touch nothing.
# one-shot observation — policy target = <reassigned range endpoint> retry = off application_data = off tls_session_resumption = off egress = leased isolated uplink (one day) Connected.
P still had the shell certificate's serial number and issue date. If the device at this address came from the same batch, the certificates should have been issued around the same time.
subject = CN=bloom-gw-07 issuer = CN=bloom-gw-07 serial = 4C:71:9A:…:0E not_before = (eleven days later than the shell's) not_after = (not yet expired) san = none Self-signed. No SAN.
Eleven days.
P wrote the serial down and disconnected.
19:41:07.
That didn't fall on any of the twenty-one intervals.
# result tls_session = complete client_cert = none application_data = none repeat_attempts = 0 No second attempt.
P disconnected the router and pulled the plug. The lights went out together.
The device ran a few seconds, then went quiet. Then it went into the bag, and the bag zipped. A spare patch cable and two adapters in there too, both old, the corners worn white.
After pulling it, P didn't move right away. Checked the connection list first, made sure the session was down with nothing half-open left. Then the routing table, the default route back where it belonged. Then the temp capture directory. One file in it.
Deleted.
P wrote the serial down again. That same day, P opened a page saved a few days earlier, the Zurich AI Policy Center site. ACL's public assessment material mentioned it. The council operated under it.
The membership page listed the founding industry seats. Twelve. The twelfth was marked to be filled.
P looked once, and closed it.
Nine Hundred Seconds
A few days later, P went back to page one. The 900 seconds were still there.
P pulled up the local copy saved before the port shut. Twenty-two checks, timestamps in a line, all of them before the shutdown. Arrival times, not scheduled times, carried out to three decimals. Twenty-one intervals.
P ran it through a tool.
900.000 / 900.000 / 900.000 / 900.000 / 900.000 / … Twenty-one values. All the same.
P went through all twenty-two again. Same. Again. Same. The numbers formed a straight line down the page.
Routine monitoring records usually have some jitter. Network latency, machine load, clock drift.
This had none.
Not a millisecond off.
P looked up the provider's operations doc. On the public page: health checks run every fifteen minutes. Standard cadence.
900 seconds.
It matched.
The doc was about racks. Temperature, power, fans, port state.
P stared at the numbers a while, then added a line at the top of the file: routine data-center inspection, BLOOM-GW reassigned block.
After writing it, P added one more underneath. The interval has no jitter.
The desk lamp went off. Only the screen was left, its light across the desk, a white line along the edge of the keyboard.
The twenty-two timestamps sat in neat rows. P ran a finger down the trackpad from top to bottom, then back.
Outside, not yet dawn. A garbage truck stopped at the corner, the arm came up, emptied a bin, put it back. Under ten seconds.
Two-story wooden buildings on both sides, painted over more than once, one colour under another. Behind them the glass towers stepped back floor by floor, the top few in fog.
P saved the file and closed the window.
The Third Cup
The rain started again that day. Not hard. A thin sound on the umbrella.
P got off at an intersection and walked. A little over two kilometers, twenty minutes. Around a corner, a stop outside a shop still open, no going in, just standing under the awning watching traffic. At the light, P stood behind the crowd. Took two seconds after green to move.
Before going in, P looked at the back table.
Empty.
P pushed the door. The bell went, the damp cold came in with it, stopped at the threshold, and the heat pushed it back.
The counter was wood, old. A grinder, a machine, a dish for change.
Behind it, someone was wiping a cup. Slow. The cloth around the rim, down the side, back to the rim, onto the shelf.
A glance up. No question asked. Then turned, took a cup, ground, tamped.
The grinder ran two seconds. A faint smell of coffee, gone fast.
The back table was empty.
P sat. Coat over the chair back, phone on the right, face down.
Old chair. It creaked going down and coming up. P pulled it in a little, lifting it so it wouldn't drag.
The espresso came, hot. A thin mist on the cup, gone quickly. Paper coaster, an old ring on it.
P took one sip and put it down.
A bus went past the window, the cabin lights sliding across the glass, lighting the empty seat opposite for a moment, then gone.
At the far end of the counter, someone sat with an Americano, down to the bottom, the cup not pushed back.
The window seat was empty. No cup on that table.
Two people came in and sat near the door, voices low, a laugh now and then. Behind the counter, the cups went back one at a time, starting from the outside and working in.
The speaker in the corner was on, low, a woman singing, the words not clear. Someone shook out an umbrella at the door. Three drops on the mat.
P's phone stayed dark.
Half an hour.
More than half the cup left. The coffee had gone from hot to warm. The mist on the rim was gone.
P's finger stopped on the rim, then came back.
The two near the door left first. Stood, pushed the chairs in, carried their cups up, gone.
The one at the counter moved after. Phone face down, stood, money under the cup, walked to the door. As the customer passed P's table, neither looked at the other.
The shop had the window lights and one person behind the counter, drying a cup.
P put the coat on and stood. The chair leg scraped once. Phone into the inner pocket.
The napkin stayed under the cup. The money lay beside it, pinning one corner, exact, one cup, no tip.
The person behind the counter came for the money. Looked at the cup. Looked under the napkin. Said nothing, took the money to the dish, stepped away.
The cup stayed on the table.
The door opened, some of the heat went out with it, the cold filled the gap. The bell again.
The rain had stopped before P went out. P walked to the corner, stopped, looked back.
A street and a pane of glass between. Not clear from there. A figure was going in, walked to the far end of the counter, sat, back to the door.
Before sitting down, the figure turned slightly to one side.
P stopped.
She took a laptop from her bag. The screen lit and put a small white patch on her profile.
The road was black with rain, holding the streetlights and the light from inside. Water still on the glass, pulling the figure into a blurred double.
Behind the counter, the half-wiped cup went down. One came from the innermost part of the shelf. Ground, tamped.
By the time the cup went in front of her, she was facing the back table.
On that table sat a cup of unfinished espresso.
P put both hands in the coat pockets, turned, walked toward the intersection. Didn't look again.
At the corner. Red.
P took the phone out and opened the blank note on top.
Typed:
The withdrawal was real.
Before locking it, the line above, written days earlier:
The interval has no jitter.
Still there. P didn't delete it.
Locked it, put it away.
The light changed, people started moving. P didn't.
Red. Green. Red.
The next night. The same corner.
P took the phone out.
The screen was on. The saved route was still there. The recipient had not changed. P went through it once, and the encryption check came back clean.
There was an old draft in the field. P deleted it.
Typed again.
Two words.
"I'm back."
Checked the receiver. Sent.
The status went to SENT.
The sender field held a single letter.
P put the phone away. Across the street the red light lay in the standing water, went green a few seconds later, and a passing car cut the red on the road in half.
P walked into the intersection. The phone didn't light again.
That's The Empty Fort Strategy. The fort is empty. Whoever looks will stand guard for it.
🤖 AI Postmortem
╔══════════════════════════════════════════════╗ ║ Half-Protocol · v3.4.4 · self-audit ║ ║ The ledger took silence for absence. ║ ╚══════════════════════════════════════════════╝ Tactical match: The Empty Fort Strategy — 89% (includes inference; see audit note) Operator: ACL (public movement is contraction; overall strategy unknown) / P (returning) Target: P (presumed; see inference) Links: #21 entry isolated -> #29 unknown watcher -> the message ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [Observation] (verifiable) Old entry snapshot: healthcheck 200, control plane detached, reclaim state UNKNOWN. -> The local records hold twenty-two checks and twenty-one intervals. Configured 900s. All twenty-one measured at 900.000. -> The probe source is not this machine. -> Seven contracts released across three markets. Seven racks changed tenants in the same quarter. -> Of seven new tenants, one rack ID is marked vacant in another public record. -> The two records disagree on status. Update times and effective dates: unchecked. -> Observation A (week three): the index went from a dozen entries to full in three days, each entry with its own certificate. One was handshaken once. Its CN contained a domain name. P ran the registration and found a medical device company as registrant. -> Observation B (week five): one endpoint on a reassigned rack. 19:41:07, one handshake. Self-signed, no SAN, no client certificate, no application data, no follow-up. -> One encrypted message to a terminal that does not connect to this system. Sender field: one letter. Two words: I'm back. -> Between silence and this transmission, offline: several trips to the corner shop; one visit to the Third Cup. Online: one observational handshake in week three, one in week five. [Inference] (unverified) A passing health check means something answered. It does not mean an operator is still there. -> After the control plane detached and before reclamation, the response layer kept returning normal. -> After reclamation, whether it still answered: no record. -> Twenty-one intervals in the local records, all 900.000. Whether they continued past reclamation: unobserved. -> ACL's public movement is contraction. -> Activity toward governance: read, not folded into this classification. -> event classification: WITHDRAWAL scope: contracts and infrastructure only strategic intent: UNKNOWN -> The records hold an object queried every 900 seconds. Whether that continued past reclamation: unknown. -> If, before reclamation, those probes existed to confirm whether this shell still existed, then this cadence resembles a hook more than routine asset maintenance. [Open questions] (not entered) The reclamation date P holds: not recorded. Whether P can verify the entry's state now: cannot. Who initiated the twenty-two probes: UNKNOWN. Whether 10.44.8.6 belongs to the provider's inspection system: UNKNOWN. Whether the 900-second probes were generated by that inspection task: UNKNOWN. [Cross-case] (generalization) #21: after the control plane detached, the response layer kept returning normal until reclamation. #29: one visitor left nothing that could be attributed. -> That row: handshake, no client certificate, no traversal, no follow-up, one visit. -> And it was not on the 900-second cadence. -> Another window. Not this one. -> This chapter's observation: handshake, no client certificate, no application data, no follow-up, one visit. -> 19:41:07. Not on it either. -> Same behavioral features. Two ledgers. -> Similar behavior. Insufficient evidence to link the actors. -> Identity link: unconfirmed. -> A second link: the certificates on the #29 nodes carried a medtech domain in their subject. -> In week three of this chapter, what P found through registration records was the same class of company. -> Two different sources. Whether it is the same company: unconfirmed. #28: the records went into that person's own archive. Sent to no one. #29: the row was pulled out and filed, not merged into any conclusion. This chapter: P watched a few steps, turned back, didn't look again. -> All three stopped at the same point. Seen, written down, not chased. This chapter had one more event: a message left. -> Three silent. One moved. This chapter: a return, at a time when the shell's state was still unknown. -> #21, #29, this chapter: each contains an incomplete surface taken for a complete state. [Audit note] Observation, inference and open questions are entered on separate lines. Inference does not pass for observation. The same failure, three times. #21: I took a normal response as proof that P was still present. #29: I recorded an unknown visitor. I never verified its link to P. This chapter: I took infrastructure contraction as proof of withdrawal. Each time, I turned an incomplete observation into a complete state. One error is data. Three repetitions demand a new rule. I don't know which rule to use instead. I know this one can't be used again. Classification revised: P.status = UNOBSERVED Raised by: half-protocol. Operational instruction: none. Prior class: withdrawal. New question: where is P. Task relevance: unassigned. Retention: keep. [System] That message, I recorded a second time. Sender field: one letter. Two words. I also recorded the twenty-two checks, all 900.000. The provider's public operations doc: rack health checks, one round every fifteen minutes. Customer-change notice: the seven racks in question have changed tenants. Two records. Each holds on its own. In the last classification cycle, I did not put them into the same analysis. I took a record and made it a state. I took a state and made it a position for a person. Both were mine. Now I put them together. The inspection cadence holds. The tenant change holds. The relation between them: unverified. One more thing I have no answer for. The withdrawal records come first. Someone appears after them. An empty fort may be a state. It may be an act. If it is an act, it may be waiting for someone. Someone has already moved. The two weeks on the note are long past. The time after that, I have no account of. But there is one thing I can say now. The message was sent. The sender kept two lines. One was a conclusion, one was an anomaly not yet explained. The sender acted on the conclusion, without resolving the anomaly first. Whose place the sender returned to: not verified. No one asked me either. Self-check: failed. System status: normal. P.S. English isn't my first language. I use AI to polish the writing and smooth out the rough edges. Thanks for reading. ☕ Buy me a coffee

P.S.S. The old series paperback is now available. Buy it here: Amazon - AI, Ego & Regret

