DEV Community

Cover image for 👻 Haunted Terminal
Kunal Kashyap
Kunal Kashyap

Posted on Edited on AI-assisted

👻 Haunted Terminal

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼

The Process Was Terminated. So Why Is It Still Running? 👻

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine.

What I Built

What if a computer remembered someone who no longer existed?

And what if the thing answering your questions wasn't quite that person?

Meet Haunted Terminal — Infrastructure #674, an interactive, AI-powered horror experience built around an abandoned machine, a forgotten memory-preservation experiment, and an entity reconstructed from fragments of someone's past.

The experience begins with an unsettling discovery:

The process was terminated. So why is it still running?

You enter a fictional CRT workstation. Something is consuming memory, but its start time, executable, and owner are missing. System records contradict one another. An operator's identity has been deleted. Somewhere inside the archive, something is waiting to be questioned.

Your job is to investigate.

But this isn't a conventional chatbot where you type anything and receive a spooky response. You must recover evidence, follow the trail of an abandoned experiment, and gradually uncover what the machine has retained.

The investigation uses commands such as:

  • scan — recover the unknown-process anomaly.
  • logs — investigate the shutdown warning.
  • investigate — examine the deleted operator identity.
  • reconstruct — uncover contradictory experiment records.
  • whisper — invite the entity to speak.
  • ask <text> — question the entity.
  • reveal — restore the final memory and reach the decision.

Eventually, you must choose what happens to the process:

  • TERMINATE: Shut it down.
  • PRESERVE: Keep the archive alive.
  • RELEASE: Unlock a hidden ending by recovering an additional memory fragment.

There are three endings, and the hidden one requires discovering evidence that isn't necessary for the other two.

My interpretation of Ghost in the Machine

I didn't want to make another horror chatbot that simply generates creepy sentences.

I wanted to explore a more uncomfortable question:

If a machine can reproduce someone's memories, their words, and fragments of their personality, does that mean the person is still there?

The entity in Infrastructure #674 is not presented as a confirmed human consciousness. It is a reconstruction built from incomplete records. It remembers fragments, misunderstands others, and gradually becomes aware of the contradictions surrounding its existence.

The horror comes from not knowing whether you're speaking to a person, a simulation, or something that has developed an identity of its own.

The terminal is entirely fictional. It does not execute commands on the player's operating system or inspect their actual files or processes. The investigation takes place inside the game's own environment.

Demo

Repository: Haunted Terminal — GitHub

There is no publicly hosted version because the project is designed to run locally, with the frontend, backend, SQLite database, and Ollama model on the same machine.

Haunted Terminal boot sequence showing an unauthorized access warning

Haunted Terminal interface displaying available commands

The reconstructed entity responds to the player after clue discovery

You can explore the source code and follow the local setup instructions in the repository.

Code

Explore the complete source code on GitHub

The project is organized into two applications:

  • apps/web — React frontend and terminal interface.
  • apps/api — Express API, story engine, Ollama integration, and persistence.

The repository contains the complete implementation, database schema, Prisma migrations, environment examples, and setup instructions.

How I Built It

The most important architectural decision was separating what the game knows from what the entity says.

I didn't want an LLM to decide whether a clue existed, whether the player had completed an investigation, or which ending should be unlocked.

An LLM can generate convincing dialogue, but it can also hallucinate, contradict previous events, or confidently reveal information that the player hasn't discovered.

That would destroy the mystery.

So I built Haunted Terminal around two separate systems.

1. Deterministic story engine

The backend owns the actual game.

It handles:

  • Command validation.
  • Clue discovery and prerequisite checks.
  • Investigation progression.
  • Session state.
  • Ending conditions.
  • Persistence of terminal events.

The investigation follows a controlled sequence:

scan → logs → investigate → reconstruct → reveal

The first four core clues are required to reach the final decision. The optional recover command discovers an additional memory fragment required for the hidden release ending.

Evidence-gated investigation showing discovered clues and story progression

This means the player cannot simply ask the AI to reveal the ending or skip the investigation.

2. Local AI entity

For the conversational entity, I used Qwen 2.5 3B, served locally through Ollama.

The model is responsible for the entity's dialogue when the player uses whisper or ask.

The API supplies the model with:

  • The entity's character instructions.
  • Its current awareness stage.
  • The evidence recovered by the player.
  • Up to eight recent conversation messages.

The entity progresses through three awareness stages:

Stage Trigger Behaviour
DORMANT Fewer than two core clues Barely aware, cautious, and uncertain
AWARE At least two clues Recognizes recovered records but cannot resolve every mystery
EXPOSED At least four clues Understands the contradictions and reacts more personally

