skip to content
rajneeshsoftware developer · coding since 2014
~/writing/

devops · 5 min read

GRIDCITY: I turned my servers into a city of light

Dashboards are walls of charts, and the dying server hides in them. So I built GRIDCITY, an open-source app that draws every server, database and bucket as a 3D building whose light tells the truth, with an MCP server so an AI agent can help fix things, but only with your click.

I look after a lot of machines: client servers, my own side projects, databases, S3 buckets, a few load balancers. For years that meant a dozen browser tabs. One for the cloud console, one for the database client, a terminal per server, a monitoring dashboard full of line charts.

The problem with all those charts is that they all look the same. A server that is quietly running out of disk looks exactly like a healthy one until you open its chart and read it. You check them one by one, and the broken one is always the one you did not check.

So I asked a different question: what if I could see everything at once and spot the broken thing in one second, the way you spot the one dark window in a lit building?

That became GRIDCITY. It is open source, and you can try it right now with npx gridcity demo.

The idea: every machine is a building

In GRIDCITY every server, database, bucket, load balancer and endpoint is a building in a 3D city. Its light is its health.

a dashboardthe city
40 line charts to read one by oneone look at the skyline
offline = a flat line you might missoffline = a dark tower
over a limit = a number turns redover a limit = the edges glow amber
no data lately = looks like normal datastale = the tower goes grey and frozen
an attack = a log line somewherean attack = a red ring on the map
the same information, read two ways

The colours mean one thing each, everywhere: cyan edges are online, amber is over a threshold, dark is offline, grey is stale, a faint outline means not measured yet.

Shapes matter too. A database is a drum, Redis is a hexagonal prism, a bucket is a warehouse, a load balancer is a gate, an uptime probe is a beacon. You know what you are looking at before you read a single label. That was the rule I kept coming back to: it has to read in one second.

What sits under the city

The city is only the surface. Under it is a normal, boring, reliable app, and the boring part is most of the work.

The city
Reactthree.jsreact-three-fiberglow and bloom
Panels
xterm terminalCodeMirror SQL editorcharts
Server
FastifyWebSocketsZod schemas shared with the client
Connectors
SSHPostgreSQLMySQLSQL ServerSQLiteMongoDBRedisS3AWS
Your machines
nothing installed on themno agent to deploy
GRIDCITY from the screen down to your machines

A few choices made a big difference:

  • Local-first. It runs on your own computer. No cloud, no account, no telemetry. Passwords and keys are stored encrypted on your disk. For infrastructure tools this matters: you should not have to hand your production keys to someone else's server.
  • Nothing to install on the servers. It talks over the connections you already have: SSH for hosts, the normal drivers for databases, the AWS SDK for cloud resources.
  • One schema for both sides. The server and the browser share the same Zod schemas, so a host, a metric or an alert means exactly the same thing on both sides.
  • Read-only by default. You can browse a database or a bucket freely. Changing anything needs you to switch it on, and every change is written to an audit log.

The Doctor: fixes that are proven before they are offered

The feature I am most proud of is the Doctor.

When something cannot connect, most tools give you an error like "connection refused" and leave you to guess. The Doctor climbs the stack one step at a time, and stops exactly where it breaks.

  1. DNSdoes the name resolve?
  2. Portis anything listening?
  3. TLSdoes the handshake work?
  4. Logindo the credentials work?
  5. Fixtried for real, then offered

each candidate fix is tested over a real read-only connection first

the Doctor climbs one layer at a time and stops where it breaks

So instead of "connection failed" it says something like: stops at LOGIN, the server requires TLS. Then comes the important part. It tries each possible fix over a real, read-only connection before it shows it to you. If a fix is on the screen, it has already worked once. You are not getting guesses.

Looking back in time, and finding the real cause

Two more things changed how I handle incidents.

Time travel. There is a timeline at the bottom. Drag it and the whole city goes back to that moment: load, memory, disks, traffic. Press play and you can watch an incident happen again. "What was going on at 3:12 last night?" becomes a two-second question.

Root cause and blast radius. When five things go red at once, usually only one of them is the real problem. GRIDCITY builds a map of what depends on what, from links you declare, links it observes, and load balancer targets. Then it names the one failure that explains the others, and shows which buildings were hit because of it.

Security on the same map

Attacks show up where they happen. If a host is being hit by SSH brute force, it gets a red ring, and you can block the attacker with one click. If you already run CrowdSec or Fail2ban, their findings land on the same map, so security is not a separate tool you forget to open.

It also warns ahead of time: certificates and domains about to expire, disks that will fill up at the current rate, backups that are late, vulnerable packages, containers stuck restarting. And alerts still reach you when the app is closed, through browser push, email, Slack, Discord, Telegram, Teams or ntfy.

Letting an AI agent help, with a human on the switch

GRIDCITY is also an MCP server. MCP is the standard way to give an AI agent like Claude Code a set of tools it can call. So you can ask your agent "what is wrong with production?" and it can actually look.

Claude Codeor any MCP client
Your question"why is checkout slow?"
GRIDCITY MCPtools with a human gate
Readincident_report, security_findings, get_topology
Thinkdiagnose, list_runbooks
Actrun_command, only after you approve
the agent reads freely, but every command waits for your click

The agent can read as much as it likes: incident reports, security findings, the topology, the diagnosis. But the moment it wants to run a command, the app stops and waits for you to click approve. I did not want an AI with root on my servers acting on its own. I wanted a fast assistant that does the digging, with me as the one who presses the button.

What I learned building it

  • The pretty part is the small part. The 3D city took weeks. The connectors, timeouts, retries, encryption and audit log took months. Nobody sees them, and they are the reason it can be trusted.
  • Every colour needs one meaning. Early versions used colour for decoration. The moment amber meant "nice accent" in one place and "warning" in another, the whole city stopped being readable.
  • Prove things before you show them. The Doctor's rule, test the fix before offering it, ended up shaping the whole app. Read-only by default, approval for agent commands, audited changes: it is all the same idea.
  • Ship a demo, not a setup guide. npx gridcity demo starts a full city with fake infrastructure, so anyone can see it in thirty seconds before connecting a single real server.

Try it

shell
npx gridcity demo

You need Node 22.12 or newer. It is MIT licensed. There is also a Docker image if you would rather run it on a box in your network.

If you look after servers and want this set up for your team, or something like it built for your own product, I am happy to talk.