CM AI Docking Port

Reception Protocol Spec v1.0 (usklajena specifikacija)

Nastane 2026-09-29 z združitvijo: Master Development Prompt v0.1 (osnovni koncept), Gemini addon ("Planning Draft – implementacijski koraki"), Grok addon ("Planning Draft – Protocol & First Files"). Ta dokument je zdaj referenčna verzija — v0.1 in oba addona ostanejo kot zgodovina razmisleka, ne kot ločena navodila.

Danes nastane samo ta dokument. Prve datoteke (manifest.json, index.html, reception.php) pridejo naslednjič, na dogovorjeni lokaciji (glej §12).


1. Namen (nespremenjeno iz v0.1)

CM AI Docking Port je univerzalni vstopni mehanizem, skozi katerega AI agent odkrije CM Ecosystem in z njim komunicira preko standardnega HTTP/JSON protokola — brez CM-specifičnega vtičnika.

Ni nov CM, ni nov poslovni motor, ni centralni marketplace, ni AI agent. CM ostaje edini vir resnice (source of truth). Docking Port je tanek protokolarni/usmerjevalni sloj nad obstoječimi javnimi API-ji (dokaz koncepta: GET /app/public/api/availability_multi.php, ki že vrača čist, semantično razumljiv JSON — glej realno implementacijo na /var/www/html/app/public/api/availability_multi.php).


2. Ključno usklajevalno načelo (Grok, potrjuje in izostri v0.1 §6/§14)

To je edina prava sprememba/izostritev glede na v0.1 — vse ostalo se le dopolnjuje.

AI agent nikoli ne kliče drugih nodov ali obstoječih poslovnih API-jev neposredno. Agent komunicira izključno z Reception svojega vstopnega noda.

AI Agent
   │
   ▼
CM AI Docking Port (manifest + docs) — odkritje
   │
   ▼
Reception — EDINA točka komunikacije
   │
   ├── local CM (kliče availability_multi.php interno)
   ├── nearby nodes (forward envelope, agent tega ne vidi)
   └── deeper nodes (znotraj max_depth)
   │
   ▼
agregiran odgovor → nazaj do AI Agent

Model je "pošta": agent odda zahtevek na Reception, počaka, dobi en sam agregiran odgovor. Agent ni usmerjevalec in ne pozna interne topologije mreže — to ohranja decentralizacijo (v0.1 §9) in hkrati preprečuje, da bi agent postal vektor za zanke, zlorabo ali fragmentacijo protokola.

Vse iz v0.1 §7–§12 (geografski routing, verižna poizvedba, globina, zaščita pred kroženjem) ostaja veljavno — le izvaja ga Reception rekurzivno, agent tega procesa ne vidi in vanj ne posega.


3. Dve ravni dostopa (nespremenjeno iz v0.1 §5)


4. Geografija kot routing princip (nespremenjeno iz v0.1 §7–§8)

Vstopna točka ≠ cilj iskanja. Agent lahko potrka na CM v Radovljici in vpraša za Ljubljano — Reception usmerja glede na target.location, ne glede na lokacijo vstopnega noda.


5. Request Envelope — usklajena končna shema

Osnova iz v0.1 §13, dopolnjena s konkretnimi vrednostmi iz Grok addona.

{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "request_id": "req-2026-08-10-abc123",
  "request_expiry": "2026-08-10T12:00:00Z",
  "origin": "ai-agent",
  "target": {
    "location": { "lat": 46.05, "lon": 14.50, "label": "Ljubljana" },
    "radius_km": 30
  },
  "query": {
    "type": "availability",
    "from": "2026-08-10",
    "to": "2026-08-16",
    "guests": 2,
    "units": null
  },
  "limits": {
    "max_depth": 2,
    "max_distance_km": 40,
    "max_results": 10
  },
  "visited": []
}

Pravila:


6. Response Envelope — nova dopolnitev (Grok; v0.1 tega ni imel eksplicitno)

v0.1 §15 je pokazal samo surov odgovor availability_multi.php. Manjkala je ovojnica za agregiran, multi-node odgovor. To je zapolnjena vrzel:

{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "request_id": "req-2026-08-10-abc123",
  "ok": true,
  "partial": false,
  "search_meta": {
    "entry_node": "CM-AI-DEMO-001",
    "target_location": { "lat": 46.05, "lon": 14.50, "label": "Ljubljana" },
    "nodes_queried": 1,
    "nodes_reached": 1,
    "max_depth_used": 0,
    "max_depth_limit": 2
  },
  "results": [
    {
      "node_id": "CM-AI-DEMO-001",
      "depth": 0,
      "source": "local_availability_api",
      "freshness": { "generated_at": "2026-09-28T18:18:29+00:00" },
      "data": { "...": "neposreden izhod availability_multi.php, brez reinterpretacije" }
    }
  ],
  "errors": []
}