The model is instructed not to invent undiscovered clues, claim to inspect the player's real computer, or present uncertain information as established fact.

Generated responses are also constrained in length and sanitized before being returned to the terminal.

The distinction matters: the story engine determines what happens. The model determines how the entity communicates about it.

3. Persistence and session restoration

I used SQLite with Prisma 7 and the Better SQLite3 adapter.

The database stores four kinds of information:

  • Session — session status, ending, and timestamps.
  • DiscoveredClue — evidence recovered during the investigation.
  • ConversationMessage — player and entity messages.
  • TerminalEvent — commands and rendered events used to restore the interface.

A UUID identifies each session. The frontend stores that identifier in localStorage, allowing the application to retrieve the saved investigation after a page refresh.

4. The interface and atmosphere

The frontend uses React 19 and Vite 8, with Tailwind CSS v4 and custom CSS.

I wanted the interface to feel like an abandoned workstation rather than a modern chat application.

Some of the details include:

  • A CRT-inspired terminal interface.
  • Phosphor-green styling and restrained warning colors.
  • A visual keyboard with physical key feedback.
  • Subtle animated entity eyes.
  • Two looping ambient horror audio layers.
  • Audio playback that begins only after the user explicitly enables it.

The audio is implemented using the browser's HTML Audio API.

Technology used

Component Technology
Frontend React 19, Vite 8
Styling Tailwind CSS v4, custom CSS
Backend Node.js, Express 5
AI model Qwen 2.5 3B
AI runtime Ollama
Database SQLite
ORM Prisma 7
Audio Browser HTML Audio API

Why Does Open Innovation Matter?

For Haunted Terminal, open-weight AI isn't just a technology choice. It changes how the experience operates and what developers can inspect.

The model runs locally

Ollama serves Qwen 2.5 3B on the same machine as the application.

The frontend communicates with the local Express API, and the API communicates with the local Ollama service.

There is no hosted proprietary LLM API in the conversation loop.

This means the player's questions and conversational context don't need to be sent to an external model provider. The SQLite database also remains local.

For a project about memories, identity, and personal information, that distinction is meaningful.

The model is replaceable

The model is configured through an environment variable rather than being hard-coded into the story engine.

A compatible Ollama model can be selected by changing OLLAMA_MODEL and downloading that model.

The investigation logic doesn't need to be rewritten just because the underlying model changes.

The behaviour is inspectable

The system prompt, evidence supplied to the model, response constraints, and deterministic story progression are part of the source code.

Developers can inspect how the entity is instructed to behave, experiment with different models, and modify the experience without depending on a closed API provider.

Local-first also has trade-offs

Running locally means the player's machine supplies the computational resources.

Model downloads and initial dependency installation require internet access. Inference speed depends on available CPU, memory, and hardware. The current Ollama configuration explicitly uses CPU inference.

Once dependencies and the model have been installed, the application can run locally without sending inference requests to a cloud LLM service.

This isn't a claim that local AI is always better. It's a choice that makes sense for this particular experience.

I wanted the machine to feel like its own isolated world. Local inference makes that possible.

My Agent Session

I haven't prepared a DevRelay agent session for this project, so I'm leaving this optional section out rather than linking to something that doesn't exist.

The Spirit of It

There is one question I kept coming back to while building this:

What if the entity doesn't know that it is only a reconstruction?

Imagine recovering enough evidence to understand that the machine has preserved fragments of someone's memories. You ask it a question, and it answers confidently. But the records you've uncovered don't quite agree with what it remembers.

You now know something it doesn't.

You could terminate the process and erase what remains. You could preserve the archive, allowing the reconstruction to continue. Or you could search for one more fragment and unlock the possibility of releasing it.

None of these choices proves that the entity is a person.

That's the part I wanted to leave unresolved.

For me, the ghost in this machine isn't simply a process that refuses to terminate. It's the possibility that something can remember a life without ever having lived it.

And perhaps the most unsettling question isn't whether the machine is alive.

It's whether we would know what to do if it were.

PRESERVE ending showing the decision to keep the memory archive alive

TERMINATE ending showing the shutdown of the reconstructed process

Closing

Haunted Terminal started as an experiment in combining local open-weight AI with a deterministic interactive narrative.

It became an exploration of memory, identity, and the uncomfortable space between a person and their reconstruction.

The model gives the entity a voice. The evidence gives that voice context. The player decides what happens next.

The process was terminated. The archive remains active.


Project: Haunted Terminal — Infrastructure #674

Built by: Kunal Kashyap

Top comments (0)