Delphi ecrivait android:debuggable=true meme en Release : sur un telephone
avec le debogage USB actif, adb run-as ouvrait le dossier prive de l'app.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
En release le telephone ne sait que reprendre le coffre du depot : l'ecran
d'accueil n'offre plus que Download vault, et RegisterLocal refuse aussi.
Depot vide (404) ou conteneur illisible : rien n'est cree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le scanner du systeme demandait avec quoi ouvrir le lien. L'app ouvre la
camera arriere, decode une image sur quatre et remplit URL + jeton des
qu'un lien pmserver://sync est lu. Rien n'est enregistre avant Save.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le scanner du telephone envoyait le texte a une recherche web (Google,
Chrome) : le jeton serait parti a un appui pres. Le lien a schema propre
n'a qu'une application pour l'ouvrir. Rien n'est enregistre avant Save.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le poste genere le QR (DelphiZXingQRCode, Apache 2.0) depuis ses prefs :
URL + jeton, jamais le mot de passe de synchro. Le telephone lit le texte
copie par l'appareil photo et remplit les champs ; Save valide.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lancee, fermee par la croix (tray), puis relancee : la fenetre revenait
derriere les autres, seule son icone dans la barre des taches. Mesure :
visible, non minimisee, pas au premier plan.
La 2e instance, qui recoit le double-clic, reveille la 1re par un
message ; mais la 1re n'a recu aucune entree, et Windows lui refuse
SetForegroundWindow. La 2e cede maintenant son droit
(AllowSetForegroundWindow) avant d'envoyer le message.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les onglets etaient visibles par defaut puis masques a la reponse de
/ping : Register restait affiche quelques millisecondes. Ils naissent
maintenant invisibles (visibility, place reservee) et ne sont reveles
qu'une fois la reponse connue — licence valide, ils apparaissent sans
rien decaler ; licence refusee, Register n'est jamais montre.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A l'arrivee sur l'ecran de connexion, /ping est interroge : son 404 JSON
exact ne peut venir, avant connexion, que de la 2e couche. Dans ce cas les
onglets disparaissent et seul Login reste. Tout autre echec laisse
Register visible — le masquer a tort empecherait un vrai client de creer
son compte. Le test porte sur le corps JSON, pas sur le seul statut : le
verrou du jeton repond aussi 404, sans corps.
Cosmetique : /register etait deja refuse cote serveur. On cesse seulement
de proposer un formulaire qui n'aboutit jamais.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Avant toute session, le 404 generique ne peut venir que de la 2e couche :
les routes appelees a ce stade existent toutes, et leurs vraies erreurs
portent un autre texte. api() vide son message, toast() ignore un message
vide — qui a rapiece la 1re couche ne voit rien.
Apres connexion rien ne change : pieces jointes et dossiers renvoient un
404 'Not found' legitime qu'il faut montrer. Prix assume : une route
perdue par un build casse echouerait aussi en silence avant connexion.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Exe d'essai qui saute l'ecran, licence retiree : toasts « Not found »,
impossible d'entrer. Le 404 est le generique d'une route inexistante,
donc aucun indice qu'une verification de licence existe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le 404 a corps vide etait remplace par la page HTML d'Indy, distincte du
404 JSON d'une route inexistante : on pouvait lister les routes reelles.
La 2e couche renvoie maintenant exactement le meme JSON.
Ajoute un exe d'essai qui saute l'ecran d'activation, pour eprouver la
2e couche depuis l'application. Define de compilation
LICENCE_TEST_SKIP_SCREEN, jamais un reglage : un interrupteur dans
config.txt ferait sauter la 1re couche sans rapiecage. Build dedie,
.dcu a part ; verifie que seul l'exe d'essai porte le define.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
API interrogee en direct, sans passer par l'ecran : licence active /ping
repond pong, licence retiree toutes les routes tombent en 404, y compris
POST /register ; /index.html reste servi.
Notes les deux pieges qui faussaient le resultat — config.txt lu depuis
le dossier courant, et le oui retenu par un processus que fermer la
fenetre ne quitte pas — et une observation : le 404 de la 2e couche
porte la page d'Indy, distincte du 404 JSON d'une route inexistante, ce
qui revele les routes existantes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
$p et $outDir etaient relatifs (".\Android64\..."). Lance depuis un
autre dossier que la racine du depot, build-apk compilait bien dans le
depot mais repack empaquetait un dossier inexistant sous le repertoire
courant, y semait des copies de ressources, puis echouait sur un
Copy-Item ("Container cannot be copied onto existing leaf item").
Les deux chemins partent maintenant de $PSScriptRoot. Ca ne se voyait
pas tant que le script etait appele depuis la racine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sans FireDAC.Phys.SQLiteWrapper.Stat, FireDAC charge sqlite3.dll au
demarrage. Sur ce poste ca marchait quand meme : le dossier bin
d'Embarcadero, qui en contient un, est dans le PATH. Sur une machine
sans Delphi, l'exe refusait de demarrer.
C'est le Win64 qui l'a revele — la DLL du PATH est 32 bits — mais le
Win32 livre etait casse de la meme facon chez quiconque n'a pas Delphi,
paquet prepare pour un tiers compris.
Prouve avec un PATH reduit a Windows : avec l'unite SQLite s'ouvre
(3.42.0), sans elle l'erreur exacte. Lire la table d'import ne
suffisait pas, le chargement est dynamique.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le groupe Base_Win64 du .dproj n'avait ni prefixes d'unites — d'ou
"Unite 'Windows' non trouvee" en recompilant TMS — ni Cfg_1_Win64 :
l'exe aurait compile sans icone et sans PerMonitorV2, donc flou sur un
ecran haute resolution. Complete en miroir exact de Win32, et Win64
coche dans l'IDE.
Builds Release 32 et 64 bits produits sur E:, un dossier .dcu par
plateforme — c'est ce qui empeche la pollution des sources TMS qui avait
casse le build Win32 le 7 septembre. Verifie : aucun .dcu depose.
Empreinte de licence identique en 32 et 64 bits, les deux couches a
TRUE des deux cotes : une licence vaut pour les deux exe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
\a s'etait change en BEL dans E:\a pmanager. Le commit precedent
affirmait qu'il ne restait plus rien sans l'avoir mesure apres coup —
c'est ce qui l'a laisse passer. Mesure cette fois : zero.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
\t et \r s'etaient changes en tabulation et en retour chariot dans les
chemins du piege TMS. Verifie apres coup : plus aucun caractere de
controle parasite dans le fichier.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'ecran d'activation ne consultait qu'un branchement : le rapiecer
ouvrait tout, l'API ne demandant rien. Chaque route de PM.Router passe
maintenant par PM.LicenceGate.ApiLicenceOk avant son handler.
Aucune fonction de decision partagee avec PM.Licence — sinon un seul
rapiecage ouvrirait les deux couches. La verification est refaite de
bout en bout, appels CNG redeclares compris ; seules la cle publique et
le nom de la pref sont partages, parce que ce sont des donnees.
Branchee dans DispatchRequest : les fichiers de l'ecran d'activation
passent par TryServeEmbedded, non garde, sinon l'ecran ne se chargerait
plus. Un oui est retenu pour le processus, un non jamais.
Le risque etait qu'elle refuse un client licencie. Auto-test sur un
vecteur lie a une empreinte fixe, sens accepte en premier ; et accord
verifie en conditions reelles sur ce poste : IsLicensed = TRUE,
ApiLicenceOk = TRUE.
Documente aussi le piege du jour, sans lien avec ce code : des .dcu TMS
x64 deposes a cote des sources cassaient le build Win32, et la sortie
redirigee vers E: faute de place sur Z:.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
origin pointe sur le Gitea auto-heberge. CLAUDE.md affirmait "pas de
remote configure (dev local)", ce qui a cesse d'etre vrai sans que le
document suive — 427 commits d'avance au moment du constat.
Rien n'est pousse automatiquement, et c'est note : lire "il y a un
remote" ne doit pas se comprendre comme "c'est sauvegarde ailleurs".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le formulaire New entry n'avait pas le champ et AddEntry ecrivait
totp_secret = '' en dur : il fallait enregistrer l'entree puis la rouvrir
pour poser un second facteur, alors que l'editeur portait deja le champ.
Meme intitule que l'editeur ("2FA secret"), masque pour une note comme
site et identifiant, et achemine par &o= — t, s, u, p, k, d etaient
pris.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Differe volontairement : l'APK ne part chez personne aujourd'hui. Le
jour venu, l'instantane publie par le poste portera la licence et le
telephone exigera d'y en trouver une — il le lit deja, donc aucun
nouveau format, aucune empreinte Android, aucune emission de plus.
Les trois raisons de ne pas refaire un second dispositif sont notees
pour ne pas les re-deriver : Java attend du DER la ou CNG accepte r||s
brut, ANDROID_ID est remis a zero par une reinitialisation d'usine (donc
des clients legitimes refuses), et un APK se repaquette en dix minutes.
Et l'APK n'aura PAS a verifier la signature, seulement la presence :
payer le DER et le JNI pour durcir un controle que le repaquetage retire
serait le mauvais echange.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A, B, C, D, E2, E3 passes, rien a corriger. E1 (autre compte Windows)
reste non joue, faute d'un second compte : note comme tel plutot que
presente comme verifie.
E3 ajoute au plan pendant la campagne — remplacer l'exe d'une
installation existante par une nouvelle version. C'est le cas qu'on vit
reellement a chaque livraison, et une desactivation a la mise a jour ne
se verrait qu'au premier client mecontent.
Deux observations gardees : C3 a ete plus probant que prevu (le port
avait change entre les deux lancements, donc l'origine aussi), et un
port ephemere sans ligne de repli dans le journal n'est pas un defaut
mais un config.txt absent ou port=0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LICENCE-TESTS.md : cinq series. B eprouve ce qui doit tomber (chaine
quelconque, licence tronquee, charge utile trafiquee, autre poste, autre
paire de cles), C le risque symetrique et plus couteux — refuser un
client qui a paye (espaces colles, retour a la ligne, redemarrage
machine). D est dediee a la stabilite de l'empreinte, le defaut deja
rencontre. E delimite la portee de l'activation.
Le plan citait cmd://licence/clear sans moyen de l'appeler : un bouton
"Deactivate (debug)" au bas de Settings -> Licence, revele seulement
quand config.txt existe. Il donne au passage son unique appelant a
ClearLicence, qui etait du code mort.
Le document dit aussi ce qu'aucun essai ne prouve : rapiecer l'exe reste
possible, il n'y a pas de revocation, et le brouillage n'est pas du
chiffrement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La premiere privee a ete supprimee sans copie : plus aucune licence
nouvelle n'etait emettable. Regeneration + reemission, indolore
aujourd'hui parce qu'une seule licence circulait.
licence.ps1 ecrit desormais la cle dans %USERPROFILE%\Documents, hors du
depot. Elle vivait dans tools\, ou elle n'attendait qu'un menage : le
.gitignore la protegeait du depot, rien ne la protegeait de son
proprietaire.
Verifie : la licence de la nouvelle paire passe, celle de la paire
perdue tombe — la regeneration a bien remplace, pas ajoute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le frontend etait lisible dans l'onglet Image de Process Explorer, avec
les commentaires. BuildAssets.ps1 brouille chaque fichier avant brcc32,
PM.EmbeddedAssets le debrouille au moment de servir.
C'est de l'opacite, pas du secret : la cle est dans l'exe. Ca supprime
strings et l'onglet Image ; la memoire, un debogueur et la reponse HTTP
rendent toujours le texte. D'ou XOR plutot qu'AES — a securite egale
(nulle contre qui a l'exe), dix lignes, et aucune force affichee qu'on
n'a pas.
Une magie PMA1 prefixe le clair : sans elle, deux constantes qui derivent
serviraient du binaire au navigateur, ecran blanc sans trace.
Verifie en comparant les flux des deux cotes bloc par bloc, puis par
aller-retour sur app.js, index.html et style.css. Le frontend ne se
trouve plus dans le binaire.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Porteur, courriel, date d'emission et Installation ID, lus dans la charge
utile deja verifiee. Rempli une fois au demarrage sur les deux chemins
(deja licencie, activation reussie) : rien n'y change pendant une
session. Section masquee hors navigateur hote.
Les champs sont echappes avant interpolation dans ExecuteJavaScript bien
qu'ils viennent d'une charge utile signee — signee ne veut pas dire
inoffensive.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BoardUUID s'appuyait sur un COM initialise par quelqu'un d'autre. Appele
apres WebView2 il lisait le vrai UUID de carte mere, appele avant il
echouait et retombait sur MachineGuid : deux empreintes pour une seule
machine, et une licence liee a l'une que l'autre refuse. Il suffisait que
licence/status parte avant l'init du navigateur pour verrouiller
l'utilisateur hors de son coffre.
CoInitializeEx est desormais appele sur place. Pas de CoUninitialize
symetrique : essaye, il rend une violation d'acces — les mandataires WMI
et le cache de type-info de ComObj se liberent plus tard, dans un
appartement qu'on vient de fermer.
Verifie avec un harnais qui n'initialise PAS COM : il rend maintenant la
meme empreinte que l'application.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Une cle flottante etait sortie. Un schema hors ligne n'a pas de
revocation — rien ne peut aller dire a un exe installe qu'une cle ne vaut
plus — donc la seule prise est de cesser d'accepter toute la classe.
MachineMatches exige desormais une empreinte, et -Floating disparait du
script : il ne peut plus fabriquer une cle que l'exe rejetterait.
Effet de bord assume : plus de licence de site, et un poste sans
empreinte exploitable n'est plus licenciable du tout — mieux qu'une
empreinte degeneree qui ouvrirait partout.
Verifie : liee a ce poste acceptee, liee a un autre refusee, les deux
flottantes deja emises refusees.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
C'etait la decision prise, et je l'avais mal lue : "pour le moment 1"
repondait a la question 1, pas "une seule licence".
PM.MachineId melange l'UUID de la carte mere (WMI) et la serie du volume
systeme, puis n'en publie que 16 hexa : l'empreinte se recopie dans un
courriel, l'UUID brut n'a pas a circuler. Un UUID nul ou tout a FF est
rejete — VM et images clonees l'annoncent, et deux postes tomberaient
alors sur la meme empreinte.
Le champ m est compare APRES la signature. m absent = licence flottante,
acceptee : seule la cle privee peut en emettre, et le champ ne peut pas
etre retire sans casser la signature. Cote script -Machine est
obligatoire sauf -Floating explicite.
L'ecran d'activation affiche l'Installation ID : une licence liee ne peut
plus etre emise avant l'installation.
Verifie sur ce poste : liee a lui acceptee, liee a un autre refusee,
flottante acceptee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le cyan de .auth-hint dit "voici l'etat des choses" (session expiree,
coffre verrouille). Un refus n'est pas un etat, c'est un echec : il doit
se distinguer a la couleur avant d'etre lu. Variante .is-error posee sur
le refus, retiree a chaque nouvel essai.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'ecran d'activation devient franchissable. Verifie en executant l'unite
contre la vraie cle : licence emise acceptee et charge utile lue, licence
signee par une AUTRE paire refusee — c'est celle-la qui prouve que la cle
tient reellement la porte — charabia et chaine vide refuses.
La cle privee reste hors du depot (gitignoree) et doit etre deplacee dans
un coffre : qui la detient fabrique des licences.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>