Commit Graph

1847 Commits

Author SHA1 Message Date
admin 970616b72b New apps 2026-10-10: Grocy and LubeLogger on the website; Monica stopped
gates / gates (push) Failing after 11m41s
The catalogue side is app-catalog-felhom.eu b7f0f7c. Here: the evidence, the
website, the register and the operator's view.

Website
- Two cards in the Otthon & Eletmod / Home & Lifestyle section of BOTH apps
  pages, with assets: grocy-logo.svg is grocy's own icon with its single fill
  made white like the other logos, lubelogger-logo.png is the app's own icon
  with its dark background dropped and the mark made white (the rule
  SparkyFitness's PNG follows), and six screenshots of each app's own UI with a
  household's own data, taken headless on the bench from the published template.
- The app count moved 56 -> 58 in 15 places per language set, both languages,
  and the open-source tile 49 -> 51. Checked by asking the same patterns for the
  new number afterwards. marketing/facebook/COPY.md still says 56 and is NOT
  changed: the post it carries is already scheduled.

Evidence
- documentation/audits/new-apps-2026-10-10/ — FIT.md (checklist group 0 for all
  three, with the Hungarian-UI column), the bench and box transcripts, the
  memory samples, the screenshots and the gate runs.

Register: 137 -> 139 rows, 2 opened, 0 closed.
- R-926 after a remove-keeping-backups and restore, nobody has checked what the
  app page shows for an after_install app's generated password. Measured here:
  LubeLogger is safe (its login is derived from the environment at every start,
  the new password signs in, the data is back); grocy's install password still
  signs in from the restored database but the deployed environment no longer
  carries ADMIN_PASSWORD at all. Seven apps are in the class.
- R-927 Monica, stopped at checklist 0.2 with the measurements, waiting on the
  operator.

Gate scripts: the same console trap in nineteen of them and in repo_gates.py
itself, where it ABORTED THE WHOLE RUNNER at the first gate — printing a
non-ASCII character on this workstation's cp1250 console raised
UnicodeEncodeError before the gate had decided anything, and reuse_refs_check.py
died while printing a NOTE. All now reconfigure their own streams. Red-proof
that the remaining script-test failures are not mine: test_due_checks_gate.py
fails the same 5 of 42 with the change reverted.
2026-10-10 12:34:05 +02:00
admin e0be6e7cd6 release 2026-10-10: CI runner restart (operator yes) evidence; report
gates / gates (push) Successful in 6m13s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-10 11:11:31 +02:00
admin a406efc7d5 release 2026-10-10: hub 0.145.0 deployed (security check PASS live), controller 0.305.0 delivered, kernel button offers -22; R-921/R-922 released; R-925 consequences; build skill: sync only the hub when other resources drift
gates / gates (push) Successful in 6m26s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-10 10:02:33 +02:00
admin b4083a06ed manifests: hub 0.145.0
gates / gates (push) Successful in 5m55s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-10 09:40:08 +02:00
admin ca9408f153 hub v0.145.0: the kernel approval rule (operator ruling 2026-10-10, 09 §3 decision 195) + release entry for the security fix, R-922, MAIL-HOLD
gates / gates (push) Successful in 6m3s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-10 09:27:54 +02:00
admin ca584d30b6 kernel night 9→10 read back: demo-hp 7.0.14-22 (~3.5 min down), demo-felhom 7.0.14-23 (~65 s), no false alarm, Approve kernel set not shown (boxes on different kernels)
gates / gates (push) Successful in 5m53s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-10 07:36:10 +02:00
admin 0e9e5260ae gates: run poster-facts last, matching the docstring order
gates / gates (push) Successful in 5m56s
scripts/test_repo_gates_docstring.py pins the numbered list in repo_gates.py's
docstring against the GATES table IN ORDER. I listed poster-facts as 17 but
inserted it before decoy-coverage, so the sets matched and the order did not:
'same set, different order'. Caught on DooPlex, where script-tests runs (it
needs fcntl and cannot run on Windows).

Moved the table entry after decoy-coverage rather than renumbering the
docstring: the gate genuinely belongs last, since it only reports.
2026-10-09 18:15:55 +02:00
admin a08bd3cbd5 architecture: the system poster committed, its facts given a home, and a rule to keep them together
gates / gates (push) Failing after 5m29s
PART A -- the poster. documentation/architecture/felhom-system-poster.html
(307 KB). Secret scan first: ZERO IPv4, zero PEM blocks, zero ssh keys, zero
Bearer. The one EAA... match is base64 inside an embedded "mime":"font/woff2"
blob, not a Facebook token. "token"/"secret"/"password" appear 11 times and
every one is a NAME ("6. ep0 read token", "the hub seal key"); the poster
itself says "Names only; no secret values". All five long base64 blobs are
declared assets: 1 image/png, 3 text/javascript, 1 font/woff2.

It renders with NO network: the source mentions cdn.jsdelivr.net and Google
Fonts, but the loaded requests are only the HTML plus blob:/data: URLs -- the
bundler inlined everything. Measured, not assumed, and it matters: this is a
disaster-recovery document, so needing the internet to draw would be a defect.
No console errors.

The operator's three Claude Design fixes are all present: (a) no "WG" badge,
WireGuard only for the tunnel, no badge on the ep0-copy tile; (b) the box ->
ep0 arrow reads "encrypted on the box, sent through WireGuard"; (c) "Known
gaps" holds two items and NOT the household-keys sentence, which is now a
neutral "By design" note under the ep0 household namespace.

ONE FACT ON IT WAS WRONG. The felhom.eu tile said "served from DooPlex through
Cloudflare". It is not: Cloudflare is DNS only and the traffic goes direct --
measured this morning for the privacy notice, which states exactly that. The
poster would have contradicted a published page. Fixed in place (a label):
"served from DooPlex, Cloudflare DNS only". The first wording overflowed the
fixed-size tile, so it was shortened to fit and the evidence lives in the
facts file instead -- checked by re-rendering, not by hoping.

PART B -- the facts and the rule. DESIGN-PROMPT-...md is renamed
felhom-system-poster.facts.md (one home per fact), with the three fixes folded
in as explicit instructions so a regeneration cannot undo them, plus a new
"Badges" section saying a "WG" chip must never come back.

New rule, section 6 "The system poster stays true", added IDENTICALLY to all
five copies of unprompted-work.md (the four repos and the workspace root on
DooPlex; verified identical by diff before and after) and to
PROMPT-TEMPLATE.md's end-of-session checklist as a FIFTH coupled artifact.

scripts/poster_facts_gate.py WARNS when the facts file has a newer commit than
the poster. It never fails a push, deliberately: a refresh needs Claude Design
and the operator, --no-verify is forbidden here, so a blocking gate would leave
deleting it as the only way out. It compares COMMIT times, not mtimes, because
a checkout rewrites mtimes and every fresh clone would shout.

RED-PROOF -- and it found a real bug in the gate. The first run warned
correctly but exited 1: a single non-ASCII character in its own warning raised
UnicodeEncodeError on this cp1250 console. A gate whose entire contract is
"never fails a push" was failing pushes. Fixed (ASCII output + an encode
guard), and the decoy now asserts BOTH the warning and exit 0. Three branches
proven: facts newer -> warns, rc 0; poster newer -> quiet, rc 0; poster
missing -> "could not tell", rc 2, not a false all-clear.

The decoy itself was seen to fail, twice, on Linux (the suite needs fcntl and
cannot run on Windows): breaking the warning gives STALE_WARNS=False, and
making it exit 1 gives RC_STALE=1. All 80 felhom.eu decoys behave.

PART C -- do box reports pass through Cloudflare? NO. Two channels. DNS from
PUBLIC resolvers (not DooPlex's own, which answers the LAN address):
hub.felhom.eu is a CNAME to dooplex.hopto.org -> 37.191.56.193, not a
Cloudflare address, and no cf-ray comes back. The manifest: an ordinary k3s
Ingress, Cloudflare named only in a DNS setup comment. THE CONTROL that makes
the negative mean something: iso.felhom.eu resolves to 172.67.x / 104.21.x,
real Cloudflare addresses -- so the method does detect proxying.

So nothing is added to the Cloudflare row: the hub path does not touch it.
06-offsite-connectivity.md section 1 claimed the public edge is a
Cloudflare-Tunnel and "DooPlex has no public IP" -- both untrue today. Kept
and marked STALE with the measurement rather than rewritten, because that
paragraph is the reason ep0 exists and the argument needs its premise visible.
total-loss-of-dooplex.md's "today a CNAME to dooplex.hopto.org" is confirmed
correct.

Register: 137 before, 137 after, 0 opened, 0 closed -- every finding here was
small and fixed in the session.
2026-10-09 18:09:50 +02:00
admin e3741ae493 facebook: the first post SCHEDULED for 2026-10-12 19:00 (R-917 -> VERIFY)
gates / gates (push) Successful in 6m5s
Not public. It sits in Planner until Monday evening and can still be changed
or deleted there.

THE POST. Operator chose version 6.2 (kozepes), Hungarian only, 638 Unicode
characters, 0 emoji, 0 hashtags. He chose Monday over today on the reasoning
offered: it was Friday 15:08, the weakest evening of the week for a first
post, and three days in Planner is review time.

  post id   1360018983863273_122096547315511222
  due       2026-10-12 19:00 Europe/Budapest = epoch 1791824400 = 17:00 UTC
  link      https://felhom.eu/

READ BACK, and the hex check recomputed OUTSIDE the probe so it is not the
same instrument twice: is_published False; scheduled time sent == read;
message sha256 887514383eff997c on both sides (COPY.md 6.2 and what Facebook
returned). Present in GET /{page}/scheduled_posts. And from a DIFFERENT
CHANNEL than the API: Planner shows it on H 12 at 19:00 with the link card.

SCENARIO A, the dry check that had to come first. A throwaway scheduled post
WITH THE LINK was accepted -- so `link` is not refused on a scheduled post,
which was the open question. Read back hex-equal and unpublished, seen
PRESENT in the scheduled list, deleted, seen ABSENT in the same list. The
removal proof comes from the LIST, not from an error after DELETE, which is
what R-914 asked for.

CHECKED RATHER THAN COPIED. The post repeats the website's "56 alkalmazas".
The apps page carries 57 <div class="app-card"> while saying 56 -- which
reads as an off-by-one until you read the category line, "6 alkalmazas + 1
beepitett". The 57th card is FileBrowser, built into every box and
deliberately not counted; index.html says "56 telepitheto alkalmazas" too.
NOT-A-FINDING, and a "fix" would have made a live public page wrong.

R-917 -> VERIFY (close when the operator confirms it published and is
pinned). R-914 noted, NOT closed: schedule-post covers text + link only; the
photo path stays unbuilt because the spike could not prove a scheduled PHOTO
stays hidden, and the pin, comment moderation and post-insight read-back are
still missing.

Secret scan on the committed evidence: planted EAA decoy 1 -> 0 after
deletion, 0 access_token, no run.log.
2026-10-09 15:14:55 +02:00
admin 3a77ed73aa facebook: the first post drafted (COPY.md section 6) + fb_probe schedule-post
gates / gates (push) Successful in 5m58s
DRAFTS. COPY.md section 6: two versions of the Page's first post, shortened
from section 2. Hungarian, tegezo, no price. 6.1 = 347 characters, 6.2 = 638
(counted as Unicode characters). Every claim carries a source comment naming
the line of website/index.html it rests on; the first line of each carries the
point alone, because Facebook cuts after about three lines.

Deliberate: ZERO emoji and ZERO hashtags, though the brief allows two of each.
The Felhom design system uses no emoji (the website gate holds it at 0) and
two hashtags would serve no real search.

CHECKED, because the post repeats it: "56 alkalmazas" is CORRECT. The apps
page carries 57 <div class="app-card"> but states 56, which looks off by one
until you read the category line -- "6 alkalmazas + 1 beepitett". The 57th
card is FileBrowser, built into every box and deliberately not counted.
index.html says "56 telepitheto alkalmazas" too. I nearly "fixed" a live page
into being wrong. NOT-A-FINDING.

SCHEDULE-POST. A third sub-command on fb_probe.py, reusing its token loader
(R-453), redaction, Bearer call and evidence writer:

  - the body is READ FROM COPY.md by section name. The Hungarian never passes
    through a shell or an argv string (brief 9.6); the caller names a section.
  - published is ALWAYS "false" and NO argument can change it (brief 9.7).
    The operator's review in Planner is the safety net, so an immediate post
    must be unreachable, not merely not-the-default.
  - check_when refuses a time under Meta's 10-minute floor or over its
    6-month ceiling, BEFORE the call, so a bad time is a readable local
    refusal rather than a Graph error.
  - budapest_to_epoch uses the real tz database. If zoneinfo has no
    Europe/Budapest it REFUSES rather than falling back to a hardcoded
    +01:00/+02:00 -- guessing the offset is how a post goes out an hour wrong
    across a DST boundary.
  - list_scheduled tries /scheduled_posts then feed?is_published=false and
    RECORDS WHICH ANSWERED; when both are refused it returns None so the
    caller says "unproven" instead of claiming a removal it never saw.

TESTS: 30, of which 2 skip on Windows (no tzdata in this interpreter; the
command runs on the Linux host, which has the system zoneinfo).

RED-PROOF of the guard that matters, as the brief requires. Made
schedule_form accept published=..., ran ScheduleForm, and watched
test_no_argument_can_publish_immediately FAIL:
    AssertionError: 'true' != 'false' : published changed published
Guard restored, all 30 green again.

No post has been made. The dry check and the real post come next; the
operator picks the version and the time first.
2026-10-09 15:06:47 +02:00
admin a05345eeb6 R-924: live run + restore test evidence; STATUS and report
gates / gates (push) Successful in 5m55s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 15:02:37 +02:00
admin 96f68f1b44 dooplex-offsite: the operator signing keys ride the nightly encrypted copy; restore test proves each key matches its public half (R-924, operator ruling option A)
gates / gates (push) Successful in 6m7s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 14:49:08 +02:00
admin fe80548144 website: the header mark had no white fill — wrong source file (and R-916 corrected)
gates / gates (push) Successful in 5m36s
I used assets/logo_notext.svg for the header mark. That file is the
LIGHT-BACKGROUND variant: it has SEVEN #00408d fills and ZERO #ffffff, so on
the dark nav the cloud rendered as a hollow outline. The operator caught it.

logo_notext_white.svg is not the answer either -- it is a white OUTLINE
version, also unfilled. Rendered all four candidates side by side on the real
nav background to see it rather than infer it: only logo.svg (and logo.png)
carry the real artwork -- white cloud, #051343 house and servers, #008ddf
swoosh.

FIX: assets/logo-mark.svg, cut from logo.svg -- keep <defs>, path6 and g2;
drop the five wordmark paths and the two empty <text> elements; viewBox
cropped to the mark (70 0 505 295). Vector, correct fills, no text.
All 18 pages point at it; site.css cache-bust v7 -> v8. The CSS comment now
says which file NOT to swap back in, and why.

AND A CORRECTION THAT REMOVES AN OPERATOR TASK (R-916). That row says the
project has no usable vector master because logo.svg "sets felhom.eu as live
text" in 'M+ 2c'/'Vremena Grotesk', and it waits on "the machine with the
fonts". MEASURED today, while cutting the mark out of that very file:

  - all five wordmark elements are <path> with real d= geometry
  - NO <text> element has any content; the two present are empty leftovers
  - the font-family strings the row cites are Inkscape METADATA left on
    CONVERTED paths (-inkscape-font-specification). A grep for font-family
    finds them and reads as live text -- the likeliest way the original
    diagnosis went wrong.
  - proof from a renderer that lacks both fonts: Chrome draws logo.svg's
    lettering identical to logo.png's, side by side.

NOT re-tested: librsvg specifically -- DooPlex has no rsvg-convert, inkscape
or cairosvg installed today, so the original librsvg/DejaVu observation could
not be reproduced either way. It does not change the structural fact: there
is no font left to substitute. Consequence: the 645x408 PNG is not the only
faithful copy and does not cap picture size.

R-916 state -> READY with "re-check before doing any work: this may need
nothing from the operator at all", rather than closed on my say-so.
2026-10-09 14:28:22 +02:00
admin de52bcbdc8 facebook: the Meta app is LIVE (R-915 closed); website lockup entry
gates / gates (push) Successful in 5m47s
R-915 closed and moved to CLOSED-ITEMS in the same commit. What it had waited
on was R-813's Terms of Service URL, which did not exist until the closed-test
legal set went live this morning.

Filled in the app's Basic settings and verified they persisted across a full
reload (not from the save toast):
  Privacy policy URL              https://felhom.eu/adatkezeles
  Terms of Service URL            https://felhom.eu/feltetelek
  Data deletion instructions URL  https://felhom.eu/adatkezeles
  Category                        "Vallalkozasok es oldalak"

THE APP ICON WAS NOT REQUIRED. The original row listed it among the Live
blockers; Meta answered "All required app settings are complete" with it still
empty. Recorded so the next session does not go hunting for one -- and it
could not be uploaded from here anyway: the icon control creates its file
input only on click, and clicking opens a native dialog the browser tooling
cannot drive.

Published, and READ BACK FROM A FULL PAGE RELOAD rather than the success
toast, because Meta's toasts have lied on this Page before: the badge reads
Published, the string Unpublished is gone, and the page now offers Unpublish.

Nothing was granted. No permission added, no use case changed; the scopes stay
"Ready for testing", which is all a system user needs for the business's own
Page. Going Live changes WHO CAN SEE what the app posts, not what it may do.

R-917 and R-920 are unblocked by this -- both were waiting on posting starting.
R-813's next action (a) is spent.

Also carries the marketing CHANGELOG entry for the header/footer change pushed
in 625d38645c.

CI for the three commits before this one: #875 SUCCESS (after a re-run -- the
first attempt was R-887's lost-job flake, "Set up job" 11m48s then every step
0s), #876 SUCCESS, #877 SUCCESS.
2026-10-09 14:06:35 +02:00
admin 625d38645c website: one brand lockup in the header, and footer links that look like links
gates / gates (push) Successful in 5m54s
HEADER. logo.png carries its OWN "felhom.eu" lettering under the mark, so
putting it next to a typed <span class="logo-text"> said the name twice and
squeezed the mark down to 40px to make room. Now:
  - the mark alone, bigger: assets/logo_notext.svg at 54px (44px under 600px).
    Vector, so it stays crisp. Its two <text> elements are EMPTY, so R-916's
    missing-font problem does not apply to this file -- checked, not assumed.
  - the name beside it is the brand's OWN lettering, cropped from logo.png to
    the new assets/logo-wordmark.png (632x108, tight bbox).

Why an image and not text: the wordmark's face is "M+ 2c"/"Vremena Grotesk"
(R-916). Nothing here has it, and the site deliberately loads NO external
font -- the privacy notice published this morning states exactly that, so
pulling in Google Fonts to match a logo would have made a published claim
false. Using the artwork is also what the Facebook covers already do. The
white/blue split the operator asked for is in the artwork itself.

Accessibility kept: the mark is alt="" aria-hidden (decorative), the wordmark
carries alt="felhom.eu", so the link still has its accessible name.

FOOTER. The legal links were unstyled, so the browser painted them default
blue and PURPLE once visited. They now follow the site's own convention
(.page-kapcsolat .sidebar-card a): --blue-bright, no underline, underline on
hover. :visited is pinned to the same colour deliberately -- a legal link
that changes colour after one read looks like it stopped working. The same
fix applied to .legal a on the two new legal pages, which had --blue instead
of --blue-bright.

18 of 18 pages carry the new lockup; the English pages keep their /en/ logo
href. site.css cache-bust v6 -> v7 on all 18, per the website rules. Verified
rendered in a browser on both a Hungarian and an English page: both images
load, no .logo-text left anywhere, footer links computed rgb(46,168,245).
site_gates OK (nav/footer per language, BOM, cache-busting, twins).
2026-10-09 13:47:22 +02:00
admin eba522f06e secrets: the 12 remaining reused logins as a checklist; repo stays public (operator rulings)
gates / gates (push) Successful in 5m44s
Operator ruled 2026-10-09, after seeing the measurement:
  (a) he rotates the remaining services himself -- CC's scope stopped at the
      Felhom boundary;
  (b) felhom.eu STAYS anonymously readable for now, because making it private
      breaks the website git-sync and the installer tag fetch, which both
      clone with NO credentials (R-110).

The standing consequence of (b): no secret may ever enter this repo again,
which manifest_bearer_gate.py now enforces with no exemption. If (b) is ever
reversed, the 32 <!-- source --> comments served on /adatkezeles must be
stripped in the same change, because they are a map of the repo.

secrets.md now carries the exact checklist -- namespace / secret / key, 12
rows, RE-MEASURED after the Felhom rotation by hashing against the value git
history still serves, which also confirms the three CC rotated are absent
from it.

Two things recorded with it, both learned the hard way the same day:
  - a DATABASE password is not changed by editing the Secret. bookstack-db
    root-password is read at first init and then lives in the engine, exactly
    like umami's POSTGRES_PASSWORD did.
  - check the app can still RESTART before trusting the rotation: umami ran
    124 days at 512Mi and was OOMKilled on every restart attempt.

And the urgency, measured rather than assumed: these are not LAN-only.
nextcloud / paperless / bookstack / qbittorrent .dooplex.hu all resolve in
PUBLIC DNS to the public address and answer HTTPS with a login page. No login
was attempted; reachability is the point.
2026-10-09 13:37:28 +02:00
admin e467785fd3 security: rotate the published secrets and de-git felhom.secret.yaml (R-925, P1)
gates / gates (push) Successful in 5m23s
WHY .gitignore "was not working": it was working. git never consults
.gitignore for a file it ALREADY TRACKS. The rule `*secret*` matched fine --
proved by dropping an untracked copy in and watching check-ignore name
`.gitignore:3:*secret*`. The file had been tracked since feea0606, which is
ironically the commit that de-gitted the Resend key.

WHAT THE EXPOSED VALUE ACTUALLY WAS. Not an analytics password: the GITEA
ADMIN ACCOUNT PASSWORD (is_admin true; /api/v1/admin/users answered 200), in
a repo gitea.dooplex.hu serves anonymously to the internet. That is push
access to every repo -- including the one whose website/ is git-synced live
and whose scripts/ is published by tag to every new box installer (R-110).
Re-ranked P2 -> P1 on that measurement; my first ranking had only measured
the analytics blast radius.

Every committed value was still live. Nothing had ever been rotated.

ROTATED (values never echoed; written to a 0600 file on DooPlex):
  umami-config        APP_SECRET + POSTGRES_PASSWORD. The password was
                      changed INSIDE postgres (ALTER USER) as well as in the
                      Secret -- the env var is only read at first init, so
                      patching the Secret alone would have changed nothing.
  healthchecks-config SECRET_KEY + SUPERUSER_PASSWORD (nothing consumes them,
                      there is no healthchecks Deployment).
  gitea-creds         no longer holds the admin password at all: a SCOPED
                      token (read:package + read:repository).
  gitea admin         new random password; gitea-system/gitea-admin updated.

VERIFIED, not assumed:
  - new admin password -> 200, OLD PUBLISHED PASSWORD -> 401 (the leak is dead)
  - umami: a real beacon returns 200 (so the app authenticates to postgres and
    writes) while a bogus site id still returns 400 (so the 200 means something)
  - hub: "Registry version check: latest = 0.304.0" AND "Template fetched
    (5881 bytes)", no auth failures
  - BOTH token scopes are load-bearing, and the second was found by breaking
    it: a package-only token made the hub log "Template fetch: unexpected
    status 403", because the template fetcher reads a raw file out of the
    felhom-controller repo, not the registry.

AN INCIDENT CAUSED BY THE FIX, recorded because it is the useful part: the
rollout restart needed to pick up the new umami secret put umami into
CrashLoopBackOff and took stats.felhom.eu down (503) for ~4 minutes. Not the
rotation -- at memory 512Mi that pod runs for months but CANNOT RESTART:
startup (Prisma + Next.js) peaks over the limit and is OOMKilled (exit 137).
Raised to 1Gi IN THE MANIFEST, not just live, per .claude/rules/manifests.md
("never bare kubectl set -- the next sync reverts it and the fix silently
disappears").

THE GATE: KNOWN_BACKLOG is removed from manifest_bearer_gate.py, as its own
comment instructed. Red-proofed with a decoy: exit 1 with it, exit 0 without.
An exemption kept this visible for three months and changed nothing.

WHAT REMAINS (operator, and it is bigger than what was fixed): the same
password is still the admin password in ~12 other namespaces -- nextcloud,
paperless, bookstack (a DATABASE ROOT password), tandoor, calibre,
adventurelog, gokapi, qbittorrent, servarr, homepage. Rotating Gitea does not
touch them. Also owed: a kisfenyo Gitea token sits in plaintext in the local
homelab-manifests remote URL and was printed to a session transcript during
this investigation, so it should be replaced regardless (R-580's shape).

NOT a finding: homelab-manifests is private (404 anonymously) and does not
contain the password; ArgoCD's repo credential is a separate token and was
untouched by the rotation.
2026-10-09 13:21:18 +02:00
admin 07773bf58f R-923: STATUS, capability map, session report (break the circle)
gates / gates (push) Successful in 5m38s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 13:01:29 +02:00
admin e5b9151e05 hub (unreleased, SECURITY): /preferences and /notify refuse a per-customer key acting for another household (403); red-proved
gates / gates (push) Successful in 5m49s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 12:48:54 +02:00
admin d55c590c5a hub (unreleased): R-922 option A — a household's clear deletes its notification address (email_cleared); MAIL-HOLD — a restored hub sends no mail until released; two log lines drop the address; runbooks: mail hold is restore step 1; 07 §6.4 R-921 pre-check; R-921/R-922 narrowed
gates / gates (push) Successful in 5m25s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 12:37:29 +02:00
admin 2c48feb325 security: R-925 — live secrets committed to a world-readable Gitea repo
gates / gates (push) Successful in 5m45s
Found while publishing the legal pages: a background security review flagged
the <!-- source --> comments served in website/adatkezeles.html. Chasing what
those comments point AT found something larger.

MEASURED, and the control is what makes it mean anything:
  - manifest_bearer_gate.py itself prints
    "manifests/felhom.secret.yaml:39 KNOWN-BACKLOG committed secret"
  - that file holds live-shaped values, not placeholders: SECRET_KEY (69),
    SUPERUSER_PASSWORD (18), Umami APP_SECRET (66) and POSTGRES_PASSWORD (34),
    plus a username/password pair. Values were never printed, only measured
    by length.
  - gitea.dooplex.hu resolves to 37.191.56.193, the website's public address
  - anonymous curl: 200 and 1686 bytes for that file; 200 for hub internals
  - OFF-NETWORK CONTROL: fetched from outside the operator's network, a real
    path returns the file's first line and a nonsense path returns 404. So
    the 200 is genuine anonymous read from the internet, not a LAN-only ACL.

This contradicts documentation/runbooks/secrets.md:3-4, which states secret
values are never committed and the manifests carry only placeholders. That
promise is false today.

Ranked P2, not P1, and the row says why so the operator can overrule: the
exposed credentials guard analytics and an undeployed healthchecks instance,
NOT household data. Measured: umami-db is a ClusterIP service with no
external IP, so the Postgres password is not internet-reachable. No customer
box, hub token or escrow key is in the file.

NOTHING WAS CHANGED. Making the repo private could break the public day-0
installer path (R-110), and rotation plus repo visibility are operator
decisions on production infrastructure. The row carries the order: rotate
first (de-git alone kills nothing — the runbook says so), then decide
visibility, then CC does the de-git and tightens the gate.

Coupled and easy to miss: the 32 source comments on /adatkezeles are harmless
while the repo is public, but become a map of it the moment it is private, so
that is one change and not two. Their traceability is already kept in
documentation/legal/*-1.0.md.
2026-10-09 12:20:52 +02:00
admin 4c388a398b R-923/R-924: total-loss-of-dooplex runbook (every recovery secret, where it lives, walked on paper), break-glass sheet (names only), Vaultwarden off-site proven (push, restore test, throwaway start); 'off DooPlex' claims corrected; R-924 filed
gates / gates (push) Successful in 5m41s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 12:13:41 +02:00
admin 30b932bb10 website: publish the closed-test legal set (R-813 narrowed)
gates / gates (push) Successful in 5m34s
Two new Hungarian pages, live on push: /adatkezeles (privacy notice) and
/feltetelek (what the free closed test is, and is not). Operator rulings of
2026-10-09: no company exists yet, so the operator is named as a PRIVATE
PERSON with no postal address and no phone; publish before the lawyer has
seen it, because the site was collecting data with no notice at all; the full
ASZF, the impresszum and the review wait for the company (R-802, R-809).

- All 18 pages carry the operator, info@felhom.eu and both links in the
  footer. Counted, not assumed: 18 pages found = 18 with both links = 18
  structurally valid. English footers say the legal texts are Hungarian.
- The contact form stopped claiming something untrue. The old consent said
  "az adatokat harmadik felnek nem adjuk ki" while Resend, Cloudflare and
  Google carry the message. Both languages replaced; the link opens in a new
  tab so a filled form is not lost.
- site_gates.py NO_TWIN gains the two pages ON PURPOSE, with the reason in a
  comment: an unreviewed English legal text would be worse than an honest
  pointer from the English footer.
- documentation/legal/{adatkezelesi-tajekoztato,feltetelek}-1.0.md are the
  text of record, DERIVED from the published HTML so they cannot drift. The
  drafts are kept and marked superseded for the closed test.

FOUR LOAD-BEARING CLAIMS WERE MEASURED, not copied from a vendor or a README:

  cookies      zero, and no local storage - checked in the browser WITH A
               POSITIVE CONTROL (a probe cookie WAS visible to the same
               method) after the tracker fired; no Set-Cookie on any response
  beacon       the exact Umami payload: site id, screen, language, title,
               url, referrer - no visitor identifier
  Cloudflare   DNS only: the public A record 37.191.56.193 is not a
               Cloudflare address, so site traffic cannot be proxied
  fsn1         Falkenstein, Germany (Hetzner's own location list)

Also measured: felhom-ep0-copy-gc.timer is installed and RAN SUCCESSFULLY
(2026-10-09 08:00, exit 0), so "deleted within 30 days" is true today where
on 2026-10-08 it was written but not switched on.

Three retentions are stated as having NO deadline, deliberately and with the
operator's word: website statistics, web server logs and contact messages
have no automatic deletion, and the pages say so instead of promising a date
nothing enforces. Every other period is enforced by configuration and cited.

No placeholder survived onto either page (0 of "[[", control: the draft still
has 36). The impresszum and the full ASZF are NOT published.

R-813 -> NARROWED. R-915 unblocked: the operator enters the two URLs in the
Meta app's Basic settings; the Live switch stays a separate decision.
R-917 and R-920 -> DEFERRED on the operator's (c): park, publish as posts
once posting starts.
2026-10-09 12:11:37 +02:00
admin 4cee21acf7 dooplex-offsite: Vaultwarden (the password manager) rides the nightly encrypted copy (R-923); R-923 filed (operator finding); R-922 ruling A recorded
gates / gates (push) Successful in 5m32s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 11:53:35 +02:00
admin 81c835827e R-232/R-173: hub restored into a throwaway k3s (runbook §3 steps 4–5 proven, corrected); R-173, R-861, R-518 closed; R-921, R-922 filed; STATUS, capability map, report
gates / gates (push) Successful in 5m23s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 10:54:59 +02:00
admin 59f1ad2b86 facebook: verify COPY.md section 5 even though Meta refused the paste
gates / gates (push) Successful in 5m52s
The task asked for a hex compare between what is pasted into Messenger and
COPY.md section 5. There was nothing to paste (R-920), so the half that is
still checkable was checked:

  all four questions BYTE-IDENTICAL to website/gyik.html (hex equal)
  each answer at most three sentences (2/3/3/3), each ending in the gyik link

A refused edit is not a reason to leave the copy unverified.
2026-10-09 10:42:12 +02:00
admin 6a82d7a6d0 facebook: the Page-details report, STATUS and the CI evidence
gates / gates (push) Failing after 13m5s
Same two Windows-only gate failures as the previous commit (instructions: the
Windows workspace CLAUDE.md is MEANT to diverge from the DooPlex-shaped
versioned copy; script-tests: WinError 32, no sqlite3 CLI, POSIX shell tests).
All 18 gates are green on DooPlex at 97d3c29f9c, run in a throwaway worktree
beside the sibling repos, and CI run #863 for that commit is Success.

unproven.py --summary unchanged: 35 of 55 not walked.
2026-10-09 10:37:51 +02:00
admin 994826ec28 R-232: Gitea restored from the ep0 copy into a throwaway (bench 9401) — runbooks/gitea-restore.md; Part B alarm + install + failmail evidence
gates / gates (push) Successful in 5m33s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 10:29:08 +02:00
admin 97d3c29f9c facebook: Page details — categories added, hours and Messenger FAQ refused by Meta
gates / gates (push) Successful in 5m24s
Second Facebook task of the day. Page settings changed by hand in the operator's
Chrome; every edit read back from a channel other than the one that made it,
because Meta's toasts have lied here before (2026-10-08: "A modositas nincs
mentve" arrived with a partial save).

- Contact (B1) was ALREADY SET, by the operator, before this run: Graph reads
  emails ["info@felhom.eu"] and phone "+36702378499". Verified, not typed.
- Place (B2) already city-only Budapest, as the operator chose. Service area
  REFUSED: Facebook offers the field but its picker has no "Magyarorszag",
  only cities. Control: "Szeged" returns Szeged. Left unset rather than
  narrowed to Budapest, which would shrink a coverage claim nobody authorised.
- Categories (B3) DONE: Informatikai vallalat (kept first, the only one shown)
  + Internetes ceg + Szoftverceg. Facebook's Hungarian list has no IT-support,
  IT-consulting or cloud category; eleven terms searched, and the one true
  match is a repair counter, which the fences rule out.
- Hours (B4) CANNOT be set and need not be: Facebook requires a street address
  first, which the fences forbid. Measured on the rendered page with controls
  present -- Zarva 0, Nyitva 0 while Budapest 1, Informatikai vallalat 1. The
  Page will never show "Zarva".
- Messenger FAQ (B5) REFUSED -> R-920: the automation does not exist for this
  Page. Catalogue holds exactly three templates; search "kerdes" returns none
  while the control "uzenet" returns two. COPY.md section 5 is written anyway,
  questions verbatim from gyik.html and answers condensed from each question's
  own answer, and waits like section 2 does (R-917).
- Link preview correct in both languages, re-scraped once each; only the
  expected fb:app_id warning, deliberately not fixed. Both report HTTP 206
  where a plain curl gets 200 -- Facebook's scraper, preview complete.

fb_probe.py read now also reports emails, phone, category_list, location,
single_line_address and hours, ONE FIELD PER CALL: a batched fields= list fails
whole when any member is unreadable, which would let one refused field hide the
other five. Refusals are logged verbatim and never retried; "null" and "not
returned" are logged apart. hours is never returned by Graph, which is why B4's
read-back had to come from the rendered page.

Also corrects the "Business & legal" header, which read 12 rows (P2 5) over a
section holding 11 (P2 4); with R-920 it is 12 (P2 4, P3 1, P4 7). Only the
section this commit edits -- the other drifting headers belong to a session
that owns the register.

No post, no invite, no money, no app or portfolio change, website unchanged.
2026-10-09 10:28:30 +02:00
admin 02a54e26f5 dooplex-offsite: failure mail survives the shared config's unset variable (found by the live dry run); Part B push + restore evidence
gates / gates (push) Successful in 5m6s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 10:15:10 +02:00
admin 1707c928a9 dooplex-offsite: nightly encrypted copy of Gitea + DooPlex secrets to ep0, restore test, failure mail (R-232 b/h)
gates / gates (push) Successful in 5m4s
Part A plan + readings, Part E read-backs (R-861 a, R-518) in audits/dooplex-survival-2026-10-09/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 10:08:31 +02:00
admin 9a55f0bbc7 marketing/facebook: the Facebook APP measured; cover C to your layout (R-919)
gates / gates (push) Successful in 4m43s
The app is no longer the unmeasured view. Solved against the laptop frame, your
Facebook Lite shot gives a visible window of x 349..1290 and your Chrome-mobile
shot x 349..1291 -- the LITE APP CROPS EXACTLY LIKE SIGNED-IN MOBILE WEB, and
both agree with the emulator's 351..1289. Their profile circle is at x 645..1021
from y 451, LOWER than the emulator's y 369, so that figure was pessimistic
rather than wrong and the margin it bought was real. Five views measured now;
the signed-out one is still the binding constraint.

Cover C follows your draft: the wordmark big on top (88 px, was 44), a small
gap, then the catchphrase. The catchphrase is ONE line, not the two you drew,
and the measurement forces it: with the signed-out circle starting at y 232, a
wordmark that size plus two catchphrase lines cannot both sit above it. Drawn as
two lines it measured 392 sampled text pixels behind that circle -- "saját
szabályaid" read "saját szab" there, which is the R-919 defect itself. Sizing
the two lines to fit instead drops the capital to 25 px, the legibility floor.
One line keeps the wordmark big, the capital at 34 px and every measured view
clean: 0 text pixels behind any circle. The laptop moved right and shrank to
540 px to free the width; 64% of it is cropped or covered somewhere, which the
build reports and which is what the decor layer is for.
2026-10-09 09:24:23 +02:00
admin ff27a107c8 D6 / R-138: every stored Cloudflare token checked once (operator present), all 4 PASS; R-138 closed
gates / gates (push) Successful in 4m35s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 09:00:05 +02:00
admin 53ec983849 marketing/facebook: record why CI #856 went red (the runner, not the gates)
gates / gates (push) Successful in 4m15s
gates.yml #856 for b9073e8f reports Failure, but the gate entry point never ran:
"Set up job" took 11m54s (normally seconds) and passed, then every following
step failed at 0s with an empty log. No gate verdict exists in that run.

The gates ARE green at that commit: repo_gates.py --fast in a throwaway worktree
checked out at b9073e8f and placed BESIDE the sibling repos gives rc=0, all 18
OK. The worktree was removed; git worktree list shows only the main tree.

A first attempt at that check, run from /tmp, reported script-tests FAILED and
five INCONCLUSIVE gates purely because the siblings were not beside it. Recorded
so it is not mistaken for a real failure next time: a gate run at the wrong path
convicts the layout, not the commit.

Not claiming CI passed - it did not run. This commit re-triggers it.
2026-10-09 08:44:25 +02:00
admin 0cb169923c Day 4 (2026-10-09): kernel night read back, release delivered, ep0-copy job live, D1-D4 proven; R-279/R-30/R-79/R-35/R-901 closed
gates / gates (push) Successful in 4m46s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 08:37:18 +02:00
admin b9073e8fb6 marketing/facebook: a phone has TWO views of the cover, not one (R-919)
gates / gates (push) Failing after 11m54s
You saw the cover uncropped signed out, after R-919 said the sides are cut.
Both are true; my first measurement was one view generalised to "the phone".

SIGNED IN, confirmed on your real phone (Chrome/Android, 1080 px): the crop is
real. The cover band is 708 px tall across 1080 = 1.525:1 against the file's
2.628:1, and solving the laptop frame's left edge against that scale puts the
window at x 351..1298 where the emulated Pixel 9 said 351..1289 -- the left edge
to the pixel. R-919 is confirmed on hardware, not replaced.

SIGNED OUT, measured from your screenshot: the box is 412x132 = 3.121:1, WIDER
than the file, so the height is cut, not the width -- and the file's blue top
rule is still visible at the top edge, which puts the cut at the BOTTOM: the top
525 px of 624 survives. The circle is far bigger and higher: x 486..1150 from
y 232, 41% of the width.

So the sides are cut for one visitor and the bottom for the other. build.py now
carries both views; SAFE is their intersection, (391,40)-(1249,485).

Two changes made that workable rather than merely safe:
- the circles are modelled as DISCS, not rectangles running to the bottom. Near
  its top a disc is a few pixels wide; the rectangle was discarding most of the
  lower cover for nothing, which is much of why the frame looked empty.
- TWO LAYERS. The READ layer (catchphrase, wordmark) must survive every view and
  the check fails on it. The DECOR layer may be cropped or covered, and the build
  REPORTS the cost instead of forbidding it (B 39%, C 62%). Before the split one
  check governed both, so no laptop big enough to read could ever pass.

Red-proof again: the two-view geometry convicted all three covers as they stood
-- A 908 content px under a circle (its wordmark sat inside the signed-out
circle), B 1715 plus content past the cut bottom, C 1962. control_old_window
still convicts the pre-R-919 layout, so there are two controls now.

Covers redrawn: C is your laptop idea -- catchphrase and wordmark left, the
dashboard at 640 px (was 370) running off the right edge, readable at last. B's
home motif grew the same way. A lifted into the tighter band. Capitals 48/45/43
against the 25 px floor. Profile pictures untouched, not in the diff.

Still VERIFY: three views measured, all disagreeing with each other and with
Meta's help page, and the Facebook APP is still the one nobody has measured.
2026-10-09 08:21:08 +02:00
admin 69fa9cf784 ep0-copy-gc: read ep0's real {"data": [...]} namespace answer; any other shape aborts (found on the first live dry run, guard held)
gates / gates (push) Successful in 4m31s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 07:30:18 +02:00
admin ba7174012a marketing/facebook: the mark alone on the profile, the real lettering on the covers
gates / gates (push) Successful in 4m28s
Operator refinements after looking at the rebuilt pictures. No geometry changed:
R-919's measured constants, its band and every check stand, and the red-proof
still convicts the old layout.

Profile picture: the logo MARK only. The mark plus the "felhom.eu" lettering was
too much for the 176 px Facebook shows -- and smaller than the shape the Page
carried before this work, because build.py shrinks the artwork to fit inside the
circle where the original was simply cropped by it. Dropping the lettering grows
the mark from 69% to 76% of the circle; canvas 932 -> 648 and the profile_width
floor 720 -> 640 (twice Meta's recommended 320 source; 720 was above what the
mark alone needs and would have shrunk it for nothing). logo.png is split at a
MEASURED row: ink y 5-289, blank band y 290-295, wordmark y 296-403.

Covers: the headline is now set the way the WEBSITE sets its h1. site.css
.page-index .hero-text h1 is font-weight 700, letter-spacing -0.03em; this file
used ExtraBold 800 with no tracking, so the cover did not actually match the
page. HEADLINE_WEIGHT/HEADLINE_TRACK now carry those, with real per-glyph
spacing. Bold is narrower so headlines grew: A 57->62, B 58->63, C 47->50.

"felhom.eu" is no longer typed anywhere: wordmark() draws the logo's own
lettering, which is set in "M+ 2c"/"Vremena Grotesk" -- fonts this machine does
not have (R-916) -- so any typed version was a look-alike. The website hero
shows the same logo.png on the same dark background, so the covers match the
page. The DOMAIN constant is removed rather than left as dead code.

Caught before it shipped: the preview's new profile paragraph added a fourth %d
and the argument tuple stayed in the old order, so the phone paragraph rendered
"the centre 76 px of the 938-pixel width, with a profile circle 1640
cover-pixels across". Every number was real, just in the wrong slot, which is
why it read as plausible instead of crashing. Found by reading the rendered
paragraph back, not by the exit code.

Built on DooPlex; all checks pass, the phone simulation still shows 0 px cut and
0 px under the profile circle on all three covers. R-919 stays VERIFY.
2026-10-09 07:29:57 +02:00
admin 702e19ee58 manifests: hub 0.144.0 (operator present)
gates / gates (push) Successful in 4m20s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 07:22:48 +02:00
admin 962b52c3b5 audits: kernel night 8->9 read-back; release 2026-10-09 delivery evidence (agent update signed, floors 0.304.0)
gates / gates (push) Successful in 4m44s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 07:14:20 +02:00
admin 550a4538e7 marketing/facebook: R-919 report filled in (gates rc=0, CI #849 green, reproducibility proven)
gates / gates (push) Successful in 4m37s
DooPlex gates at the pushed commit 796a9fdf: rc=0, all 18 OK. A plain rebuild
there left git status --porcelain -- marketing/ empty, so the committed PNGs are
exactly what the committed script produces on the build machine; both controls
fired in that run.

CI gates.yml #849 for 796a9fdf40 is green. Recorded how it was read, because
both obvious ways are wrong here: the Gitea API still 401s on every credential
in the store, and the run's own page redirects to the internal id and renders
through JavaScript, so its HTML carries "success", "failure", "running" and
"cancelled" as template strings whatever the outcome. The conclusion comes from
the list page, from the same flex-item row that holds the commit link -- and
splitting that list on class="flex-item" chops the row, because the child divs
share the prefix, which would attribute the icon to a neighbouring run.
2026-10-09 07:08:03 +02:00
admin 8dbdac5fc2 hub v0.144.0: CHANGELOG release entry (D1, D2, D3, D6, D7; R-243, R-901, R-304, R-415)
gates / gates (push) Successful in 4m30s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-09 07:02:44 +02:00
admin 796a9fdf40 marketing/facebook: covers rebuilt to the MEASURED phone view (R-919 -> VERIFY)
gates / gates (push) Successful in 4m28s
build.py believed a phone shows 640x360 of the cover. It shows 412x274 =
1.504:1 -- the full height and only the centre 938 px of 1640, 351 px off each
side. PHONE_HDR = (412, 274) now drives CW_PHONE, so SAFE is (391,40)-(1249,584),
858 px wide with a 40 px margin inside each crop edge.

QUIET is no longer one formula for both circles: the computer circle is
left-anchored, the phone circle is CENTRED and hides only its measured box
(637,369,393). The old shared formula, applied to a centred circle, would have
blanked everything from x 0 to x 1054 below y 345.

Every cover draws in a derived BAND (408,48)-(1249,340), and a cap check holds
each headline's capital at >= 4% of the cover height; A/B/C come out at 45, 45
and 37 px against a 25 px floor.

RED-PROOF, and it is permanent: control_old_window() runs on every build and
draws the headline where the old assumption put it (x 328); the check must
reject it. The phone's crop edge is x 351, so 23 px were cut; the measured safe
edge is x 391. Against the covers as committed at a76207945e the new check
convicted 3 of 3 -- A 23/22 px past the left/right edges, B 63/58 px plus 1017
content pixels under the phone circle, C 63/82 px plus 1778.

Covers redrawn inside the band: A one centred line; B the home motif scaled
with two tiles moved into the lower-right strip, the one area below the band a
phone still shows beside the profile circle; C the dashboard screen 470 -> 370
px so frame and base fit. preview.html shows the phone panel at the measured
938x624 with the centred circle and no longer claims 640x360.

Profile pictures untouched -- sha256 identical before and after, and not in the
diff. Built on DooPlex: the PNGs are byte-reproducible there (Pillow 11.1.0) and
not across Pillow versions; the README now says so.

R-919 is VERIFY, not closed: the geometry is measured on ONE emulated device in
the mobile website, so the operator's check in the Facebook app is the
acceptance. Nothing uploaded, no Graph API call, FACEBOOK_API never read.
2026-10-09 06:59:53 +02:00
admin a76207945e marketing/facebook: the phone check ran after all — the cover IS cut on phones (R-919)
gates / gates (push) Successful in 4m31s
Your suggestion to use DevTools device mode was right; "cannot be checked" was
too quick a conclusion. On an emulated Pixel 9 (412x924, mobile UA, with a
reload so Facebook serves the mobile bundle) the Page's mobile header renders
412x274 = 1.504:1. Facebook keeps the cover's full height and shows only the
centre 938 px of its 1640 px width -- 351 px off each side. build.py's safe
area leaves 306 px clear per side, 45 px too few, and the light text in
cover-c.png runs x333..x997 against a left crop edge of x351, so 18 px are lost:
the headline reads "aját szabályaid" and the wordmark "elhom.eu" on every phone.

Nothing re-cropped on Facebook (the task's fence). The fix is build.py's safe
area (<=938 px, ~900 for margin) plus the measured 172 px centred phone profile
circle, then a hand re-upload. R-919 opened; R-918 closed and moved to
CLOSED-ITEMS with the recipe that made the check possible.

Meta's help page is wrong about Meta's own rendering: it states 2.4:1 for the
mobile cover where the header measures 1.504:1, so this session's first-pass
arithmetic -- explicitly labelled arithmetic, not a check -- under-predicted the
crop about fivefold. That is recorded rather than quietly deleted.

Also: the evidence secret scan needed --exclude=README.md, because that README
quotes the search strings and was matching itself (4 false EAA hits, 1 false
access_token); with the exclusion, control 1 -> 0 and no real hit. And the
Business & legal section header was already miscounted before this session
(said 9/P2 4, actually held 10/P2 5) -- corrected; five other section headers
drift too and are named in the report, left for a session that owns the register.
2026-10-08 21:50:57 +02:00
admin 6a2670101a marketing/facebook: report filled in (gates, CI run #846 green), STATUS updated
gates / gates (push) Successful in 4m42s
The DooPlex gate run at 8664cbda is rc=0, all 18 gates OK; the Windows run's two
failures are platform artifacts (fcntl, symlink privilege, C:/E: mount, cp1250
console) and the deliberate workspace-CLAUDE.md divergence.

CI gates.yml #846 for 8664cbda78: Success, 4m39s. The Gitea API refused every
credential in the store (401), so the run was read from Gitea's web UI, which
serves the Actions list unauthenticated; /actions/runs/846 redirects to
/actions/runs/1573, R-417's index-vs-id offset again, so both numbers are on the
screenshot.

STATUS: the Facebook section rewritten for what is now live, the open-items
count corrected to the counted 140.
2026-10-08 20:57:30 +02:00
admin 8664cbda78 marketing/facebook: Page settings set by hand (intro, action button, Messenger welcome, username felhom.eu)
gates / gates (push) Successful in 4m36s
Ran marketing/facebook/TASK-page-setup-windows.md from the Windows workstation
through Claude in Chrome. Evidence in
documentation/audits/facebook-page-setup-2026-10-08/, report in
REPORT-facebook-page-setup.md.

Done, each read back from a different channel than the edit: the intro is
COPY.md §1 (Graph `about` hex-equal, 99 chars); the action button is
"További információ" to https://felhom.eu/; the Messenger welcome is COPY.md §3
(hex-equal) and the automation is on; the username is felhom.eu, so the Page
answers at facebook.com/felhom.eu.

Not done: COPY.md §2 has no field to go in — Facebook's current Pages
experience has no long-description field and the Graph `description` is null
and unwritable with the robot key's scopes. R-917, four options, operator's
call; no Hungarian copy was shortened.

Phase A read Meta's size page first-hand: it carries none of the 820x312 /
640x360 figures our README asserted, and its own two ratios contradict the file
it recommends. README's grade and comparison rewritten; what we build is
unchanged.

Phase C measured the computer width (cover whole, no crop, profile circle 16 px
clear). The 390 px phone check was NOT performed: narrowing a desktop browser
never gives Facebook's phone layout. R-918.

Nothing posted; no ad, no boost, no Meta Verified; the Meta app stays in
development mode; no host, guest, box or hub touched.
2026-10-08 20:49:14 +02:00
admin 1d6927345c marketing/facebook: handover task for the Windows session (Page setup through Claude in Chrome)
gates / gates (push) Successful in 4m41s
2026-10-08 19:58:09 +02:00
admin 0fd4c818ff Merge branch 'main' of https://gitea.dooplex.hu/admin/felhom.eu
gates / gates (push) Failing after 11m7s
# Conflicts:
#	documentation/architecture/11-os-updates.md
2026-10-08 19:36:58 +02:00
admin 55e9522d0b os updates 2026-10-08 19:32:58 +02:00
admin f0dc6a9d4e Facebook Page: profile pictures, three covers, preview page and texts (marketing/facebook); R-916 opened
gates / gates (push) Successful in 4m24s
build.py (Pillow, site tokens + Plus Jakarta Sans) with in-build checks: profile circle (today's shape 6823 pixels
outside, new 0), cover safe area and profile-circle clearance, sizes read back. COPY.md: intro 99/100, long
description, Messenger welcome, three first posts, each claim traced to the website. R-914 gets the listing-based
removal proof and the picture-API tasks; R-915 the footer-link note; R-916 the logo's vector master.
2026-10-08 19:19:50 +02:00