diff --git a/README.md b/README.md
index 6ceb56c..39a8cc0 100644
--- a/README.md
+++ b/README.md
@@ -22,21 +22,29 @@
## See it working first
-Before setup, this sample n8n configuration shows GPT-5.6 model aliases in the
-OpenAI Chat Model selector. The exact model list depends on the signed-in
-ChatGPT account and can change over time.
+This documented example follows a tested n8n OpenAI credential through a
+published Telegram-triggered workflow. It is a product-operation record, not
+an endorsement, sponsorship, or affiliation by OpenAI, n8n, Hostinger,
+Telegram, or AppBuildersPH.
-
- Compatibility preview: the model selector includes gpt-5.6-sol, gpt-5.6-luna, and gpt-5.6-terra.
+
+ The n8n OpenAI credential connection test succeeded; the credential value itself is obscured.
-After Relmio completes the installation, Docker shows the existing n8n stack
-and the new private OAuth sidecar running together.
+
+
+ This signed-in account's n8n model list includes gpt-5.6-terra; model availability is account-dependent and can change.
+
-
- Completed setup: the existing n8n stack and the n8n-openai-oauth sidecar are both running.
+
+ The published Telegram-triggered n8n workflow completed successfully across its HTTP Request, Basic LLM Chain, and AI Agent branches.
+
+
+
+
+ Telegram received the HTTP Request, Basic LLM Chain, and AI Agent outputs sent by that workflow.
## Quick install
@@ -380,7 +388,13 @@ you want to inspect, reproduce, improve, or debug the method, use
Do **not** run an installer command on the VPS. Run it on the computer where
you will complete the browser sign-in.
-### 1. Start the newest published wizard
+### 1. Choose an installation method in the hosted wizard
+
+The hosted installation page starts with a selector for macOS/Linux,
+Homebrew, PowerShell, Command Prompt, and NPX. Choose the terminal already
+installed on your computer, then run the displayed command locally.
+
+
Choose a terminal already installed on your computer.
@@ -452,9 +466,10 @@ npm view relmio version
```
> [!NOTE]
-> Every wizard screenshot below comes from the built-in sanitized preview. It
-> uses a reserved documentation IP, fake fingerprint, fake container data, and
-> fake models. No real VPS, credential, or browser session is shown.
+> These current product screenshots omit or redact sensitive values. In
+> particular, the VPS identity screen redacts the address and SSH fingerprint;
+> none of the screenshots includes an OAuth token, password, private key, or
+> live wizard session URL.
### 2. Complete or reuse the local ChatGPT sign-in
@@ -469,11 +484,10 @@ credential already exists, **Continue to VPS** reuses it. Use **Refresh
ChatGPT sign-in** when it is expired, belongs to another account, or you want
a new session.
-After a fresh login, check the **Credential updated** timestamp. It is shown
-in your computer's local time so you can tell that the browser approval
-actually reached the wizard.
+After a fresh login, the browser confirmation states that the ChatGPT
+credentials were saved locally; return to the terminal to continue.
-
+
If a browser extension named **Sign in with ChatGPT** or **OpenAI OAuth**
captures the callback, temporarily disable that extension and start the
@@ -486,11 +500,11 @@ Enter the VPS address exactly as your provider shows it. Select **Check server
identity**, compare the SHA-256 fingerprint with the intended server, and
confirm it before the password field unlocks.
-
+
-The screenshot uses the reserved documentation address `192.0.2.10`, a fake
-fingerprint, and an empty password field. Never publish a real password,
-private key, session URL, or OAuth file.
+The screenshot redacts the VPS address and SSH fingerprint and obscures the
+password field. Never publish a real password, private key, session URL, or
+OAuth file.
### 4. Choose the detected n8n container and shared network
@@ -498,7 +512,7 @@ The wizard connects over SSH, runs read-only Docker discovery, and lists the
running n8n container and its networks. Choose the network that n8n should
share with the sidecar; on many Hostinger templates it is named `proxy`.
-
+
### 5. Review and approve the exact plan
@@ -507,7 +521,7 @@ managed directory, service, Docker network, and private hostname. It also
states the forbidden actions: no n8n edit, rebuild, restart, or recreation;
no host port; and no Traefik route.
-
+
Only after you select the approval checkbox can the wizard upload the OAuth
file and build the separate sidecar.
@@ -517,7 +531,7 @@ file and build the separate sidecar.
The final screen appears only after the sidecar is healthy, the model list is
reachable, and Docker reports no published host port.
-
+
Use the button beside each value to copy it individually, or select
**Copy credential settings** for the labeled credential set. Then create or
diff --git a/docs/images/examples/gpt-56-model-selector.png b/docs/images/examples/gpt-56-model-selector.png
index 9ce452c..a9f5f1b 100644
Binary files a/docs/images/examples/gpt-56-model-selector.png and b/docs/images/examples/gpt-56-model-selector.png differ
diff --git a/docs/images/examples/n8n-openai-credential-connected.png b/docs/images/examples/n8n-openai-credential-connected.png
new file mode 100644
index 0000000..36a541e
Binary files /dev/null and b/docs/images/examples/n8n-openai-credential-connected.png differ
diff --git a/docs/images/examples/telegram-model-results.png b/docs/images/examples/telegram-model-results.png
new file mode 100644
index 0000000..8337c1f
Binary files /dev/null and b/docs/images/examples/telegram-model-results.png differ
diff --git a/docs/images/examples/telegram-n8n-workflow-execution.png b/docs/images/examples/telegram-n8n-workflow-execution.png
new file mode 100644
index 0000000..e249a80
Binary files /dev/null and b/docs/images/examples/telegram-n8n-workflow-execution.png differ
diff --git a/docs/images/setup/00-install-methods.png b/docs/images/setup/00-install-methods.png
new file mode 100644
index 0000000..a6773c4
Binary files /dev/null and b/docs/images/setup/00-install-methods.png differ
diff --git a/docs/images/setup/01-local-sign-in-ready.png b/docs/images/setup/01-local-sign-in-ready.png
index ab51e8d..2a9b145 100644
Binary files a/docs/images/setup/01-local-sign-in-ready.png and b/docs/images/setup/01-local-sign-in-ready.png differ
diff --git a/docs/images/setup/02-vps-identity-confirmed.png b/docs/images/setup/02-vps-identity-confirmed.png
index 7673fbb..19d1147 100644
Binary files a/docs/images/setup/02-vps-identity-confirmed.png and b/docs/images/setup/02-vps-identity-confirmed.png differ
diff --git a/docs/images/setup/03-n8n-detected.png b/docs/images/setup/03-n8n-detected.png
index ee02fc2..90c4897 100644
Binary files a/docs/images/setup/03-n8n-detected.png and b/docs/images/setup/03-n8n-detected.png differ
diff --git a/docs/images/setup/04-install-plan.png b/docs/images/setup/04-install-plan.png
index e285754..101bae9 100644
Binary files a/docs/images/setup/04-install-plan.png and b/docs/images/setup/04-install-plan.png differ
diff --git a/docs/images/setup/05-bridge-ready.png b/docs/images/setup/05-bridge-ready.png
index 3367ec9..e44932e 100644
Binary files a/docs/images/setup/05-bridge-ready.png and b/docs/images/setup/05-bridge-ready.png differ
diff --git a/npm/README.md b/npm/README.md
index 1acba05..33d8911 100644
--- a/npm/README.md
+++ b/npm/README.md
@@ -15,21 +15,29 @@
## See it working first
-Before setup, this sample n8n configuration shows GPT-5.6 model aliases in the
-OpenAI Chat Model selector. The exact model list depends on the signed-in
-ChatGPT account and can change over time.
+This documented example follows a tested n8n OpenAI credential through a
+published Telegram-triggered workflow. It is a product-operation record, not
+an endorsement, sponsorship, or affiliation by OpenAI, n8n, Hostinger,
+Telegram, or AppBuildersPH.
-
- Compatibility preview: the model selector includes gpt-5.6-sol, gpt-5.6-luna, and gpt-5.6-terra.
+
+ The n8n OpenAI credential connection test succeeded; the credential value itself is obscured.
-After Relmio completes the installation, Docker shows the existing n8n stack
-and the new private OAuth sidecar running together.
+
+
+ This signed-in account's n8n model list includes gpt-5.6-terra; model availability is account-dependent and can change.
+
+
+
+
+ The published Telegram-triggered n8n workflow completed successfully across its HTTP Request, Basic LLM Chain, and AI Agent branches.
+
-
- Completed setup: the existing n8n stack and the n8n-openai-oauth sidecar are both running.
+
+ Telegram received the HTTP Request, Basic LLM Chain, and AI Agent outputs sent by that workflow.
Relmio is a local browser wizard that installs a private
@@ -184,28 +192,47 @@ it is not currently advertised as supported.
## Visual walkthrough
-All images below are sanitized previews with reserved addresses, fake server
-data, and no real credential or session information.
+These current product screenshots omit or redact sensitive values. The VPS
+identity screen redacts the address and SSH fingerprint; none of the images
+includes an OAuth token, password, private key, or live wizard session URL.
+
+### 1. Choose a hosted installation method
+
+
+
+The hosted page starts with the installation-method selector. Run the selected
+command locally, not on the VPS.
+
+### 2. Complete the local ChatGPT/Codex sign-in
+
+
+
+The browser confirmation states that the ChatGPT credentials were saved locally.
+
+### 3. Verify the VPS identity
-### 1. Confirm the local ChatGPT/Codex sign-in
+
-
+Confirm the SSH host fingerprint before authentication. The screenshot redacts
+the address and fingerprint and obscures the password field.
-### 2. Verify the VPS identity
+### 4. Choose the detected n8n container and network
-
+
-### 3. Choose the detected n8n container and network
+Discovery is read-only; choose the existing n8n container and shared Docker
+network.
-
+### 5. Review the exact sidecar-only plan
-### 4. Review the exact sidecar-only plan
+
-
+The plan names the allowed sidecar work and explicitly excludes edits or
+restarts of n8n, a published host port, and a Traefik route.
-### 5. Copy the verified n8n settings
+### 6. Copy the verified n8n settings
-
+
Use these values in an n8n OpenAI credential:
@@ -265,7 +292,8 @@ has the importable cURL version.
- Relmio never edits, rebuilds, recreates, stops, or restarts the existing n8n
container.
- This is an unofficial community project, not affiliated with or endorsed by
- OpenAI, xAI, or n8n. Provider access, models, limits, and policies can change.
+ OpenAI, xAI, n8n, Hostinger, Telegram, or AppBuildersPH. Provider access,
+ models, limits, and policies can change.
- Use it only where your account, subscription, provider terms, and applicable
policies allow.
diff --git a/test/package-contents.test.js b/test/package-contents.test.js
index 94a8707..86efde8 100644
--- a/test/package-contents.test.js
+++ b/test/package-contents.test.js
@@ -33,7 +33,11 @@ const expectedPackedFiles = new Set([
"docs/images/examples/gpt-56-model-selector.png",
"docs/images/examples/gpt-56-sol-chat-model-run.png",
"docs/images/examples/hosted-chat-connected.png",
+ "docs/images/examples/n8n-openai-credential-connected.png",
"docs/images/examples/sidecar-docker-containers-running.png",
+ "docs/images/examples/telegram-model-results.png",
+ "docs/images/examples/telegram-n8n-workflow-execution.png",
+ "docs/images/setup/00-install-methods.png",
"docs/images/setup/01-local-sign-in-ready.png",
"docs/images/setup/02-vps-identity-confirmed.png",
"docs/images/setup/03-n8n-detected.png",
@@ -244,6 +248,22 @@ test("npm package substitutes a registry-safe README without changing GitHub dia
npmReadme,
/https:\/\/cdn\.jsdelivr\.net\/npm\/relmio@latest\/docs\/images\/setup\/05-bridge-ready\.png/u,
);
+ assert.match(
+ npmReadme,
+ /https:\/\/cdn\.jsdelivr\.net\/npm\/relmio@latest\/docs\/images\/setup\/00-install-methods\.png/u,
+ );
+ assert.match(
+ npmReadme,
+ /https:\/\/cdn\.jsdelivr\.net\/npm\/relmio@latest\/docs\/images\/examples\/n8n-openai-credential-connected\.png/u,
+ );
+ assert.match(
+ npmReadme,
+ /https:\/\/cdn\.jsdelivr\.net\/npm\/relmio@latest\/docs\/images\/examples\/telegram-model-results\.png/u,
+ );
+ assert.match(
+ npmReadme,
+ /https:\/\/cdn\.jsdelivr\.net\/npm\/relmio@latest\/docs\/images\/examples\/telegram-n8n-workflow-execution\.png/u,
+ );
assert.match(
npmReadme,
/https:\/\/cdn\.jsdelivr\.net\/npm\/relmio@latest\/docs\/images\/examples\/hosted-chat-connected\.png/u,
diff --git a/test/project.test.js b/test/project.test.js
index 98212fb..97349d2 100644
--- a/test/project.test.js
+++ b/test/project.test.js
@@ -2,6 +2,46 @@ import assert from "node:assert/strict";
import { access, readFile } from "node:fs/promises";
import test from "node:test";
+const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10];
+const maximumReadmeImageDimension = 10_000;
+
+function assertMetadataFreeReadmePng(image, name) {
+ assert.ok(image.length >= 33, `${name} is too short to be a PNG`);
+ assert.deepEqual([...image.subarray(0, 8)], pngSignature, `${name} signature`);
+ assert.equal(image.readUInt32BE(8), 13, `${name} IHDR length`);
+ assert.equal(image.toString("ascii", 12, 16), "IHDR", `${name} IHDR type`);
+
+ const width = image.readUInt32BE(16);
+ const height = image.readUInt32BE(20);
+ assert.ok(
+ width >= 1 && width <= maximumReadmeImageDimension,
+ `${name} has an unreasonable width: ${width}`,
+ );
+ assert.ok(
+ height >= 1 && height <= maximumReadmeImageDimension,
+ `${name} has an unreasonable height: ${height}`,
+ );
+
+ const chunkTypes = [];
+ let offset = 8;
+ while (offset < image.length) {
+ assert.ok(offset + 12 <= image.length, `${name} has a truncated PNG chunk`);
+ const length = image.readUInt32BE(offset);
+ const chunkEnd = offset + 12 + length;
+ assert.ok(chunkEnd <= image.length, `${name} has an oversized PNG chunk`);
+ chunkTypes.push(image.toString("ascii", offset + 4, offset + 8));
+ offset = chunkEnd;
+ }
+
+ assert.equal(offset, image.length, `${name} PNG length`);
+ assert.equal(chunkTypes[0], "IHDR", `${name} first PNG chunk`);
+ assert.equal(chunkTypes.at(-1), "IEND", `${name} final PNG chunk`);
+ assert.ok(
+ chunkTypes.every((type) => ["IHDR", "IDAT", "IEND"].includes(type)),
+ `${name} has unexpected PNG metadata chunks: ${chunkTypes.join(", ")}`,
+ );
+}
+
test("project pins the reviewed SSH dependency and Node runtime", async () => {
const [packageContents, license, notice] = await Promise.all([
readFile("package.json", "utf8"),
@@ -168,7 +208,7 @@ test("package-manager workflow builds review artifacts before release publicatio
}
});
-test("public README documents the latest npm walkthrough and sanitized images", async () => {
+test("public README documents the latest npm walkthrough and screenshot records", async () => {
const readme = await readFile("README.md", "utf8");
assert.match(
@@ -179,8 +219,21 @@ test("public README documents the latest npm walkthrough and sanitized images",
readme,
/npx --yes --ignore-scripts relmio@latest/u,
);
- assert.match(readme, /docs\/images\/setup\/01-local-sign-in-ready\.png/u);
- assert.match(readme, /docs\/images\/setup\/05-bridge-ready\.png/u);
+ const requiredReadmeImages = [
+ "docs/images/setup/00-install-methods.png",
+ "docs/images/setup/01-local-sign-in-ready.png",
+ "docs/images/setup/02-vps-identity-confirmed.png",
+ "docs/images/setup/03-n8n-detected.png",
+ "docs/images/setup/04-install-plan.png",
+ "docs/images/setup/05-bridge-ready.png",
+ "docs/images/examples/n8n-openai-credential-connected.png",
+ "docs/images/examples/gpt-56-model-selector.png",
+ "docs/images/examples/telegram-n8n-workflow-execution.png",
+ "docs/images/examples/telegram-model-results.png",
+ ];
+ for (const imagePath of requiredReadmeImages) {
+ assert.ok(readme.includes(imagePath), `README references ${imagePath}`);
+ }
assert.match(readme, /```mermaid/u);
assert.match(readme, /Copy credential settings/u);
assert.match(readme, /\[Changelog\]\(CHANGELOG\.md\)/u);
@@ -204,81 +257,30 @@ test("public README documents the latest npm walkthrough and sanitized images",
assert.match(readme, /## Legal/u);
});
-test("README walkthrough images are metadata-free PNG files", async () => {
- const images = await Promise.all(
- [
- "01-local-sign-in-ready.png",
- "02-vps-identity-confirmed.png",
- "03-n8n-detected.png",
- "04-install-plan.png",
- "05-bridge-ready.png",
- ].map((name) => readFile(`docs/images/setup/${name}`)),
- );
-
- for (const image of images) {
- assert.deepEqual(
- [...image.subarray(0, 8)],
- [137, 80, 78, 71, 13, 10, 26, 10],
- );
- assert.equal(image.readUInt32BE(16), 1440);
- assert.ok(image.readUInt32BE(20) >= 1_000);
-
- const chunkTypes = [];
- let offset = 8;
- while (offset < image.length) {
- const length = image.readUInt32BE(offset);
- const type = image.toString("ascii", offset + 4, offset + 8);
- chunkTypes.push(type);
- offset += 12 + length;
- }
- assert.equal(offset, image.length);
- assert.equal(chunkTypes[0], "IHDR");
- assert.equal(chunkTypes.at(-1), "IEND");
- assert.ok(
- chunkTypes.every((type) => ["IHDR", "IDAT", "IEND"].includes(type)),
- `unexpected PNG metadata chunk: ${chunkTypes.join(", ")}`,
- );
- }
-});
-
-test("README example images are metadata-free PNG files", async () => {
- const names = [
- "gpt-56-ai-agent-luna-run.png",
- "gpt-56-ai-agent-sol-run.png",
- "gpt-56-ai-agent-workflow.png",
- "gpt-56-luna-chat-model-run.png",
- "gpt-56-model-selector.png",
- "gpt-56-sol-chat-model-run.png",
- "hosted-chat-connected.png",
- "sidecar-docker-containers-running.png",
+test("README walkthrough and proof images are metadata-free PNG files", async () => {
+ const paths = [
+ "docs/images/setup/00-install-methods.png",
+ "docs/images/setup/01-local-sign-in-ready.png",
+ "docs/images/setup/02-vps-identity-confirmed.png",
+ "docs/images/setup/03-n8n-detected.png",
+ "docs/images/setup/04-install-plan.png",
+ "docs/images/setup/05-bridge-ready.png",
+ "docs/images/examples/gpt-56-ai-agent-luna-run.png",
+ "docs/images/examples/gpt-56-ai-agent-sol-run.png",
+ "docs/images/examples/gpt-56-ai-agent-workflow.png",
+ "docs/images/examples/gpt-56-luna-chat-model-run.png",
+ "docs/images/examples/gpt-56-sol-chat-model-run.png",
+ "docs/images/examples/hosted-chat-connected.png",
+ "docs/images/examples/sidecar-docker-containers-running.png",
+ "docs/images/examples/telegram-model-results.png",
+ "docs/images/examples/telegram-n8n-workflow-execution.png",
+ "docs/images/examples/gpt-56-model-selector.png",
+ "docs/images/examples/n8n-openai-credential-connected.png",
];
- const images = await Promise.all(
- names.map((name) => readFile(`docs/images/examples/${name}`)),
- );
+ const images = await Promise.all(paths.map((path) => readFile(path)));
- for (const image of images) {
- assert.deepEqual(
- [...image.subarray(0, 8)],
- [137, 80, 78, 71, 13, 10, 26, 10],
- );
- assert.ok(image.readUInt32BE(16) >= 1);
- assert.ok(image.readUInt32BE(20) >= 1);
-
- const chunkTypes = [];
- let offset = 8;
- while (offset < image.length) {
- const length = image.readUInt32BE(offset);
- const type = image.toString("ascii", offset + 4, offset + 8);
- chunkTypes.push(type);
- offset += 12 + length;
- }
- assert.equal(offset, image.length);
- assert.equal(chunkTypes[0], "IHDR");
- assert.equal(chunkTypes.at(-1), "IEND");
- assert.ok(
- chunkTypes.every((type) => ["IHDR", "IDAT", "IEND"].includes(type)),
- `unexpected PNG metadata chunk: ${chunkTypes.join(", ")}`,
- );
+ for (const [index, image] of images.entries()) {
+ assertMetadataFreeReadmePng(image, paths[index]);
}
});