Automation Pipeline4 min read

My Dashboard Showed Follow Counts but Could Not Say Whether I Was Allowed to Follow

The four gates that decide whether Instagram cold-following may be switched on lived only inside a CLI script. The dashboard had numbers and no permission. A 31-line change put the verdict on screen, and I explain why I kept the gate check out of the 21-second API cache.

#dashboard#instagram#gates#observability#subprocess
A dashboard full of numbers on the left, a banner with a lock icon and 2/4 on the right
Plenty of numbers, but the answer to whether I could switch it on lived only in a terminal.

All the numbers, none of the answer

I run a social bot dashboard on a tailnet port, :8892. It shows per-account status and figures pulled from the platform API. Those figures sit behind a 21-second cache. At a glance it looks complete.

But the one question that mattered for Instagram cold-following, meaning following accounts that have never interacted with mine, was nowhere on that screen. Whether cold-follow was allowed to run was decided by a separate Python script. It checks four gates and, with a --json flag, prints the result as JSON. You had to open a terminal and run it yourself.

Does your dashboard show you the current state, or does it tell you whether the next action is permitted?

Mine only did the first. Plenty of numbers, no permission. The code that checked the preconditions for the action already existed. Its output just never reached the place I actually look at.

What happens when gates live only in a CLI

The gate script is cheap. It greps local files and never touches an external API. The cost was not the problem. The location was. To see the verdict you had to remember a separate command, and at the exact moment you are staring at the dashboard deciding whether to enable the bot, the verdict is not in front of you.

Since the dashboard already pulled API figures through a 21-second cache, dropping the gate result into that same cache looked like the obvious move. But that would tie the gate verdict to the API refresh interval. Fix a local file to satisfy a gate, and the screen could stay locked for up to 21 seconds. The gates are local checks with nothing to do with the API. There was no reason for them to share its cache lifetime.

What would you do, put it in the cache or recompute it on every render?

Asking again on every render via subprocess

I went with the second. Every time the dashboard renders a page it spawns the gate script as a subprocess with the --json flag and draws the returned JSON straight into a banner. If every gate passes, a single green circle says READY. Otherwise a lock icon shows N/4 for how many passed, with one chip per gate so you can see which one is blocking.

Skipping the cache is justified by one fact: the gates are local greps, so running them on every render costs almost nothing, and in exchange they are fully decoupled from the 21-second cache. One screen now carries two different freshness regimes. API figures update slowly. Gate verdicts update immediately.

The change touched one dashboard file, 31 lines added and 1 removed. Not a single line of verdict logic was written. It is glue code that calls a script that was already there.

What the live check showed

Opening it over the tailnet on :8892, the gates stood at 2 of 4 unmet, and the lock banner and chips rendered as intended. In other words, at the time of writing cold-follow must stay off, and that fact is now visible on the dashboard instead of only in a terminal.

Self-check list

  • Is there a precondition check for some action that exists as code, but whose result is shown somewhere other than the screen you normally monitor?
  • Have you tied a cheap local check to the same cache interval as an expensive external API, so a verdict that should update instantly lags by the cache lifetime?
  • Does your dashboard list state numbers without ever saying whether the next action is permitted?

The honest part

This change only moved the verdict to a visible place. Whether four gates are a sufficient condition for cold-following to be safe is not something this commit can answer. I do not yet know which gates account for the 2 of 4 unmet, or how long it will take to satisfy them. Spawning a subprocess on every render is fine at the current scale, but if more people start refreshing the dashboard or the gate checks grow heavier, the cache conversation will have to come back.

Related