Magnemite
Features

MITM configs

The config file each MITM needs, written onto every box in a group.

Installing a MITM is half the job. Aegis, Atlas and GoCheats all read a JSON file next to themselves and do nothing useful until it has the correct parameters.

Youu can fill that file on the device group, in Settings → Device groups → MITM data.

What a group declares

FieldWhat it is
MITM packageThe scanner these boxes run. Also what the MITM column shows.
Column headerShorter name for that column. Defaults to the last dotted segment.
Config file pathWhere the file goes on the box, eg. /data/local/tmp/aegis_config.json.
Config JSONThe file's contents, with placeholders.
Install on joinBuilds every box joining this group is given, before anybody asks. Empty by default.

The Fill from buttons put in the real shapes for Aegis, Atlas, GoCheats and Cosmog, paths and key sets. Aegis (Redroid) and Cosmog (Redroid) are the same two for a group of containers, with "workers": "{{device.workers}}" instead of a fixed number, so each container gets the count its machine was scaled with.

Install on join

A box that has just enrolled is in its group with nothing on it — a container that has only now come up, or hardware somebody has just flashed. Everything else the fleet does assumes the scanner and its MITM are already there, so the first move after every scale-up was a trip to Manual deploy to install the same two builds on the same kind of box again.

Put those builds in the group's Install on join and the box does it itself. A container coming up at four in the morning after a scale-up arrives with the scanner on it.

This is the one control here that installs with nobody watching

It is empty on every group and nothing happens until somebody fills it — the same care the monitoring rules are seeded disabled with. An upgrade that starts installing on its own is the version of this nobody asked for.

What it does, precisely:

  • One build per package. Two builds of one package would mean a new box installing one over the other in whichever order the scheduler happened to pick, so the second is refused with the first one named.
  • A build, not "the newest". Installing whatever is latest is what a rollout is for, and answering it here would make every new box the first to try a version nobody chose. This answers the narrower question — what does a box need before it is any use — and that is a build somebody picked and has already seen work.
  • Cached builds only, because that is what a rollout accepts. One listed by a source and not yet downloaded is refused when you add it, rather than at the one moment nobody is watching.
  • Each build is its own rollout, so they appear on Rollouts noted joined its group and are followed, retried and cancelled like any other.
  • One after the other, the MITM last. The builds go in the order they were added, except the group's MITM package, which always goes last, and each waits for the one before it to succeed: until then its rollout shows as pending, after whatever it waits for. The MITM's install is the one that writes its config and starts it, so it wants the game already on the box.
  • No hooks, but the one that starts the MITM. The game goes in with neither hook: on a new box there is nothing running to stop, and the MITM a post-install hook would start is not installed yet. The MITM's own install runs only the post-install hook, after its config is written.
  • A failed game holds the MITM back. It stays pending rather than going in without it. Retry on the failed job lets it go once that succeeds, and Start now on the waiting rollout sends it regardless.
  • Not before the box has settled. Nothing starts until the box has been up for Settings → Tuning → New boxes settle for (120 s), counted from its boot. A minute-old Android is still compiling its packages and settling Magisk, which is a poor moment to push a 170 MB game into. The job says held 118s while the box settles after booting; 0 installs on enrolment.
  • A box that already has one gets nothing for it. The check is what the box reports installed, not whether it has enrolled before — so a re-enrolment queues nothing, and a box whose /data was wiped gets everything again, which is exactly when it needs to.
  • Removing a build from the list uninstalls nothing. The boxes that got it keep it; the list only says what the next box gets.

A box that enrolled on a token without auto-approve is skipped, because a rollout cannot target an unapproved box. It gets its builds the moment you approve it.

Putting boxes in a group

One box: the Group select on its device page. Many: pick them on Fleet and press Change group in the bar at the bottom, the same place Rename lives. The dialog says where the selection is now and how many actually move, boxes already in the target group are left alone.

Moving a box changes what it installs, not what is on it: the new group's config, its install hooks and its concurrency cap apply from the next deploy. Nothing is pushed at the moment of the move.

Placeholders

Anything a box has to say about itself is a placeholder, substituted per device before the file is written.

{
  "authBearer": "s3cret",
  "deviceName": "{{device.name}}",
  "rotomUrl": "http://rotom.lan:7072",
  "workers": 8
}

Available: device.id, device.name, device.serial, device.model, device.manufacturer, device.androidVersion, device.abi, device.localIp, device.publicIp, device.rotomOrigin, device.rotomDeviceId, device.workers, group.name.

device.workers is how many workers a Redroid container is configured for: its own number, else its machine's, else Settings → Tuning, as set on the Machines page. It is what makes a machine's capacity a number the dashboard owns — containers times workers — rather than one hard-coded into the group's config, where it would be the same for every machine in the group.

A group that mixes containers and real hardware

Only a container has a worker count, so {{device.workers}} is empty on a TV box — and an empty known placeholder is refused when the config is rendered, naming the box. That is the refusal working, and the fix is a group per kind rather than a default nobody chose.

Two things are refused rather than papered over:

  • An unknown placeholder. {{device.nmae}} would otherwise be written through literally, and a scanner started against that is worse off than one never configured. Caught at Save.
  • A known placeholder with no value on a box. Substituting "" is how a whole site ends up reporting to Rotom under the same blank origin. Caught when the config is rendered for a deploy, naming the box and the field, and logged as a warning on that box's job.

rotomOrigin is empty until Rotom matches the box

So a first deploy on a fresh fleet often refuses it on most boxes. That is the refusal working. Either use {{device.name}}, which every box has, or deploy again once the Rotom sync has run.

When it is written

With the install. A rollout of a group's own MITM carries that group's config (if toggled on). The agent writes it after the install verifies and before the post-install hook. A join install is such a rollout, so the MITM in a group's Install on join arrives already configured.

By hand, on one box. The device page's Execute has two buttons built from the box's group:

  • Show MITM config — cat of the group's config path, so you see the file as it is on the box right now.
  • Rewrite MITM config… — the group's config rendered for this box, in an editor. Write and restart writes it with nothing installed: the box stops the MITM with the group's pre-install hook, writes the file exactly as an install does, and starts it again with the post-install hook. A group without hooks gets am force-stop and the app's launcher. An edit goes to this box alone; the group keeps its own template.

The restart is the point of the second one, because a running MITM does not re-read a file it has already read. It needs an agent that knows push_config (1.0.5 or later); an older one says so instead of offering the button.

When a container's workers change. A Redroid box whose worker count moves — its own, its machine's, or the one in Settings — has its config rewritten by the hub, the way Rewrite config does it, a few boxes at a time. Only a config that says {{device.workers}} is rewritten; any other is the same file either way.

Saving a group writes to the database and nothing else. An edited config reaches the fleet on the next deploy of that group's MITM (if toggled on), or a box at a time with Rewrite config, and not before.

How it lands

The agent takes the config object off the install job, or off push_config for a rewrite by hand, and:

  1. Writes to a temp file and renames it into place, so a scanner reading the file while it is replaced sees the old bytes or the new ones, never a half-written middle.
  2. Reads the file back and reports its sha256. That answers "what is on the box", where hashing what the hub sent would only answer "what did the hub mean".

A config that cannot be written fails the job, rather than leaving a scanner running on someone else's settings. The downloaded bundle is kept, so the retry costs an attempt and not a second download.

The config holds credentials

authBearer, deviceAuthToken, an email address. It is never written to the hub's log, never into an audit entry.

Lineage

The paths and the per-box name substitution are both aconf's, which has been doing this on the same hardware for years. Credits to @TechG3n for the original idea.

On this page