Skip to content

Commit 41511fc

Browse files
author
Ivan
committed
feat: introduce portable data directory support with MESHCHAT_DATA_DIR and related CLI flags
1 parent 2603a45 commit 41511fc

11 files changed

Lines changed: 176 additions & 13 deletions

File tree

docs/agents/overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ Common overrides (CLI flags usually mirror these):
276276
| `MESHCHAT_HEADLESS` / `--headless` | Do not auto-launch a browser |
277277
| `MESHCHAT_STORAGE_DIR` / `--storage-dir` | App storage root |
278278
| `MESHCHAT_RETICULUM_CONFIG_DIR` / `--reticulum-config-dir` | Reticulum config dir |
279+
| `MESHCHAT_DATA_DIR` / `--data-dir` | Portable root for storage + Reticulum when those two are unset |
279280
| `MESHCHAT_PUBLIC_DIR` / `--public-dir` | Frontend assets dir |
280281
| `MESHCHAT_AUTH` / `--auth` | Enable web auth |
281282
| `MESHCHAT_NO_HTTPS` / `--no-https` | HTTP instead of HTTPS |

docs/en/getting-started.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@ Use the search bar to query both sets at once. MeshChatX guide text is currently
7979

8080
## Storage locations
8181

82-
| Data | Typical path |
82+
| Data | Typical path (CLI default) |
8383
| --------------------- | -------------------------------------------------- |
84-
| MeshChatX app data | `~/.reticulum-meshchatx/` on Linux and macOS |
85-
| Reticulum config | `~/.reticulum/` |
84+
| MeshChatX app data | `./storage` (or `--storage-dir` / `MESHCHAT_STORAGE_DIR`) |
85+
| Reticulum config | `~/.reticulum` (or `--reticulum-config-dir` / `MESHCHAT_RETICULUM_CONFIG_DIR`) |
86+
| Portable bundle | `<data-dir>/storage` and `<data-dir>/.reticulum` when using `--data-dir` / `MESHCHAT_DATA_DIR` |
87+
| Desktop Electron data | `~/.reticulum-meshchatx` and `~/.reticulum` unless overridden at launch |
8688
| Per-identity database | `<storage>/identities/<identity_hash>/database.db` |
8789
| Docker volume | `meshchatx-config` mounted at `/config` |
8890

docs/en/installation.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ Common flags and environment variables:
139139
| `--ssl-key` | `MESHCHAT_SSL_KEY` | auto | TLS private key path |
140140
| `--headless` | `MESHCHAT_HEADLESS` | false | Do not open a browser |
141141
| `--auth` | `MESHCHAT_AUTH` | false | Require HTTP basic auth for the UI |
142-
| `--storage-dir` | `MESHCHAT_STORAGE_DIR` | `./storage` | Application data directory |
143-
| `--reticulum-config-dir` | (see `--help`) | `~/.reticulum` | Reticulum configuration |
142+
| `--storage-dir` | `MESHCHAT_STORAGE_DIR` | `./storage` | Application data directory |
143+
| `--reticulum-config-dir` | `MESHCHAT_RETICULUM_CONFIG_DIR` | `~/.reticulum` | Reticulum configuration |
144+
| `--data-dir` | `MESHCHAT_DATA_DIR` | none | Portable root (`storage` + `.reticulum` subdirs when the two paths above are unset) |
144145
| `--identity-file` | `MESHCHAT_IDENTITY_FILE` | none | Load identity from file |
145146
| `--rns-log-level` | `MESHCHAT_RNS_LOG_LEVEL` | none | Reticulum log level |
146147
| `--auto-recover` | `MESHCHAT_AUTO_RECOVER` | false | Attempt SQLite recovery on start |
@@ -149,6 +150,35 @@ Common flags and environment variables:
149150

150151
CLI flags override environment variables when both are set.
151152

153+
### Portable installs (removable media, Tails, USB sticks)
154+
155+
MeshChatX already supports relocating all persistent state off the home directory. Use either explicit paths or a single data root:
156+
157+
```bash
158+
export PERSIST="/media/amnesia/Persistent/meshchatx"
159+
mkdir -p "$PERSIST"
160+
161+
meshchatx --headless \
162+
--data-dir="$PERSIST"
163+
```
164+
165+
That creates and uses `$PERSIST/storage` for MeshChatX (identities, SQLite, plugins) and `$PERSIST/.reticulum` for Reticulum interfaces and transport config. You can set the same layout with environment variables:
166+
167+
```bash
168+
export MESHCHAT_DATA_DIR="$PERSIST"
169+
meshchatx --headless
170+
```
171+
172+
Equivalent explicit form (overrides any `--data-dir` subpaths when you set these yourself):
173+
174+
```bash
175+
meshchatx --headless \
176+
--storage-dir="$PERSIST/storage" \
177+
--reticulum-config-dir="$PERSIST/.reticulum"
178+
```
179+
180+
On Windows portable Electron builds, storage and Reticulum config default next to the `.exe` when `PORTABLE_EXECUTABLE_DIR` is set. On Linux and macOS desktop builds, Electron still defaults to `~/.reticulum-meshchatx` and `~/.reticulum` unless you pass the flags above (or set `MESHCHAT_DATA_DIR` / `MESHCHAT_STORAGE_DIR` / `MESHCHAT_RETICULUM_CONFIG_DIR` in the environment before launch).
181+
152182
## Reticulum manual bundle
153183

