Pregled
Osnova svih adresa je https://foldy.io/audit/; putanje ispod navedene su u odnosu na nju. Server se sastoji od tri dijela:
- glavno mjesto „onym audit“ — stablo potpisanih dokumenata u korijenu i dvije adrese za upis (
orders, responses);
- hub — svako može postati revizor (
hub/api/…), a njegovi dokumenti nalaze se u a/<ime>/;
- Discovery provajder — potpisani katalozi u
discovery/ koje čitaju Onym aplikacije.
Nema naloga, lozinki ni kolačića. Sve što mijenja stanje potpisano je Ed25519 ključem onoga ko djeluje: revizora, naručioca ili subjekta. Server provjerava potpis i čuva bajtove tačno onakve kakvi su potpisani. Dokument ne može falsifikovati, jer ne drži nijedan ključ revizora. Sve što server vraća može se provjeriti bez povjerenja u njega; u pregledaču to radi verify.js.
Formate samih dokumenata (manifest, atestacija, lista statusa, narudžba, ponuda, odgovor, opoziv) određuje profil implementacije. Ova stranica opisuje transport: adrese, tijela zahtjeva, provjere, odgovore i ograničenja. Izvorni kod: github.com/Dubieroj/onym-audit (hub/, server/).
Opšta pravila
Kanonski JSON
Isti kao u Discovery-Static-Ed25519 §3:
- najviši nivo je objekat; ključevi su sortirani po UTF-8 bajtovima na svakoj dubini; zapis je kompaktan, bez razmaka;
- escape sekvencom zapisuju se samo
", \ i U+0000–U+001F (dvoznakovni oblici gdje postoje, inače \u00xx malim slovima);
- brojevi mogu biti samo nenegativni cijeli brojevi do 253−1;
- duplirani ključevi, nevalidan UTF-8 i usamljeni surogati se odbijaju, a ne ispravljaju.
Server kanonizuje sve što prima i čuva kanonske bajtove.
Potpisi, ključevi, sažeci, vrijeme
| Potpis | Ed25519 nad kanonskim bajtovima dokumenta bez polja signature (uklanja se samo na najvišem nivou); vrijednost je standardni base64 s dopunom (padding). Pored svakog potpisanog fajla nalazi se .sig: isti string i prelazak u novi red. |
| Ključ | onym:key:<64 hex znaka malim slovima> — javni Ed25519 ključ. Otisak na stranicama je prvih 8 bajtova SHA-256 ključa, razdvojenih dvotačkom. |
| Sažetak | sha256:<64 hex> od tačnih bajtova fajla. |
| Vrijeme | RFC 3339 u UTC, s tačnošću do sekunde: 2026-09-25T10:00:00Z. Drugi oblici se odbijaju. |
Potpisani zahtjev
Adrese označene sa 🔏 potpis primaju {"request": {…}}. Unutrašnji objekat je potpisan navedenim ključem, sadrži issuedAt koji od sata servera odstupa najviše ±10 minuta i nema suvišnih polja. Tako pregledač dokazuje da posjeduje ključ, bez sesija i lozinki:
import { canonical, parseStrict } from "https://foldy.io/audit/verify.js";
const now = new Date().toISOString().replace(/\.\d+Z$/, "Z");
const req = parseStrict(JSON.stringify({ action: "order-status", orderId, sponsor: key, issuedAt: now }));
const sig = await crypto.subtle.sign("Ed25519", privateKey, new TextEncoder().encode(canonical(req)));
req.set("signature", btoa(String.fromCharCode(...new Uint8Array(sig))));
await fetch("https://foldy.io/audit/hub/api/a/alice/order-status", {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ request: JSON.parse(canonical(req)) }),
});
Odgovori i greške
Odgovori su JSON sa Cache-Control: no-store. Greška huba je {"error": "<tekst>"}; greška glavnog mjesta je {"error": "<kod>", "detail": "<tekst>"}.
| 400 | tijelo se ne može parsirati ili sadrži nepoznato polje |
| 403 | potpis pogrešnog ključa ili zastario issuedAt |
| 404 | nema takvog revizora, atestacije ili otvorenog zahtjeva |
| 409 | sukob s već objavljenim: nepromjenljiv dokument, vraćanje verzije unazad, zauzeto ime |
| 413 | tijelo prelazi ograničenje |
| 415 | fetch: odgovor nije ni JSON ni tekst |
| 422 | dokument nije prošao provjeru; razlog je u tekstu greške |
| 429 | prekoračeno je ograničenje broja zahtjeva |
| 502 | spoljna adresa je nedostupna ili ne odgovara kako treba |
| 503 | red je pun ili je hub zauzet teškim provjerama |
| 507 | prostor revizora na hubu je popunjen (20 MiB) |
Ograničenja
- Tijelo zahtjeva hubu ima najviše 4 MiB; svaki dokument najviše 1 MiB; stablo jednog revizora najviše 20 MiB.
- Brzina: „kanta tokena“ po IP adresi (za IPv6 po cijeloj /64): kapacitet 60, dopuna 1 token u sekundi. Cijena poziva navedena je uz svaku adresu. GET zahtjevi za liste nisu ograničeni.
- „Teški“ pozivi (
digest, conformance) izvršavaju se najviše dva istovremeno na cijelom hubu.
- Glavno mjesto: narudžba najviše 64 KiB, odgovor najviše 16 KiB, ukupno ograničenje od 20 zahtjeva uz dopunu od 1 u sekundi.
Statički fajlovi
Dokumenti se isporučuju onakvi kakvi jesu, bajt po bajt, sa Access-Control-Allow-Origin: *, X-Content-Type-Options: nosniff i Cache-Control: no-cache; bez kolačića i bez dnevnika pristupa. Stranice rade uz CSP script-src 'self'.
Čitanje i provjera
Glavno mjesto (korijen /audit/) i svaki revizor huba (a/<ime>/) imaju isto stablo:
manifest.json + .sig | potpisani manifest revizora: ključ operatora, delegirani ključ statusa, politike, metodologije, ponude |
status.json + .sig | lista statusa, potpisana delegiranim ključem; ponovo se potpisuje svakih 6 sati, nextUpdate je 48 sati unaprijed |
attestations/<id>.json | atestacije, nepromjenljive |
revocations/<id>.json | potpisani opozivi |
responses/<atestacija>/<id>.json | potpisani odgovori subjekata |
offers/<id>.json | ponude: metodologija, cijena, rok, uslovi objave |
orders/<id>.json | narudžbe s tri potpisa, objavljuju se zajedno s atestacijom |
scopes/, reports/, methodology/, policies/ | dokumenti fiksirani sažecima |
Zapis u listi statusa:
{"attestationId": "onym-discovery-2026-09-24",
"attestation": {"uri": "https://foldy.io/audit/attestations/onym-discovery-2026-09-24.json",
"digest": "sha256:…"},
"state": "active" | "revoked" | "superseded" | "expired",
"supersededBy": null, "revocation": null,
"responses": [{"uri": "…", "digest": "…"}]}
Kako provjeriti atestaciju bez povjerenja u bilo šta:
- potpis manifesta odgovara njegovom
operator;
- lista statusa je potpisana ključem
statusKey iz manifesta, auditorKey se poklapa sa operator, a nextUpdate još nije nastupio;
- sažetak fajla atestacije poklapa se sa zapisom u listi, i zapis je u stanju
active;
- atestacija je potpisana ključem operatora i nije istekla; fiksirani dokumenti (obim, metodologija, izvještaj) poklapaju se sa svojim sažecima.
import { verifyAttestation } from "https://foldy.io/audit/verify.js";
const get = (p) => fetch("https://foldy.io/audit/" + p).then((r) => r.text());
const d = await verifyAttestation({
manifestText: await get("manifest.json"),
statusText: await get("status.json"),
attText: await get("attestations/onym-discovery-2026-09-24.json"),
target: null, credited: true,
});
// d.display: "attested" | "revoked" | "superseded" | "expired" | "status-unknown" | …
Stranica svake atestacije (verdict/?id=<id> za glavno mjesto i verdict/?a=<ime>&id=<id> za revizora huba) izvršava ove provjere u pregledaču i prikazuje svaku od njih.
Glavno mjesto „onym audit“
POST orders
Prihvatanje narudžbe (Audit.md §6). Tijelo je ili gola narudžba koju su potpisali subjekt i sponzor, ili omotnica:
{"order": {…}, "scopeText": "šta pregledati", "contact": "bilo koji tekst, neobavezno"}
U omotnici je order.scope.uri = <osnova>scopes/order-<orderId>.md, a SHA-256 od scopeText (1–8192 bajta) jednak je order.scope.digest. Kontakt ima najviše 256 bajtova, bez kontrolnih znakova; ne objavljuje se.
202 {"orderId", "state": "queued-for-review", "digest"} — narudžba je u redu čekanja. To još nije saglasnost revizora.
Greške: 422 order_invalid, 409 order_conflict (pod ovim id-jem je već druga narudžba), 503 inbox_full (1000 narudžbi), 413 oversize, 429 rate_limited.
POST responses
Odgovor subjekta na atestaciju (Audit.md §5.7.5). Tijelo je goli potpisani odgovor, najviše 16 KiB; attestationId i responseId imaju 16–128 znakova iz [A-Za-z0-9_-]. Nakon upisa lista statusa se ponovo potpisuje, a odgovor se pojavljuje pored atestacije.
201 {"uri", "digest"}
Greške: 404 attestation_unknown, 422 subject_response_invalid, 409 response_conflict (odgovori su nepromjenljivi).
Hub: registracija i objavljivanje
Revizor huba drži svoj ključ u pregledaču (kabinet revizora); hub vidi samo potpisane dokumente. Put revizora: claim → potpisivanje manifesta → register → publish atestacija → po potrebi revoke.
POST hub/api/claim 5 tokena
Rezervacija imena i dobijanje delegiranog ključa statusa koji manifest mora navesti.
{"slug": "my-auditor"}
Ime: ^[a-z][a-z0-9-]{2,31}$, ne smije biti među rezervisanim imenima (admin, api, hub, audit, onym, onym-audit, foundation, sobor, root, status, www). Rezervacija traje 30 minuta; ponovljeni poziv u tom roku vraća istu rezervaciju.
200 {"slug", "statusKey", "base", "componentId", "expires"}
Greške: 409 — ime je zauzeto; 422 — ime nije dozvoljeno.
POST hub/api/register 10 tokena
Objavljivanje ili zamjena manifesta zajedno s dokumentima na koje upućuje.
{"slug": "my-auditor",
"manifest": {…potpisani manifest…},
"docs": {"policies/independence.md": "tekst", "offers/manual-review.json": "{…}", …}}
componentId = onym:component:<ime>; statusEndpoint = <osnova>status.json; statusKey je onaj izdat pri claim;
contact je bilo koji tekst od najviše 256 bajtova, bez kontrolnih znakova (na stranicama postaje link samo ako počinje sa mailto: ili https://);
- ponovna registracija moguća je samo istim ključem operatora (inače 403) i ne smije biti starija od trenutne:
validUntil nije raniji, a kad je jednak, bajtovi su isti (inače 409);
- putanje dokumenata:
(policies|methodology|scopes|reports|offers|notes)/<ime>.(md|json); prihvataju se samo dokumenti na koje upućuje manifest;
- svaka referenca u manifestu upućuje ili na fajl ovog revizora ili na zajednički dokument glavnog mjesta, i sažetak se poklapa;
- ponude su potpisane ključem revizora, odnose se na metodologiju iz manifesta, nisu istekle i ne vraćaju objavljenu verziju unazad.
201 {"page", "manifest", "digest"}
POST hub/api/a/{slug}/publish 5 tokena
Objavljivanje atestacije s njenim dokumentima.
{"attestation": {…},
"docs": {"scopes/….md": "…", "reports/….json": "…", "orders/<orderId>.json": "…"}}
- potpisana ključem revizora,
status = statusEndpoint, unsolicited.policy = sažetak objavljene politike;
- id je nov: atestacije su nepromjenljive, a ispravka je nova atestacija koja zamjenjuje staru (supersede);
- metodologija, obim, skala i izvještaj fiksirani su isto kao pri registraciji;
- za
engagement: "commissioned" prilaže se tačno jedna narudžba s tri potpisa (subjekt, sponzor, revizor); orderRef je njen sažetak; subjekt, sponzor, artefakt, metodologija i obim se poklapaju.
201 {"uri", "digest"} — objavljeno.
202 {"uri", "digest", "held": true, "releaseAt"} — neuspjeh po narudžbi s embargom: hub zadržava atestaciju, izvještaj, narudžbu i obim, i objavljuje ih automatski kad embargo istekne.
POST hub/api/a/{slug}/revoke 5 tokena
{"revocation": {…potpisani opoziv…}}
Opoziv se uvijek prihvata, čak i kad je prostor popunjen.
201 {"uri"}
Greške: 404 — nema takve atestacije; 409 — već je opozvana.
POST a/{slug}/responses 5 tokena
Odgovor subjekta na atestaciju revizora huba. Tijelo je goli potpisani odgovor. Najviše 10 odgovora po atestaciji; odgovori su nepromjenljivi (409).
201 {"uri"}
GET hub/api/a/{slug}/export
Lista svih fajlova u stablu revizora, da biste ga prenijeli na svoj server: hub je zamjenjiva podrazumijevana opcija.
200 {"base", "files": ["manifest.json", …]}
POST hub/api/a/{slug}/stellar-link 5 tokena 🔏 ključ revizora
Povežite revizora s nalogom koji već ima na glavnoj Stellar mreži. Prvo vlasnik naloga jednom transakcijom (kabinet revizora je sastavlja; potpisuje je bilo koji novčanik: preko QR koda, SEP-7 linka ili u Stellar Labu) upisuje na taj nalog dva unosa podataka:
onym-audit-auditor | javni ključ revizora, 32 bajta |
onym-audit-proof | potpis ključa revizora, 64 bajta, nad "onym-audit-link-v1\nPublic Global Stellar Network ; September 2015\n" + G adresa |
Saglasnost obje strane vidi se na mreži: nalog je potpisao transakciju, ključ revizora — dokaz. Revizoru ne treba nalog na glavnoj mreži. Zatim:
{"request": {"action": "stellar-link" | "stellar-unlink", "auditor": "onym:component:<ime>",
"account": "G…", "issuedAt": "…", "signature": "…"}}
Hub čita nalog sa horizon.stellar.org i provjerava oba unosa. Ako se slažu, objavljuje sam potpisani zahtjev kao a/<ime>/stellar-link.json; stranica revizora ga isto tako ponovo provjerava u pregledaču.
201 {"account", "uri"}; stellar-unlink → {"unlinked": true} (unose na nalogu uklanja njegov vlasnik).
Greške: 403 — nije potpisano ključem revizora ili zahtjev nije svjež; 422 — unosa nema, navode drugi ključ ili se potpis ne slaže.
Narudžbe
Pregled može naručiti bilo ko, kod bilo kog revizora koji ima ponude. Pregledač pravi ključ za narudžbu i njime potpisuje narudžbu i kao subjekt i kao sponzor. Revizor je supotpisuje kad objavi atestaciju.
POST a/{slug}/orders 10 tokena
{"order": {…}, "scopeText": "šta pregledati", "contact": "neobavezno"}
- narudžbu su potpisali subjekt i sponzor, i ona tačno odgovara ponudi revizora: metodologija, model cijene, uslovi objave;
fee.offerId je ponuda iz manifesta ili rsp-…, odgovor na zahtjev koji je još otvoren (tada je sponzor autor zahtjeva, a subjekt, metodologija i artefakt se poklapaju sa zahtjevom);
scope.uri = <osnova>scopes/order-<orderId>.md, a scopeText (1–8000 bajtova) hešira se u scope.digest;
- kontakt nije obavezan: bilo koji tekst od najviše 256 bajtova; vidi ga samo revizor.
202 {"orderId", "state": "queued-for-review", "digest"}. Zahtjev na koji je odgovoreno tom ponudom se zatvara.
Greške: 409 — narudžba je već izvršena ili je pod ovim id-jem druga narudžba; 503 — u redu je 100 narudžbi.
POST hub/api/a/{slug}/order-status 1 token 🔏 ključ sponzora
{"request": {"action": "order-status", "orderId": "…", "sponsor": "onym:key:…",
"issuedAt": "…", "signature": "…"}}
200 {"queued": bool, "held": bool} — da li narudžba čeka u redu i da li je njen rezultat zadržan pod embargom. To može saznati samo vlasnik ključa narudžbe.
POST hub/api/a/{slug}/inbox 2 tokena 🔏 ključ revizora
{"request": {"action": "list" | "decline", "auditor": "onym:component:<ime>",
"orderId": "…samo za decline", "issuedAt": "…", "signature": "…"}}
list → {"orders": [{"order", "scopeText", "contact", "receivedAt"}], "held": [{"attestationId", "releaseAt"}]}
decline → {"declined": "<orderId>"}
Zahtjevi
Naručilac koji ne zna kome da se obrati objavljuje zahtjev: tačne bajtove, komponentu subjekta, metodologiju i obim, upućen svim revizorima ili jednoj Stellar adresi. Revizori odgovaraju potpisanim ponudama; naručilac bira jednu i naručuje po njoj.
POST hub/api/requests 5 tokena
{"request": {
"requestVersion": 1,
"requestId": "req-<20 znakova [a-z0-9]>",
"to": null | "onym:key:…",
"subject": "onym:component:<ime>",
"methodologyClass": "security-review" | "conformance-run",
"artifact": {"kind": "source" | "build" | "deployment", "source": "…", "revision": "…", "artifactHash": null},
"scopeText": "1–8000 bajtova",
"requester": "onym:key:…",
"issuedAt": "…", "expiresAt": "… (najkasnije za 30 dana)",
"signature": "…ključem requester"
}}
to: null — zahtjev je vidljiv svima; inače samo adresatu. conformance-run se prihvata samo za kind: "deployment".
201 {"requestId", "digest"}
Greške: 409 — ovaj id već postoji; 503 — otvoreno je 500 zahtjeva.
GET hub/api/requests
200 — otvoreni javni zahtjevi, od najnovijih ka najstarijim: [{…zahtjev, "responses": N}].
POST hub/api/requests/for 2 tokena 🔏 ključ adresata
{"request": {"action": "requests-for", "key": "onym:key:…", "issuedAt": "…", "signature": "…"}}
200 — zahtjevi upućeni ovom ključu.
POST hub/api/requests/{id}/responses 5 tokena
Revizor odgovara ponudom.
{"slug": "my-auditor", "offer": {…}}
offerId = rsp-<sufiks requestId>; ponuda je potpisana ključem revizora, a metodologija je ona iz zahtjeva; na adresirani zahtjev odgovara samo adresat; obim je fiksiran; ponuda nije istekla i ne vraća prethodnu unazad.
201 {"offer": "<uri>"}
Greške: 404 — zahtjev je zatvoren ili je istekao.
POST hub/api/requests/{id}/mine 2 tokena 🔏 ključ autora zahtjeva
{"request": {"action": "responses", "requestId": "…", "issuedAt": "…", "signature": "…"}}
200 {"open": bool, "responses": [{"slug", "name", "operator", "fingerprint", "page", "completedOrders", "customers", "offer"}]}
Trezor
POST hub/api/vault 2 tokena 🔏 ključ trezora
Da bi naručilac vidio svoje narudžbe i zahtjeve na bilo kom uređaju, aplikacija čuva njihovu listu šifrovanu (AES-256-GCM, ključ se izvodi iz fraze) pod posebnim ključem za potpis, izvedenim iz iste fraze. Hub čuva šifrovani tekst koji ne može pročitati, pod ključem koji se nigdje drugdje ne pojavljuje.
{"request": {"action": "get" | "put", "key": "onym:key:…", "data": "base64, samo za put",
"issuedAt": "…", "signature": "…"}}
get → {"data", "updatedAt"} ili {"data": null}
put → {"updatedAt"}; podaci najviše 256 KiB; ukupno na hubu najviše 20.000 trezora.
Liste
GET hub/api/auditors
[{"slug", "name", "operator", "fingerprint", "page", "attestations", "offers", "completedOrders", "customers"}]
GET hub/api/seat
Isto za glavno mjesto ("slug": "").
GET hub/api/library
Sve atestacije svih revizora, od najnovijih ka najstarijim. Ovo je indeks, a ne autoritet: pregledač svaki zapis provjerava prema potpisanim fajlovima.
[{"attestationId", "auditor", "auditorSlug", "auditorBase", "fingerprint", "subject", "result", "state", "methodologyClass", "kind", "source", "engagement", "issuedAt", "expiresAt", "orderRef", "uri"}]
curl -s https://foldy.io/audit/hub/api/library | jq '.[0]'
Discovery provajder
Provajder onym:component:onym-audit-discovery objavljuje potpisane kataloge koje Onym aplikacije dodaju u Settings → Discovery Providers. Sve su to statički fajlovi:
discovery/manifest.json + .sig | seat: "discovery", profil static-ed25519-v1, mogućnosti signed-snapshot-v1 i local-filtering-v1 |
discovery/catalogs/onym-auditors.json | revizori (seatTypes: ["audit"]), najviše 512 zapisa |
discovery/catalogs/onym-audited-services.json | Onym servisi koji imaju važeću reviziju, sa statusom |
discovery/policies/*.md, discovery/privacy.md | politike fiksirane sažecima u manifestu |
discovery/status/<komponenta>/ | stranica statusa na koju upućuje zapis kataloga |
Snimci se ponovo potpisuju pri svakoj promjeni na hubu, ali ne češće od jednom u 2 minuta, i isporučuju se sa Cache-Control: no-cache. Polja snimka: {version, catalogId, providerId, implementationProfileId, policyDigest, sequence, previousDigest, generatedAt, expiresAt, entries, signature}. Zapis u onym-audited-services:
{"componentId": "onym:component:onym-relayer", "seatType": "notary",
"operator": "onym:key:…", "manifest": {"uri": "…", "digest": "sha256:…"},
"profiles": […], "relationship": "other-disclosed", "placement": "policy-ranked",
"listedAt": "…", "evidence": [],
"status": {"state": "review" | "warning", "uri": "https://foldy.io/audit/discovery/status/onym-relayer/"}}
Aplikacija prikazuje status kao oznaku pored servisa i kao link u prozoru za saglasnost. Otisak ključa provajdera u aplikaciji je prvih 16 hex znakova ključa operatora: 4074 4514 be2a 1414.
Sidro na Stellar mreži
Registar revizora učvršćuje se na Stellar test mreži: zapis podataka onym-audit-status na nalogu revizora (to je isti ključ kao ključ operatora) čuva SHA-256 kanonskog JSON-a
{"anchorVersion": 1, "auditor": "onym:component:…",
"entries": [{"attestationId", "attestation": "sha256:…", "state", "supersededBy", "revocation"}, …]}
sa zapisima sortiranim po attestationId. Ponovno potpisivanje radi svježine ne mijenja sidro; izdavanje i opoziv ga mijenjaju. Mreža čuva svaki zapis s vremenom ledgera, pa se registar ne može naknadno prepisati niti različitim čitaocima prikazati na različit način. Za provjeru služi stellar.js: anchorState(account, statusText) šalje upit javnom Horizon čvoru i poredi rezultat.