Author SHA1 Message Date
Dennis Juhler Aagaard 5844fab166 docs: remove outdated live login disclaimer
CI / validate (pull_request) Successful in 1m52s
2026-09-25 12:31:31 +02:00
Dennis Juhler Aagaard 602cbe9340 docs: clarify the MyOpel security PIN
CI / validate (pull_request) Failing after 1m37s
2026-09-25 12:27:55 +02:00
Dennis Juhler Aagaard 94274656d5 docs: switch app UI and user docs to English
CI / validate (pull_request) Failing after 1m11s
2026-09-25 10:58:15 +02:00
dennis 2d232f2aae Merge pull request 'Remove outdated HAOS validation note' (#3) from docs/remove-haos-validation-note into main
CI / validate (push) Successful in 1m36s
Reviewed-on: #3
2026-09-25 10:48:44 +02:00
Dennis Juhler Aagaard 5fcc48668a docs: remove outdated HAOS validation note
CI / validate (pull_request) Successful in 2m0s
2026-09-25 10:38:31 +02:00
dennis 5b77f688f3 Merge pull request 'Update Home Assistant app installation path' (#2) from docs/home-assistant-app-navigation into main
CI / validate (push) Successful in 1m38s
Reviewed-on: #2
2026-09-25 10:00:52 +02:00
Dennis Juhler Aagaard 8fc0be3c36 test: allow Docker stop transport overhead
CI / validate (pull_request) Successful in 2m0s
2026-09-24 22:11:24 +02:00
Dennis Juhler Aagaard f4cda13d9c docs: update Home Assistant app navigation
CI / validate (pull_request) Failing after 2m24s
2026-09-24 22:05:54 +02:00
8 changed files with 137 additions and 123 deletions
+28 -29
View File
@@ -1,8 +1,8 @@
# Stelloauth for Home Assistant # Stelloauth for Home Assistant
Et Home Assistant custom add-on, som kører Stelloauth og CloakBrowser lokalt til A Home Assistant app (formerly known as an add-on) that runs Stelloauth and
OAuth-opsætning af integrationen Stellantis Vehicles. Repositoryet understøtter CloakBrowser locally for OAuth setup of the Stellantis Vehicles integration.
`amd64` og `aarch64`. The repository supports `amd64` and `aarch64`.
```text ```text
Home Assistant Core Home Assistant Core
@@ -10,50 +10,49 @@ Home Assistant Core
| POST http://0031621f-stelloauth:8080/worker | POST http://0031621f-stelloauth:8080/worker
v v
+--------------------------------------------------+ +--------------------------------------------------+
| Ét add-on / én container | | One app / one container |
| | | |
| Stelloauth 0.0.0.0:8080 | | Stelloauth 0.0.0.0:8080 |
| | | | | |
| | CDP http://127.0.0.1:9222 | | | CDP http://127.0.0.1:9222 |
| v | | v |
| CloakBrowser 127.0.0.1:9222 | | CloakBrowser 127.0.0.1:9222 |
+--------------------------------------------------+ +--------------------------------------------------+
``` ```
Én add-on giver de to processer samme Supervisor-livscyklus og holder Running both processes in one app gives them the same Supervisor lifecycle and
CloakBrowsers CDP-port på containerens loopback-interface. Add-onen bygges keeps CloakBrowser's CDP port on the container's loopback interface. The app
lokalt fra kilde, fordi CloakBrowser Binary License ikke tillader, at dette builds locally from source because the CloakBrowser Binary License does not
repository genudgiver en afledt image med den proprietære CloakBrowser-binær. allow this repository to redistribute a derived image containing the
Der publiceres derfor ingen prebuilt images. proprietary CloakBrowser binary. No prebuilt images are published.
## Installation ## Installation
1. Tilføj 1. Go to **Settings → Apps → Install app → ⋮ → Repositories** and add
`https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon.git` som `https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon.git`.
repository i Home Assistants Tilføjelsesbutik. 2. Install **Stelloauth**, enable **Start on boot** and **Watchdog**, then start
2. Installér **Stelloauth**, aktivér **Start ved opstart** og **Watchdog**, og the app.
start add-onen. 3. Follow the complete guide in [stelloauth/DOCS.md](stelloauth/DOCS.md).
3. Følg den fulde vejledning i [stelloauth/DOCS.md](stelloauth/DOCS.md).
HAOS-installation er ikke gennemført eller påstået som valideret. Målmaskinen When the integration asks for a security PIN, enter the four-digit PIN you
havde ved inspektionen approximately 4 GB free, mens det lokalt byggede image chose when activating Remote Control in the MyOpel app. MyOpel does not send
fyldte 2.571 GB; frigør om nødvendigt mere diskplads før installation. you a new PIN; the SMS verification code is separate.
## Fastlåste upstream-kilder ## Pinned upstream sources
| Komponent | Version | Commit / OCI index digest | | Component | Version | Commit / OCI index digest |
| --- | --- | --- | | --- | --- | --- |
| Stelloauth | `v0.6.0` | `367d4f8c02a3b072c59142c49dffc129edc8548b` | | Stelloauth | `v0.6.0` | `367d4f8c02a3b072c59142c49dffc129edc8548b` |
| Stelloauth image contract | `v0.6.0` | `sha256:51b2194ec9b80cc484d11c016ec5436a12161277a493afdab7078959966d5aa9` | | Stelloauth image contract | `v0.6.0` | `sha256:51b2194ec9b80cc484d11c016ec5436a12161277a493afdab7078959966d5aa9` |
| CloakBrowser | `0.5.10` | `f04c23da285b3b3d3cf10c8f9d282e7adc1d52ce` / `sha256:2ed5b2d047cbdde22cde7ef1a796526c716aadaa5bccbe1db5ade49282b64a76` | | CloakBrowser | `0.5.10` | `f04c23da285b3b3d3cf10c8f9d282e7adc1d52ce` / `sha256:2ed5b2d047cbdde22cde7ef1a796526c716aadaa5bccbe1db5ade49282b64a76` |
| Go-builder | `1.27.1-bookworm` | `sha256:69a7b9788769bec032d238959b61854e9ae87f57be9029ec04e9885fabf99195` | | Go builder | `1.27.1-bookworm` | `sha256:69a7b9788769bec032d238959b61854e9ae87f57be9029ec04e9885fabf99195` |
| Stellantis Vehicles | `2026.9.4` | `9e0ef96f8fe478af291da4c38c923ada78d0ebf6` | | Stellantis Vehicles | `2026.9.4` | `9e0ef96f8fe478af291da4c38c923ada78d0ebf6` |
Dockerfile og patches er build-opskriften. Supervisor henter det officielle The Dockerfile and patches define the build. Supervisor downloads the official
CloakBrowser-image og bygger alene et lokalt image til intern brug. CloakBrowser image and builds a local image for internal use only.
## Licens ## License
Repositoryets eget arbejde er MIT-licenseret; se [LICENSE](LICENSE). Dette The repository's original work is licensed under the MIT License; see
omfatter ikke CloakBrowsers proprietære binær. Den er fortsat omfattet af den [LICENSE](LICENSE). The proprietary CloakBrowser binary is not included. It
separate **CloakBrowser Binary License** og redistribueres ikke af repositoryet. remains subject to the separate **CloakBrowser Binary License**.
+7 -6
View File
@@ -2,10 +2,11 @@
## 0.1.0 ## 0.1.0
- Fastlåser Stelloauth `v0.6.0` og CloakBrowser `0.5.10` til verificerede - Pin Stelloauth `v0.6.0` and CloakBrowser `0.5.10` to verified commits and
commits og OCI-digests. OCI digests.
- Tilføjer fælles procesovervågning, readiness, watchdog og begrænset shutdown. - Add shared process supervision, readiness checks, watchdog support, and a
- Dokumenterer intern Login service URL: bounded shutdown.
- Document the internal Login service URL:
`http://0031621f-stelloauth:8080/worker`. `http://0031621f-stelloauth:8080/worker`.
- Begrænser CDP til loopback og hardener URL-validering, request-størrelse, - Restrict CDP to loopback and harden URL validation, request size limits,
rate limiting og logredigering. rate limiting, and log redaction.
+67 -62
View File
@@ -1,15 +1,15 @@
# Installation og drift # Installation and operation
1. Gå til **Indstillinger → Tilføjelser → Tilføjelsesbutik → ⋮ → 1. Go to **Settings → Apps → Install app → ⋮ → Repositories** and add this
Repositorier**, og tilføj præcis exact URL:
`https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon.git`. `https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon.git`.
2. Installér **Stelloauth**, aktivér **Start ved opstart** og **Watchdog**, og 2. Install **Stelloauth**, enable **Start on boot** and **Watchdog**, then start
start derefter add-onen. Installationen bygger et lokalt image fra kilde til the app. Home Assistant builds a local image from source for the selected
den valgte `amd64`- eller `aarch64`-arkitektur. Repositoryet publicerer ikke `amd64` or `aarch64` architecture. This repository does not publish a
et prebuilt image. prebuilt image.
3. Vent, til loggen i denne rækkefølge viser de fem faste readiness-beskeder: 3. Wait for the app log to show these five readiness messages in order:
```text ```text
Cleaning CloakBrowser profiles Cleaning CloakBrowser profiles
@@ -19,71 +19,76 @@
Stelloauth listening on 0.0.0.0:8080 Stelloauth listening on 0.0.0.0:8080
``` ```
4. Behold host-porten deaktiveret i normal drift. Ved kortvarig fejlfinding kan 4. Keep the host port disabled during normal operation. For brief
`8080/tcp` tilknyttes host-port `8080`. Kontrollér derefter troubleshooting, map `8080/tcp` to host port `8080`. Then check
`http://192.168.1.20:8080/` eller worker-endpointet `http://192.168.1.20:8080/` or the worker endpoint at
`http://192.168.1.20:8080/worker`, og **deaktivér porttilknytningen igen**, `http://192.168.1.20:8080/worker`. **Disable the port mapping again** when
når kontrollen er færdig. Worker-endpointet modtager MyOpel-oplysninger og you finish. The worker endpoint receives MyOpel details and has no separate
har ingen egen autentificering. authentication.
5. Åbn konfigurationen af **Stellantis Vehicles**. Angiv præcis 5. Open the **Stellantis Vehicles** integration's configuration and set
`http://0031621f-stelloauth:8080/worker` som **Login service URL**. **Login service URL** to exactly
Integrationen tilføjer ikke `/worker`; hele stien skal derfor stå i feltet. `http://0031621f-stelloauth:8080/worker`. The integration does not append
`/worker`, so include the full path.
6. Vælg **Brand: Opel** og **Country: DK**, og gennemfør derefter integrationens 6. Select **Brand: Opel** and **Country: DK**, then complete the integration's
OAuth-opsætning med dine MyOpel-oplysninger. OAuth setup with your MyOpel details.
7. Add-onens tre muligheder er: When asked for a security PIN, enter the four-digit PIN you chose when
activating Remote Control in the MyOpel app. Do not wait for MyOpel to send
you a new PIN; the SMS verification code is separate.
- `queue_timeout`: hvor længe et loginforsøg må vente på den ene session. 7. The app provides three options:
- `rate_limit_count`: højeste antal loginforsøg i hver periode.
- `rate_limit_duration`: længden af rate limit-perioden.
`CLOAK_MAX_SESSIONS` er fastlåst til én session, fordi CloakBrowsers gratis - `queue_timeout`: how long a login attempt may wait for the single session.
niveau tillader ét samtidigt login. Samtidige forsøg bliver derfor køet. - `rate_limit_count`: the maximum number of login attempts allowed in each
period.
- `rate_limit_duration`: the length of the login rate-limit period.
8. Hvert OAuth-forsøg får en midlertidig profil under `/tmp/cloakserve`. `CLOAK_MAX_SESSIONS` is fixed at one because CloakBrowser's free tier allows
CloakBrowser rydder inaktive browserprocesser efter 30 sekunder, og one concurrent login. Additional attempts wait in the queue.
process manageren rydder gamle profiler ved opstart. Credentials, cookies,
tokens og OAuth-koder gemmes ikke i `/data`. CDP lytter kun på loopback
`127.0.0.1:9222`, og logs bruger faste, redigerede hændelser uden email,
passwords, URLs, koder eller tokens.
9. De målte resultater fra den reelle `linux/amd64`-kørsel under Rosetta var: 8. Each OAuth attempt gets a temporary profile under `/tmp/cloakserve`.
CloakBrowser removes inactive browser processes after 30 seconds, and the
process manager removes old profiles at startup. Credentials, cookies,
tokens, and OAuth codes are not stored in `/data`. CDP listens only on the
loopback address `127.0.0.1:9222`. Logs use fixed, redacted messages and do
not include email addresses, passwords, URLs, codes, or tokens.
- Image: 2,571,693,650 bytes (2.571 GB decimal / 2452.56 MiB). 9. Measurements from a real `linux/amd64` run under Rosetta:
- Dokumenteret Task 4-måling: 121,5 MiB.
- Stop: cirka 9.3 sekunder.
- `amd64` runtime bestod under Rosetta; `aarch64` build bestod.
- Mål-HAOS havde ved inspektionen approximately 4 GB free. Den knappe
plads sammenholdt med image- og build-lag kan forhindre installationen;
frigør plads først. Der er ikke verificeret en vellykket HAOS-installation.
Der er ikke gennemført et live MyOpel-login. - Image: 2,571,693,650 bytes (2.571 GB decimal / 2,452.56 MiB).
Login-flow RAM: not measured without real MyOpel credentials. - Documented Task 4 measurement: 121.5 MiB.
- Stop time: about 9.3 seconds.
- The `amd64` runtime passed under Rosetta; the `aarch64` build passed.
10. Fejlfinding og fjernelse: 10. Troubleshooting and removal:
- Mangler en readiness-besked, så se efter timeout: CloakBrowser har 60 - If a readiness message is missing, check for a timeout. CloakBrowser has
sekunder og Stelloauth 30 sekunder. Ret årsagen og genstart add-onen. 60 seconds and Stelloauth has 30 seconds. Fix the cause, then restart the
- Et ugyldigt eller ikke-tilladt authorize-URL giver HTTP `400`. app.
- For mange loginforsøg giver HTTP `429`; vent den konfigurerede periode. - An invalid or disallowed authorize URL returns HTTP `400`.
- Hvis repository-URL eller hostname ændres, ændres Supervisor-repository-ID - Too many login attempts return HTTP `429`; wait for the configured
og dermed `0031621f-stelloauth`. Beregn og brug den nye interne URL. rate-limit period.
- Ved disk pressure: kontrollér fri plads og fjern unødvendige images eller - If the repository URL or hostname changes, the Supervisor repository ID
backups via de normale Supervisor-funktioner før et nyt build. and `0031621f-stelloauth` hostname also change. Calculate and use the new
- Hvis den interne URL ikke kan nås, brug kun den midlertidige portkontrol internal URL.
fra trin 4 og deaktivér porttilknytningen bagefter. - If disk space is low, check available space and remove unneeded images or
- Fjernelse sker i **Indstillinger → Tilføjelser → Stelloauth → Afinstallér**. backups through Supervisor before building again.
Supervisor stopper containeren og fjerner add-onens lokale data; fjern - If the internal URL cannot be reached, use the temporary port check from
også repositoryet fra Tilføjelsesbutikken, hvis det ikke længere bruges. step 4, then disable the port mapping again.
- To remove the app, go to **Settings → Apps → Stelloauth → Uninstall**.
Supervisor stops the container and removes the app's local data. If you
no longer need the repository, remove it from **Settings → Apps → Install
app → ⋮ → Repositories** as well.
## Kilder og licenser ## Sources and licenses
Add-on-version `0.1.0` bygger Stelloauth `v0.6.0` fra commit App version `0.1.0` builds Stelloauth `v0.6.0` from commit
`367d4f8c02a3b072c59142c49dffc129edc8548b` og bruger det officielle `367d4f8c02a3b072c59142c49dffc129edc8548b` and uses the official CloakBrowser
CloakBrowser `0.5.10`-image ved OCI index digest `0.5.10` image at OCI index digest
`sha256:2ed5b2d047cbdde22cde7ef1a796526c716aadaa5bccbe1db5ade49282b64a76`. `sha256:2ed5b2d047cbdde22cde7ef1a796526c716aadaa5bccbe1db5ade49282b64a76`.
Repositoryets egne filer og patches er MIT-licenserede. CloakBrowsers The repository's original files and patches are licensed under the MIT License.
proprietære binær er fortsat under den separate **CloakBrowser Binary License**; The proprietary CloakBrowser binary remains subject to the separate
den er ikke MIT-licenseret eller redistribueret af dette repository. **CloakBrowser Binary License**. It is not licensed under MIT or redistributed
by this repository.
+6 -6
View File
@@ -1,10 +1,10 @@
# Stelloauth # Stelloauth
Lokal OAuth-worker til integrationen Stellantis Vehicles. Add-onen bygger A local OAuth worker for the Stellantis Vehicles integration. This app builds
Stelloauth `v0.6.0` og CloakBrowser `0.5.10` lokalt, understøtter `amd64` og Stelloauth `v0.6.0` and CloakBrowser `0.5.10` locally, supports `amd64` and
`aarch64` og eksponerer som standard ingen host-port. `aarch64`, and exposes no host port by default.
Se [den fulde installations- og fejlfindingsvejledning](DOCS.md). See the [installation and troubleshooting guide](DOCS.md).
CloakBrowsers binær er under den separate CloakBrowser Binary License og er The CloakBrowser binary is covered by the separate CloakBrowser Binary License,
ikke omfattet af repositoryets MIT-licens. not by this repository's MIT License.
+6 -6
View File
@@ -1,10 +1,10 @@
configuration: configuration:
queue_timeout: queue_timeout:
name: Køventetid name: Queue timeout
description: Angiver hvor længe et loginforsøg må vente i køen. description: Sets how long a login attempt may wait in the queue.
rate_limit_count: rate_limit_count:
name: Loginforsøg name: Login attempts
description: Angiver det maksimale antal loginforsøg i hver periode. description: Sets the maximum number of login attempts in each period.
rate_limit_duration: rate_limit_duration:
name: Rate limit-periode name: Rate limit period
description: Angiver periodens længde for begrænsning af loginforsøg. description: Sets the length of the login attempt rate-limit period.
+1 -1
View File
@@ -7,4 +7,4 @@ configuration:
description: Sets the maximum number of login attempts in each period. description: Sets the maximum number of login attempts in each period.
rate_limit_duration: rate_limit_duration:
name: Rate limit period name: Rate limit period
description: Sets the length of the login attempt rate limit period. description: Sets the length of the login attempt rate-limit period.
+20 -11
View File
@@ -55,45 +55,51 @@ def test_user_guide_documentation_contract() -> None:
documentation = (ROOT / "stelloauth/DOCS.md").read_text(encoding="utf-8") documentation = (ROOT / "stelloauth/DOCS.md").read_text(encoding="utf-8")
for required_text in ( for required_text in (
REPOSITORY_URL, REPOSITORY_URL,
"Installér **Stelloauth**", "Settings → Apps → Install app → ⋮ → Repositories",
"aktivér **Start ved opstart** og **Watchdog**", "Install **Stelloauth**",
"enable **Start on boot** and **Watchdog**",
"http://0031621f-stelloauth:8080/worker", "http://0031621f-stelloauth:8080/worker",
"http://192.168.1.20:8080/worker", "http://192.168.1.20:8080/worker",
"Brand: Opel", "Brand: Opel",
"Country: DK", "Country: DK",
"Image: 2,571,693,650 bytes (2.571 GB decimal / 2452.56 MiB).", "Image: 2,571,693,650 bytes (2.571 GB decimal / 2,452.56 MiB).",
"Dokumenteret Task 4-måling: 121,5 MiB.", "Documented Task 4 measurement: 121.5 MiB.",
"Stop: cirka 9.3 sekunder.", "Stop time: about 9.3 seconds.",
"approximately 4 GB free", "**Settings → Apps → Stelloauth → Uninstall**",
"Der er ikke gennemført et live MyOpel-login.",
"Login-flow RAM: not measured without real MyOpel credentials.",
): ):
assert required_text in documentation assert required_text in documentation
assert "deaktivér porttilknytningen igen" in documentation assert "Disable the port mapping again" in documentation
assert "Seneste idle RAM" not in documentation assert "approximately 4 GB free" not in documentation
assert "successful HAOS installation has not been verified" not in documentation
def test_root_readme_repository_source_and_license_facts() -> None: def test_root_readme_repository_source_and_license_facts() -> None:
readme = (ROOT / "README.md").read_text(encoding="utf-8") readme = (ROOT / "README.md").read_text(encoding="utf-8")
for required_text in ( for required_text in (
REPOSITORY_URL, REPOSITORY_URL,
"Settings → Apps → Install app → ⋮ → Repositories",
"v0.6.0", "v0.6.0",
"367d4f8c02a3b072c59142c49dffc129edc8548b", "367d4f8c02a3b072c59142c49dffc129edc8548b",
"0.5.10", "0.5.10",
"f04c23da285b3b3d3cf10c8f9d282e7adc1d52ce", "f04c23da285b3b3d3cf10c8f9d282e7adc1d52ce",
"CloakBrowser Binary License", "CloakBrowser Binary License",
"MIT-licenseret", "licensed under the MIT License",
): ):
assert required_text in readme assert required_text in readme
assert "Indstillinger" not in readme
assert "Installér" not in readme
def test_translations_cover_every_option() -> None: def test_translations_cover_every_option() -> None:
keys = set(load_yaml("stelloauth/config.yaml")["options"]) keys = set(load_yaml("stelloauth/config.yaml")["options"])
translations = {}
for language in ("da", "en"): for language in ("da", "en"):
translation = load_yaml(f"stelloauth/translations/{language}.yaml") translation = load_yaml(f"stelloauth/translations/{language}.yaml")
assert set(translation["configuration"]) == keys assert set(translation["configuration"]) == keys
translations[language] = translation["configuration"]
for entry in translation["configuration"].values(): for entry in translation["configuration"].values():
assert set(entry) == {"name", "description"} assert set(entry) == {"name", "description"}
assert all(isinstance(value, str) and value.strip() for value in entry.values()) assert all(isinstance(value, str) and value.strip() for value in entry.values())
assert translations["da"] == translations["en"]
def test_dockerfile_uses_approved_pins_and_builds_patched_stelloauth() -> None: def test_dockerfile_uses_approved_pins_and_builds_patched_stelloauth() -> None:
@@ -277,6 +283,9 @@ def test_runtime_requires_process_baseline_after_cdp_close_and_zero_stopped_pid(
assert mapping_index < ready_index < baseline_index < close_index < return_index assert mapping_index < ready_index < baseline_index < close_index < return_index
assert "{{.State.Pid}}" in runtime_test assert "{{.State.Pid}}" in runtime_test
assert '[ "$state" = "exited 0 0" ]' in runtime_test assert '[ "$state" = "exited 0 0" ]' in runtime_test
assert 'docker stop --time 10 "$container"' in runtime_test
assert "if float(sys.argv[1]) > 12.0:" in runtime_test
assert "container stop exceeded 12 seconds" in runtime_test
def test_runtime_process_normalization_retains_every_unknown_wrapped_child( def test_runtime_process_normalization_retains_every_unknown_wrapped_child(
+2 -2
View File
@@ -345,8 +345,8 @@ PY
printf 'seconds=%s\n' "$elapsed" > "$timing_artifact" printf 'seconds=%s\n' "$elapsed" > "$timing_artifact"
python3 - "$elapsed" <<'PY' python3 - "$elapsed" <<'PY'
import sys import sys
if float(sys.argv[1]) > 10.0: if float(sys.argv[1]) > 12.0:
raise SystemExit(f"container stop exceeded 10 seconds: {sys.argv[1]}") raise SystemExit(f"container stop exceeded 12 seconds: {sys.argv[1]}")
PY PY
state="$(docker inspect --format '{{.State.Status}} {{.State.ExitCode}} {{.State.Pid}}' "$container")" state="$(docker inspect --format '{{.State.Status}} {{.State.ExitCode}} {{.State.Pid}}' "$container")"
[ "$state" = "exited 0 0" ] || fail "$container state is $state, want exited 0 with PID 0" [ "$state" = "exited 0 0" ] || fail "$container state is $state, want exited 0 with PID 0"