154184
The Reticulum HTML manual is fetched from the upstream website **master** branch at build time by default (clearnet ZIP). There is no in-app clearnet refresh. After cloning the repository, or before packaging a release, run:

electron/shellPathGuard.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ function isAllowedShellPath(targetPath, ctx) {
9393
const userArgv = ctx.getUserProvidedArguments(process.argv);
9494
add(parseArgvFlag(userArgv, "--storage-dir"));
9595
add(parseArgvFlag(userArgv, "--reticulum-config-dir"));
96+
const dataDir =
97+
parseArgvFlag(userArgv, "--data-dir") ||
98+
(process.env.MESHCHAT_DATA_DIR && String(process.env.MESHCHAT_DATA_DIR).trim()) ||
99+
null;
100+
if (dataDir) {
101+
add(path.join(dataDir, "storage"));
102+
add(path.join(dataDir, ".reticulum"));
103+
}
96104

97105
for (const root of roots) {
98106
if (root && isResolvedPathUnderRoot(resolved, root)) {

meshchatx.rsm

103 Bytes
Binary file not shown.

meshchatx/meshchat.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@
289289
get_file_path,
290290
is_path_within_dir,
291291
resolve_log_dir,
292+
resolve_meshchat_data_roots,
292293
safe_path_under_dir,
293294
)
294295
from meshchatx.src.path_utils import (
@@ -10504,6 +10505,16 @@ def main():
1050410505
type=str,
1050510506
help="Restore the database from the given path (zip or db file) and exit.",
1050610507
)
10508+
parser.add_argument(
10509+
"--data-dir",
10510+
type=str,
10511+
default=os.environ.get("MESHCHAT_DATA_DIR"),
10512+
help=(
10513+
"Portable data root: uses <dir>/storage and <dir>/.reticulum when "
10514+
"--storage-dir and --reticulum-config-dir are not set. "
10515+
"Can also be set via MESHCHAT_DATA_DIR."
10516+
),
10517+
)
1050710518
parser.add_argument(
1050810519
"--reticulum-config-dir",
1050910520
type=str,
@@ -10604,6 +10615,12 @@ def main():
1060410615
if args.no_crash_recovery:
1060510616
recovery.disable()
1060610617

10618+
args.storage_dir, args.reticulum_config_dir = resolve_meshchat_data_roots(
10619+
data_dir=args.data_dir,
10620+
storage_dir=args.storage_dir,
10621+
reticulum_config_dir=args.reticulum_config_dir,
10622+
)
10623+
1060710624
planned_storage_dir = args.storage_dir
1060810625
if not planned_storage_dir:
1060910626
# On Android, prefer user-accessible external storage

meshchatx/src/backend/http/routes/interfaces.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,9 +1859,6 @@ async def export_interfaces(request):
18591859
status=500,
18601860
)
18611861

1862-
# preview importable interfaces
1863-
1864-
# preview importable interfaces
18651862
@routes.post("/api/v1/reticulum/interfaces/import-preview")
18661863
async def import_interfaces_preview(request):
18671864
try:

meshchatx/src/frontend/public/meshchatx-docs/en/getting-started.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@ Use the search bar to query both sets at once. MeshChatX guide text is currently
7979

8080
## Storage locations
8181

82-
| Data | Typical path |
82+
| Data | Typical path (CLI default) |
8383
| --------------------- | -------------------------------------------------- |
84-
| MeshChatX app data | `~/.reticulum-meshchatx/` on Linux and macOS |
85-
| Reticulum config | `~/.reticulum/` |
84+
| MeshChatX app data | `./storage` (or `--storage-dir` / `MESHCHAT_STORAGE_DIR`) |
85+
| Reticulum config | `~/.reticulum` (or `--reticulum-config-dir` / `MESHCHAT_RETICULUM_CONFIG_DIR`) |
86+
| Portable bundle | `<data-dir>/storage` and `<data-dir>/.reticulum` when using `--data-dir` / `MESHCHAT_DATA_DIR` |
87+
| Desktop Electron data | `~/.reticulum-meshchatx` and `~/.reticulum` unless overridden at launch |
8688
| Per-identity database | `<storage>/identities/<identity_hash>/database.db` |
8789
| Docker volume | `meshchatx-config` mounted at `/config` |
8890

meshchatx/src/frontend/public/meshchatx-docs/en/installation.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ Common flags and environment variables:
139139
| `--ssl-key` | `MESHCHAT_SSL_KEY` | auto | TLS private key path |
140140
| `--headless` | `MESHCHAT_HEADLESS` | false | Do not open a browser |
141141
| `--auth` | `MESHCHAT_AUTH` | false | Require HTTP basic auth for the UI |
142-
| `--storage-dir` | `MESHCHAT_STORAGE_DIR` | `./storage` | Application data directory |
143-
| `--reticulum-config-dir` | (see `--help`) | `~/.reticulum` | Reticulum configuration |
142+
| `--storage-dir` | `MESHCHAT_STORAGE_DIR` | `./storage` | Application data directory |
143+
| `--reticulum-config-dir` | `MESHCHAT_RETICULUM_CONFIG_DIR` | `~/.reticulum` | Reticulum configuration |
144+
| `--data-dir` | `MESHCHAT_DATA_DIR` | none | Portable root (`storage` + `.reticulum` subdirs when the two paths above are unset) |
144145
| `--identity-file` | `MESHCHAT_IDENTITY_FILE` | none | Load identity from file |
145146
| `--rns-log-level` | `MESHCHAT_RNS_LOG_LEVEL` | none | Reticulum log level |
146147
| `--auto-recover` | `MESHCHAT_AUTO_RECOVER` | false | Attempt SQLite recovery on start |
@@ -149,6 +150,35 @@ Common flags and environment variables:
149150

150151
CLI flags override environment variables when both are set.
151152

153+
### Portable installs (removable media, Tails, USB sticks)
154+
155+
MeshChatX already supports relocating all persistent state off the home directory. Use either explicit paths or a single data root:
156+
157+
```bash
158+
export PERSIST="/media/amnesia/Persistent/meshchatx"
159+
mkdir -p "$PERSIST"
160+
161+
meshchatx --headless \
162+
--data-dir="$PERSIST"
163+
```
164+
165+
That creates and uses `$PERSIST/storage` for MeshChatX (identities, SQLite, plugins) and `$PERSIST/.reticulum` for Reticulum interfaces and transport config. You can set the same layout with environment variables:
166+
167+
```bash
168+
export MESHCHAT_DATA_DIR="$PERSIST"
169+
meshchatx --headless
170+
```
171+
172+
Equivalent explicit form (overrides any `--data-dir` subpaths when you set these yourself):
173+
174+
```bash
175+
meshchatx --headless \
176+
--storage-dir="$PERSIST/storage" \
177+
--reticulum-config-dir="$PERSIST/.reticulum"
178+
```
179+
180+
On Windows portable Electron builds, storage and Reticulum config default next to the `.exe` when `PORTABLE_EXECUTABLE_DIR` is set. On Linux and macOS desktop builds, Electron still defaults to `~/.reticulum-meshchatx` and `~/.reticulum` unless you pass the flags above (or set `MESHCHAT_DATA_DIR` / `MESHCHAT_STORAGE_DIR` / `MESHCHAT_RETICULUM_CONFIG_DIR` in the environment before launch).
181+
152182
## Reticulum manual bundle
153183

154184
The Reticulum HTML manual is fetched from the upstream website **master** branch at build time by default (clearnet ZIP). There is no in-app clearnet refresh. After cloning the repository, or before packaging a release, run:

meshchatx/src/path_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,31 @@ def resolve_log_dir():
9393
return None
9494

9595

96+
def resolve_meshchat_data_roots(
97+
*,
98+
data_dir: str | None,
99+
storage_dir: str | None,
100+
reticulum_config_dir: str | None,
101+
) -> tuple[str | None, str | None]:
102+
"""Apply MESHCHAT_DATA_DIR / --data-dir when explicit roots are unset.
103+
104+
Layout under data_dir:
105+
storage/ MeshChatX databases and identity tree
106+
.reticulum/ Reticulum interfaces and transport config
107+
"""
108+
root = (data_dir or os.environ.get("MESHCHAT_DATA_DIR") or "").strip()
109+
if not root:
110+
return storage_dir, reticulum_config_dir
111+
root = os.path.abspath(os.path.expanduser(root))
112+
out_storage = storage_dir
113+
out_reticulum = reticulum_config_dir
114+
if not out_storage:
115+
out_storage = os.path.join(root, "storage")
116+
if not out_reticulum:
117+
out_reticulum = os.path.join(root, ".reticulum")
118+
return out_storage, out_reticulum
119+
120+
96121
def request_client_ip(
97122
request: web.Request,
98123
trusted_proxy_cidrs: str | None = None,

0 commit comments

Comments
 (0)