The simulator writes its own history, not yours

`--mock` is a fake radio, not a fake MeshTerm: what it adverted was recorded to
`meshterm.db` like anything a real companion said, so its four invented contacts
turned up in the mesh walk, the dashboard and the map of the mesh actually being
run. A `--mock` run that names no database now records to
`<config_dir>/meshterm-mock.db`, created on first use.

`--db` still wins — where a run records and which radio it talks to are different
questions, so an explicit path is a precedence rule and not the usage error
`--mock --port` is. `$MESHTERM_HOME` remains the only thing that isolates a run
whole: the contact and channel caches, the outbox and the remembered devices are
untouched by this, and the docs say so where they used to warn that `--mock`
recorded into the real history.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5 committed 2026-09-19 22:59:11 -04:00
1 parent bf29f73311
commit ff86ff059f
6 files changed
+101 -13

No files matched your search

+10
View File
@@ -9,6 +9,16 @@ one caveat SemVer makes for a leading zero: while the major version is still `0`
## [Unreleased]
### Changed
- **`--mock` keeps its invented mesh out of your history.** The simulator is a fake radio,
not a fake MeshTerm: what it adverted was written to `meshterm.db` like anything a real
companion said, and its four invented contacts then turned up in the mesh walk, the
dashboard and the map of the mesh you actually run. A `--mock` run now records to
`meshterm-mock.db` beside it, created on first use. Naming a database with `--db` still
wins, and `$MESHTERM_HOME` is still the only thing that isolates a run whole — the
contact and channel caches, the outbox and the remembered devices are untouched by this.
## [0.9.0] — 2026-09-22
**The first public release.** Everything in MeshTerm is new today, so instead of a list of
+12 -8
View File
@@ -57,8 +57,8 @@ With no command, MeshTerm launches the interactive menu instead.
| `--ble ADDRESS` | Bluetooth address of a companion; selects the BLE transport. |
| `--ble-pin PIN` | Pairing PIN, if the Bluetooth companion asks for one. |
| `--tcp HOST[:PORT]` | Network address of a TCP companion; selects the TCP transport. Default port 5000. |
| `--mock` | Use the built-in simulator instead of real hardware. Nothing transmits. |
| `--db PATH` | Use this SQLite database instead of `~/.meshterm/meshterm.db`. **It moves the database and nothing else** — see [Where MeshTerm keeps its state](#where-meshterm-keeps-its-state). |
| `--mock` | Use the built-in simulator instead of real hardware. Nothing transmits, and the run records to `meshterm-mock.db` rather than your real history. |
| `--db PATH` | Use this SQLite database instead of `~/.meshterm/meshterm.db` (or `meshterm-mock.db`, under `--mock`). **It moves the database and nothing else** — see [Where MeshTerm keeps its state](#where-meshterm-keeps-its-state). |
| `--json` | Print the answer as JSON instead of aligned text. |
| `--absolute` | Print times as ISO-8601 instants rather than relative ages, for this run. |
| `-q`, `--quiet` | Suppress console logging entirely (the log file still records). |
@@ -97,6 +97,7 @@ of two directories from the same shell.
| File | What it holds |
| --- | --- |
| `meshterm.db` | The history: every packet overheard, every trace walked, every message. **The one file `--db` moves.** |
| `meshterm-mock.db` | The same, for the simulator: where a `--mock` run records when it doesn't name a database itself. Absent until you run one. |
| `config.toml` | Machine setup — device profiles, where the database lives. Yours to write; MeshTerm only reads it. |
| `preferences.toml` | Your overrides of MeshTerm's own behaviour. Lists only what you changed. |
| `courier.json` | The outbox — queued messages, waiting and finished. |
@@ -114,7 +115,8 @@ weight but none of the identity: the contact and channel caches, the outbox, the
admin passwords and the remembered device all live beside it in the config directory and
would still be the ones you use every day. To run against a scratch state — a test, a
demo, a second radio — set `MESHTERM_HOME`, and set `--db` inside it if you want the
database somewhere else again.
database somewhere else again. The same holds for the database `--mock` picks for itself:
it keeps the simulator's invented mesh out of your history, not out of the caches.
---
@@ -140,11 +142,13 @@ Three things the simulator does that a radio does not, all visible in the sample
*forced* path (`--path "a1,d4"`) resolves properly. The forced form is what the samples
use, and what to build a golden file from.
> **`--mock` records into your real history unless you tell it not to.** The simulator is
> a fake radio, not a fake MeshTerm: what it adverts is written to the database and the
> caches like anything else, and invented nodes then turn up in the mesh walk, the
> dashboard and the map. Give it its own home — `MESHTERM_HOME=... meshterm --mock …` —
> or its own database with `--db`, and it leaves your mesh alone.
> **`--mock` records into its own history, not yours — but it still writes the caches.**
> The simulator is a fake radio, not a fake MeshTerm: what it adverts is written down like
> anything else. The database it writes is `meshterm-mock.db`, so its invented nodes stay
> out of your mesh walk, dashboard and map; naming a database with `--db` overrides that
> choice. The contact and channel caches, the outbox and the remembered devices are still
> the ones you use every day, so a demo that should touch nothing at all gets a home of
> its own: `MESHTERM_HOME=... meshterm --mock …`.
---
+3 -1
View File
@@ -53,7 +53,9 @@ Both live in `~/.meshterm`, along with everything else MeshTerm remembers: the S
history, the outbox, the contact and channel caches, stored admin passwords, and the log.
**`$MESHTERM_HOME` moves the whole directory**, which is the way to run a second radio —
or a `--mock` session — without touching the one you use every day. `--db` moves the
database alone; the rest stays where it was. [`docs/cli.md`](cli.md#where-meshterm-keeps-its-state)
database alone; the rest stays where it was. A `--mock` run that names neither writes to
`meshterm-mock.db` beside the real history, so the simulator's invented nodes never enter
it. [`docs/cli.md`](cli.md#where-meshterm-keeps-its-state)
lists every file.
Timestamps are stored as UTC and rendered in your local time. History older than the
+17 -3
View File
@@ -22,7 +22,7 @@ from . import __version__
from .context import AppContext
from .core import exitcodes, win32dll
from .core.admin_store import AdminStore
from .core.config import Settings
from .core.config import MOCK_DB_FILENAME, Settings
from .core.connection import DeviceCommandError, is_connection_lost
from .core.device_config import DeviceConfigError
from .core.device_store import DeviceStore
@@ -175,7 +175,9 @@ def main_callback(
help="Network address host[:port] of a TCP companion (selects the TCP transport)",
),
mock: bool = typer.Option(False, "--mock", help="Use the built-in simulator"),
db_path: Path | None = typer.Option(None, "--db", help="SQLite database path"),
db_path: Path | None = typer.Option(
None, "--db", help="SQLite database path (--mock records to meshterm-mock.db)"
),
json_output: bool = typer.Option(False, "--json", help="Machine-readable output"),
absolute: bool = typer.Option(
False, "--absolute", help="Print absolute timestamps instead of relative ages"
@@ -198,7 +200,8 @@ def main_callback(
ble_pin: Optional BLE pairing PIN for the Bluetooth companion.
tcp: Explicit network address ``host[:port]``, selecting the TCP transport.
mock: Whether to use the simulator instead of real hardware.
db_path: Override the database location.
db_path: Override the database location. Without it, a ``--mock`` run records to
the simulator's own database rather than the real history.
json_output: Print the answer as JSON instead of aligned text.
absolute: Print times as ISO-8601 instants rather than relative ages, for this run.
quiet: Suppress console logging (file logging continues).
@@ -232,6 +235,17 @@ def main_callback(
settings = Settings.load()
if db_path is not None:
settings.db_path = db_path
elif mock:
# The simulator is a fake radio, not a fake MeshTerm: everything it adverts was
# written to the real history like anything a companion said, and its four
# invented contacts then turned up in the mesh walk, the dashboard and the map of
# the mesh you actually run. A pretend radio gets a pretend history —
# ``<config_dir>/meshterm-mock.db``, created on first use — while ``--db`` still
# wins, because naming a database is a deliberate claim about where a run records.
# ``$MESHTERM_HOME`` remains the only thing that isolates a run whole: the contact
# and channel caches, the outbox and the remembered devices live beside the
# database either way (see docs/cli.md).
settings.db_path = settings.config_dir / MOCK_DB_FILENAME
# Two front ends, two consoles, and which one this process gets is settled here — a
# named subcommand is a scripted run, so it prints on the plain, colourless,
+12 -1
View File
@@ -34,6 +34,17 @@ else: # pragma: no cover - exercised only on 3.10
#: can move it between cases.
CONFIG_DIR_ENV = "MESHTERM_HOME"
#: The history's filename inside the config directory, when ``config.toml`` and ``--db``
#: both leave it alone.
DB_FILENAME = "meshterm.db"
#: The simulator's history, kept beside the real one and used by ``--mock`` runs that did
#: not name a database themselves. ``--mock`` is a fake radio, not a fake MeshTerm: what
#: the simulator adverts is recorded like anything a real companion said, so without a
#: database of its own its four invented contacts turn up in the mesh walk, the dashboard
#: and the map of the mesh you actually run.
MOCK_DB_FILENAME = "meshterm-mock.db"
def default_config_dir() -> Path:
"""Return the directory MeshTerm uses for config and data.
@@ -138,7 +149,7 @@ class Settings:
def __post_init__(self) -> None:
"""Derive dependent paths that were not explicitly provided."""
if self.db_path is None:
self.db_path = self.config_dir / "meshterm.db"
self.db_path = self.config_dir / DB_FILENAME
def resolve_profile(self, name: str | None) -> DeviceProfile | None:
"""Look up a profile by name, falling back to the default profile.
+47
View File
@@ -707,6 +707,53 @@ def test_a_closing_message_goes_to_stderr_too(run) -> None: # noqa: ANN001
assert "records stored" in result.stderr
# -- the simulator's own history ------------------------------------------------------
def test_mock_records_into_a_database_of_its_own(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A pretend radio gets a pretend history, so a demo run leaves the real mesh alone.
The simulator is a fake radio, not a fake MeshTerm: what it adverts used to be written
to ``meshterm.db`` like anything a companion said, and its invented contacts then sat
in the mesh walk, the dashboard and the map of the mesh actually being run. The run
still records — the history is how a ``--mock`` session's own tools read back what it
heard — just not there.
"""
from meshterm.cli import app
home = tmp_path / "home"
monkeypatch.setenv("MESHTERM_HOME", str(home))
result = CliRunner().invoke(app, ["--mock", "info"])
assert result.exit_code == exitcodes.OK, result.output
assert (home / "meshterm-mock.db").exists()
assert not (home / "meshterm.db").exists()
def test_an_explicit_db_still_wins_under_mock(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""``--db`` names one exact database, which is a claim the simulator does not override.
Unlike ``--port``, it is not a contradictory one: where a run records and which radio
it talks to are different questions, so this is a precedence rule and not a usage
error. It is also what the suite's own ``run`` fixture depends on.
"""
from meshterm.cli import app
home = tmp_path / "home"
monkeypatch.setenv("MESHTERM_HOME", str(home))
named = tmp_path / "named.db"
result = CliRunner().invoke(app, ["--mock", "--db", str(named), "info"])
assert result.exit_code == exitcodes.OK, result.output
assert named.exists()
assert not (home / "meshterm-mock.db").exists()
assert not (home / "meshterm.db").exists()
# -- devices under --mock -------------------------------------------------------------