mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-10-22 21:22:04 +00:00
Bring security to the top
This commit is contained in:
1 parent
05815e87bf
commit
6285bced57
1 file changed
+65
-60
@@ -10,8 +10,66 @@ it admin by running `setperm <server_pubkey> 3` on their repeater. Because every
|
||||
through that one server identity, MeshTender can mediate *which users* may control a repeater — so
|
||||
a repeater can be shared without ever handing out keys, and access can be revoked instantly.
|
||||
|
||||
AGPL-3.0, with the name and logo reserved. That combination is deliberate and the reasoning is
|
||||
worth reading before you deploy your own copy: [License, and why AGPL](#license-and-why-agpl).
|
||||
## Why it holds full admin, and why you shouldn't host your own
|
||||
|
||||
MeshTender asks for the strongest access a MeshCore repeater can grant. That's forced by the
|
||||
protocol rather than chosen for convenience, and it shapes who should be running the server — and,
|
||||
in turn, why this is AGPL rather than the MIT license it would otherwise carry. Worth reading before
|
||||
you deploy a copy of your own.
|
||||
|
||||
### The permission problem it exists to solve
|
||||
|
||||
MeshCore's repeater ACL has four roles — guest (0), read-only (1), read-write (2), admin (3) — but
|
||||
only one of them can run commands. A remote CLI command is processed if and only if the sender is
|
||||
admin (`if (type == PAYLOAD_TYPE_TXT_MSG && … && client->isAdmin())`, `simple_repeater/MyMesh.cpp`).
|
||||
There is no "may set transmit power but not rewrite the region table" role, and guest is available
|
||||
to anyone with a blank password. So any tool that runs commands on your repeater on your behalf
|
||||
holds **full admin**, because nothing weaker can do anything at all.
|
||||
|
||||
MeshTender's whole job is to put the missing permission system on top of that single grant: a
|
||||
per-user command allowlist that denies by default, a per-org ceiling the repeater owner opts into,
|
||||
an audit log of who ran what and when, and revocation that takes effect immediately without
|
||||
touching the hardware. An org can share repeater administration under limits MeshCore itself cannot
|
||||
express.
|
||||
|
||||
### Why you shouldn't host your own
|
||||
|
||||
Those limits are enforced by the server, which means whoever runs the server is *outside* them:
|
||||
they hold the key, and every restriction is theirs to lift. That is unavoidable, and it decides
|
||||
who should be running it.
|
||||
|
||||
If an org's admins host their own instance, then that org's members are back to trusting those
|
||||
admins with unrestricted access to their repeaters — the exact situation MeshTender exists to fix,
|
||||
now with a UI implying the restrictions are real. The guarantee only holds when whoever runs the
|
||||
server isn't a party to the trust question the software is meant to settle — which, for an org's
|
||||
own repeaters, its own admins always are. For most MeshCore projects self-hosting is the obviously
|
||||
correct answer; here it quietly removes the point of the software.
|
||||
|
||||
### Which means trusting me, so here is what that is worth
|
||||
|
||||
I'm aware this is a hard sell, and it should be. What I can offer:
|
||||
|
||||
- **Distance.** Administering a repeater takes a modem within LoRa range of it. I'm not near your
|
||||
mesh, so the identity key on my server is not usefully exploitable by me except *through this
|
||||
app* — where it is logged.
|
||||
- **The audit log.** Every command carries who sent it and when, per repeater, visible to the
|
||||
people who granted access.
|
||||
- **Revocation on the hardware.** You granted access on the device with `setperm`, and you can
|
||||
withdraw it there the same way, whatever the server does. See the [docs](https://meshtender.com/docs).
|
||||
- **Open source.** The thing above is only checkable if you can read what's running, which is why
|
||||
this repository exists.
|
||||
- **Reproducible builds.** Rebuild the commit `/version` reports and confirm the digest matches, so
|
||||
"the source is public" and "the source is what's deployed" are separate, checkable claims. See
|
||||
[Verifying a build](#verifying-a-build).
|
||||
|
||||
None of that is proof. Together it's the strongest position I know how to take given a protocol
|
||||
with no permissions.
|
||||
|
||||
AGPL closes the remaining hole. If someone does run a modified copy as a service, section 13
|
||||
requires that its users can get *its* source — so their instance can be audited exactly the way
|
||||
this one can, rather than being an unauditable server wearing familiar software's clothes. The
|
||||
mechanics of that, and what a fork has to change, are in
|
||||
[License, self-hosting, and forking](#license-self-hosting-and-forking).
|
||||
|
||||
## How the hardware control path works
|
||||
|
||||
@@ -299,67 +357,14 @@ Two things change the digest, and both are intentional:
|
||||
- **The platform must match.** `mise run image` targets `linux/amd64`; pass
|
||||
`--platform linux/arm64` to verify that variant.
|
||||
|
||||
## License, and why AGPL
|
||||
## License, self-hosting, and forking
|
||||
|
||||
MeshTender is free software under the **GNU AGPL v3.0** ([`LICENSE`](LICENSE)). Its **name and
|
||||
logo are not** ([`TRADEMARKS.md`](TRADEMARKS.md)). Both of those follow from the same fact about
|
||||
what this app is, so it's worth explaining rather than leaving as a puzzle.
|
||||
logo are not** ([`TRADEMARKS.md`](TRADEMARKS.md)). Why that pairing, rather than the MIT license
|
||||
this project would otherwise carry, is the argument at the top:
|
||||
[Why it holds full admin](#why-it-holds-full-admin-and-why-you-shouldnt-host-your-own).
|
||||
|
||||
### The permission problem MeshTender exists to solve
|
||||
|
||||
MeshCore's repeater ACL has four roles — guest (0), read-only (1), read-write (2), admin (3) — but
|
||||
only one of them can run commands. A remote CLI command is processed if and only if the sender is
|
||||
admin (`if (type == PAYLOAD_TYPE_TXT_MSG && … && client->isAdmin())`, `simple_repeater/MyMesh.cpp`).
|
||||
There is no "may set transmit power but not rewrite the region table" role, and guest is available
|
||||
to anyone with a blank password. So any tool that runs commands on your repeater on your behalf
|
||||
holds **full admin**, because nothing weaker can do anything at all.
|
||||
|
||||
MeshTender's whole job is to put the missing permission system on top of that single grant: a
|
||||
per-user command allowlist that denies by default, a per-org ceiling the repeater owner opts into,
|
||||
an audit log of who ran what and when, and revocation that takes effect immediately without
|
||||
touching the hardware. An org can share repeater administration under limits MeshCore itself cannot
|
||||
express.
|
||||
|
||||
### Why that argues for one instance, not many
|
||||
|
||||
Those limits are enforced by the server, which means whoever runs the server is *outside* them:
|
||||
they hold the key, and every restriction is theirs to lift. That is unavoidable, and it decides
|
||||
who should be running it.
|
||||
|
||||
If an org's admins host their own instance, then that org's members are back to trusting those
|
||||
admins with unrestricted access to their repeaters — the exact situation MeshTender exists to fix,
|
||||
now with a UI implying the restrictions are real. The configuration that actually delivers the
|
||||
guarantee is one instance run by someone who isn't a party to any org's internal trust question.
|
||||
For most MeshCore projects self-hosting is the obviously correct answer; here it quietly removes
|
||||
the point of the software.
|
||||
|
||||
### Which means trusting me, so here is what that is worth
|
||||
|
||||
I'm aware this is a hard sell, and it should be. What I can offer:
|
||||
|
||||
- **Distance.** Administering a repeater takes a modem within LoRa range of it. I'm not near your
|
||||
mesh, so the identity key on my server is not usefully exploitable by me except *through this
|
||||
app* — where it is logged.
|
||||
- **The audit log.** Every command carries who sent it and when, per repeater, visible to the
|
||||
people who granted access.
|
||||
- **Revocation on the hardware.** You granted access on the device with `setperm`, and you can
|
||||
withdraw it there the same way, whatever the server does. See the [docs](https://meshtender.com/docs).
|
||||
- **Open source.** The thing above is only checkable if you can read what's running, which is why
|
||||
this repository exists.
|
||||
- **Reproducible builds.** Rebuild the commit `/version` reports and confirm the digest matches, so
|
||||
"the source is public" and "the source is what's deployed" are separate, checkable claims. See
|
||||
[Verifying a build](#verifying-a-build).
|
||||
|
||||
None of that is proof. Together it's the strongest position I know how to take given a protocol
|
||||
with no permissions.
|
||||
|
||||
AGPL closes the remaining hole. If someone does run a modified copy as a service, section 13
|
||||
requires that its users can get *its* source — so their instance can be audited exactly the way
|
||||
this one can, rather than being an unauditable server wearing familiar software's clothes.
|
||||
|
||||
### Self-hosting and forking
|
||||
|
||||
The license permits it and this file isn't going to pretend otherwise; the paragraphs above are an
|
||||
Self-hosting is permitted and this file isn't going to pretend otherwise — the case above is an
|
||||
argument, not a restriction. If you run a modified copy, the AGPL asks you to publish your source
|
||||
and offer it to your users — `SourceURL` in `internal/web/version.go` is the constant behind the
|
||||
footer link and the `/version` field, so point it at your fork. Separately, the MeshTender name and
|
||||
|
||||
Reference in new issue
Block a user