This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine.
What I Built
DEADNET is a short murder mystery that takes place on a dead woman's computer.
Nora Vale died in her room on Halloween night, 1999. The police called it an accident. You play Adam, her friend. Her neighbour Mara leaves you Nora's old computer with a note asking you to take a look before it's collected.
The computer is a late-90s desktop with ordinary apps: Messenger, Mail, Photos, a Calendar, My Computer, a Trash Bin and a few games. You insert a recovery USB, hear it boot, and start opening things. The clues are in her conversations, emails and photo captions. You learn what happened by noticing where people's stories don't line up.
The day before she died, Nora was making fun of Mara for cooking enough soup for twelve people. “she owns two bowls adam.” Her calendar still has dinner at Mara's in it. Bring bread. Stay for once.
Her last conversation in Messenger is with you. You didn't answer it in time.
Then her account comes back online, in a window called Unknown Signal. You can talk to her and show her what you've found. She's played by an open-weight model, and she only knows what you've shown her, so she can't just tell you the answer.
When you think you've worked it out, you open the Casebook on your recovery USB and answer three questions: who came into the room, why, and how they got in. A wrong answer doesn't end the game. It just tells you something doesn't fit.
Demo
Play it in your browser. It takes about 15 minutes. Each visitor gets their own copy of the case, and progress saves automatically. If nobody has played for a while, the first load can take up to a minute while the server starts.
One thread to pull
Start with Mail → “Home, somehow.”, sent by Len at 03:07 on Halloween morning:
Made the 2:58 by about six seconds. Ticket machine’s broken, so the driver wrote one out by hand. Kept it. First thing I’ve won all week.
I’m home. It’s raining through my kitchen window again. Tell Mara I’ll look at her heater Monday.
It sounds like someone getting home from a miserable shift. Then you read management's statement, which puts Len on the third floor at 03:17. Both accounts can't be right.
Save the email to your USB Casebook, then attach it in Unknown Signal. You can ask Nora what she remembers about Len leaving. Her next reply is generated from the conversation and the evidence she's actually seen; opening an email on your own doesn't give her that knowledge.
The next connection is yours to make. A photo caption records someone borrowing an office spare. Nora's last chat explains what she had found and who wanted to speak to her. Together, the records give you a timeline, a way into the room, and a reason for the visit.
In the Casebook, choose a person, a motive and a way in. If you name the wrong person, the game sends you back to the contradiction:
that doesn’t fit what you found. who asked to come to my room? and who had already gone home?
You can reopen the records and revise your theory without restarting. You don't need to win a mini-game to finish; those are simply games Nora kept on her computer.
Code
sea-deep
/
deadnet
A murder mystery on a dead friend's 1999 computer. Investigate her messages, confront an AI ghost, and solve Room 317.
DEADNET — Room 317
Mara gives you her dead friend’s computer. You are Adam Moore. The last message Nora sent was to you.
Insert your recovery USB, turn on the computer, and look through Nora’s Messenger, mail, photos and calendar. Get to know the person who lived here. When her account signs in again, show her what you find. Her memory is uncertain; the things she left behind can contradict it.
Play online · The story behind it (DEV) · Source · Scripted static exhibition · Credits and licenses
Run
Node 22.13+ and npm:
npm ci
cp .env.example .env
npm run dev
Open http://127.0.0.1:3000. Insert the USB, then power on. The computer fills the browser; the tray also offers browser fullscreen. Windows drag, resize, minimize and maximize. On phones, a single tap opens an app and each window fills the available space.
For live dialogue, add OPENROUTER_API_KEY to the…
MIT licensed. Borrowed code, fonts, images and sounds are credited in CREDITS.
How I Built It
The desktop is React with 98.css. A small Node server stores each player's case and talks to the model, so no API keys reach the browser.
The main design decision was splitting what's fixed from what's live:
- Fixed: the evidence and the solution. Every player gets the same messages, timestamps and photos, and the same answer.
- Live: the characters. The model decides what Nora remembers, how sure she is and what she asks you.
I did this because language models are good at sounding unsure and bad at keeping facts straight. If the model could change the evidence, it could also accidentally solve the case for you, or make it unsolvable.
So each character only sees what they've actually been shown. Replies come back in a fixed format and get checked before they're saved. A reply that cites an invented or unwitnessed evidence ID is rejected. Dialogue can still be mistaken, but it cannot create a collectable clue, change the evidence or decide whether your theory wins.
The clue Nora shouldn't know yet
One bug made that distinction concrete. The photograph looked harmless in the UI, but its model-facing description also explained the motive from a different conversation. Show Nora the key photo, and the prompt had already handed her an answer you hadn't discovered together.
The fix was in the information I gave her:
- Before: the photograph's description established access and explained why someone wanted to silence Nora.
- After: the photograph establishes access. The motive has to come from the separate conversation.
I also removed a later memory hint that named the key's owner before Nora had seen the photo caption. The tests now check all eight combinations of the three items she can have seen. Showing her one item must not quietly give her the other two.
That changed how I thought about the ghost. Her uncertainty needs a reason. She is piecing the night together with you, and each thing you show her changes what she can say.
The model. The browser version now uses Gemma 4 31B through OpenRouter, pinned to google/gemma-4-31b-it:free. It runs on a hosted provider, not inside your browser. The local option is Gemma 4 E2B through Ollama. Both use the same character prompts and evidence rules; System Monitor records the actual model and request trace.
Making it feel like someone's computer
My first version was a file hunt: log files, a damaged backup you had to unlock, and at one point you had to play mini-games to recover it. It worked, but it felt like an escape room rather than someone's computer. I rebuilt it around chats with friends, emails, a photo album and a calendar.
That gave the ordinary messages a job, too. An earlier chat has Nora complaining that Adam's bat game ate her evening when she was supposed to be packing. She says she's finally moving in November, somewhere with a window that opens. Later, you're reading that conversation knowing she never got to move.
The same desktop can tell you she was funny, she was planning a future, and somebody's account of her last night is wrong. I wanted the player to notice all three.
Hosting. It runs on Render. Each browser gets its own private case, so players never see each other's conversations. The game autosaves, and you can save to a file, load it back or start over from the Start menu. If the free model is busy, it tells you and lets you explicitly switch to labelled scripted replies. It never silently replaces a failed live response. Requests are capped at eight per visitor and thirty globally per day, with burst limits and cooldowns; resetting the case doesn't reset the allowance.
There's also one voice clip: Nora's last voicemail, recorded with ElevenLabs' premade Lily voice, hushed and hurried. She warns Adam not to come through the lobby and says Len wasn't in the building. It's something to compare with the email and management's statement, rather than a narrator explaining the solution.
The clip is cached, so replaying it doesn't generate more audio. There's a transcript next to it. Voice, model, transcript and delivery settings identify the cache entry; concurrent playback requests share a single render. The existing render record documents one generation taking 1.927 seconds; replay used the saved audio.
Following Nora's reply with Sentry. After finding the premature clue, I wanted to see the boundary around a whole turn: which evidence is available, when the model runs, whether the reply passes validation, and what gets saved. Each turn now has a Sentry agent trace with those steps underneath it. Live model spans record the provider, model and token counts. Players' messages, prompts and replies stay out of the telemetry.
One verification gives Nora an invalid reply that tries to contact Mara from the private signal. The validator rejects it and the root trace is marked rejected; Nora's private message never reaches another resident. Here is the actual Sentry trace, captured in Safari:
This capture uses a deterministic fixture in Sentry's verification environment. It checks the guard and the tracing, rather than measuring hosted Gemma's speed. Trace record and screenshots.
Checking the case. There are 40 Node tests for knowledge boundaries, invented evidence, wrong theories, the ending, saves, request limits, the audio cache and telemetry privacy. Another 38 browser scenarios cover desktop and phone layouts, including a complete investigation with a wrong accusation and recovery. They use fixtures, so these checks don't spend model or voice credits. Verification.
The GitHub Actions checks run the tests, typecheck, both builds and the browser scenarios on every push. A second workflow deploys the scripted exhibition to GitHub Pages. That makes a broken desktop or ending a failing build, rather than something a judge has to find.
Why Does Open Innovation Matter?
It keeps the game playable. The story is about a computer that keeps answering after its owner is gone. With open-weight models, Nora doesn't depend on one company keeping one model available. The prompts and rules are in the repo, and any compatible open model can play her.
It can run privately. You can run the whole thing locally with Gemma through Ollama, so your conversations never leave your machine.
It's free to play. The hosted version uses a free Gemma route with a small daily request budget to protect the account. Running Gemma locally removes that shared API allowance; there is no per-message provider charge.
The most useful lesson was learning where to put the model. It brings a voice to the conversation; the code keeps track of what happened and what each character has seen. That division is small enough to inspect, test and change with another open model.
The Spirit of It
Mara keeps writing to Nora after she's gone. There's a scarf on her chair, a dinner in the calendar, and a deleted letter in which Nora almost says yes to a new flat. Before you have a suspect, you have the shape of a life that was supposed to continue.
Halloween is the night that life stops. On November 2, you're sitting at the computer with everything she left behind. You can put a name on what happened. You can't get back to 03:14 and answer her in time.
The ending, if you'd like to see where the investigation leads
After a supported report, Mara writes: “i printed her name properly this time. NORA VALE. not ‘the woman in 317’.” Nora hears the rain again, and the line goes quiet.
My Agent Session
I used Entire to capture the actual Codex session behind the evidence fix. The checkpoint links the conversation and tool work to the implementation commit.
The session excerpt includes the edit that removed the premature clue and the test results that followed. It was exported from checkpoint 01M682MCRY0YFDMAADPPK7ETN8; private setup and internal reasoning are omitted. The native checkpoint stays local, and the public excerpt can be read without an account. Entire setup and capture details.
Prize Categories
- Best Use of Render: the game is hosted on Render as a web service, with a private case per visitor, automatic saving, and API keys kept on the server.
- Best Use of Gemma: Gemma drives the characters through the pinned OpenRouter route online, or through Ollama for private local play.
- Best Use of ElevenLabs: Nora's last voicemail, an important clue, is an ElevenLabs recording, cached and shown with a transcript.
- Best Use of Entire: the actual Codex development session is linked to its commit, with the debugging edit and test output shared above.
- Best Use of Sentry Agent Tracing: evidence scoping, model execution, reply validation and persistence are traced together; the verification capture shows a rejected private reply, and tests check that telemetry excludes conversation text and credentials.
- Best Use of GitHub Copilot: entered through the challenge's GitHub Actions route. The existing workflows test and build the game, check the complete browser investigation, and deploy the scripted exhibition.
All characters and events are fictional. I used AI coding assistants while building DEADNET and while writing this post.








