marketing + seo: the three-week tester campaign, schedule-post --link, the not-indexed audit
gates / gates (push) Successful in 6m15s

COPY.md section 7: five Hungarian posts for Tue/Thu 19:00 over three weeks, each
with a source comment per claim and one link. The Monday post (2026-10-12, 6.2)
is untouched and no slot shares its day.

fb_probe.py gains --link (default unchanged). Every scheduled post used to
attach the home page's preview card whatever its text said, so a post about
/teszteles would have sent the people who clicked its card to the home page.
build_parser() split out of main() so a test can parse argv. Red-proved: both
new tests fail with "Namespace object has no attribute link" without the flag.

Search Console audit (seo-2026-10-10/not-indexed.md): all seven not-indexed
URLs with a verdict each - four expected by design, two waiting on Google's
crawl queue, one fault on our side. That one is /api/contact, the contact
form's POST endpoint crawled as a page; robots.txt now disallows /api/. The
performance baseline (0 clicks, 47 impressions, position 73, nine queries) is
recorded there to compare against in a month.
This commit is contained in:
2026-10-10 16:13:21 +02:00
parent 9d530b8f4a
commit 5bea5f2ebf
6 changed files with 313 additions and 8 deletions
@@ -0,0 +1,130 @@
# Search Console — the seven not-indexed URLs, with a verdict each (2026-10-10)
> **Read in the operator's own Chrome** (Claude in Chrome, property `sc-domain:felhom.eu`), by opening
> each of the five „not indexed" reasons and copying its URL list. Property language is Hungarian, so
> the reason names are quoted as Search Console shows them.
>
> **State at the time of reading:** 6 indexed, **7 not indexed**, five reasons. Last index update
> 2026-10-04.
---
## The table
| # | URL | Search Console's reason | Verdict | Why |
|---|---|---|---|---|
| 1 | `https://www.felhom.eu/` | Alternatív oldal megfelelő kanonikus címkével | **EXPECTED** | The `www.` host serves the same page and its `canonical` points at `https://felhom.eu/`. Exactly one of the pair should be indexed, and the right one is. Nothing to fix. |
| 2 | `https://www.felhom.eu/kapcsolat` | Alternatív oldal megfelelő kanonikus címkével | **EXPECTED** | Same as #1, for the contact page. |
| 3 | `https://www.felhom.eu/api/contact` | Nem található (404) | **OUR SIDE — FIXED** | This is the contact form's **POST endpoint**, not a page; a GET returns 404. Google reached it anyway (first crawled 2026-04-29) and reports a 404 on the site. Fixed by adding `Disallow: /api/` to `website/robots.txt` in this session, so it is not crawled as a page again. The endpoint itself is unchanged and the form still works. |
| 4 | `http://felhom.eu/` | Átirányítást tartalmazó oldal | **EXPECTED** | The plain-HTTP home page redirects to HTTPS. The redirect is the point; the HTTPS page is indexed. Nothing to fix. |
| 5 | `https://felhom.eu/letoltes` | Felfedezve – jelenleg nincs indexelve | **NOT A FAULT — Google's queue** | Google knows the URL and **has not crawled it yet** („Utolsó feltérképezés: nincs"). The page is reachable, is in the sitemap, is linked from the nav and carries its own title, description and canonical. This is the crawl budget of a new, low-authority site, not a defect. The lever is the operator's „Indexelés kérése". |
| 6 | `https://felhom.eu/en/download` | Felfedezve – jelenleg nincs indexelve | **NOT A FAULT — Google's queue** | Same as #5, for the English twin. |
| 7 | `https://felhom.eu/sitemap.xml` | Feltérképezve – jelenleg nincs indexelve | **EXPECTED** | A sitemap is not a page and is never meant to appear in results. Google crawled it (2026-09-17) and correctly did not index it. Nothing to fix. |
**Totals:** 4 expected by design, 2 waiting on Google, **1 fault on our side, fixed in this session**.
---
## What was fixed here
**`website/robots.txt`** gained, with its reason in a comment:
```
Disallow: /api/
```
Nothing else on the site was changed for indexing. The canonical tags, the `hreflang` pairs, the
per-page titles and the sitemap were already correct and were deliberately **not** redone.
---
## The sitemap entry is stale — this needs the operator
Read on the same day from Search Console → Indexelés → **Webhelytérképek**:
| Submitted sitemap | Type | Submitted | Last read | State | Pages discovered |
|---|---|---|---|---|---|
| `https://felhom.eu/sitemap.xml?v=2` | Webhelytérkép | 2026-02-11 | 2026-10-05 | Sikeres | **8** |
Two things are wrong with that row, and **neither can be fixed from the website**:
1. **The submitted URL carries `?v=2`.** `robots.txt` advertises the clean
`https://felhom.eu/sitemap.xml`, so the submitted entry and the advertised one are different URLs.
2. **It reports 8 discovered pages while the sitemap holds 18.** The file listed 16 after the English
twins landed on 2026-10-08 and 18 after this session added `/teszteles` and `/en/testing`. The
number 8 is what Google took from an older read; the last read (2026-10-05) predates both changes.
---
## The exact clicks for the operator
All of this is in Search Console, property **felhom.eu**. CC does not submit anything here; these are
the operator's own clicks.
**A. Remove the old sitemap entry**
1. Left menu → **Indexelés → Webhelytérképek**.
2. In „Beküldött webhelytérképek", click the row `https://felhom.eu/sitemap.xml?v=2`.
3. Top right of the detail panel → the three-dot menu → **„Webhelytérkép eltávolítása"**.
**B. Submit the clean sitemap**
1. Same page, the „Új webhelytérkép hozzáadása" box at the top.
2. Type `sitemap.xml` (the `https://felhom.eu/` part is already in front of the field).
3. Click **KÜLDÉS**. The row should come back as „Sikeres" within a few minutes, and the discovered
page count should rise from 8 towards 18 over the following days.
**C. Ask for indexing on three pages**
For each of these three URLs in turn:
- `https://felhom.eu/`
- `https://felhom.eu/teszteles`
- `https://felhom.eu/alkalmazasok`
1. Paste the URL into the search box at the very top („A(z) »felhom.eu« alá tartozó bármelyik URL-t
ellenőrizheti") and press Enter.
2. Wait for the check to finish (a few seconds).
3. Click **„Indexelés kérése"**, then wait for the „Indexelés kérve" confirmation before moving to the
next URL.
Google rate-limits this, so if the third one refuses, leave it for the next day. Requesting indexing
is a **hint, not a command** — it usually takes days, and it does not guarantee indexing.
---
## Performance baseline — the number to compare against in a month
Search Console → Teljesítmény, search type Web, **last 3 months (2026-07-07 … 2026-10-06)**:
| Metric | Value |
|---|---|
| Összes kattintás | **0** |
| Összes megjelenítés | **47** |
| Átlagos CTR | **0 %** |
| Átlagos pozíció | **73** |
**Every query with an impression** (there are only nine, so this is the whole list, not a top-10 cut):
| # | Query | Clicks | Impressions | CTR | Position |
|---|---|---|---|---|---|
| 1 | felhő tárhely | 0 | 18 | 0 % | 98.6 |
| 2 | felhőtárhely | 0 | 5 | 0 % | 99.0 |
| 3 | felhő alapú tárhely | 0 | 3 | 0 % | 92.0 |
| 4 | biztonsági mentés dokumentumok | 0 | 2 | 0 % | 85.0 |
| 5 | ingyenes felhő tárhelyek | 0 | 2 | 0 % | 95.5 |
| 6 | minden ok | 0 | 1 | 0 % | **2.0** |
| 7 | saját meghajtó | 0 | 1 | 0 % | 58.0 |
| 8 | saját felhő tárhely | 0 | 1 | 0 % | 83.0 |
| 9 | felhő szolgáltatás | 0 | 1 | 0 % | 100.0 |
**What this says, plainly.** Eight of the nine queries sit at position 58–100 — that is page six and
worse, where nobody looks, which is why 47 impressions produced zero clicks. They are also the wrong
queries: „felhő tárhely" is what someone types when they want Google Drive, and against Drive, iCloud
and Dropbox this site will not rank. The one query at position 2 („minden ok") is an accident of
wording, not an intent anyone can sell to.
**The conclusion this baseline supports:** search will not deliver the first five testers. The page
built in this session exists so that people who arrive **from a person** — a colleague, a friend, a
Facebook group — land on something that answers „what do I need, and what do I get". Search is the
slow lane and is worth keeping correct, not worth waiting for.
**Re-read this table in a month (2026-11-10)** and compare the four headline numbers and the position
of any query that mentions „otthoni szerver" or „saját felhő", which is what the new page targets.
+22
View File
@@ -1,3 +1,25 @@
## 2026-10-10 — a three-week tester campaign (COPY.md §7), and schedule-post learns --link
- **Five Hungarian posts** written into `facebook/COPY.md` §7, for Tuesdays and Thursdays 19:00
Europe/Budapest over three weeks: 2026-10-13 (tester recruitment → `/teszteles`), 10-15 (where
your photos are → `/biztonsagimentes`), 10-20 (Grocy + LubeLogger → `/alkalmazasok`), 10-22 (the
recovery code → `/gyik`), 10-27 (what the box does today → `/`). **The Monday post scheduled for
2026-10-12 19:00 (§6.2) was not touched**, and no slot falls on the same day as it.
- **Every claim carries a source comment** naming the page and line it already stands on. No price,
no availability promise, no response-time promise. 471–514 characters each.
- **Zero emoji, deliberately, though the brief allowed two:** §6's reasoning holds — the brand does
not use them and the website gate keeps them at 0, so zero is the only value consistent with the
other surfaces. Checked with a matcher that was first shown to convict a real emoji.
- **The app count says 58**, matching every prose claim on the website. The apps page renders 59
cards and the catalog holds 60 template directories; that gap is NOT resolved here and is filed as
a register row.
- **`scripts/facebook/fb_probe.py` gained `--link`** (default unchanged: `https://felhom.eu/`).
Before this, every scheduled post attached the home page's preview card whatever its text said —
so a post about `/teszteles` would have sent the people who clicked its card to the home page, and
the card is what a reader clicks. `build_parser()` was split out of `main()` so a test can parse
argv without running a command. **Red-proved:** with the flag removed both new tests fail with
`'Namespace' object has no attribute 'link'`; with it, the suite is 32 green.
# marketing — CHANGELOG # marketing — CHANGELOG
## facebook — the Page's first post, drafted and SCHEDULED (2026-10-09) ## facebook — the Page's first post, drafted and SCHEDULED (2026-10-09)
+124
View File
@@ -207,3 +207,127 @@ Minden éjjel mentés készül három helyre. A távoli mentés kulcsát egyedü
Most néhány magyar háztartással zárt tesztet indítunk. Nem kötelez semmire, először csak beszélgetünk: https://felhom.eu/kapcsolat Most néhány magyar háztartással zárt tesztet indítunk. Nem kötelez semmire, először csak beszélgetünk: https://felhom.eu/kapcsolat
``` ```
## 7. Háromhetes kampány — tesztelőket keresünk (2026-10-10)
A cél **nem** az elérés, hanem **öt tesztelő háztartás**. Ezért az első poszt a konkrét ajánlat, a
többi négy pedig egy-egy okot mutat meg, amiért valaki egyáltalán akarna otthoni szervert — és
mindegyik ugyanoda vezet vissza.
**A szabályok, amiket ez az öt poszt betart:**
- **Minden állítás ma is ott van a weboldalon**, soronként megjelölve forrás-kommenttel. Új ígéret
nincs. Ár nincs, rendelkezésre állás nincs, válaszidő nincs.
- **Emodzsi: egy sem.** A §6 indoklása itt is áll: a Felhom arculata nem használ emodzsit, és a
weboldal gate-je 0-ra tartja. A feladat „legfeljebb 2 emodzsit" engedett — a 0 ennek a része, és
ez az egyetlen érték, ami a többi felülettel egyezik.
- **Hashtag: egy sem** (§6 indoklása).
- **Az első két sor önmagában elmondja a lényeget**, mert a Facebook kb. három sor után „Továbbiak"-ra
vág.
- **Egy link a végén.** A linket a `--link` kapcsoló is megkapja, nem csak a szöveg: a megjelenő
kártyára kattint az olvasó, és 2026-10-10 előtt minden ütemezett poszt a `https://felhom.eu/`
kártyáját vitte, bármiről is szólt.
- **Tegező hangnem, „kérjük" nélkül.** Mind magyar; angol változat nem készül (a zárt teszt magyar
háztartásoknak szól).
**Az ütemezés** keddenként és csütörtökönként 19:00-kor (Europe/Budapest), három héten át. A hétfői,
már beütemezett poszt (2026-10-12 19:00, §6.2) **érintetlen marad**, és egyik időpont sem esik vele
egy napra.
| # | Szakasz | Időpont (Budapest) | Nap | Link (`--link`) |
|---|---|---|---|---|
| 1 | 7.1 | 2026-10-13 19:00 | kedd | `https://felhom.eu/teszteles` |
| 2 | 7.2 | 2026-10-15 19:00 | csütörtök | `https://felhom.eu/biztonsagimentes` |
| 3 | 7.3 | 2026-10-20 19:00 | kedd | `https://felhom.eu/alkalmazasok` |
| 4 | 7.4 | 2026-10-22 19:00 | csütörtök | `https://felhom.eu/gyik` |
| 5 | 7.5 | 2026-10-27 19:00 | kedd | `https://felhom.eu/` |
### 7.1
<!-- forras: website/teszteles.html — „Legfeljebb 5 haztartas" (operator ruling D2, 2026-10-10) -->
<!-- forras: website/teszteles.html — „A domain a te nevedre szol… a dijat a teszt alatt mi fizetjuk, ket evre" (D1) -->
<!-- forras: documentation/legal/feltetelek-1.1.md 5. pont — „Nincs dij a teszt alatt — se toled, se felenk" -->
<!-- forras: documentation/legal/feltetelek-1.1.md 3. pont — „Egy otthoni szervert beallitunk nalad" -->
<!-- forras: website/index.html:152-154 — „Zart teszt — Nehany magyar haztartassal most kezdjuk el elesben hasznalni" -->
<!-- forras: website/index.html:165-167 — „Ha ez erdekel, irj — nem kotelez semmire, eloszor csak beszelgetunk" -->
```
Keresünk öt magyar háztartást, aki kipróbálja az otthoni szerverünket.
A teszt alatt nincs díj — se tőled, se felénk. A szervert mi állítjuk be nálad és mi üzemeltetjük, a domaint pedig két évre mi fizetjük, és az a te nevedre szól: ha a teszt véget ér, nálad marad.
Cserébe egyetlen dolgot kérünk: mondd el, mi nem jó benne.
Informatikai tudás nem kell — egy gép kell, ami bekapcsolva tud maradni, és egy kis türelem.
Nem kötelez semmire: https://felhom.eu/teszteles
```
### 7.2
<!-- forras: website/index.html:121 — „Minden ejjel keszul egy mentes a dobozon, egy masodpeldany egy masodik meghajtora (ha van), es egy titkositott masolat egy tavoli tarhelyre" -->
<!-- forras: website/biztonsagimentes.html — a 3-2-1 szabaly harom pontja -->
<!-- forras: website/index.html:144 — „Ha elmegy a net, az otthoni halozaton tovabbra is elered a fotoidat" -->
```
Hol vannak most a fotóid?
Ha a válasz „valahol a felhőben", akkor három kérdés jön utána: meddig ingyenes, ki fér hozzá, és mi történik, ha egyszer kizár a fiókodból.
A Felhom dobozon a fényképeid nálad vannak, a saját gépeden. És nem egy példányban: minden éjjel készül egy mentés a dobozon, egy másodpéldány egy második meghajtóra, és egy titkosított másolat egy távoli tárhelyre.
Ha elmegy a net, otthon akkor is ott vannak.
Így működik: https://felhom.eu/biztonsagimentes
```
### 7.3
<!-- forras: website/alkalmazasok.html — Grocy kartya: „Nyilvantartja, mi van otthon, es szol, ha valami elfogyott vagy lejar. Bevasarlolista… vonalkodolvasas a telefon kamerajaval… hazimunkak beosztasa" -->
<!-- forras: website/alkalmazasok.html — LubeLogger kartya: „A csaladi auto teljes tortenete egy helyen: szervizek, javitasok, tankolasok es koltsegek. Emlekeztetok olajcserere, muszakira es biztositasra" -->
<!-- forras: website/index.html — „58 telepitheto alkalmazas" -->
```
Egy app, amire nem gondoltál otthoni szerver kapcsán: a bevásárlólista.
A Grocy nyilvántartja, mi van otthon, és szól, ha valami elfogyott vagy lejár. A hiányzót magától felveszi a listára, a vonalkódot a telefonod kamerájával olvasod be.
A másik kedvencünk a LubeLogger: a családi autó története egy helyen — szervizek, tankolások, költségek, emlékeztetővel a műszakira.
Egyik sem küld semmit senkinek: a saját gépeden futnak. Összesen 58 alkalmazás telepíthető.
A teljes lista: https://felhom.eu/alkalmazasok
```
### 7.4
<!-- forras: website/index.html:125 — „A tavoli mentesed titkositasanak kulcsa egy helyreallitasi kod, ami pontosan egyszer jelenik meg — a te kepernyodon. Mi nem ismerjuk, es nem tudjuk visszaallitani" -->
<!-- forras: documentation/legal/feltetelek-1.1.md 7. pont — „A kulcsat egy helyreallito kod zarja, amit csak te ismersz — nalunk nincs meg, es nem tudjuk potolni" -->
```
Van egy kód, amit nálunk sem lehet megtalálni.
A távoli mentésed titkosítva készül, és a kulcsát egy helyreállítási kód zárja. Ez pontosan egyszer jelenik meg: a te képernyődön. Nálunk nem marad belőle másolat.
Az egyik oldala, hogy a mentésedet rajtad kívül senki nem tudja elolvasni — mi sem. A másik, hogy ha a kód elvész, nekünk sem nyitható ki.
Ezért kerül papírra, jó helyre. Ez az ára annak, hogy tényleg a tiéd legyen.
Erről és a többi gyakori kérdésről: https://felhom.eu/gyik
```
### 7.5
<!-- forras: website/index.html „Mit tud a doboz ma?" — fenykepek/dokumentumok/media, halozati megosztas, haromszintu mentes, 58 alkalmazas, internetkimaradas -->
<!-- forras: website/index.html:12 — „Professzionalis otthoni szerver telepites es uzemeltetes. Te iranyitasz, mi segitunk." -->
```
Mit tud ma egy Felhom doboz? Nem terv és nem ígéret — ezek ma működő funkciók.
A telefonod fotói maguktól feltöltődnek rá. A beszkennelt papírjaidat felismeri és kereshetővé teszi. A mappái megjelennek a Windows Intézőben és a Mac Finderben. 58 alkalmazás telepíthető rá pár kattintással, parancssor nélkül. Minden éjjel mentés készül három szinten. És ha elmegy a net, otthon akkor is eléred a fájljaidat.
A telepítést és az üzemeltetést mi csináljuk. Te csak használod.
https://felhom.eu/
```
+17 -8
View File
@@ -516,9 +516,9 @@ def scenario_sched_dry(g, page):
return 0, res return 0, res
def scenario_sched_post(g, page, message, when): def scenario_sched_post(g, page, message, when, link=POST_LINK):
pid, ptok = page["id"], page["token"] pid, ptok = page["id"], page["token"]
c = g.call("P1-create", "POST", pid + "/feed", ptok, form=schedule_form(message, when)) c = g.call("P1-create", "POST", pid + "/feed", ptok, form=schedule_form(message, when, link=link))
if not c.ok: if not c.ok:
log("P REFUSED: %s" % json.dumps(c.err, ensure_ascii=False)) log("P REFUSED: %s" % json.dumps(c.err, ensure_ascii=False))
return 6, {"created": False, "error": c.err} return 6, {"created": False, "error": c.err}
@@ -543,8 +543,8 @@ def scenario_sched_post(g, page, message, when):
# ---------------------------------------------------------------- main # ---------------------------------------------------------------- main
def main(argv=None): def build_parser():
global VERBOSE """The CLI surface, separate from main() so a test can parse argv without running a command."""
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument("-v", action="store_true", help="print each call: method, path (no query), status, error") ap.add_argument("-v", action="store_true", help="print each call: method, path (no query), status, error")
ap.add_argument("--version", default="v26.0", help="Graph API version (default v26.0)") ap.add_argument("--version", default="v26.0", help="Graph API version (default v26.0)")
@@ -555,9 +555,18 @@ def main(argv=None):
help="COPY.md section to post, e.g. 6.1 or 6.2 (schedule-post)") help="COPY.md section to post, e.g. 6.1 or 6.2 (schedule-post)")
ap.add_argument("--at", default=None, ap.add_argument("--at", default=None,
help="when to publish, 'YYYY-MM-DD HH:MM' in Europe/Budapest (schedule-post)") help="when to publish, 'YYYY-MM-DD HH:MM' in Europe/Budapest (schedule-post)")
ap.add_argument("--link", default=POST_LINK,
help="schedule-post: the URL whose preview card is attached (default %s). "
"The card is what a reader clicks, so a post about one page names that page "
"here, not just in its text." % POST_LINK)
ap.add_argument("--dry", action="store_true", ap.add_argument("--dry", action="store_true",
help="schedule-post: the throwaway link+schedule check, then delete it") help="schedule-post: the throwaway link+schedule check, then delete it")
a = ap.parse_args(argv) return ap
def main(argv=None):
global VERBOSE
a = build_parser().parse_args(argv)
VERBOSE = a.v VERBOSE = a.v
try: try:
token = load_key(a.credentials) token = load_key(a.credentials)
@@ -607,9 +616,9 @@ def run(cmd, g, token, a=None):
except (ValueError, OSError) as e: except (ValueError, OSError) as e:
log("REFUSED: %s" % e) log("REFUSED: %s" % e)
return 2 return 2
log("P section %s: %d characters, publishing at %s (epoch %d)" log("P section %s: %d characters, link %s, publishing at %s (epoch %d)"
% (a.section, len(message), a.at, when)) % (a.section, len(message), a.link, a.at, when))
rc, _ = scenario_sched_post(g, page, message, when) rc, _ = scenario_sched_post(g, page, message, when, link=a.link)
log("schedule-post: done (rc=%d)" % rc) log("schedule-post: done (rc=%d)" % rc)
return rc return rc
rc, _ = scenario_d(g, page, 0) rc, _ = scenario_d(g, page, 0)
+17
View File
@@ -107,6 +107,23 @@ class ScheduleForm(unittest.TestCase):
f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600, link=None) f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600, link=None)
self.assertNotIn("link", f) self.assertNotIn("link", f)
def test_a_per_post_link_reaches_the_form(self):
"""2026-10-10: the campaign posts each point at their OWN page, and the preview CARD is what
a reader clicks. Before --link existed, every scheduled post attached https://felhom.eu/ no
matter what its text said, so a post about /teszteles sent its readers to the home page.
This pins the whole path: the CLI flag must reach the form."""
a = fb_probe.build_parser().parse_args(
["schedule-post", "--section", "7.1", "--at", "2026-10-13 19:00",
"--link", "https://felhom.eu/teszteles"])
self.assertEqual(a.link, "https://felhom.eu/teszteles")
f = fb_probe.schedule_form("x", fb_probe.time.time() + 3600, link=a.link)
self.assertEqual(f["link"], "https://felhom.eu/teszteles")
self.assertEqual(f["published"], "false") # still unreachable, even with a link set
def test_link_defaults_to_the_home_page_when_the_flag_is_absent(self):
a = fb_probe.build_parser().parse_args(["schedule-post", "--at", "2026-10-13 19:00"])
self.assertEqual(a.link, "https://felhom.eu/")
class ScheduleWindow(unittest.TestCase): class ScheduleWindow(unittest.TestCase):
NOW = 1_760_000_000 NOW = 1_760_000_000
+3
View File
@@ -1,5 +1,8 @@
User-agent: * User-agent: *
Allow: / Allow: /
Disallow: /szolgaltatasok-nonpublic Disallow: /szolgaltatasok-nonpublic
# /api/ is the contact form's POST endpoint, not a page: a GET returns 404 and Search
# Console reported it as "Not found (404)" (seen 2026-10-10, first crawled 2026-04-29).
Disallow: /api/
Sitemap: https://felhom.eu/sitemap.xml Sitemap: https://felhom.eu/sitemap.xml