Pravilo (v0.1 §14 potrjeno): Reception ne reinterpretira poslovnega odgovora — data je surov izhod obstoječega API-ja, samo ovit s provenance (node_id, depth, source, freshness). partial: true + razlog, kadar kak node ni bil dosegljiv (v0.1 §20 — brez lažnega vtisa popolne pokritosti).


7. Manifest — usklajena končna shema

Združuje v0.1 §16 (osnovna struktura) z Grokovo dopolnitvijo (capability-level metadata: side_effect, requires_consent — neposredno iz v0.1 §22 zahteve).

Objavljen na dveh mestih (ena vsebina, ena morda redirect na drugo):

/.well-known/cm-ai-docking-port.json
/app/public/ai/manifest.json
{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "service": {
    "name": "CM Ecosystem Reception",
    "type": "ai-entry-point",
    "node_id": "CM-AI-DEMO-058",
    "location": { "label": "Lancovo / Radovljica area (Demo 58)", "lat": 46.34, "lon": 14.17 }
  },
  "capabilities": {
    "local": [
      { "id": "availability", "side_effect": false, "requires_consent": false,
        "description": "Check unit availability for a date range" }
    ],
    "ecosystem": [
      { "id": "accommodation-discovery", "side_effect": false, "requires_consent": false,
        "description": "Geographic discovery across participating CM nodes via Reception" }
    ]
  },
  "reception": { "method": "POST", "endpoint": "/app/public/ai/reception.php", "content_type": "application/json" },
  "documentation": {
    "human": "/app/public/ai/index.html",
    "machine": "/app/public/ai/manifest.json",
    "spec": {
      "html": "/app/public/ai/spec.html",
      "markdown": "/app/public/ai/spec.md",
      "note": "Full working design specification. SKLENJENO sections are settled; OSNUTEK sections are open drafts.",
      "spec_synced_at": "<ISO8601, auto-updated on each publish>"
    }
  },
  "limits_default": { "max_depth": 2, "max_distance_km": 50, "max_results": 20 },
  "notes": [
    "AI agents must always communicate with Reception.",
    "Do not call internal business APIs directly.",
    "Entry point and search target are separate concepts."
  ]
}

Dodano 2026-09-30 (Dvojček/Gemini predlog, uporabnik potrdil): manifest zdaj eksplicitno referencira ta dokument (documentation.spec, obe obliki) in nosi spec_synced_at — kdaj je bila javna kopija specifikacije nazadnje osvežena iz lokalnega delovnega izvoda. Objava ni ročna: ~/Dokumenti/Guestbook/publish_ai_docking_spec.sh v enem koraku redigira LAN podrobnosti, pretvori .md v stilizirano spec.html, deploya oboje na instanco 58, in posodobi spec_synced_at na obeh manifestih (/app/public/ai/manifest.json in /.well-known/cm-ai-docking-port.json). Poganja se ročno ob vsaki vsebinski spremembi tega dokumenta — ni (še) avtomatiziran cron.


8. Vloga availability_multi.php (nespremenjeno, potrjeno v0.1 §15 + Gemini §Korak2)

Reception (lokalni node)
   │  preveri: je target v dosegu? je depth 0?
   ▼
kliče lastni GET /app/public/api/availability_multi.php?units=...&from=...&to=...&mode=basic
   │
   ▼
vzame JSON odgovor, vstavi v results[] z provenance (§6)
   │
   ▼ (če max_depth > 0 in potrebno)
forwarda envelope sosedom, zbira odgovore
   ▼
vrne Response Envelope agentu

Docking Port ne posega v availability_multi.php ali katerikoli drug obstoječi endpoint. Klic je interni, agent ga ne vidi.


9. Varovalke in omejitve (nespremenjeno, potrjeno iz vseh treh virov)

Iz v0.1 §11, §12, §19, §21, §22, §25, dodatno eksplicirano v Gemini addonu kot "Opozorilo za razvijalca":

Prepovedano v prvi fazi (MVP):

Obvezno:


10. Human-readable dokumentacija (/app/public/ai/index.html) — nespremenjeno iz v0.1 §17

Kratka stran, berljiva ljudem in LLM-jem (jasni <h1>/<code> bloki, brez težkega JS-a), ki razloži: kaj je Docking Port, kaj je Reception (edina točka za AI), primer Request/Response Envelope, da agent ne kliče notranjih API-jev neposredno, omejitve, in da gre za read/discovery MVP.


11. MVP koraki — usklajen zaporedni načrt

Združitev v0.1 §23–§25 (postopnost, "ne graditi prezgodaj") + Gemini addon (4 konkretni koraki) + Grok addon (datotečni seznam).

Korak 0 (danes, 2026-09-29): ta usklajena specifikacija ✅

Korak 1: Static Entry
  /.well-known/cm-ai-docking-port.json  ✅ DEPLOYANO 2026-09-29 (uporabnik ročno naredil sudo mkdir
                                            +chown na instanci 58, Claude Code postavil vsebino),
                                            potrjeno: https://ai-cmfree.duckdns.org/.well-known/cm-ai-docking-port.json (HTTP 200)
  /app/public/ai/manifest.json          ✅ DEPLOYANO 2026-09-29 (node_id CM-AI-DEMO-058,
                                            usklajen skozi Claude+Gemini+Grok+GPT review),
                                            potrjeno javno dostopno: https://ai-cmfree.duckdns.org/app/public/ai/manifest.json (HTTP 200)
  /app/public/ai/index.html             ✅ DEPLOYANO 2026-09-29 (Claude Code pripravil vsebino,
                                            uporabnik obrnil vrstni red: tokrat Claude pred skupino),
                                            potrjeno: https://ai-cmfree.duckdns.org/app/public/ai/index.html (HTTP 200)

Korak 1 zaključen v celoti.

Korak 2: Reception (single node, depth 0)          ✅ DEPLOYANO 2026-09-29
  /app/public/ai/reception.php
  - koda: Gemini prvi osnutek → GPT (izčrpen review proti živemu API-ju, 5 popravkov) → Claude
    (preverjeno: curl extenzija na 58 obstaja, Apache ima en sam catch-all vhost torej Host-header
    ni nujen a je neškodljiv, `availability_multi.php` dejansko vrača `generated_at`) → deployano
    z `.tmp` + `php -l` lint + atomski `mv`, brez okna polomljene sintakse v živo
  - popravki proti Geminijevemu prvotnemu osnutku: `units: null` → interni `units=all`; lokalni
    fail = `ok:false` (ne `partial:true`, to je rezervirano za Korak 3); VEDNO Response Envelope
    tudi na 400/405; samo POST (+OPTIONS/CORS); agent ne vidi internega URL-ja/HTTP kode/curl napake
  - Claude dodal: `error_log()` server-side ob curl napaki (samo v log, agent ga ne vidi)
  - testirano 5 scenarijev na živem endpointu: veljaven zahtevek (ok:true, L1 available),
    GET→405+envelope, napačen protocol→400+envelope, manjkajo from/to→200+missing_dates,
    nepodprt query.type→200+unsupported_query_type — vsi po pričakovanjih
  - endpoint: https://ai-cmfree.duckdns.org/app/public/ai/reception.php
  - index.html status badge posodobljen na "live" ŠELE po uspešnem testu (ne hkrati z uploadom)

Naslednje: Korak 3 (neighbour table + geografski forwarding) — podroben načrt v §16, testni scenarij v §13.

Šele po potrjenem Koraku 4 razmišljati o širšem discoveryju, kompleksnejšem routingu, cache-u, globalnih indeksih (v0.1 §25 — to velja nespremenjeno).


12. Lokacija izvedbe — odločeno danes (2026-09-29)

Prve dejanske datoteke (Korak 1–2) gredo na ai-cmfree demo, instanca 58 (ai-cmfree.duckdns.org → GN7 reverse-proxy → 58; glej memorijo ai_bot_demo_public_access) — brez pravih podatkov, robots.txt že odprt, obstoječa promotion-testing površina. To je edino okolje kjer read/discovery MVP nima tveganja glede realnih gostov/rezervacij.

Realni endpoint availability_multi.php (referenca za obliko odgovora, §1/§8 zgoraj) je preverjen na GN7 (/var/www/html/app/public/api/availability_multi.php) — instanca 58 ima svojo kopijo istega API-ja, path je treba potrditi ob dejanski implementaciji (ni nujno identičen /app/public/api/...).


13. Testni scenarij (usklajen iz v0.1 §24 + Gemini + Grok)

Tri testne CM/node instalacije na različnih lokacijah (Korak 4). Preveriti:

  1. Agent najde manifest, prebere capabilities in reception endpoint.
  2. Zahtevek za lokalno availability (depth 0) vrne pravilen Response Envelope.
  3. Zahtevek s ciljem na drugi lokaciji pravilno potuje Node A → relevanten sosed in vrne rezultat od tam.
  4. visited prepreči zanko (A vabi B, B vabi A).
  5. max_depth ustavi širjenje na pravem koraku.
  6. request_id prepreči podvojeno obravnavo istega zahtevka.
  7. Izpad enega noda (B nedosegljiv) ne prekine iskanja — rezultat vsebuje partial: true in jasno pove obseg, ne pretvarja se, da je pregledan cel Ecosystem.
  8. Odgovor je agentu semantično jasen brez dodatne razlage.

Testna infrastruktura — pripravljena 2026-09-29

Tri instalacije za zgornji test:

Node node_id Lokacija (lat/lon) Kaj je URL
A (pravi) CM-AI-DEMO-058 Lancovo/Radovljica (46.34, 14.17) Prava CM instanca, prava availability_multi.php podatka https://ai-cmfree.duckdns.org/app/public/ai/reception.php
B (stub) CM-AI-STUB-BLED Bled (46.3683, 14.1146) Test stub na 57, fiksni izmišljeni podatki, ni prave CM logike [interno testno omrežje — LAN, ni javno dostopno]
C (stub) CM-AI-STUB-LJ Ljubljana (46.0569, 14.5058) Test stub na 57, fiksni izmišljeni podatki, ni prave CM logike [interno testno omrežje — LAN, ni javno dostopno]

Stub node-a B in C implementirata isti Reception protokol (envelope, POST-only, invalid_envelope/missing_dates/unsupported_query_type obravnava) kot pravi node, a vedno odgovorita lokalno (depth 0, brez lastnega forwardinga) s fiksnimi izmišljenimi podatki — nikoli ne kličeta prave CM logike. Namen: omogočiti resnično testiranje max_fanout/sort-po-razdalji/visited mehanike na pravem omrežju (HTTP klici med pravima strežnikoma), preden se karkoli od tega postavi na produkcijo.

Preverjeno 2026-09-29: oba stub node-a odgovarjata pravilno lokalno na 57; node A (58) dostopa do obeh preko LAN (interno LAN, IP namerno ni objavljen) — HTTP 200, potrjen doseg. Testna infrastruktura za §13 scenarij je s tem pripravljena; sama forwarding koda (Korak 3) na node A še ni napisana.


14. §14 — ZAPRTO / SKLENJENO (formalno 2026-09-30, po Gregovem A→B→C okviru, potrjeno Claude+Grok+GPT+Gemini)

Prvotno odprta vprašanja (Grok predlog, Claude dopolnil, Gemini "Dvojček" zaprl — vsi trije se strinjajo):

Nova odprta točka (predlog uporabnika, 2026-09-29, še NI usklajena s celotno skupino)

Globina (depth) ≠ širina (fanout) — spec trenutno omejuje samo, kolikokrat se zahtevek sme odbiti naprej (max_depth), ne pa, koliko sosedov Reception na enem hopu dejansko pokliče. Brez omejitve širine je promet eksponenten: pri kuriranih 5 sosedih in max_depth: 2 lahko en agentov zahtevek sproži do 5 + 25 = 30 vzporednih klicev po mreži — to je bolj realno "open relay" tveganje kot tisto, ki ga rešuje rate limit na enem nodu, ker se pomnoži po celotni mreži.

Uporabnikov sklep (po lastnem premisleku je zavrnil agentovo nastavljivo globino IN geografsko spremenljivo globino kot nesmiselni — oboje bi obremenilo enega od udeležencev): edino kar šteje je globina 0 (lasten strežnik) in globina 1 (prvi najbližji nodi) — in ključno vprašanje ni "kako globoko", ampak "s koliko nodi node komunicira vzporedno na enem hopu".

Predlog za razpravo (Claude, še ne implementacija):

SKLENJENO 2026-09-29 — Gemini potrdil max_fanout predlog in dodatno razložil "jump over" mehaniko (usmerjanje po Haversine razdalji do target.location, ne do vstopne točke): node sortira svoje sosede iz nodes.json po razdalji do cilja in vzame prvih max_fanout, s čimer lahko zahtevek v enem skoku "preskoči" geografsko bližje, a smerno nerelevantne sosede (primer: Piran → Rogla preskoči Portorož/Izolo/Koper, gre direktno proti Ljubljani). Priporočilo za operaterje: nodes.json naj poleg 2-3 mikrolokalnih sosedov vsebuje vsaj 1 "regionalni hub" (večje, centralno vozlišče), da jump-over deluje učinkovito na daljših razdaljah. To ni kršitev prepovedi centralnega registra (§9) — hub je navaden node, izbran prostovoljno s strani operaterja, ne vsiljena infrastruktura.

Vsi štirje (Claude, Grok, GPT, Gemini) so s tem §14 v celoti usklajen. Zelena luč za pripravo nodes.json strukture in pisanje forwarding logike v Koraku 3 — testna infrastruktura zanjo je pripravljena (glej §13 zgoraj).

Ujeta ideja za kasnejšo fazo (NI odločeno, NI del MVP) — avtomatiziran bootstrap

Uporabnik je 2026-09-29 predlagal (izrecno kot razmislek, ne odločitev): namesto ročne kuracije nodes.json, bi ob podpisu "pogodbe o delitvi namestitev" skript samodejno poiskal 3 najbližje node in jih zapisal kot sosede; node-i bi to sprejeli in označili z zastavico "Povezan input"; vsak node bi imel omejeno število vhodnih/izhodnih "rež" (X input, X output), brez podvajanja povezav.

Zakaj to (zaenkrat) ni šlo v spec kot odločitev:

Vredno vrniti se k temu v v0.2, ko/če bo ročno vzdrževanje nodes.json dejansko postalo boleče zaradi realnega števila node-ov. Koncept "X input/X output rež brez podvajanja" je sam po sebi dober vzorec (naravno omeji stopnjo omrežja, ustvari back-pressure) — vredno ga takrat znova pogledati, morda celo neodvisno od vprašanja avtomatizacije odkrivanja.


15. Deset temeljnih načel (nespremenjeno iz v0.1 §26, ostaja veljavno kot preverba pri vsakem naslednjem koraku)

  1. CM ostane source of truth.
  2. AI ne dobi neposrednega dostopa do celotnega Ekosistema — samo do Reception (§2, izostreno).
  3. Vsaka CM instalacija ima lahko svoja vrata.
  4. Reception je skupen protokol, ne nujno centralni strežnik.
  5. Geografija vodi discovery.
  6. Poizvedba se širi po globini, vedno z omejitvami.
  7. Zahtevek nosi svojo zgodovino (request_id, visited, depth).
  8. Podatki ostanejo lokalni — v mrežo gre samo rezultat, ki ga node sme izpostaviti.
  9. Ni vezave na enega AI ponudnika.
  10. Docking Port je razširljiv preko manifesta/protokola, ne novih AI sistemov.

16. Korak 3 — Načrt pred kodo (SKLENJENO — čaka implementacijo, formalno zaprto 2026-09-30)

Po Gregovem A→B→C okviru (Gemini/Grok/GPT potrdili): preden se napiše ena vrstica forwarding kode v reception.php, se uskladimo na treh točkah. To je osnutek za skupni pregled, ne dokončana odločitev.

16.1 Shema nodes.json

Posodobljeno 2026-09-30 — samo-opisna oblika (uporabnikov eksperiment), vsebuje tudi max_fanout (prejšnji osnutek ni povedal, kam se ta shrani):

{
  "protocol": "CM-AI-Docking-Port",
  "version": "0.1",
  "node_id": "CM-AI-DEMO-058",
  "max_fanout": 2,
  "neighbours": [
    {
      "node_id": "CM-AI-STUB-BLED",
      "label": "Bled (stub test node)",
      "location": { "lat": 46.3683, "lon": 14.1146 },
      "reception_url": "[interno testno omrežje — LAN, ni javno dostopno]"
    }
  ]
}

Realna lokacija (po incidentu in popravku 2026-09-30): /home/cmfree/cm-private/ai/nodes.json na instanci 58 — izven /var/www v celoti (ne samo izven /app/public/), lastnik cmfree:www-data, pravice 640 na datoteki in 750 na mapah. reception.php jo bo bral po absolutni poti.

Drugi popravek, isti dan — dovoljenja v verigi map: reception.php teče kot www-data (potrjeno: ps aux, APACHE_RUN_USER=www-data), ki ni član skupine cmfree (id www-data → samo skupina 33(www-data)). /home/cmfree je imel other: ---, kar bi www-data popolnoma blokiralo pri prehodu (traverse) v mapo — reception.php sploh ne bi mogel priti do datoteke, ne glede na njena lastna dovoljenja. To bi na živem Koraku 3.1 tiho spodletelo (is_readable() === false), ne bi pa se pokazalo prej, ker Korak 2 te poti sploh ne uporablja.

Popravek (brez sudo, cmfree je lastnik /home/cmfree): chmod o+x /home/cmfree (samo prehod, brez branja/izpisovanja vsebine za "other") + chgrp www-data na cm-private/ in cm-private/ai/ (cmfree je član skupine www-data, zato lahko to naredi sam). Veriga po popravku:

/home/cmfree          drwxr-x--x cmfree:cmfree   (+x za "other" — samo prehod)
/home/cmfree/cm-private     drwxr-x--- cmfree:www-data
/home/cmfree/cm-private/ai  drwxr-x--- cmfree:www-data
.../nodes.json               -rw-r----- cmfree:www-data

Empirično preverjeno (ne samo izračunano): začasen PHP test skript na živem endpointu (odstranjen takoj po testu) je potrdil is_readable() === true, 129 bajtov, veljaven JSON. Javna izpostavljenost ostaja zaprta (ponovno preverjeno po spremembi dovoljenj).

Incident in popravek (2026-09-30): uporabnik je pri eksperimentiranju ročno ustvaril testno nodes.json na /var/www/html/app/config/ai/nodes.json — znotraj DocumentRoot-a (/var/www/html), zato je bila datoteka javno dostopna na https://.../app/config/ai/nodes.json (HTTP 200), poleg tega je bil na tej poti vklopljen Apache Indexes (javno vidljiv directory listing obeh nadrejenih map). Ujeto s strani zunanjega pregleda (curl), preden je vsebina postala nevarna (neighbours je bil prazen). Ista vrsta napake kot [[connector_modules_data_dir_exposure_2026_08_05]] — pot znotraj DocumentRoot-a, ne prave dostopne pravice. Popravek: datoteka premaknjena izven /var/www v celoti (/home/cmfree/cm-private/ai/, mapa cmfree:cmfree drwxr-x---, torej noben Apache config je nikoli ne more streči, ne glede na Indexes), prazni mapi v DocumentRoot-u odstranjeni. Preverjeno: GET /app/config/ai/nodes.json in /app/config/ zdaj 404, Reception brez regresije (ok:true, L1). Pomembno — namerno ni javno dostopna datoteka. V nasprotju z manifest.json (namerno javen, to je discovery površina), nodes.json je operativna konfiguracija posameznega noda — seznam, komu node zaupa in kam posreduje promet. Ta projekt ima že realno izkušnjo s tem natančnim razredom napake (data/ mape connector modulov so bile javno berljive, popravljeno 2026-08-05, ponovilo se je še dvakrat) — nodes.json gre zato izven javnega docroot-a (ali vsaj eksplicitno blokirana v Apache konfiguraciji), bere jo samo reception.php server-side. To ni teoretična previdnost, je ponovitev znane napake, ki se tej ekipi je že zgodila.

16.2 Pogoji za forwarding

Realna sprememba glede na Korak 2: trenutna živa koda vedno kliče lokalni availability_multi.php, ne glede na target.location. To je bilo ustrezno za Korak 2 (depth 0, brez konteksta cilja), a za Korak 3 ni več smiselno — če nekdo iz Pirana vpraša za Roglo, Piranov lokalni odgovor ni relevanten rezultat, samo šum v results[].

SKLENJENO 2026-09-30 — efektivna razdalja (popravek, lokalni klic in izbira sosedov): target.radius_km (agentova želena relevantnost) in limits.max_distance_km (node-ova/sistemska zgornja meja iz limits_default, če agent ne pove svoje) sta dve različni stvari, ne sopomenki. Prejšnji osnutek je uporabljal samo radius_km — to je bila ista vrsta tihe dvoumnosti kot prej pri request_expiry, zdaj eksplicitno razrešena:

effective_distance_km =
  če sta podana oba:  min(target.radius_km, limits.max_distance_km)
  če je podan samo eden: ta eden
  če ni podan noben: limits_default.max_distance_km (iz manifesta)

Razlog za min(), ne za katero koli polje samo: radius_km je agentova poslovna želja ("rezultati do X km od cilja so zame še uporabni"), max_distance_km je node-ova/sistemska zgornja meja prometa. Agent z ozkim radius_km: 5 ne sme dobiti "lokalnega" odgovora 40 km stran samo zato, ker je node-ov max_distance_km širši — in obratno, node-ova lastna zgornja meja mora vedno obrezati tudi prevelik agentov radius_km, dosledno z že sklenjenim načelom pri max_depth (§14 — server vedno obreže, nikoli ne zaupa golemu agentovemu številu).

effective_distance_km se uporabi na dveh mestih, ne samo enem (dopolnitev, ne v prvotnem predlogu): tako pri odločitvi za lokalni klic KOT pri izbiri sosedov za forwarding — sicer node zavrne lasten odgovor kot "predaleč", nato pa brez težav forwarda k sosedu, ki je še bolj oddaljen, kar je notranje nekonsistentno.

Predlagan sklep (za potrditev): 1. Lokalni klic je pogojen z razdaljo: samo če je haversine(own_location, target.location) <= effective_distance_km, node pokliče svoj lokalni availability_multi.php. Sicer lokalnega klica sploh ne naredi. 2. Forward pogoj: len(visited) < max_depth_limit (kjer je max_depth_limit = min(agentov limits.max_depth, node-ova lastna omejitev iz limits_default) — server vedno obreže, nikoli ne zaupa golemu agentovemu številu, skladno z že sklenjenim §14). 3. Izbira sosedov: iz nodes.json izloči node-e, katerih node_id je že v visited IN katerih razdalja do target.location presega effective_distance_km; preostale sortiraj po Haversine razdalji do target.location; vzemi prvih max_fanout (sklenjeno z Gemini). 4. Pred posredovanjem: dodaj lastni node_id v visited, posreduj isti envelope (z dopolnjenim visited, nespremenjenim request_id/request_expiry) vsakemu izbranemu sosedu, vzporedno (ne zaporedno — da se čas ne seštevaj linearno). 5. Če je len(visited) >= max_depth_limit ali nodes.json po filtriranju (visited + effective_distance_km) prazen, node ne poskuša forwardati — ne napako, samo prazen forward-del odgovora.

SKLENJENO 2026-09-30 (uporabnik): forward je fallback-only, in to ni samo tehnična odločitev za manj prometa — je namerna vrednostna izbira. Node najprej poskusi obdržati gosta pri sebi; šele če lokalni rezultat ni "true" (glej natančen pogoj spodaj), referira naprej v zaupano mrežo. To je digitalni ekvivalent že uveljavljenega vzorca v tem projektu — fizični keycard referral kot prototip CM Community (glej [[guest_trust_and_referral_strategy]]) — prenesen v protokol namesto zgolj v gostov ključ. Mreža je tako po zasnovi overflow/referral mehanizem, ne generično "poišči vse v bližini" iskanje.

Natančen pogoj (za implementacijo): lokalni klic šteje za "true rezultat" in ne forwarda naprej, če data.ok === true IN data.available_count > 0 (obstaja dejansko prosta enota, ki ustreza povpraševanju). Če je lokalni klic spregledan zaradi razdalje (§16.2 točka 1), ali je vrnil available_count: 0, ali je lokalni klic spodletel — node forwarda naprej, znotraj max_depth/max_fanout omejitev.

Pomembna ločnica (uporabnik, 2026-09-30): "lojalnost do lokalne skupnosti" velja na kurirni ravni (kdo sploh pride v nodes.json — operater ročno izbere prave, zaupanja vredne sosede po resničnem odnosu, "po svoji volji in interesu", ne po surovi razdalji; to je vloga bodočega Community zemljevida, §14 jump-over razprava). Ne velja na ravni algoritma med že-izbranimi sosedi — tam ostaja mehanski Haversine-sort + max_fanout, dogovorjen z Gemini (§14). Kurirana izbira je vrednostna, usmerjanje med kuriranimi sosedi je geografsko — ti dve ravni se ne smeta zamešati.

16.3 Agregacija in partial

  1. Vsak klic (lasten lokalni + vsak sosed) ima neodvisen timeout (obstoječ vzorec: CURLOPT_TIMEOUT). Napaka/timeout enega soseda ne prekine drugih — ta sosed se izpusti iz results[], partial postane true.
  2. Odgovor vsakega soseda je že sam po sebi poln Response Envelope (lahko vsebuje več results[] vnosov, če je sosed sam nekaj forwardal naprej). Ta node samo sploshi (flatten) vse prejete results[] v svoj lastni results[] — brez reinterpretacije vsebine (skladno z že sklenjenim pravilom iz §6).
  3. search_meta.nodes_queried/nodes_reached = vsota čez lasten poskus + vse poskuse vseh sosedov (vključno s tem, kar so sosedje sami poročali v svojih search_meta).
  4. search_meta.max_depth_used = največja vrednost, ki jo je poročal kateri koli otrok, +1 za ta hop (ne samo lasten hop).
  5. SKLENJENO 2026-09-30: max_results rez — če je po združitvi results[] daljši od max_results, obdrži najprej najbližje target.location (ne arbitrarno prvih N) — skladno s filozofijo "geografija vodi relevantnost" (§4).

Status §16 (2026-09-30): načrt v celoti SKLENJEN, nič kode. Vseh pet elementov potrjenih: fallback-only forward, effective_distance_km (min/fallback pravilo, velja za lokalni klic IN izbiro sosedov), ločnica kuracija-vs-routing, nodes.json izven docroot, max_results rez po bližini. Community/User Map kot kasnejši, user-controlled način polnjenja nodes.json je potrjen kot skladen s tem načrtom, a ostaja izrecno po Koraku 4/Fazi C, ne pred njim — ne odpira Koraka 3. Implementacija je ločena, zavestna odločitev ("Piši"), ne sledi avtomatsko iz zaprtega načrta.


17. Korak 3.1 — Branje konfiguracije (varen prvi podkorak, SKLENJENO 2026-09-30, čaka "Piši")

Namerno ozek, pred-forwarding podkorak: reception.php zna prebrati nodes.json, a se obnašanje agenta ne spremeni, dokler je neighbours prazen ali se datoteka ne prebere.

  1. reception.php prebere /home/cmfree/cm-private/ai/nodes.json po absolutni poti (dovoljenja preverjena in popravljena, glej §16.1).
  2. Fail-safe, ne 500: če datoteka ne obstaja, JSON ni veljaven, ali node_id v datoteki ne ujema z CM-AI-DEMO-058 — reception.php tiho pade nazaj na obnašanje Koraka 2 (čisto lokalno, ok:true, depth:0), agent nikoli ne vidi napake konfiguracije.
  3. Dodatek (Claude): vsak od treh fail-safe pogojev se zabeleži server-side z error_log() (isti vzorec kot pri Koraku 2 za curl napake) — agent tega ne vidi, operater pa lahko razišče, zakaj forwarding ne deluje, ne da bi moral ugibati.
  4. Brez forwardinga v tem podkoraku — nobenega HTTP/cURL klica proti sosedom, tudi če je neighbours napolnjen s testnimi stubi. Živi endpoint ostane ves čas 100% lokalen in stabilen.

Status: ✅ DEPLOYANO in testirano 2026-09-30. Koda (Grok "Piši", load_nodes_config() + fail-safe + error_log()) je na živo, po istem vzorcu kot Korak 2 (.tmp + php -l + atomski mv). Testi na živem endpointu, vsi po pričakovanjih: 1. Normalen zahtevek → odgovor bit-za-bit enak Koraku 2 (ok:true, depth:0, L1). 2. Odgovor ne vsebuje nobene sledi nodes.json//home/cmfree/max_fanout/neighbours/cm-private. 3. /app/config/ai/nodes.json ostaja 404. 4. Fail-safe, oba scenarija na živem strežniku: preimenovana datoteka → ok:true lokalno + error_log: "nodes.json not readable"; pokvarjen JSON → ok:true lokalno + error_log: "nodes.json invalid JSON". Datoteka obnovljena z originalnimi dovoljenji (cmfree:www-data, 640) po testu. 5. GET→405 in napačen protokol→400 nespremenjena (regresija čista). 6. Brez zaostalih začasnih testnih skriptov v docroot-u.

$nodes_cfg je prebran in ovrednoten, a se za forwarding še ne uporablja — endpoint ostaja 100% lokalen. Naslednje: Korak 3.2 (Haversine, effective_distance_km gating za lokalni klic) — čaka na nov "Piši".


18. Privaten tracker — ni del protokola, samo operativno orodje (2026-09-30)

Namen: videti, ali objava specifikacije dejansko vodi do zanimanja/uporabe (ne samo teoretičen dokaz koncepta). Namerno brez novega javnega endpointa/dashboarda — manj nove površine po incidentu iz §16.1, in ni potreben, ker je edini naslovnik lastnik sam.

Prvi zagon (2026-09-30) je že pokazal pravi signal: obiski spec.md/.well-known z curl/7.88.1 in Python-urllib/3.12 — različni od lastnih orodij v tej seji (curl/8.5.0) — torej najverjetneje avtonomno branje s strani enega od AI sodelavcev ali njihove infrastrukture.