Registre sur la chaîne
La chaîne est la couche d'inviolabilité d'Agreely. Elle consigne qu'un consentement a existé, qu'une révocation s'est produite, qu'une identité d'entreprise a été créée ou a fait l'objet d'une rotation, qu'un document de consentement existait, et que le journal d'accès est intègre, d'une manière que personne (y compris Agreely) ne peut réécrire en douce. C'est une preuve, jamais le chemin de lecture.
La chaîne est une preuve, pas le chemin de lecture
La vérification synchrone lit une ligne dans le backend d'Agreely et ne touche jamais à la chaîne, à l'indexeur ni à aucun état d'ancrage. Un octroi ou une révocation fait autorité à l'instant où il est validé dans la base de données; l'ancrage se produit ensuite de façon asynchrone. Ainsi, une révocation refuse dès la toute prochaine vérification, sans aucune attente de la chaîne, et la chaîne donne tout de même à un auditeur une preuve indépendante et à l'épreuve de la falsification de ce qui s'est passé.
Ce qui est ancré
Quatre types de données sont écrits sur Base Sepolia. Chaque charge utile est une
valeur bytes32 opaque -- une racine, un hachage ou un engagement -- jamais des
renseignements personnels, jamais un identifiant ou une chaîne DID citoyen.
- Consentement -- la racine de Merkle d'un consentement signé ainsi que le
consentRefpar cellule couvrant chaque portée de collecte; les révocations font passer une ref deActiveàRevoked. (anchorBatch/revoke) - Registre DID d'entreprise -- une table de correspondance de
domainHash(keccak256(domaine-d-entreprise)) vers la chaîne DID opérationnelle de l'entreprise, l'empreinte de la clé de signature et le CID IPFS du did.json courant. Prend en charge l'enregistrement, la rotation et la révocation. (registerCompanyDid/rotateCompanyDid/revokeCompanyDid, émettantCompanyDidRegistered/CompanyDidRotated/CompanyDidRevoked) - Document de consentement --
keccak256( utf8(cid) )du CID IPFS du document de divulgation publié. Un ancrage à émission seule et sans arête prouvant que le document existait à un instant donné, sans lien vers quelque consentement ou citoyen que ce soit. (anchorIdentityutilisé comme ancrage de document, émettantIdentityAnchored) - Racines d'époque du journal d'accès -- une racine à l'épreuve de la
falsification sur l'époque périodique du journal d'accès, satisfaisant
l'obligation de piste d'audit de la Loi 25. (
AuditRootAnchored)
Le DID citoyen n'est PAS ancré
L'identité du citoyen -- l'identifiant did:agreely:citizen:..., le document DID, les clés publiques -- n'est jamais écrite sur la chaîne. L'identité citoyenne est hors chaîne et pseudonyme par conception. Seuls des hachages et des racines opaques atteignent la chaîne : jamais un identifiant pouvant créer un lien, jamais des renseignements personnels. Le lien du citoyen dans un reçu est sa signature WebAuthn, vérifiable par le résolveur DID citoyen hors chaîne à /did/{did}, et non un enregistrement sur la chaîne. C'est délibéré : inscrire un identifiant citoyen sur la chaîne romprait la non-corrélation et pourrait constituer un traitement interdit de renseignements personnels au sens de la Loi 25.
AgreelyRegistry
AgreelyRegistry est un petit contrat Solidity : une table de correspondance de
clés opaques, une liste d'autorisation onlyRelayer régie par Ownable, et des
événements indexés que l'indexeur hors chaîne décode mot pour mot. Il n'y a aucun
ether, aucun appel externe et aucun delegatecall, de sorte que la réentrance est
structurellement impossible.
Un portefeuille chaud relayeur d'Agreely approvisionné (jamais le citoyen) soumet
les écritures. Chacune ne porte que des clés opaques : une racine, un engagement
ou un bytes32 sans arête, jamais de renseignements personnels, jamais une chaîne
DID.
anchorBatch
function anchorBatch(bytes32 merkleRoot, bytes32[] calldata consentRefs) external onlyRelayer
Ancre un consentement : sa racine de Merkle signée plus les consentRefs par
cellule qu'il couvre. Le merkleRoot est stocké contre chaque ref de sorte qu'un
reçu détenu puisse vérifier l'inclusion d'une cellule sur la chaîne. Une ref déjà
Active est sans effet (aucune écriture d'état, aucun événement), de sorte qu'une
tâche de relayeur re-livrée ne peut ni agiter l'état ni dupliquer un journal. Une
ref Revoked peut être ré-ancrée vers Active, ce qui est le chemin de
ré-octroi. L'argument consentRefs[] existe pour qu'une future fenêtre de
regroupement réservée au relayeur puisse rassembler plusieurs consentements sous
une seule racine sans aucune agitation du contrat.
revoke
function revoke(bytes32 consentRef) external onlyRelayer
Fait passer une ref Active à Revoked. Elle annule la transaction si la ref
n'est pas Active (jamais ancrée, ou déjà révoquée), de sorte qu'une tâche de
révocation re-livrée est interceptée par le contrôle préalable du relayeur plutôt
que d'émettre un second ConsentRevoked parasite.
anchorIdentity
function anchorIdentity(bytes32 commitment) external onlyRelayer
Ancre un engagement bytes32 opaque. Émission seule : aucune écriture dans
la table de correspondance, de sorte que l'ancrage ne renvoie jamais à un
quelconque consentRef. Achemine actuellement deux types d'engagements :
l'ancrage de document de consentement (keccak256( utf8(cid) ) pour le CID IPFS
d'une divulgation publiée), et les engagements opaques issus des événements de
cycle de vie de création ou de rotation de DID citoyen. Aucune de ces écritures
ne porte un identifiant DID, un identifiant client ni aucune donnée pouvant créer
un lien.
registerCompanyDid / rotateCompanyDid / revokeCompanyDid
Ces méthodes constituent le registre DID d'entreprise introduit lors du
redéploiement du 4 juillet 2026. registerCompanyDid stocke la chaîne DID
opérationnelle, l'empreinte de la clé de signature (keyId) et le CID IPFS du
did.json, indexé par domainHash = keccak256(domaine vérifié en minuscules).
rotateCompanyDid met à jour la clé et le CID pour un domaine existant.
revokeCompanyDid marque l'enregistrement d'un domaine comme révoqué.
Un vérificateur dérive le même domainHash depuis le champ alsoKnownAs du
did.json du reçu (le domaine de l'entreprise) et interroge le registre pour
confirmer que la clé, la chaîne DID et le CID du did.json sont durablement sur la
chaîne.
État et événements
enum State { None, Active, Revoked, Superseded }
struct Anchor {
State state;
bytes32 merkleRoot;
uint64 anchoredAt;
uint64 revokedAt;
}
event AnchorBatch(bytes32 indexed merkleRoot, uint256 refCount, uint64 timestamp);
event ConsentAnchored(bytes32 indexed consentRef, bytes32 indexed merkleRoot, uint64 timestamp);
event ConsentRevoked(bytes32 indexed consentRef, uint64 timestamp);
event IdentityAnchored(bytes32 indexed commitment, uint64 timestamp);
event AuditRootAnchored(bytes32 indexed epochRoot, uint64 timestamp);
event CompanyDidRegistered(bytes32 indexed domainHash, string did, bytes32 keyId, string didDocCid, uint64 verifiedAt);
event CompanyDidRotated(bytes32 indexed domainHash, bytes32 newKeyId, string newDidDocCid, uint64 rotatedAt);
event CompanyDidRevoked(bytes32 indexed domainHash, uint64 timestamp);
event RelayerSet(address indexed relayer, bool allowed);
State.None est la valeur par défaut, de sorte qu'une ref inconnue se relit comme
None. Superseded est réservé au remplacement lors d'un ré-ancrage ou d'un
renouvellement. Deux méthodes de lecture ouvertes, registry(consentRef) et
stateOf(consentRef), permettent à tout vérificateur d'interroger directement
l'état d'une ref.
L'indexeur hors chaîne décode l'ensemble complet des événements : AnchorBatch,
ConsentAnchored, ConsentRevoked, IdentityAnchored, AuditRootAnchored,
CompanyDidRegistered, CompanyDidRotated, CompanyDidRevoked et RelayerSet.
Les charges utiles d'ancrage ne portent que des valeurs bytes32 opaques (des
racines ou des engagements), jamais de renseignements personnels.
Modèle DID d'entreprise : trois couches
Plusieurs utilisateurs ont été confondus par le fonctionnement de l'identité d'entreprise. Voici l'explication en langage courant des deux DID d'entreprise et des trois couches vérifiables qui les ancrent.
Les deux DID
Une entreprise dans Agreely possède deux DID a des positions différentes sur le spectre de l'auto-gestion de l'identité.
DID opérationnel (actif aujourd'hui) : did:web:agreely.ca:c:<slug>
C'est le DID qu'Agreely héberge pour le compte de l'entreprise. C'est le id
dans le did.json de l'entreprise, l'issuer sur chaque reçu signé, et la chaîne
DID stockée dans le registre sur la chaîne. Son document DID (le did.json
contenant la clé publique de l'entreprise) est servi à
https://agreely.ca/c/<slug>/did.json. Tout vérificateur le résout par une simple
requête HTTPS GET, sans infrastructure particulière.
DID réservé (futur, pas encore actif) : did:web:<domaine-d-entreprise>
Il s'agit d'un espace réservé pour une future mise à niveau vers l'auto-hébergement.
Dès qu'une entreprise sert son propre did.json à
https://<domaine-d-entreprise>/.well-known/did.json, elle peut prendre l'entière
garde de son identifiant. Aujourd'hui ce DID n'est ni résolu ni utilisé pour
signer. Il n'apparaît que dans le champ alsoKnownAs du did.json opérationnel,
en tant qu'ancre de domaine dont le vérificateur se sert pour dériver le
domainHash. Aucun did.json n'est servi sur le domaine propre de l'entreprise
aujourd'hui. Ne prétendez pas que le modèle auto-hébergé est actif; il est réservé
pour une future mise à niveau de souveraineté.
Le domaine de l'entreprise est l'ancre d'identité dans les deux cas : l'enregistrement
DNS TXT, le domainHash sur la chaîne et le champ alsoKnownAs du did.json font
tous référence au domaine de l'entreprise.
Couche 1 : L'enregistrement DNS TXT (preuve de contrôle du domaine)
L'entreprise publie un enregistrement TXT permanent dans son propre DNS :
Hôte : _agreely.<domaine-d-entreprise>
Valeur : agreely-did=did:web:agreely.ca:c:<slug>; key=<empreinte-sha256>
Cet enregistrement est la déclaration propre de l'entreprise indiquant quel
DID et quelle clé lui appartiennent. Agreely vérifie l'enregistrement avant que
quoi que ce soit n'arrive sur la chaîne : pas d'enregistrement TXT valide, pas
d'inscription sur la chaîne, pas de résolution du document DID. L'empreinte de
clé dans le TXT (key=...) est le même keyId stocké dans le registre sur la
chaîne, de sorte que la déclaration DNS, le document DID et le registre désignent
tous une seule et même clé.
Couche 2 : Le document DID (did.json)
Le did.json est le Document DID W3C contenant la clé publique de signature de
l'entreprise. Agreely l'héberge à https://agreely.ca/c/<slug>/did.json -- c'est
le chemin de résolution did:web. Une copie IPFS est également épinglée via
Lighthouse et son CID est enregistré sur la chaîne.
IPFS est une copie ancrée pour la durabilité et l'intégrité, pas le chemin de
résolution. did:web se résout toujours via HTTPS. L'épinglage IPFS garantit
que le cliché du did.json est récupérable et à l'épreuve de la falsification
indépendamment du serveur d'Agreely, et le CID sur la chaîne permet à un
vérificateur de confirmer que la copie épinglée n'a pas changé depuis
l'enregistrement.
Le champ alsoKnownAs du did.json porte did:web:<domaine-d-entreprise> (le DID
réservé). Un vérificateur utilise le domaine de l'entreprise à l'intérieur de
alsoKnownAs pour dériver le domainHash et confirmer qu'il correspond à
l'enregistrement sur la chaîne. Il s'agit d'un contrôle de liaison de domaine,
pas d'une étape de résolution DID.
Couche 3 : Le registre sur la chaîne
Lorsque l'enregistrement DNS TXT est vérifié, Agreely met en file d'attente une
transaction registerCompanyDid qui stocke :
| Champ | Valeur |
|---|---|
domainHash |
keccak256(domaine-d-entreprise en minuscules) -- la clé de domaine opaque |
did |
La chaîne DID opérationnelle (did:web:agreely.ca:c:<slug>) |
keyId |
Empreinte SHA-256 en bytes32 de la clé de signature (identique au DNS TXT) |
didDocCid |
CID IPFS du cliché du did.json au moment de l'enregistrement |
Le vérificateur dérive le domainHash depuis le domaine alsoKnownAs du reçu et
interroge le registre. Une correspondance prouve que l'entreprise a enregistré
cette clé et ce DID de façon durable sur la chaîne, indépendamment du backend
d'Agreely.
Déploiement
Le registre est en service sur Base Sepolia (chain id 84532) à
0x3532a9DfF3C910152543afCa49De7c66113C5312. Le réseau principal Base (8453)
n'est pas encore déployé.
La chaîne est une preuve, jamais le chemin de lecture, et elle ne conditionne jamais la vérification synchrone.
Ensuite
- Intégrité du document sur IPFS : d'où vient le CID de document ancré, et pourquoi IPFS est une copie ancrée et non le chemin de résolution did:web.
- Vérifier un reçu de bout en bout par rapport à la racine ancrée.
- Documents DID : les formes des documents DID d'entreprise et citoyen qu'un vérificateur résout.