From 47159a4b25ad1b9be3112d39032132c5e72ef9f6 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Sun, 11 Oct 2026 18:21:10 +0100 Subject: [PATCH 01/28] docs(tunnel): say LPM CLI keeps captures out of git Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 2 +- content/docs/infra/tunneling.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 86ae04ed..8da34df8 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -281,7 +281,7 @@ History lives in `.lpm/inspector.db` under the command directory. Run history commands from the directory that started the tunnel. Captured data can contain full bodies, cookies, authorization headers, and signature headers. -Add `.lpm/inspector.db*` to `.gitignore`. Database files use mode `0600` on Unix and inherit project ACLs on Windows. +Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, so captures stay out of git wherever the project sits in a repository. It leaves a linked `.lpm/.gitignore` as it is. An ignore rule doesn't stop a file git already tracks from being committed, so the capture warning names any capture file git tracks; run `git rm --cached .lpm/inspector.db*` and commit. Database files use mode `0600` on Unix and inherit project ACLs on Windows. ```bash lpm tunnel inspect # newest 20 captures diff --git a/content/docs/infra/tunneling.mdx b/content/docs/infra/tunneling.mdx index e9420e02..958dbbc7 100644 --- a/content/docs/infra/tunneling.mdx +++ b/content/docs/infra/tunneling.mdx @@ -254,7 +254,7 @@ LPM CLI records forwarded requests and local responses in the current project. O The database can contain full headers, request bodies, response bodies, and session details. The tunnel service does not store these request bodies. -Protect the database as sensitive development data. Do not commit it to source control. +Protect the database as sensitive development data. LPM CLI adds `inspector.db*` to `.lpm/.gitignore` before it creates the database, and warns when git already tracks it. Live capture memory has fixed limits. The request buffer retains up to 1,000 requests or 64 MiB. From a48dc35c2f0d24843f6c17fd2afb3350cc242702 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Sun, 11 Oct 2026 18:21:10 +0100 Subject: [PATCH 02/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 8ed26fec..6d91594f 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,11 +241,11 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-09-20T10:39:33+01:00" + "modified": "2026-10-11T18:21:10+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-09-20T10:39:33+01:00" + "modified": "2026-10-11T18:21:10+01:00" }, "installation.mdx": { "published": "2026-05-01T09:47:04+01:00", From f4edbb65a8b885588b4ff32f8f3a6232e73f8e8f Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Sun, 11 Oct 2026 20:01:50 +0100 Subject: [PATCH 03/28] docs(tunnel): say what the capture warning checks, and how to clean up a committed capture Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 8 +++++++- content/docs/infra/tunneling.mdx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 8da34df8..296b816b 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -281,7 +281,13 @@ History lives in `.lpm/inspector.db` under the command directory. Run history commands from the directory that started the tunnel. Captured data can contain full bodies, cookies, authorization headers, and signature headers. -Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, so captures stay out of git wherever the project sits in a repository. It leaves a linked `.lpm/.gitignore` as it is. An ignore rule doesn't stop a file git already tracks from being committed, so the capture warning names any capture file git tracks; run `git rm --cached .lpm/inspector.db*` and commit. Database files use mode `0600` on Unix and inherit project ACLs on Windows. +Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already has the rule, so git ignores the database and its `-wal` and `-shm` files wherever the project sits in a repository. It leaves a linked or read-only `.lpm/.gitignore` as it is. The capture files are owner-only on Unix and inherit project ACLs on Windows. LPM CLI refuses a linked `.lpm` directory or capture file, since a cloned repository could point it anywhere. + +At each start, the capture warning asks git how it treats the capture files: + +- When git ignores them, the warning says `.lpm/.gitignore` keeps them out of git. +- When git doesn't ignore them, for example because `.lpm/.gitignore` is a link, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. +- When git tracks them, the warning names them and gives the command to stop tracking them, such as `git rm --cached -- '.lpm/inspector.db'`. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. ```bash lpm tunnel inspect # newest 20 captures diff --git a/content/docs/infra/tunneling.mdx b/content/docs/infra/tunneling.mdx index 958dbbc7..f22eee52 100644 --- a/content/docs/infra/tunneling.mdx +++ b/content/docs/infra/tunneling.mdx @@ -254,7 +254,7 @@ LPM CLI records forwarded requests and local responses in the current project. O The database can contain full headers, request bodies, response bodies, and session details. The tunnel service does not store these request bodies. -Protect the database as sensitive development data. LPM CLI adds `inspector.db*` to `.lpm/.gitignore` before it creates the database, and warns when git already tracks it. +Protect the database as sensitive development data. Do not commit it to source control. LPM CLI adds `inspector.db*` to `.lpm/.gitignore` before it creates the database, and warns when git tracks the capture files or doesn't ignore them. Live capture memory has fixed limits. The request buffer retains up to 1,000 requests or 64 MiB. From 616de8b797181af9da1f07ea6c6f5cf59ea4244c Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Sun, 11 Oct 2026 20:01:50 +0100 Subject: [PATCH 04/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 6d91594f..18dfc9aa 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,11 +241,11 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-11T18:21:10+01:00" + "modified": "2026-10-11T20:01:50+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-11T18:21:10+01:00" + "modified": "2026-10-11T20:01:50+01:00" }, "installation.mdx": { "published": "2026-05-01T09:47:04+01:00", From 649b748e1196ba4fce50476fb5e29f01ec4a5121 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Sun, 11 Oct 2026 23:35:22 +0100 Subject: [PATCH 05/28] docs(tunnel): the capture warning's wording, and advice for a linked .lpm/.gitignore Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 296b816b..cbd4d0cd 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -281,13 +281,13 @@ History lives in `.lpm/inspector.db` under the command directory. Run history commands from the directory that started the tunnel. Captured data can contain full bodies, cookies, authorization headers, and signature headers. -Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already has the rule, so git ignores the database and its `-wal` and `-shm` files wherever the project sits in a repository. It leaves a linked or read-only `.lpm/.gitignore` as it is. The capture files are owner-only on Unix and inherit project ACLs on Windows. LPM CLI refuses a linked `.lpm` directory or capture file, since a cloned repository could point it anywhere. +Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database and its `-wal` and `-shm` files wherever the project sits in a repository. It leaves a linked or read-only `.lpm/.gitignore` as it is. The capture files are owner-only on Unix and inherit project ACLs on Windows. LPM CLI refuses a linked `.lpm` directory or capture file, since a cloned repository could point it anywhere. At each start, the capture warning asks git how it treats the capture files: -- When git ignores them, the warning says `.lpm/.gitignore` keeps them out of git. -- When git doesn't ignore them, for example because `.lpm/.gitignore` is a link, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. -- When git tracks them, the warning names them and gives the command to stop tracking them, such as `git rm --cached -- '.lpm/inspector.db'`. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. +- When git ignores them, the warning says so. +- When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, so replace a linked one with a file that holds the rule. +- When git tracks them, the warning names them and gives the command to stop tracking them, such as `git rm --cached -- .lpm/inspector.db`. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. ```bash lpm tunnel inspect # newest 20 captures From f6e2d779b8ca9c2890430f0dfca5578fcb5ca1a9 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Sun, 11 Oct 2026 23:35:23 +0100 Subject: [PATCH 06/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 18dfc9aa..9ebcf452 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-11T20:01:50+01:00" + "modified": "2026-10-11T23:35:22+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From aa44477e63f878f0b9526e9f377334592bea6fd9 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 07:39:53 +0100 Subject: [PATCH 07/28] docs(tunnel): capture files a project or another user can't reach, and git asked inside .lpm Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index cbd4d0cd..b7eee0ad 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -281,13 +281,24 @@ History lives in `.lpm/inspector.db` under the command directory. Run history commands from the directory that started the tunnel. Captured data can contain full bodies, cookies, authorization headers, and signature headers. -Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database and its `-wal` and `-shm` files wherever the project sits in a repository. It leaves a linked or read-only `.lpm/.gitignore` as it is. The capture files are owner-only on Unix and inherit project ACLs on Windows. LPM CLI refuses a linked `.lpm` directory or capture file, since a cloned repository could point it anywhere. +Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database, its `-wal` and `-shm` files and LPM CLI's `inspector.db-lock` wherever the project sits in a repository. A later negation that could name a capture file, such as `!*.db` or `!Inspector.db`, undoes the rule, so LPM CLI adds it again after it. LPM CLI leaves `.lpm/.gitignore` as it is when git doesn't read it as a file, being a link or not a regular file, when it's larger than 16 MiB, or when it can't be written: read-only, or on Unix another user's. -At each start, the capture warning asks git how it treats the capture files: +The capture files are owner-only on Unix and inherit project ACLs on Windows: + +- On Unix, `.lpm` must be your own, and LPM CLI makes it writable by you alone. +- On Unix, a capture database other users could open, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. While another process has it open, LPM CLI stops and asks you to end other `lpm tunnel` and `lpm dev` runs in the project first. +- LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links or, on Unix, another user's, and a database with triggers or views, which it never creates. A cloned repository could supply any of these. + +Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. + +At each start, the capture warning asks git how it treats the capture files. It asks inside `.lpm`, so a repository of its own there, such as a submodule, answers for the files it keeps. Outside a repository, or when git doesn't answer within two seconds, the warning says nothing of git. - When git ignores them, the warning says so. -- When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, so replace a linked one with a file that holds the rule. -- When git tracks them, the warning names them and gives the command to stop tracking them, such as `git rm --cached -- .lpm/inspector.db`. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. +- When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. +- When git tracks them, the warning names them and gives the command to stop tracking them, such as `git -C .lpm rm --cached -- inspector.db`. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. +- When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository, the warning reports what the project's repository says, and that it couldn't ask inside `.lpm`. + +The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files. ```bash lpm tunnel inspect # newest 20 captures From 9dc0922cb8b51c1e7b105e154f96be0981830ab0 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 07:39:53 +0100 Subject: [PATCH 08/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 9ebcf452..bb972fe0 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-11T23:35:22+01:00" + "modified": "2026-10-12T07:39:53+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From e1c24e82dcbfb0762bf7501f1068e583f8782145 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 12:02:35 +0100 Subject: [PATCH 09/28] docs(tunnel): reading captures leaves their access alone, and what a tunnel refuses and clears Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 27 +++++++++++++++++---------- content/docs/infra/tunneling.mdx | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index b7eee0ad..ae3f06a5 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -283,20 +283,26 @@ Run history commands from the directory that started the tunnel. Captured data can contain full bodies, cookies, authorization headers, and signature headers. Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database, its `-wal` and `-shm` files and LPM CLI's `inspector.db-lock` wherever the project sits in a repository. A later negation that could name a capture file, such as `!*.db` or `!Inspector.db`, undoes the rule, so LPM CLI adds it again after it. LPM CLI leaves `.lpm/.gitignore` as it is when git doesn't read it as a file, being a link or not a regular file, when it's larger than 16 MiB, or when it can't be written: read-only, or on Unix another user's. -The capture files are owner-only on Unix and inherit project ACLs on Windows: +The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: -- On Unix, `.lpm` must be your own, and LPM CLI makes it writable by you alone. -- On Unix, a capture database other users could open, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. While another process has it open, LPM CLI stops and asks you to end other `lpm tunnel` and `lpm dev` runs in the project first. -- LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links or, on Unix, another user's, and a database with triggers or views, which it never creates. A cloned repository could supply any of these. +- On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. +- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as other `lpm tunnel` and `lpm dev` runs in the project, and retry. +- A filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, shows every file open to other users, which a copy wouldn't change. The tunnel keeps the database, and its capture warning says the database is open to other users. -Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. +`lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where the filesystem keeps Unix modes. -At each start, the capture warning asks git how it treats the capture files. It asks inside `.lpm`, so a repository of its own there, such as a submodule, answers for the files it keeps. Outside a repository, or when git doesn't answer within two seconds, the warning says nothing of git. +Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. -- When git ignores them, the warning says so. +LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the file aside and retry. + +Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. + +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. Outside a repository, or when git doesn't answer within two seconds, the warning says nothing of git. + +- When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. -- When git tracks them, the warning names them and gives the command to stop tracking them, such as `git -C .lpm rm --cached -- inspector.db`. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. -- When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository, the warning reports what the project's repository says, and that it couldn't ask inside `.lpm`. +- When a repository tracks them, the warning names them and gives the command for each repository to stop tracking them: `git rm --cached -- .lpm/inspector.db` for the project's, or `git -C .lpm rm --cached -- inspector.db` for one of `.lpm`'s own. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. +- When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository, the warning reports what the project's repository says, and asks you to check that git ignores and doesn't track `inspector.db*`. The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files. @@ -312,9 +318,10 @@ Status filters accept `2xx`, `3xx`, `4xx`, `5xx`, `error`, `err`, or an HTTP sta Provider filters are case-insensitive. `log --clear` deletes captured requests and completed sessions. Active sessions remain available for new captures. +The deleted captures are overwritten in the database, and its write-ahead log, which still holds them, is emptied. If another LPM CLI run is reading the database at that moment, the log is removed instead once the last run using the database stops. It cannot combine with filters or a count limit. `inspect --ui` cannot combine with CLI capture filters or JSON output. -If the database is corrupt, stop its users, repair or move the database, then retry. +If LPM CLI reports the database damaged, move `.lpm/inspector.db` aside and retry. LPM CLI starts a new one. ## Plans and limits diff --git a/content/docs/infra/tunneling.mdx b/content/docs/infra/tunneling.mdx index f22eee52..d0a72470 100644 --- a/content/docs/infra/tunneling.mdx +++ b/content/docs/infra/tunneling.mdx @@ -290,7 +290,7 @@ Session names group related captures: lpm tunnel 3000 --session stripe-test ``` -`lpm tunnel log --clear` clears current capture history and completed sessions for the project. It does not remove preserved legacy capture files. +`lpm tunnel log --clear` clears current capture history and completed sessions for the project, and overwrites the cleared captures in the database. It does not remove preserved legacy capture files. ### Auto-acknowledge webhooks From ac759b88db375dc904042959fe5b52e451a9d358 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 12:02:35 +0100 Subject: [PATCH 10/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index bb972fe0..e87ea622 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,11 +241,11 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-12T07:39:53+01:00" + "modified": "2026-10-12T12:02:35+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-11T20:01:50+01:00" + "modified": "2026-10-12T12:02:35+01:00" }, "installation.mdx": { "published": "2026-05-01T09:47:04+01:00", From 9a3877e1bc945399f9a0969b7c42829e4b4417ea Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 16:44:27 +0100 Subject: [PATCH 11/28] docs(tunnel): name the inspector UI as a holder, the copy's disk space, partial git answers and root's trust Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index ae3f06a5..bf69f662 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -286,18 +286,18 @@ Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as other `lpm tunnel` and `lpm dev` runs in the project, and retry. +- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The copy needs as much free disk space as the database; without it, LPM CLI says how much and stops. - A filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, shows every file open to other users, which a copy wouldn't change. The tunnel keeps the database, and its capture warning says the database is open to other users. `lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where the filesystem keeps Unix modes. -Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. +Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. Run as root, it trusts that owner: the owner can't make it change the access of a file outside the project, but by replacing `.lpm` while a tunnel starts, they can choose the directory where it creates the capture files. LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the file aside and retry. Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. Outside a repository, or when git doesn't answer within two seconds, the warning says nothing of git. +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. Outside a repository, or when neither query answers within two seconds, the warning says nothing of git. When only one answers, the warning reports what it found, such as tracked files, and asks you to check that git ignores and doesn't track the rest. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. From 5a9b36b5f9139e21662ab99f475fe1ba9c483522 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 16:44:27 +0100 Subject: [PATCH 12/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index e87ea622..7f1f4c7f 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-12T12:02:35+01:00" + "modified": "2026-10-12T16:44:27+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From a351587733f3b7ed5eb7a8c64a45824a4386bdae Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 18:39:01 +0100 Subject: [PATCH 13/28] docs(tunnel): ownership-ignoring volumes, and which git query didn't answer Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index bf69f662..61bfe02b 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -287,9 +287,9 @@ The capture files are owner-only on Unix and inherit project ACLs on Windows. `l - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. - On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The copy needs as much free disk space as the database; without it, LPM CLI says how much and stops. -- A filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, shows every file open to other users, which a copy wouldn't change. The tunnel keeps the database, and its capture warning says the database is open to other users. +- Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. -`lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where the filesystem keeps Unix modes. +`lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. Run as root, it trusts that owner: the owner can't make it change the access of a file outside the project, but by replacing `.lpm` while a tunnel starts, they can choose the directory where it creates the capture files. @@ -297,7 +297,7 @@ LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. Outside a repository, or when neither query answers within two seconds, the warning says nothing of git. When only one answers, the warning reports what it found, such as tracked files, and asks you to check that git ignores and doesn't track the rest. +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. Outside a repository, or when neither query answers within two seconds, the warning says nothing of git. When only one answers, the warning reports what it found, such as tracked files, says which repository didn't answer, and asks you to check that git ignores and doesn't track the rest. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. From 0ab01b8804669bbb2287437a0282e9e2e9fc943d Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 18:39:01 +0100 Subject: [PATCH 14/28] chore: update content dates --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 7f1f4c7f..fe88a9f9 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-12T16:44:27+01:00" + "modified": "2026-10-12T18:39:01+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From 79f2673fbc7c4bbe063811420523854b4e547463 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 21:02:43 +0100 Subject: [PATCH 15/28] docs(tunnel): repository rule for unanswered git queries, the replacement's disk room, and shared volumes in reviews Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 61bfe02b..f2e693f3 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -286,10 +286,10 @@ Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The copy needs as much free disk space as the database; without it, LPM CLI says how much and stops. +- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the copy, for the database's growth as its write-ahead log is folded in, and for SQLite's shared-memory file: about the database plus twice its `-wal` file, plus 64 KiB. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops, leaving the database as it was. - Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. -`lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. +`lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. Run as root, it trusts that owner: the owner can't make it change the access of a file outside the project, but by replacing `.lpm` while a tunnel starts, they can choose the directory where it creates the capture files. @@ -297,14 +297,14 @@ LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. Outside a repository, or when neither query answers within two seconds, the warning says nothing of git. When only one answers, the warning reports what it found, such as tracked files, says which repository didn't answer, and asks you to check that git ignores and doesn't track the rest. +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. The project is in a repository when its directory, or one above it, holds a `.git` directory or a `.git` file pointing at one (`gitdir: …`); `.lpm` has a repository of its own when it holds one. Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. - When a repository tracks them, the warning names them and gives the command for each repository to stop tracking them: `git rm --cached -- .lpm/inspector.db` for the project's, or `git -C .lpm rm --cached -- inspector.db` for one of `.lpm`'s own. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. - When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository, the warning reports what the project's repository says, and asks you to check that git ignores and doesn't track `inspector.db*`. -The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files. +The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files, matched without regard to case. So does the command for the project's repository when only git in `.lpm` answered, since the repository may spell `.lpm` otherwise: `git rm --cached -- ":(glob,icase).lpm/inspector.db*"`. ```bash lpm tunnel inspect # newest 20 captures From 5bda4f38866ec0cdb304b94be4e590092fef6d56 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Mon, 12 Oct 2026 21:03:08 +0100 Subject: [PATCH 16/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index fe88a9f9..46579300 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-12T18:39:01+01:00" + "modified": "2026-10-12T21:02:43+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From 19a747900a1a4166e4cd0b061eb8694d87c49c1f Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 01:41:03 +0100 Subject: [PATCH 17/28] docs(tunnel): the replacement's room counts the WAL once, and git itself says which repositories exist Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index f2e693f3..97c9f9ab 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -286,7 +286,7 @@ Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the copy, for the database's growth as its write-ahead log is folded in, and for SQLite's shared-memory file: about the database plus twice its `-wal` file, plus 64 KiB. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops, leaving the database as it was. +- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the database and its write-ahead log, the `-wal` file, which is folded into the database before the copy is made, and for SQLite's shared-memory file: about the database plus its `-wal` file, plus 64 KiB, and a little more for a large `-wal` file. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops, leaving the database as it was. - Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. `lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. @@ -297,7 +297,7 @@ LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once. The project is in a repository when its directory, or one above it, holds a `.git` directory or a `.git` file pointing at one (`gitdir: …`); `.lpm` has a repository of its own when it holds one. Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once, and git itself says whether the project is in a repository and whether `.lpm` has one of its own. Only when git can't run or answer does LPM CLI read the `.git` entries: a `.git` directory holding `HEAD`, or a `.git` file pointing at one (`gitdir: …`). Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered, and so is git inside `.lpm` finding no repository, as when `.lpm` is on another filesystem: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. From c222babbfe13fc9937b862acb2ce850796f599a4 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 01:41:03 +0100 Subject: [PATCH 18/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 46579300..3ecd0e66 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-12T21:02:43+01:00" + "modified": "2026-10-13T01:41:03+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From 8757735e63d52fa775d95e5ba97555b78619d5df Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 03:55:07 +0100 Subject: [PATCH 19/28] docs(tunnel): the replacement keeps 8 MiB back and leaves the captures, and git stopping short reads the .git entries Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 97c9f9ab..5a0fed33 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -286,7 +286,7 @@ Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the database and its write-ahead log, the `-wal` file, which is folded into the database before the copy is made, and for SQLite's shared-memory file: about the database plus its `-wal` file, plus 64 KiB, and a little more for a large `-wal` file. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops, leaving the database as it was. +- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the database and its write-ahead log, the `-wal` file, which is folded into the database before the copy is made, for SQLite's shared-memory file, and for the space a filesystem keeps back: about the database plus its `-wal` file plus 8 MiB, and a little more for a large `-wal` file. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops. The database keeps its captures, though its `-wal` file may already be folded into it, and stays open to other users until a retry replaces it. - Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. `lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. @@ -297,7 +297,7 @@ LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once, and git itself says whether the project is in a repository and whether `.lpm` has one of its own. Only when git can't run or answer does LPM CLI read the `.git` entries: a `.git` directory holding `HEAD`, or a `.git` file pointing at one (`gitdir: …`). Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered, and so is git inside `.lpm` finding no repository, as when `.lpm` is on another filesystem: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once, and git itself says whether the project is in a repository and whether `.lpm` has one of its own. Only when git can't run or answer, or stops looking at a filesystem boundary or at a `.git` file that doesn't point at a repository, does LPM CLI read the `.git` entries of the project and the directories above it: a `.git` directory holding `HEAD`, or a `.git` file pointing at one (`gitdir: …`). It reads `.lpm/.git` only when git can't run or answer inside `.lpm`. Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, stopped at a filesystem boundary or a broken `.git` file, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered, and so is git inside `.lpm` finding no repository: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. From 85a93194b91e14bbc68fda54afc4b9e6765df334 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 03:55:07 +0100 Subject: [PATCH 20/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 3ecd0e66..31a8a84b 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-13T01:41:03+01:00" + "modified": "2026-10-13T03:55:07+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From 1a9418add5b052c94adcbd9d330b639dddf4c8ad Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 07:22:44 +0100 Subject: [PATCH 21/28] docs(tunnel): name the files that move with the capture database, the space APFS keeps back, and how .git files are read Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 5a0fed33..d48d13b2 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -286,23 +286,23 @@ Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the database and its write-ahead log, the `-wal` file, which is folded into the database before the copy is made, for SQLite's shared-memory file, and for the space a filesystem keeps back: about the database plus its `-wal` file plus 8 MiB, and a little more for a large `-wal` file. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops. The database keeps its captures, though its `-wal` file may already be folded into it, and stays open to other users until a retry replaces it. +- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the database and its write-ahead log, the `-wal` file, which is folded into the database before the copy is made, and for SQLite's shared-memory file: about the database plus its `-wal` file plus 64 KiB, and a little more for a large `-wal` file. On APFS, which refuses writes while it still reports about 2% of the volume free, up to about 32 MiB, LPM CLI counts that space, and a little more, as taken. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops. The database and its `-wal` file keep the captures between them, though part of the `-wal` file may already be folded into the database, and the database stays open to other users until a retry replaces it. To move the database aside instead, move its `-wal`, `-shm` and `-journal` files with it. - Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. `lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. Run as root, it trusts that owner: the owner can't make it change the access of a file outside the project, but by replacing `.lpm` while a tunnel starts, they can choose the directory where it creates the capture files. -LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the file aside and retry. +LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the directory or file aside and retry. For a capture file, move the database aside together with its `-wal`, `-shm` and `-journal` files: LPM CLI removes those files when it finds them without a database, and the `-wal` file can hold captures the database doesn't yet. Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once, and git itself says whether the project is in a repository and whether `.lpm` has one of its own. Only when git can't run or answer, or stops looking at a filesystem boundary or at a `.git` file that doesn't point at a repository, does LPM CLI read the `.git` entries of the project and the directories above it: a `.git` directory holding `HEAD`, or a `.git` file pointing at one (`gitdir: …`). It reads `.lpm/.git` only when git can't run or answer inside `.lpm`. Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, stopped at a filesystem boundary or a broken `.git` file, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered, and so is git inside `.lpm` finding no repository: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. +At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once, and git itself says whether the project is in a repository and whether `.lpm` has one of its own. Only when git can't run or answer, or stops looking at a filesystem boundary or at a `.git` file it can't read, that's larger than 1 MiB or that doesn't point at a repository, does LPM CLI read the `.git` entries of the project and the directories above it, as git reads them: a `.git` directory holding `HEAD`, or a `.git` file of at most 1 MiB whose content after `gitdir: `, but for its final line breaks, is the path of one. It reads `.lpm/.git` only when git can't run or answer inside `.lpm`. A repository git refuses inside `.lpm`, where the project isn't in one, is `.lpm`'s own. Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, stopped at a filesystem boundary or a broken `.git` file, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered, and so is git inside `.lpm` finding no repository: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. - When a repository tracks them, the warning names them and gives the command for each repository to stop tracking them: `git rm --cached -- .lpm/inspector.db` for the project's, or `git -C .lpm rm --cached -- inspector.db` for one of `.lpm`'s own. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. -- When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository, the warning reports what the project's repository says, and asks you to check that git ignores and doesn't track `inspector.db*`. +- When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository or holds a repository another user owns, the warning reports what the project's repository says, when the project is in one, and asks you to check that git ignores and doesn't track `inspector.db*`. The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files, matched without regard to case. So does the command for the project's repository when only git in `.lpm` answered, since the repository may spell `.lpm` otherwise: `git rm --cached -- ":(glob,icase).lpm/inspector.db*"`. @@ -321,7 +321,7 @@ Provider filters are case-insensitive. The deleted captures are overwritten in the database, and its write-ahead log, which still holds them, is emptied. If another LPM CLI run is reading the database at that moment, the log is removed instead once the last run using the database stops. It cannot combine with filters or a count limit. `inspect --ui` cannot combine with CLI capture filters or JSON output. -If LPM CLI reports the database damaged, move `.lpm/inspector.db` aside and retry. LPM CLI starts a new one. +If LPM CLI reports the database damaged, move `.lpm/inspector.db` aside together with its `-wal`, `-shm` and `-journal` files, and retry. LPM CLI starts a new one. ## Plans and limits From 6763811f3a5ccc20254515f231abc9f3a5c9c0da Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 07:22:49 +0100 Subject: [PATCH 22/28] chore: update content dates --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 31a8a84b..6ced8d77 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-13T03:55:07+01:00" + "modified": "2026-10-13T07:22:44+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From c95aecb0417b370a84b419d93fc3deab792b81d2 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 12:35:13 +0100 Subject: [PATCH 23/28] docs(tunnel): root records another user's captures as that user, git's rules for .git entries, and shorter capture paragraphs Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index d48d13b2..541fa606 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -286,18 +286,35 @@ Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: - On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read, as earlier LPM CLI releases or a checkout can leave it, is replaced with an owner-only copy, so a handle opened before can't read later captures. Other users could read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. LPM CLI first checks the database for damage, its full-text index included, and refuses a damaged one. While another process has the database open, LPM CLI stops and asks you to close it, such as `lpm tunnel inspect --ui` or other `lpm tunnel` and `lpm dev` runs in the project, and retry. The replacement needs free disk space for the database and its write-ahead log, the `-wal` file, which is folded into the database before the copy is made, and for SQLite's shared-memory file: about the database plus its `-wal` file plus 64 KiB, and a little more for a large `-wal` file. On APFS, which refuses writes while it still reports about 2% of the volume free, up to about 32 MiB, LPM CLI counts that space, and a little more, as taken. LPM CLI checks before it opens the database; without the room, or if the disk fills meanwhile, it says how much it needs and stops. The database and its `-wal` file keep the captures between them, though part of the `-wal` file may already be folded into the database, and the database stays open to other users until a retry replaces it. To move the database aside instead, move its `-wal`, `-shm` and `-journal` files with it. +- On Unix, a capture database other users could read is replaced with an owner-only copy, so a handle opened before can't read later captures. Earlier LPM CLI releases or a checkout can leave one readable. Other users can read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. Before the copy: + - LPM CLI checks the database for damage, its full-text index included, and refuses a damaged one. + - While another process has the database open, LPM CLI stops and asks you to close it and retry. That process can be `lpm tunnel inspect --ui`, or another `lpm tunnel` or `lpm dev` run in the project. + - LPM CLI checks the free disk space before it opens the database. The copy needs about the size of the database plus its `-wal` file plus 64 KiB, and a little more for a large `-wal` file. The `-wal` file is the database's write-ahead log, folded into the database before the copy is made. The 64 KiB is for SQLite's shared-memory file. + - APFS refuses writes while it still reports about 2% of the volume free, up to about 32 MiB. LPM CLI counts that space, and a little more, as taken. + - Without the room, or if the disk fills meanwhile, LPM CLI says how much it needs and stops. The captures stay in the database and its `-wal` file, and the database stays open to other users until a retry replaces it. To move the database aside instead, move its `-wal`, `-shm` and `-journal` files with it. - Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. `lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. -Run as root, as in a container over a mounted checkout, LPM CLI accepts capture files of the project directory's owner and its own, and gives the files it creates to the project directory's owner. Run as root, it trusts that owner: the owner can't make it change the access of a file outside the project, but by replacing `.lpm` while a tunnel starts, they can choose the directory where it creates the capture files. +Run as root in a project another user owns, as in a container over a mounted checkout, LPM CLI on Linux opens the capture files as that user. The files it creates are theirs, and root's own files there are another user's to it. That user can't make it write where they couldn't, even by replacing `.lpm` while a tunnel starts. Where the process can't act as that user, as without the `CAP_SETUID` and `CAP_SETGID` capabilities, or in a user namespace that doesn't map them, LPM CLI stops and says so. On macOS and other Unix systems, it stops for any project another user owns. Run the tunnel as the project's owner then. In a project root owns, root's runs are like any user's. -LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the directory or file aside and retry. For a capture file, move the database aside together with its `-wal`, `-shm` and `-journal` files: LPM CLI removes those files when it finds them without a database, and the `-wal` file can hold captures the database doesn't yet. +LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the directory or file aside and retry. For a capture file, move the database aside together with its `-wal`, `-shm` and `-journal` files. LPM CLI removes those files when it finds them without a database, unless it refuses them as above, and the `-wal` file can hold captures the database doesn't yet. Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. -At each start, the capture warning asks git how it treats the capture files: the project's repository about the files it tracks, and git inside `.lpm` about the files a repository of its own there tracks, such as a submodule, and the files no rule ignores. It asks both at once, and git itself says whether the project is in a repository and whether `.lpm` has one of its own. Only when git can't run or answer, or stops looking at a filesystem boundary or at a `.git` file it can't read, that's larger than 1 MiB or that doesn't point at a repository, does LPM CLI read the `.git` entries of the project and the directories above it, as git reads them: a `.git` directory holding `HEAD`, or a `.git` file of at most 1 MiB whose content after `gitdir: `, but for its final line breaks, is the path of one. It reads `.lpm/.git` only when git can't run or answer inside `.lpm`. A repository git refuses inside `.lpm`, where the project isn't in one, is `.lpm`'s own. Outside any repository, the warning says nothing of git when neither query answers. In one, a query that couldn't run, took over two seconds, stopped at a filesystem boundary or a broken `.git` file, or, for the project's repository, was refused, as git refuses a repository another user owns, is unanswered, and so is git inside `.lpm` finding no repository: the warning says which repository didn't answer and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. +At each start, the capture warning asks git how it treats the capture files. It asks two questions at once: + +- The project's repository: which capture files it tracks. +- Git inside `.lpm`: which files a repository of `.lpm`'s own tracks, such as a submodule, and which files no rule ignores. + +Git itself says whether the project is in a repository and whether `.lpm` has one of its own. A repository git refuses inside `.lpm`, where the project isn't in one, is `.lpm`'s own. + +LPM CLI reads the `.git` entries of the project and the directories above it only when git can't run or answer, or stops looking. Git stops at a filesystem boundary, and at a `.git` file it can't read, that's larger than 1 MiB, or that doesn't point at a repository. LPM CLI reads `.lpm/.git` only when git can't run or answer inside `.lpm`. It follows git 2.53's rules: + +- A `.git` directory, or a link to one, is a repository's when its `HEAD` names a branch or a commit, and it has `objects` and `refs` directories. A worktree's names the directory that has them in a `commondir` file. +- A `.git` file, or a link to one, of at most 1 MiB is a repository's when its content after `gitdir: `, but for its final line breaks, is the path of such a directory. + +Outside any repository, the warning says nothing of git when neither query answers. Inside one, a query is unanswered when it couldn't run, took over two seconds, or stopped at a filesystem boundary or a broken `.git` file. So is the project's query when git refused it, as git refuses a repository another user owns, and git inside `.lpm` finding no repository. The warning then says which repository didn't answer, and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. From 2eaf106292a7e0e49deec9881f3bbf165d6ab56f Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 12:35:13 +0100 Subject: [PATCH 24/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index 6ced8d77..b4e81882 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,7 +241,7 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-13T07:22:44+01:00" + "modified": "2026-10-13T12:35:13+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", From 1b7faa112e35684f78add563edd186183afa8387 Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 15:37:45 +0100 Subject: [PATCH 25/28] docs(tunnel): split capture history into sections, keep moved captures private, and describe the path check and root's hand-over Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 87 +++++++++++++++++++++++++------- content/docs/infra/tunneling.mdx | 2 + 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index 541fa606..ec5ae384 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -281,7 +281,32 @@ History lives in `.lpm/inspector.db` under the command directory. Run history commands from the directory that started the tunnel. Captured data can contain full bodies, cookies, authorization headers, and signature headers. -Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database, its `-wal` and `-shm` files and LPM CLI's `inspector.db-lock` wherever the project sits in a repository. A later negation that could name a capture file, such as `!*.db` or `!Inspector.db`, undoes the rule, so LPM CLI adds it again after it. LPM CLI leaves `.lpm/.gitignore` as it is when git doesn't read it as a file, being a link or not a regular file, when it's larger than 16 MiB, or when it can't be written: read-only, or on Unix another user's. + +```bash +lpm tunnel inspect # newest 20 captures +lpm tunnel log # newest 50 captures +lpm tunnel log --last 10 --filter github +lpm tunnel log --status 5xx +lpm tunnel log --clear +``` + +Status filters accept `2xx`, `3xx`, `4xx`, `5xx`, `error`, `err`, or an HTTP status from `100` through `599`. +Provider filters are case-insensitive. + +`log --clear` deletes captured requests and completed sessions. Active sessions remain available for new captures. +The deleted captures are overwritten in the database, and its write-ahead log, which still holds them, is emptied. If another LPM CLI run is reading the database at that moment, the log keeps them until the last run using the database stops. SQLite then removes the log, or empties it when root ran the tunnel in another user's project. +It cannot combine with filters or a count limit. `inspect --ui` cannot combine with CLI capture filters or JSON output. + +### Keep captures private + +On Unix, LPM CLI keeps captures only in a project whose path no other user can change. A user who could rename `.lpm`, or a directory above it, while the capture files open could have captures written elsewhere, or read them. LPM CLI checks each directory from `/` to the project, and follows links: + +- The directory must be yours or root's. +- Only you may write to it, or a group that is yours alone: your primary group, named after you, with no other members. +- A sticky directory that others may write to, such as `/tmp`, is safe when the entry on the path in it is yours or root's. +- On Linux, a directory whose access control list may let others write to it is refused. On macOS, so is a directory whose access control list lets others add, remove or rename its entries. + +Otherwise LPM CLI names the directory and stops, for history commands too. Keep the project under directories only you can change. The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: @@ -291,16 +316,26 @@ The capture files are owner-only on Unix and inherit project ACLs on Windows. `l - While another process has the database open, LPM CLI stops and asks you to close it and retry. That process can be `lpm tunnel inspect --ui`, or another `lpm tunnel` or `lpm dev` run in the project. - LPM CLI checks the free disk space before it opens the database. The copy needs about the size of the database plus its `-wal` file plus 64 KiB, and a little more for a large `-wal` file. The `-wal` file is the database's write-ahead log, folded into the database before the copy is made. The 64 KiB is for SQLite's shared-memory file. - APFS refuses writes while it still reports about 2% of the volume free, up to about 32 MiB. LPM CLI counts that space, and a little more, as taken. - - Without the room, or if the disk fills meanwhile, LPM CLI says how much it needs and stops. The captures stay in the database and its `-wal` file, and the database stays open to other users until a retry replaces it. To move the database aside instead, move its `-wal`, `-shm` and `-journal` files with it. + - Without the room, or if the disk fills meanwhile, LPM CLI says how much it needs and stops. The captures stay in the database and its `-wal` file, and the database stays open to other users until a retry replaces it. To move the database aside instead, see [Recover the capture database](#recover-the-capture-database). - Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. `lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. -Run as root in a project another user owns, as in a container over a mounted checkout, LPM CLI on Linux opens the capture files as that user. The files it creates are theirs, and root's own files there are another user's to it. That user can't make it write where they couldn't, even by replacing `.lpm` while a tunnel starts. Where the process can't act as that user, as without the `CAP_SETUID` and `CAP_SETGID` capabilities, or in a user namespace that doesn't map them, LPM CLI stops and says so. On macOS and other Unix systems, it stops for any project another user owns. Run the tunnel as the project's owner then. In a project root owns, root's runs are like any user's. +### Run as root -LPM CLI refuses a linked `.lpm` directory, a linked capture file, a capture file with other hard links, on Unix a `.lpm` directory or capture file of another user's, and a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes. A cloned repository could supply any of these. Move the directory or file aside and retry. For a capture file, move the database aside together with its `-wal`, `-shm` and `-journal` files. LPM CLI removes those files when it finds them without a database, unless it refuses them as above, and the `-wal` file can hold captures the database doesn't yet. +On 64-bit Linux, root opens the capture files of a project another user owns as that user, as in a container over a mounted checkout. That user can't make it write where they couldn't, even by replacing `.lpm` while a tunnel starts. -Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. +- The files it creates belong to that user, in their primary group. A user the system doesn't know, such as the host's user in a container, gets the project's group, or group 65534 when the project's group is root's. +- When `.lpm` is root's and no one else can change it, as after `lpm install` run as root, root first gives `.lpm` and the capture files it made there to that user. It gives only regular files with no other hard links. +- A capture file root still owns after that, such as one in a `.lpm` of that user's, stops the tunnel. The error names the file. Give it to that user with `chown`, or remove it. +- When the last run stops, SQLite empties the write-ahead log and keeps the empty file. +- Where the process can't act as that user, LPM CLI stops and says so. That happens without the `CAP_SETUID` and `CAP_SETGID` capabilities, or in a user namespace that doesn't map the user. + +On macOS and other systems, root stops for any project another user owns. Run the tunnel as the project's owner, without `sudo`. In a project root owns, root's runs are like any user's. + +### Keep captures out of git + +Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database, its `-wal` and `-shm` files and LPM CLI's `inspector.db-lock` wherever the project sits in a repository. A later negation that could name a capture file, such as `!*.db` or `!Inspector.db`, undoes the rule, so LPM CLI adds it again after it. LPM CLI leaves `.lpm/.gitignore` as it is when git doesn't read it as a file, being a link or not a regular file, when it's larger than 16 MiB, or when it can't be written: read-only, or on Unix another user's. At each start, the capture warning asks git how it treats the capture files. It asks two questions at once: @@ -318,27 +353,45 @@ Outside any repository, the warning says nothing of git when neither query answe - When git ignores them and no repository tracks them, the warning says so. - When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. -- When a repository tracks them, the warning names them and gives the command for each repository to stop tracking them: `git rm --cached -- .lpm/inspector.db` for the project's, or `git -C .lpm rm --cached -- inspector.db` for one of `.lpm`'s own. An ignore rule doesn't stop a tracked file from being committed. Captures already committed stay in the repository's history and every clone of it, so rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. For a public repository, rewrite its history too. - When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository or holds a repository another user owns, the warning reports what the project's repository says, when the project is in one, and asks you to check that git ignores and doesn't track `inspector.db*`. The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files, matched without regard to case. So does the command for the project's repository when only git in `.lpm` answered, since the repository may spell `.lpm` otherwise: `git rm --cached -- ":(glob,icase).lpm/inspector.db*"`. +### Remove committed captures + +When a repository tracks capture files, the warning names them. It gives the command for each repository to stop tracking them: `git rm --cached -- .lpm/inspector.db` for the project's, or `git -C .lpm rm --cached -- inspector.db` for one of `.lpm`'s own. An ignore rule doesn't stop a tracked file from being committed. + +Captures already committed stay in the repository's history, and in every clone of it. Rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. Rotate them first. Rewriting history doesn't reach clones and forks that others already have. + +For a public repository, also remove the captures from its history. + +> **CAUTION:** Rewriting history changes the ID of every commit after the first one it changes. Everyone with a clone must clone again or reset to the new history, and open pull requests may need to be recreated. Back up the repository before you start. + +### Recover the capture database + +LPM CLI refuses some capture files and stops. A cloned repository could supply any of these: + +- a linked `.lpm` directory, or on Unix a `.lpm` directory of another user's +- a linked capture file, a capture file with other hard links, or on Unix a capture file of another user's +- a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes +- a database SQLite reports damaged + +For a refused `.lpm` directory, move it aside and retry. For a capture file or a damaged database, move the database aside together with its `-wal`, `-shm` and `-journal` files, and retry. LPM CLI starts a new database. + +The moved files hold captures, so keep them private. Move them into a directory only you can read, outside every repository. Keep each file's suffix after the database's name, so SQLite still finds the files together: + ```bash -lpm tunnel inspect # newest 20 captures -lpm tunnel log # newest 50 captures -lpm tunnel log --last 10 --filter github -lpm tunnel log --status 5xx -lpm tunnel log --clear +mkdir -p -m 700 ~/lpm-old-captures/my-project +for suffix in "" -wal -shm -journal; do + if [ -e ".lpm/inspector.db$suffix" ]; then mv ".lpm/inspector.db$suffix" ~/lpm-old-captures/my-project/; fi +done ``` -Status filters accept `2xx`, `3xx`, `4xx`, `5xx`, `error`, `err`, or an HTTP status from `100` through `599`. -Provider filters are case-insensitive. +To keep them in `.lpm` instead, give each a name that starts with `inspector.db`, such as `inspector.db.old` and `inspector.db.old-wal`, so `.lpm/.gitignore` still ignores it. Do that only when LPM CLI didn't warn that other users could read the database. Delete the files once you no longer need their captures. -`log --clear` deletes captured requests and completed sessions. Active sessions remain available for new captures. -The deleted captures are overwritten in the database, and its write-ahead log, which still holds them, is emptied. If another LPM CLI run is reading the database at that moment, the log is removed instead once the last run using the database stops. -It cannot combine with filters or a count limit. `inspect --ui` cannot combine with CLI capture filters or JSON output. +The `-wal` file can hold captures the database doesn't yet. LPM CLI removes the `-wal`, `-shm` and `-journal` files when it finds them without a database, unless it refuses them as above. -If LPM CLI reports the database damaged, move `.lpm/inspector.db` aside together with its `-wal`, `-shm` and `-journal` files, and retry. LPM CLI starts a new one. +Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. ## Plans and limits diff --git a/content/docs/infra/tunneling.mdx b/content/docs/infra/tunneling.mdx index d0a72470..ee07d1e9 100644 --- a/content/docs/infra/tunneling.mdx +++ b/content/docs/infra/tunneling.mdx @@ -256,6 +256,8 @@ The database can contain full headers, request bodies, response bodies, and sess Protect the database as sensitive development data. Do not commit it to source control. LPM CLI adds `inspector.db*` to `.lpm/.gitignore` before it creates the database, and warns when git tracks the capture files or doesn't ignore them. +On Unix, LPM CLI records captures only in a project whose path no other user can change, and stops otherwise. See [Keep captures private](/docs/infra/tunnel#keep-captures-private). + Live capture memory has fixed limits. The request buffer retains up to 1,000 requests or 64 MiB. The WebSocket buffer retains up to 5,000 events or 32 MiB. From 5be7c7fd174b3d307f1375acba6473c3f7d4ef8c Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 15:37:46 +0100 Subject: [PATCH 26/28] chore: update content dates --- lib/generated/content-dates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index b4e81882..c0dcd8cc 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,11 +241,11 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-13T12:35:13+01:00" + "modified": "2026-10-13T15:37:45+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-12T12:02:35+01:00" + "modified": "2026-10-13T15:37:45+01:00" }, "installation.mdx": { "published": "2026-05-01T09:47:04+01:00", From 341dd1d04bfdb50a0e34b39242c52d6ad623433c Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 22:07:23 +0100 Subject: [PATCH 27/28] docs: rewrite the capture privacy, root and git passages in plain English Shorter sentences, no contractions, each condition before its result, no nested list, two-item lists as prose, and the history caution's instruction first. No fact changes. Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/infra/tunnel.mdx | 99 ++++++++++++++++---------------- content/docs/infra/tunneling.mdx | 2 +- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/content/docs/infra/tunnel.mdx b/content/docs/infra/tunnel.mdx index ec5ae384..28c6caf9 100644 --- a/content/docs/infra/tunnel.mdx +++ b/content/docs/infra/tunnel.mdx @@ -294,91 +294,94 @@ Status filters accept `2xx`, `3xx`, `4xx`, `5xx`, `error`, `err`, or an HTTP sta Provider filters are case-insensitive. `log --clear` deletes captured requests and completed sessions. Active sessions remain available for new captures. -The deleted captures are overwritten in the database, and its write-ahead log, which still holds them, is emptied. If another LPM CLI run is reading the database at that moment, the log keeps them until the last run using the database stops. SQLite then removes the log, or empties it when root ran the tunnel in another user's project. +LPM CLI overwrites the deleted captures in the database. It also empties the write-ahead log, which still holds them. If another LPM CLI run reads the database at that time, the log keeps the captures until the last run that uses the database stops. SQLite then removes the log. If root ran the tunnel in the project of another user, SQLite empties the log instead. It cannot combine with filters or a count limit. `inspect --ui` cannot combine with CLI capture filters or JSON output. ### Keep captures private -On Unix, LPM CLI keeps captures only in a project whose path no other user can change. A user who could rename `.lpm`, or a directory above it, while the capture files open could have captures written elsewhere, or read them. LPM CLI checks each directory from `/` to the project, and follows links: +On Unix, LPM CLI keeps captures only in a project whose path no other user can change. Another user can try to rename `.lpm`, or a directory above it, while the capture files open. If that is possible, the user can make LPM CLI write captures to a different location, or read them. LPM CLI examines each directory from `/` to the project, and follows links: - The directory must be yours or root's. -- Only you may write to it, or a group that is yours alone: your primary group, named after you, with no other members. -- A sticky directory that others may write to, such as `/tmp`, is safe when the entry on the path in it is yours or root's. -- On Linux, a directory whose access control list may let others write to it is refused. On macOS, so is a directory whose access control list lets others add, remove or rename its entries. +- Only you, or a group that is yours alone, can write to it. Such a group is your primary group, has your name, and has no other members. +- Others can write to a sticky directory, for example `/tmp`. Such a directory is safe if the entry in it on the path is yours or root's. +- On Linux, LPM CLI refuses a directory whose access control list can let others write to it. On macOS, LPM CLI refuses a directory whose access control list lets others add, remove, or rename its entries. -Otherwise LPM CLI names the directory and stops, for history commands too. Keep the project under directories only you can change. +If a directory does not meet these rules, LPM CLI names the directory and stops. This also applies to history commands. Keep the project under directories that only you can change. -The capture files are owner-only on Unix and inherit project ACLs on Windows. `lpm tunnel` and `lpm dev --tunnel`, which record captures, make them so: +On Unix, only the owner can read and write the capture files. On Windows, they inherit the ACLs of the project. `lpm tunnel` and `lpm dev --tunnel` record captures, and set this access: -- On Unix, `.lpm` must be your own. LPM CLI makes it writable by you alone, and on macOS removes its extended ACL entries, which can grant access its mode doesn't show and pass to the files created in it. -- On Unix, a capture database other users could read is replaced with an owner-only copy, so a handle opened before can't read later captures. Earlier LPM CLI releases or a checkout can leave one readable. Other users can read it when its mode lets its group or anyone read it, or on macOS when it has an extended ACL. Before the copy: - - LPM CLI checks the database for damage, its full-text index included, and refuses a damaged one. - - While another process has the database open, LPM CLI stops and asks you to close it and retry. That process can be `lpm tunnel inspect --ui`, or another `lpm tunnel` or `lpm dev` run in the project. - - LPM CLI checks the free disk space before it opens the database. The copy needs about the size of the database plus its `-wal` file plus 64 KiB, and a little more for a large `-wal` file. The `-wal` file is the database's write-ahead log, folded into the database before the copy is made. The 64 KiB is for SQLite's shared-memory file. - - APFS refuses writes while it still reports about 2% of the volume free, up to about 32 MiB. LPM CLI counts that space, and a little more, as taken. - - Without the room, or if the disk fills meanwhile, LPM CLI says how much it needs and stops. The captures stay in the database and its `-wal` file, and the database stays open to other users until a retry replaces it. To move the database aside instead, see [Recover the capture database](#recover-the-capture-database). -- Where file modes don't keep other users out, a copy wouldn't change that: on a filesystem without Unix modes, such as FAT, exFAT or a Windows drive mounted in WSL, which shows every file open to other users; and on macOS, on a volume that ignores ownership, as external drives and disk images do by default, where every user owns every file whatever its mode. The tunnel keeps the database, and its capture warning says the database is open to other users. +- On Unix, `.lpm` must be your own. LPM CLI makes it writable only by you. On macOS, LPM CLI also removes its extended ACL entries. These entries can give access that its mode does not show, and pass that access to the files created in it. +- On Unix, if other users can read a capture database, LPM CLI replaces it with a copy that only the owner can read. A handle that another user opened before then cannot read later captures. Earlier LPM CLI releases or a checkout can leave a database readable. Other users can read it if its mode lets its group or anyone read it. On macOS, they can also read it if it has an extended ACL. +- In some locations, file modes do not keep other users out, and a copy does not change that. One is a filesystem without Unix modes, for example FAT, exFAT, or a Windows drive mounted in WSL. Such a filesystem shows every file as open to other users. The other is a macOS volume that ignores ownership, as external drives and disk images do by default. On such a volume, every user owns every file, whatever its mode. In these locations, the tunnel keeps the database, and its capture warning says that the database is open to other users. -`lpm tunnel inspect`, `log` and `replay`, and `inspect --ui`, read the captures without changing any file's access, so they work while a tunnel records to the database. When other users could read it, they warn; the next `lpm tunnel` or `lpm dev --tunnel` run in the project makes it owner-only where file modes keep other users out. On a macOS volume that ignores ownership no run can, so the warning says to keep the project on a volume that keeps ownership. +Before LPM CLI makes the owner-only copy: + +- LPM CLI makes sure that the database is not damaged, its full-text index included. It refuses a damaged database. +- If another process has the database open, LPM CLI stops and asks you to close that process and try again. That process can be `lpm tunnel inspect --ui`, or another `lpm tunnel` or `lpm dev` run in the project. +- Before LPM CLI opens the database, it makes sure that there is enough free disk space. The copy needs about the size of the database, plus its `-wal` file, plus 64 KiB. A large `-wal` file needs a little more. The `-wal` file is the write-ahead log of the database. LPM CLI folds it into the database before it makes the copy. The 64 KiB is for the shared-memory file of SQLite. +- APFS refuses writes while it still reports about 2% of the volume as free, up to about 32 MiB. LPM CLI counts that space, and a little more, as used. +- If there is not enough room, or if the disk fills during the copy, LPM CLI says how much room it needs and stops. The captures stay in the database and its `-wal` file. The database stays open to other users until a retry replaces it. To move the database aside instead, see [Recover the capture database](#recover-the-capture-database). + +`lpm tunnel inspect`, `log`, `replay`, and `inspect --ui` read the captures and do not change the access of any file. As a result, they work while a tunnel records to the database. If other users can read the database, these commands show a warning. The next `lpm tunnel` or `lpm dev --tunnel` run in the project makes the database owner-only, where file modes keep other users out. On a macOS volume that ignores ownership, no run can do this. The warning then says to keep the project on a volume that keeps ownership. ### Run as root -On 64-bit Linux, root opens the capture files of a project another user owns as that user, as in a container over a mounted checkout. That user can't make it write where they couldn't, even by replacing `.lpm` while a tunnel starts. +On 64-bit Linux, if another user owns the project, root opens its capture files as that user. This occurs, for example, in a container over a mounted checkout. As a result, that user cannot make root write where the user cannot write, also if the user replaces `.lpm` while a tunnel starts. -- The files it creates belong to that user, in their primary group. A user the system doesn't know, such as the host's user in a container, gets the project's group, or group 65534 when the project's group is root's. -- When `.lpm` is root's and no one else can change it, as after `lpm install` run as root, root first gives `.lpm` and the capture files it made there to that user. It gives only regular files with no other hard links. -- A capture file root still owns after that, such as one in a `.lpm` of that user's, stops the tunnel. The error names the file. Give it to that user with `chown`, or remove it. +- The files that root creates belong to that user, in the primary group of the user. A user that the system does not know, for example the host user in a container, gets the group of the project. If the group of the project is root's, the user gets group 65534. +- If `.lpm` is root's and no other user can change it, root first gives it to that user. Root also gives that user the capture files that root made there. This occurs, for example, after `lpm install` ran as root. Root gives only regular files that have no other hard links. +- If root still owns a capture file after that, for example one in a `.lpm` of that user, the tunnel stops. The error names the file. Give the file to that user with `chown`, or remove it. - When the last run stops, SQLite empties the write-ahead log and keeps the empty file. -- Where the process can't act as that user, LPM CLI stops and says so. That happens without the `CAP_SETUID` and `CAP_SETGID` capabilities, or in a user namespace that doesn't map the user. +- If the process cannot act as that user, LPM CLI stops and says so. This occurs without the `CAP_SETUID` and `CAP_SETGID` capabilities, or in a user namespace that does not map the user. -On macOS and other systems, root stops for any project another user owns. Run the tunnel as the project's owner, without `sudo`. In a project root owns, root's runs are like any user's. +On macOS and other systems, root stops for any project that another user owns. Run the tunnel as the owner of the project, without `sudo`. In a project that root owns, the runs of root are the same as the runs of any user. ### Keep captures out of git -Before it creates the database, LPM CLI adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files, so git ignores the database, its `-wal` and `-shm` files and LPM CLI's `inspector.db-lock` wherever the project sits in a repository. A later negation that could name a capture file, such as `!*.db` or `!Inspector.db`, undoes the rule, so LPM CLI adds it again after it. LPM CLI leaves `.lpm/.gitignore` as it is when git doesn't read it as a file, being a link or not a regular file, when it's larger than 16 MiB, or when it can't be written: read-only, or on Unix another user's. +Before LPM CLI creates the database, it adds `inspector.db*` to `.lpm/.gitignore`, unless the file already ignores the capture files. Then git ignores the database, its `-wal` and `-shm` files, and the `inspector.db-lock` file of LPM CLI, wherever the project is in a repository. A later negation that can name a capture file, for example `!*.db` or `!Inspector.db`, cancels the rule. In that case, LPM CLI adds the rule again after the negation. LPM CLI does not change `.lpm/.gitignore` in these cases: -At each start, the capture warning asks git how it treats the capture files. It asks two questions at once: +- Git does not read it as a file, because it is a link or not a regular file. +- It is larger than 16 MiB. +- LPM CLI cannot write it, because it is read-only or, on Unix, another user's. -- The project's repository: which capture files it tracks. -- Git inside `.lpm`: which files a repository of `.lpm`'s own tracks, such as a submodule, and which files no rule ignores. +At each start, the capture warning asks git how it treats the capture files. It asks the repository of the project which capture files it tracks. At the same time, it asks git inside `.lpm` which files a repository of `.lpm` itself tracks, for example a submodule. It also asks which files no rule ignores. -Git itself says whether the project is in a repository and whether `.lpm` has one of its own. A repository git refuses inside `.lpm`, where the project isn't in one, is `.lpm`'s own. +Git itself says whether the project is in a repository, and whether `.lpm` has a repository of its own. If the project is not in a repository, and git refuses a repository inside `.lpm`, that repository belongs to `.lpm`. -LPM CLI reads the `.git` entries of the project and the directories above it only when git can't run or answer, or stops looking. Git stops at a filesystem boundary, and at a `.git` file it can't read, that's larger than 1 MiB, or that doesn't point at a repository. LPM CLI reads `.lpm/.git` only when git can't run or answer inside `.lpm`. It follows git 2.53's rules: +LPM CLI reads the `.git` entries of the project and the directories above it only if git cannot run or answer, or stops its search. Git stops at a filesystem boundary. It also stops at a `.git` file that it cannot read, that is larger than 1 MiB, or that does not point at a repository. LPM CLI reads `.lpm/.git` only if git cannot run or answer inside `.lpm`. LPM CLI follows the rules of git 2.53. -- A `.git` directory, or a link to one, is a repository's when its `HEAD` names a branch or a commit, and it has `objects` and `refs` directories. A worktree's names the directory that has them in a `commondir` file. -- A `.git` file, or a link to one, of at most 1 MiB is a repository's when its content after `gitdir: `, but for its final line breaks, is the path of such a directory. +A `.git` directory, or a link to one, belongs to a repository if two things are true. Its `HEAD` names a branch or a commit, and it has `objects` and `refs` directories. The `.git` directory of a worktree names the directory that has them in a `commondir` file. LPM CLI reads a `.git` file, or a link to one, only if it is 1 MiB or less. The file belongs to a repository if its content after `gitdir: ` is the path of such a directory. The final line breaks of the content do not count. -Outside any repository, the warning says nothing of git when neither query answers. Inside one, a query is unanswered when it couldn't run, took over two seconds, or stopped at a filesystem boundary or a broken `.git` file. So is the project's query when git refused it, as git refuses a repository another user owns, and git inside `.lpm` finding no repository. The warning then says which repository didn't answer, and asks you to check that git ignores and doesn't track the capture files. When the other query answers, the warning reports what it found too, such as tracked files. +Outside any repository, the warning says nothing about git if neither query answers. Inside a repository, a query is unanswered if it cannot run or takes more than two seconds. A query that stops at a filesystem boundary or a broken `.git` file is also unanswered. The query of the project is also unanswered if git refuses it, as git refuses a repository that another user owns. A query of git inside `.lpm` that finds no repository is unanswered too. The warning then says which repository did not answer. It asks you to make sure that git ignores the capture files and does not track them. If the other query answers, the warning also reports what it found, for example tracked files. -- When git ignores them and no repository tracks them, the warning says so. -- When git doesn't ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git doesn't read a linked `.lpm/.gitignore`, or one that isn't a file, so replace it with a file that holds the rule. -- When git refuses to work inside `.lpm`, as when `.lpm` looks like a bare repository or holds a repository another user owns, the warning reports what the project's repository says, when the project is in one, and asks you to check that git ignores and doesn't track `inspector.db*`. +- If git ignores the capture files and no repository tracks them, the warning says so. +- If git does not ignore them, the warning names them. Add `inspector.db*` to `.lpm/.gitignore`. Git does not read a linked `.lpm/.gitignore`, or one that is not a file. Replace such an entry with a file that holds the rule. +- Git refuses to work inside `.lpm` if `.lpm` looks like a bare repository, or holds a repository that another user owns. In that case, the warning reports what the repository of the project says, if the project is in one. It asks you to make sure that git ignores `inspector.db*` and does not track it. -The warning names files whose names hold only letters, digits, `.`, `_`, `-` and `/`, and counts the rest. When it can't name every tracked file, its command covers all the capture files, matched without regard to case. So does the command for the project's repository when only git in `.lpm` answered, since the repository may spell `.lpm` otherwise: `git rm --cached -- ":(glob,icase).lpm/inspector.db*"`. +The warning names files whose names hold only letters, digits, `.`, `_`, `-`, and `/`. It counts the other files. If it cannot name every tracked file, its command covers all the capture files, without regard to case. The command for the repository of the project does the same if only git in `.lpm` answered, because the repository can spell `.lpm` differently: `git rm --cached -- ":(glob,icase).lpm/inspector.db*"`. ### Remove committed captures -When a repository tracks capture files, the warning names them. It gives the command for each repository to stop tracking them: `git rm --cached -- .lpm/inspector.db` for the project's, or `git -C .lpm rm --cached -- inspector.db` for one of `.lpm`'s own. An ignore rule doesn't stop a tracked file from being committed. +If a repository tracks capture files, the warning names them. It gives the command that stops the tracking for each repository: `git rm --cached -- .lpm/inspector.db` for the repository of the project, or `git -C .lpm rm --cached -- inspector.db` for a repository of `.lpm` itself. An ignore rule does not prevent the commit of a tracked file. -Captures already committed stay in the repository's history, and in every clone of it. Rotate the credentials they held: webhook signing secrets, authorization tokens and session cookies. Rotate them first. Rewriting history doesn't reach clones and forks that others already have. +Captures that you already committed stay in the history of the repository, and in every clone of it. Rotate the credentials that they hold: webhook signing secrets, authorization tokens, and session cookies. Rotate them first, because a rewrite of the history does not change clones and forks that others already have. For a public repository, also remove the captures from its history. -> **CAUTION:** Rewriting history changes the ID of every commit after the first one it changes. Everyone with a clone must clone again or reset to the new history, and open pull requests may need to be recreated. Back up the repository before you start. +> **CAUTION:** Back up the repository before you rewrite its history. A rewrite changes the ID of every commit after the first commit that it changes. Everyone with a clone must clone again, or reset to the new history. Open pull requests can break, and you must then create them again. ### Recover the capture database -LPM CLI refuses some capture files and stops. A cloned repository could supply any of these: +LPM CLI refuses some capture files and stops. A cloned repository can supply any of these: -- a linked `.lpm` directory, or on Unix a `.lpm` directory of another user's -- a linked capture file, a capture file with other hard links, or on Unix a capture file of another user's -- a database holding anything LPM CLI never creates, such as triggers, views, checks or extra indexes -- a database SQLite reports damaged +- A linked `.lpm` directory, or on Unix, a `.lpm` directory that another user owns +- A linked capture file, a capture file with other hard links, or on Unix, a capture file that another user owns +- A database that holds anything that LPM CLI never creates, for example triggers, views, check constraints, or extra indexes +- A database that SQLite reports as damaged -For a refused `.lpm` directory, move it aside and retry. For a capture file or a damaged database, move the database aside together with its `-wal`, `-shm` and `-journal` files, and retry. LPM CLI starts a new database. +If LPM CLI refuses the `.lpm` directory, move the directory aside and try again. If it refuses a capture file or a damaged database, move the database aside with its `-wal`, `-shm`, and `-journal` files. Then try again. LPM CLI starts a new database. -The moved files hold captures, so keep them private. Move them into a directory only you can read, outside every repository. Keep each file's suffix after the database's name, so SQLite still finds the files together: +The moved files hold captures, so keep them private. Move them into a directory that only you can read, outside every repository. Keep the suffix of each file after the name of the database, so that SQLite still finds the files together: ```bash mkdir -p -m 700 ~/lpm-old-captures/my-project @@ -387,11 +390,11 @@ for suffix in "" -wal -shm -journal; do done ``` -To keep them in `.lpm` instead, give each a name that starts with `inspector.db`, such as `inspector.db.old` and `inspector.db.old-wal`, so `.lpm/.gitignore` still ignores it. Do that only when LPM CLI didn't warn that other users could read the database. Delete the files once you no longer need their captures. +If LPM CLI did not warn that other users can read the database, you can keep the files in `.lpm` instead. Give each file a name that starts with `inspector.db`, for example `inspector.db.old` and `inspector.db.old-wal`, so that `.lpm/.gitignore` still ignores it. Delete the files when you no longer need their captures. -The `-wal` file can hold captures the database doesn't yet. LPM CLI removes the `-wal`, `-shm` and `-journal` files when it finds them without a database, unless it refuses them as above. +The `-wal` file can hold captures that the database does not have yet. If LPM CLI finds the `-wal`, `-shm`, and `-journal` files without a database, it removes them, unless it refuses them as above. -Opening the capture files waits up to 10 seconds while another LPM CLI run opens them, then stops. Errors opening, reading or writing the database are I/O errors, `io` in `--json` output. +If another LPM CLI run opens the capture files, LPM CLI waits up to 10 seconds, then stops. An error when LPM CLI opens, reads, or writes the database is an I/O error, `io` in `--json` output. ## Plans and limits diff --git a/content/docs/infra/tunneling.mdx b/content/docs/infra/tunneling.mdx index ee07d1e9..98d634fc 100644 --- a/content/docs/infra/tunneling.mdx +++ b/content/docs/infra/tunneling.mdx @@ -254,7 +254,7 @@ LPM CLI records forwarded requests and local responses in the current project. O The database can contain full headers, request bodies, response bodies, and session details. The tunnel service does not store these request bodies. -Protect the database as sensitive development data. Do not commit it to source control. LPM CLI adds `inspector.db*` to `.lpm/.gitignore` before it creates the database, and warns when git tracks the capture files or doesn't ignore them. +Protect the database as sensitive development data. Do not commit it to source control. LPM CLI adds `inspector.db*` to `.lpm/.gitignore` before it creates the database. It shows a warning if git tracks the capture files or does not ignore them. On Unix, LPM CLI records captures only in a project whose path no other user can change, and stops otherwise. See [Keep captures private](/docs/infra/tunnel#keep-captures-private). From cf83b2af385b236bb4ee1d36f020b5250cecfbcc Mon Sep 17 00:00:00 2001 From: Tolga Ergin Date: Tue, 13 Oct 2026 22:07:48 +0100 Subject: [PATCH 28/28] chore: update content dates Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/generated/content-dates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/content-dates.json b/lib/generated/content-dates.json index c0dcd8cc..10df2032 100644 --- a/lib/generated/content-dates.json +++ b/lib/generated/content-dates.json @@ -241,11 +241,11 @@ }, "infra/tunnel.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-13T15:37:45+01:00" + "modified": "2026-10-13T22:07:48+01:00" }, "infra/tunneling.mdx": { "published": "2026-05-01T09:47:04+01:00", - "modified": "2026-10-13T15:37:45+01:00" + "modified": "2026-10-13T22:07:48+01:00" }, "installation.mdx": { "published": "2026-05-01T09:47:04+01:00",