Skip to content

majestic: ship the eula, and ask the first login to accept it - #2346

Merged
openipc-ai merged 3 commits into
masterfrom
eula-gate
Sep 1, 2026
Merged

majestic: ship the eula, and ask the first login to accept it#2346
openipc-ai merged 3 commits into
masterfrom
eula-gate

Conversation

@widgetii

@widgetii widgetii commented Sep 1, 2026

Copy link
Copy Markdown
Member

Problem

Phase 2 of the first-boot claim flow (#2342). Majestic is licensed software (free forever
for personal and noncommercial use), and its new EULA v1.0 (en/ru/zh-CN, 2026-09-01) must
be read and accepted by the camera's human owner at the same moment they set the first
password — at either door. Today the image carries no license text and neither claim door
asks.

What this does

  • Ships the three texts at /usr/share/openipc/eula/eula.<lang>.txt through
    general/package/majestic/overlay, keyed on BR2_PACKAGE_MAJESTIC in
    general/scripts/late-overlays.list — a majestic-free image carries no license for
    software it does not have, and both gates key on the files' presence, so such an
    image behaves exactly as today. (First live use of the late-overlay mechanism; verified
    in the built rootfs below.)
  • openipc-claim gains the gate in front of the passwd loop, only while the document is
    on the image: short summary, view to read the full text, the exact words I agree to
    accept (five misses give up; decline leaves the camera unclaimed and streaming
    nothing), the same claimed-elsewhere race check the passwd loop carries. All
    user-visible text is heredoc, so it survives comment-stripping — including a notice that
    AI agents and automation must not accept on a person's behalf.
  • Whichever door claims writes /etc/eula-accepted (<version> <lang> <door> <utc>),
    informational only; /etc/shadow stays the single record of the claim, and both die
    together on firstboot.
  • CLAUDE.md documents the gate at both doors and the assistant policy (no automated
    acceptance; removing majestic from an image entirely is always legitimate).

Pairs with widgetii/majestic#530 (serves /eula.<lang>.txt pre-auth with an explicit
utf-8 charset and enforces eula=accepted in POST /setup iff the document is present)
and OpenIPC/majestic-webui#283 (setup.html shows the text by browser language and posts
the checkbox). Landing order is majestic → webui → this, same as phase 1 — do not merge
before both are merged and their artifacts republished
, or images pair the documents
with a majestic that cannot enforce the browser door (the SSH door still works, and the
new page degrades cleanly either way).

Hardware tested on

hi3516ev300, imx335 lab bench camera, flashed with the exact image from this branch
(openipc.hi3516ev300-nor-lite.tgz, local+build, 2026-09-01) via sysupgrade --url.
The unclaimed state was reproduced with /rom/etc/{passwd,shadow} copied over the live
overlay copies. The browser-door enforcement half lives in majestic and is covered by that
PR's unit tests; this image's majestic (current published tarball) was used to confirm the
old-majestic rows of the pairing matrix.

Evidence

Before (nightly master+bebd80a, same camera):

# ls /usr/share/openipc/eula
ls: /usr/share/openipc/eula: No such file or directory
(first interactive login goes straight to the password prompt; no license shown)

After — shipped rootfs:

- uImage: [2006KB/2048KB]
- rootfs.squashfs: [4816KB/5120KB]
# ls -la /usr/share/openipc/eula/
-rw-r--r--    1 root  root   6366 eula.en.txt
-rw-r--r--    1 root  root  13400 eula.ru.txt
-rw-r--r--    1 root  root   5712 eula.zh-CN.txt
# cat /etc/shells
/bin/ash
/bin/sh
/usr/sbin/openipc-claim

After — decline (view shown full text first; / still 302 → setup.html afterwards):

Type "view" to read the EULA, "I agree" to accept it, or "quit": quit
The EULA was not accepted, so the camera stays as it is: not set up
and streaming nothing. Log in again to continue.

After — exact phrase enforced (i agree, I AGREE, yes, agree, ok ×5 → give up):

Type "view" to read the EULA, "I agree" to accept it, or "quit": i agree
Not accepted. The exact words "I agree" are required (or "view", or "quit").

After — claimed from the browser (POST /setup → 200) while the SSH gate sat at the
EULA prompt, then I agree typed:

Type "view" to read the EULA, "I agree" to accept it, or "quit": I agree

This camera was just set up from somewhere else.
Reconnect and sign in with the password that was set.

After — accept:

Type "view" to read the EULA, "I agree" to accept it, or "quit": I agree
Changing password for root
New password:
Retype password:
Done. This camera is set up and streaming again.
root@hi3516ev300-imx335:~# cat /etc/eula-accepted
1.0 en ssh 2026-09-01T12:11:50Z
root@hi3516ev300-imx335:~# head -1 /etc/passwd
root:x:0:0:root:/root:/bin/sh

Majestic-free control build (gk7205v500_lite, # BR2_PACKAGE_MAJESTIC is not set): no
/usr/share/openipc/eula, no majestic binary, the gate ships dormant and the login flow
is byte-for-byte the phase-1 conversation. Size-critical hi3519v101_lite: rootfs
4736KB/5120KB, no headroom warning.

Script checks: test_shell_parse.sh (134 scripts clean), STRICT=1 test_strip_shell_comments.sh (parses identically stripped; the heredoc notices survive),
ci-matrix.py --self-test, test_sysupgrade.sh, test_excludes_report.sh,
test_load_hisilicon.sh — all green. ci-matrix --stdin widens this diff to all 99
boards, as a shared-overlay change should.

Scope

  • No kernel patches under general/package/all-patches/linux/ (those go to OpenIPC/linux)
  • No files specific to a single retail camera model (those go to OpenIPC/builder)
  • No probing or bring-up tooling (that goes to OpenIPC/ipctool)
  • Nothing under general/overlay/ or in a shared load_<vendor> script hardcodes a value specific to my board
  • Package sources come from an OpenIPC repository, and any version bump keeps at least the specificity of the pin it replaces (a new package should pin a full 40-character SHA)
  • No LD_PRELOAD, and no binaries that cannot be rebuilt from source
  • New code is selected by a defconfig, so CI actually builds it

The three language texts land at /usr/share/openipc/eula/ through the
majestic package's late overlay, so a majestic-free image carries no license
for software it does not have -- and both claim doors key on the files'
presence, so such an image keeps today's flow exactly. openipc-claim shows
the English text before the passwd loop and requires the words "I agree",
typed; majestic's setup page shows it in the visitor's language and enforces
the matching checkbox server-side. Whichever door claims records version,
language, door and time in /etc/eula-accepted, informationally -- /etc/shadow
stays the single record of the claim, and both die together on firstboot.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Gate Majestic first-boot claims on EULA acceptance

✨ Enhancement 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Ships Majestic EULA 1.0 in English, Russian, and Simplified Chinese.
• Requires and records human acceptance before SSH or serial password setup.
• Preserves the existing claim flow for Majestic-free firmware images.
Diagram

graph TD
  Config{"Majestic enabled?"} -->|"Yes: late overlay"| EULAs["Localized EULAs"] --> Gate["First-login gate"] --> Choice{"Human accepts?"}
  Choice -->|Yes| Password["Password claim"] --> Record["Acceptance record"]
  Choice -->|No| Unclaimed["Remain unclaimed"]
  Config -->|No| Legacy["Existing claim flow"]
Loading
High-Level Assessment

The conditional late-overlay and file-presence design is appropriate. It couples the legal payload and gate to Majestic-enabled images while preserving /etc/shadow as the authoritative claim state. A shared global overlay would incorrectly affect Majestic-free builds, while making the acceptance record authoritative would duplicate ownership state and complicate firstboot resets.

Files changed (6) +488 / -0

Enhancement (1) +84 / -0
openipc-claimRequire EULA acceptance before interactive camera claims +84/-0

Require EULA acceptance before interactive camera claims

• Adds a file-presence-gated EULA prompt before the password loop, supporting full-text viewing, exact-phrase acceptance, decline, bounded retries, and concurrent browser-claim detection. Successful local claims write version, language, door, and UTC time to '/etc/eula-accepted'.

general/overlay/usr/sbin/openipc-claim

Documentation (4) +399 / -0
CLAUDE.mdDocument both EULA claim gates and automation policy +18/-0

Document both EULA claim gates and automation policy

• Documents conditional EULA packaging, browser and SSH/serial acceptance behavior, and the informational acceptance record. Adds explicit guidance that AI agents must not accept or bypass the EULA for a human owner.

CLAUDE.md

eula.en.txtAdd English Majestic EULA 1.0 +129/-0

Add English Majestic EULA 1.0

• Ships the authoritative English Majestic End-User License Agreement dated September 1, 2026.

general/package/majestic/overlay/usr/share/openipc/eula/eula.en.txt

eula.ru.txtAdd Russian Majestic EULA 1.0 +147/-0

Add Russian Majestic EULA 1.0

• Ships the Russian translation of the Majestic End-User License Agreement.

general/package/majestic/overlay/usr/share/openipc/eula/eula.ru.txt

eula.zh-CN.txtAdd Simplified Chinese Majestic EULA 1.0 +105/-0

Add Simplified Chinese Majestic EULA 1.0

• Ships the Simplified Chinese translation of the Majestic End-User License Agreement.

general/package/majestic/overlay/usr/share/openipc/eula/eula.zh-CN.txt

Other (1) +5 / -0
late-overlays.listInstall EULA files only with the Majestic package +5/-0

Install EULA files only with the Majestic package

• Registers the Majestic package overlay under 'BR2_PACKAGE_MAJESTIC', preventing EULA files and their presence-triggered gates from appearing in Majestic-free images.

general/scripts/late-overlays.list

Aligned with the review fix on the majestic side: a regular, non-empty,
readable file, so a stray overlay directory or a truncated overlay write at
the eula path cannot demand acceptance of nothing.
libsvpruntime.so (617 KB) and libnnie.so shipped on all four cv500-family
boards with zero consumers: a DT_NEEDED sweep over every ELF in the
2026-08-31 nightly images of hi3516av300 lite/neo, hi3516cv500_lite and
hi3516dv300_lite finds no linker and no dlopen-style string reference,
load_hisilicon's open_nnie lines are commented out, and libsvpruntime needs
the libstdc++ that rootfs_script.sh prunes, so it could not even load on
most builds. That is ~215 KB of squashfs per board -- and the reason
hi3516av300_neo sat 12 KB from its 8192 KB cap and tipped over when the
eula landed. Measured after: 8008 KB, 184 KB of headroom.
@widgetii

widgetii commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

The first matrix run failed on exactly one board: hi3516av300_neo, rootfs 8212/8192 KB — it sat 12 KB from its cap last nightly and the EULA payload plus the freshly republished (larger) majestic tipped it over.

Fixed in 2664df5 by removing libsvpruntime.so (617 KB) and libnnie.so from hisilicon-osdrv-hi3516cv500: a DT_NEEDED sweep over every ELF in the 2026-08-31 nightly rootfs of all four cv500-family boards finds zero consumers and zero dlopen-style string references, load_hisilicon's open_nnie lines are commented out, and libsvpruntime links the libstdc++ that rootfs_script.sh deletes — so the runtime could not even load on most builds. Same static-evidence pattern as the libnl prune (#2317). Measured after the prune: 8008/8192 KB, 184 KB headroom, EULA files present.

While sizing this I also found the cv500 family ships every open_*.ko twice (hisilicon/ + updates/, ~1.7 MB compressed per board) — filed separately as #2347 since fixing it needs cv500 hardware.

@openipc-ai
openipc-ai enabled auto-merge (squash) September 1, 2026 14:18
@openipc-ai
openipc-ai merged commit 969cc99 into master Sep 1, 2026
119 checks passed
@openipc-ai
openipc-ai deleted the eula-gate branch September 1, 2026 15:12
widgetii added a commit that referenced this pull request Sep 1, 2026
…2350)

Post-merge review on #2346: the refusal an unclaimed camera gives a
non-interactive SSH attempt pointed at http://<hostname>/, a name only the
camera itself resolves, so pasting it into a browser went nowhere.

Whoever hit that refusal reached the camera over SSH, and dropbear records the
address they used in SSH_CONNECTION's third field -- the one address their
network provably routes. The hostname stays as the fallback for a local exec
with no connection to read, and an IPv6 literal gets brackets in the URL alone:
a browser wants them, ssh refuses them.

Verified on the hi3516ev300 lab bench, reset to the unclaimed state: the
refusal prints the reached address whether the camera is reached by IP or by
DNS name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants