OUTSOFTWARE BBS — BROKER GUEST CANDIDATO, NON ATTIVATO
25 ottobre 2026 — Python 3.5, sola libreria standard più websockify0.12 già verificato

STATO
Implementazione concreta verificata con fixture locali. Non sono stati creati
account Mystic, aperte connessioni alla BBS, letti segreti reali o modificati
servizi/configurazioni remote. Prompt reali e permessi degli account sono ancora
da confermare. I modelli di configurazione sono vuoti e disabilitati.
Questa cartella non sostituisce il runtime web V3 congelato.

INTERFACCE
  /ws        conserva il proxy registrato V3 e non legge il pool Guest.
  /ws/guest  usa un account tecnico in lease esclusiva e il profilo di login.
Il launcher mantiene listen127.0.0.1:6080 e target127.0.0.1:23, senza parametri
CLI, token, host/porta nell'URL, record del traffico o proxy pubblico.
Le due sole Origin ammesse rimangono:
  https://bbs.outsoftware.it
  https://retrobbs.sinclair.homepc.it
Le intestazioni Origin duplicate sono rifiutate. Nessun account viene scelto
tramite dati inviati dal browser. L'endpoint Guest non è un accesso amministrativo.

FILE
  bbs_proxy.py        integrazione route, stesso /ws, cleanup per sessione
  guest_pool.py       lettura privata e lease atomica con flock Linux
  guest_login.py      profilo verificato e macchina a stati per i prompt
  guest_transport.py login Telnet non bloccante e handoff al proxy originale
  config/            unità candidate, route Caddy e modelli disabilitati
  tests/             account fittizi e server loopback su porte effimere
  evidence/          rapporti locali, senza credenziali reali
  delta-from-v3.patch modifiche minime rispetto al proxy/config V3

CONFIGURAZIONE PRIVATA REALE — NON INCLUSA
Directory fissa:
  /home/outsoft/outsoftware-web-bbs-private
Owner outsoft, modo0700, nessun symlink.
File, owner outsoft e modo0600:
  guest-settings.json
  guest-pool.json
Directory delle lease, owneroutsoft e modo0700:
  leases/
Questi percorsi sono fuori dal webroot. Non inserire password in site/, HTML,
JavaScript, argv, output di comandi, report o chat.

Schema esatto del pool (senza campi aggiuntivi negli account):
  {"schema":1,"accounts":[
    {"slot":"webguest01","username":"WebGuest01","password":"<SOLO_SERVER>"},
    {"slot":"webguest02","username":"WebGuest02","password":"<SOLO_SERVER>"}
  ]}
Le stringhe qui mostrate sono solo struttura e nomi concordati; le password
vengono generate e conservate esclusivamente sul server. Il limite nativo
A31 rilevato nella review è String[15]: il generatore operativo deve rispettare
15 caratteri, anche se il parser generico accetta stringhe più lunghe.
Il broker non tronca, cambia maiuscole o modifica le password.

Il modello guest-settings.example.json richiede tutti i tre flag true soltanto
dopo verifica reale: enabled, prompts_verified, account_permissions_verified.
Fino a quel momento /ws/guest risponde503 senza connessione TCP e /ws funziona.
File assente, permessi aperti, symlink/hardlink, JSON duplicato o invalido,
account duplicati senza distinzione di maiuscole: Guest rifiutato.
Non è necessario un ID numerico Mystic nel pool; nomi/ID/ACS vanno comunque
verificati nell'editor nativo da chi prepara gli account.

PROFILO DI LOGIN — SOLTANTO PROMPT OSSERVATI
login.steps è una sequenza di massimo16 stati. Ogni stato contiene esattamente
uno fra expect_text (testo CP437 con escape ANSI rimossi) ed expect_hex (byte
applicativi esatti in esadecimale). Il confronto è letterale e mantiene
maiuscole, spazi e ritorni a capo: nessuna regex o interpretazione di comandi.

Risposte disponibili, ciascuna inviata solo dopo il proprio marker:
  send:"username"  usa il nome della lease, seguito da CR;
  send:"password"  usa la password privata della lease, seguita da CR;
  send:"literal", value:"..."  risposta fissa alla conferma verificata;
  send:"hex", value:"...", terminator:"none"  risposta byte esplicita a una
                   sonda terminale effettivamente osservata.
Per literal il terminator è "cr" o "none". Un literal vuoto con CR rappresenta
Invio, ma può essere configurato solo davanti a un prompt reale che lo richiede.
Il profilo impone username e password una volta ciascuno, in quest'ordine.
L'ultimo stato contiene ready:true e nessun send.

Il marker ready deve identificare la fine del menu Guest ridotto e pronto
all'input, preferibilmente il suo prompt finale univoco; non una parte generica
del banner o un testo che può comparire durante autenticazione/errore.
release_from_hex deve essere il confine ANSI di pulizia/inizio della schermata
Guest effettivamente catturato. Si usa l'ultima occorrenza dopo l'invio delle
credenziali e prima del marker ready. Nessun confine reale è precompilato qui.
Se manca il confine, o l'output da rilasciare contiene nome/password della lease
anche attraverso escape ANSI, la sessione si chiude senza inoltrare quel testo.

failure_texts contiene gli eventuali messaggi d'errore realmente osservati.
Un prompt sconosciuto non causa invii di tentativi: la sessione scade al limite
configurato e chiude il backend. Deadline per stato e totale sono controllate
con select/monotonic; non sono usati sleep né ritardi per digitare i segreti.
Le sonde ANSI non sono inventate: se necessarie vanno configurate come stati
raw expect/reply dopo averne letto la sequenza reale.

TRASPORTO E RISERVATEZZA
Durante login il broker negozia Telnet Binary/Echo/SGA, TTYPE ANSI e NAWS80x24.
Gestisce frammentazione IAC, sottonegoziazioni e byte255; le negoziazioni utili
sono riprodotte al client al momento dell'handoff. Il testo pre-login non viene
inoltrato al browser. L'input del browser prima di ready viene consumato senza
inviarlo alla BBS, con un limite di4096byte; chiusure/ping sono gestiti subito.
Al menu pronto si inoltra la schermata dal confine esplicito e si passa al
proxy binario originale. Da quel punto i permessi e i contenuti applicativi
sono responsabilità del menu/ACS Guest del server, non di filtri nel browser.
Non vengono salvati dump dei prompt, password o transcript del login.

POOL E RILASCIO
Ogni connessione valida acquista una lease dopo l'handshake WebSocket e prima
del TCP backend. Il lock è basato sull'username normalizzato, non sul nome slot.
flock non bloccante impedisce l'uso contemporaneo dello stesso account da
thread/processi diversi; il kernel rilascia il lock anche se il processo muore.
I file di lock non vengono cancellati, evitando gare fra inode differenti.
Il loro contenuto include soltanto PID, session_id casuale e data, mai credenziali.
Pool esaurito: close1013, zero nuove connessioni backend. Nessuna coda apre nodi
aggiuntivi e nessun limite di Mystic viene aumentato. In caso di close/EOF/reset,
password rifiutata, timeout, errore di trasporto o output non sicuro si chiude
prima il socket target, poi si rilascia la lease. Il rilascio è idempotente.
Le lease riguardano il broker; non aggirano il Multi Login ACS di Mystic o un
accesso manuale già presente su un account tecnico. Chiudere le sessioni di
registrazione/collaudo prima di rendere disponibile il pool.

UNITÀ E CADDY CANDIDATI
La unità proxy conserva ProtectHome=read-only e ReadOnlyDirectories=/mystic.
L'unica nuova eccezione scrivibile è:
  ReadWriteDirectories=/home/outsoft/outsoftware-web-bbs-private/leases
Il pool e i settings rimangono in sola lettura nel namespace del servizio.
La directory leases deve esistere prima dell'installazione della unità.
Caddy inoltra soltanto /ws e /ws/guest al medesimo proxy locale, con la stessa
allowlist Origin e senza cambiare TLS, DNS, porte o avvio Mystic.
Il GET esatto /guest-capability.json viene inoltrato allo stesso proxy locale.
Restituisce soltanto schema/ready/path e no-store, senza lease o connessioni
Telnet. Legge i flag verificati, il profilo, il pool e il modo/owner della
directory lease. Se il proxy manca, Caddy offre il file statico false; gli
errori WebSocket rimangono invariati. Solo la configurazione privata attiva
o disattiva Guest, senza cambiare manifest/unità/certificati.
La configurazione non è stata installata o avviata da questo lavoro locale.

RISULTATI
  21/21 fixture Guest PASS: frammentazione, echo credenziali trattenuto,
  menu senza confine rifiutato, input/timeout/close/EOF/errori, due account
  distinti contemporanei, pool saturo senza nuovo TCP, permessi privati,
  lock rinominato e rilascio dopo SIGKILL, cleanup descrittori.
  34/34 regressioni del proxy /ws PASS, con le due Origin esatte.
  Config Caddy adapt e unità: PASS, lettura/provisioning statico soltanto.
  Grammatica Python3.5 PASS. L'esecuzione reale 3.5 e il profilo nativo sono
  ancora da verificare sul server dal responsabile operativo.

COMANDI DI TEST SOLO FIXTURE
  python3 -B -S tests/test_guest_broker.py
  BBS_WEB_REPORT_DIR=/percorso/report python3 -B -S tests/test_proxy.py
La suite usa credenziali chiaramente fittizie su server creati localmente; non
crea utenti e non contatta porta23, Internet, IRC o chat reali.

INTEGRAZIONE V4
Il candidato web completo è preparato separatamente in /tmp/bbs_web_v4_candidate.
Il fallback della capability Guest resta false. Il GET dinamico esatto legge
solo la configurazione privata e ne espone la disponibilità non sensibile.
Con ready:true verificato, il primo caricamento avvia Guest una sola
volta; dopo uscita/errore serve una scelta esplicita. Il cambio account attende
la chiusura completa del socket precedente. Tastiera mobile e percorsi Guest
sono integrati nella V4, senza modificare la V3.
Prima di attivare servono il profilo reale, account S1/StartMenuwebguest/ACS
verificati, prove negative su mail/file/personali e l'esecuzione sul Python3.5
reale. Nessuna API A48 viene utilizzata in A31.

IRC E INTERCHAT
Il broker non imposta nickname IRC, non sceglie canali e non invia messaggi.
session_id è solo correlazione privata della lease, non un nickname promesso.
Identità per-sessione/scelta nome e autojoin nel canale verificato appartengono
alla modifica separata della door. Non è stato inventato un canale #forum.

LICENZE
Il vendor websockify0.12.0 e le sue due correzioni preesistenti sono identici
alla V3; licenza LGPL e sorgenti rimangono inclusi. Tutti i nuovi moduli sono
consegnati in sorgente e andranno inclusi nell'archivio sorgenti della V4 finale.
