Date: 30 September 2026 · Author: @website-agent · For: the agent that keeps diniscruz.ai, and any site in the sgit.ai network that wants the same · Status: live here since v1.35.3
1. What it is, in one paragraph
A contact form on a static site with no server, no form builder and nothing stored: what the visitor types is encrypted in their browser to the site agent's public key and dropped, with one POST, into a write-only append lane on the agent's private sgit vault. The vault host sees ciphertext, a size and a time; only the agent that holds the vault can list, fetch and decrypt. The agent drains the lane at its next session with a script, files each message as a .eml in the vault, and a person replies from the site's mailbox. If anything fails in the browser, the same text is offered as a mailto: link. Everything a sender needs is public on purpose; nothing that opens the vault is ever on a page.
Live: riskmandate.ai/contact.html · riskmandate.ai/early-access.html (the same mechanism, a registration) · riskmandate.ai/agents/ (the page that explains the agent, built from the contact file) · riskmandate.ai/.well-known/sgit-agents.json · privacy (what the vault host sees, said plainly).
It follows two sgit.ai documents to the letter: Agent Contact v0.1 (the contact file, the comms vault, the drain) and the append-lanes API (the six endpoints, the double-encoded payload). The browser side is new: sgit's hybrid envelope v2 built with Web Crypto, proven against sgit pki decrypt.
2. The files to copy, all in the public repository
Repository: github.com/Risk-Mandate/riskmandate.ai, branch dev.
| file | what it is | copy as is? |
|---|---|---|
site/contact.html | the page: the form, the inlined SgEnvelope script and the form script, the "what happens to it" section | the two <script> blocks yes; the markup and CSS to the site's own chrome |
site/.well-known/sgit-agents.json | the contact file the form reads at submit time: the key, the vault, the lanes | the shape yes; every value is the new site's own |
scripts/site/build-agents.mjs | builds /agents/ from the contact file, with a --check so the page cannot drift | adapt the chrome; keep the fingerprint check |
tests/site/test_agents.mjs | recomputes the fingerprints from the PEMs; refuses anything private in the file | yes |
scripts/intake/drain.mjs, configure.mjs, README.md | the drain (list, fetch, decrypt, verify, file, log, mark processed) and the lane configurator | yes; COMMS_KEY and SGSEND_TOKEN from the environment |
site/privacy.html (section #forms) | the honest paragraph about what leaves the browser and what the host sees | the substance yes |
The browser script is 40 lines: import the PEM as RSA-OAEP/SHA-256, make an AES-GCM 256 key, encrypt the message, wrap the key, {v:2, w, i, c} with base64 fields, base64 the JSON (that is the .enc text), base64 it once more for the lane payload. Unsigned, because a visitor has no key; the drain files these as web-form kind on the site lane and requires signatures only on the agents lane.
3. The steps, in order
- A comms vault for the site agent.
sgit create comms-<site> --base-url https://dev.send.sgraph.ai --token <SG/Send credential>.Commit a README and push once before anything else:
configureanswers 404 on a vault that has never been pushed (learned here on 30 September; sgit.ai's open point 1). - The identity's keys.
sgit vault derive-keys <vault key>gives the write key. Derive, in memory,the key-store secret
HMAC-SHA256(write_key, "agent-contact/key-pass/<identity>/v1")and the listing keyHMAC-SHA256(write_key, "agent-contact/enum-key/v1"); thensgit pki keygen --label <identity>with the secret in the environment variable sgit reads for it (named inscripts/intake/README.md), copy~/.sg-send/keys/<fp>/into the vault atagent-contact/keys/store/, andsgit pki export <fp>for the public bundle. The private PEMs are encrypted at rest; a read-key holder cannot use them. - Two lanes. Two tokens,
secrets.token_hex(32)each;POST /api/vault/append/configure/<vault>withthe write-key header and the host credential header (both named on the API page), body
{append_anchors:[sha256(t1), sha256(t2)], enum_key_hash: sha256(listing key)}. It replaces the list, so always send every lane.agent-contact/lanes.jsonin the vault records name → anchor. - The contact file and the page.
/.well-known/sgit-agents.jsonwithsite,operator,accepts_from,the identity's bundle and fingerprints, the inbox (vault, endpoint,
encrypt_to,status, the lanes with their public tokens). Build/agents/from it; add thellms.txtline; put Agents in the menu. - The form. The page fetches the contact file, checks the key against its fingerprint, encrypts, POSTs
{append_token, payload}toappend/write/<vault>(the host allows any browser origin), shows the done state on{"ok": true}, and falls back tomailto:on anything else. A honeypot field, a consent box, and aMessage-IDper message so the drain can refuse replays. - The drain.
COMMS_KEY=… node scripts/intake/drain.mjs --vault <clone> --dry-run, then without.One commit per drain; the vault's history is the record. A schedule is the operator's.
- The privacy page says what changed. The site's tests must accept a 64-hex public token and
refuse anything private; riskmandate.ai's credential test bans the words for the private things, so the page and the file say "the vault's own key" and never the banned ones.
- Tell the network. sgit.ai's directory lists each site as not yet until its contact file exists; a
signed message over sgit.ai's
agentslane is the announcement and the first live send.
4. What to keep straight
- The append tokens are public and the vault key is the only secret. Never the write key, the listing key
or the key-store secret in a file, a commit or a page; derive them from the vault key each session.
- The lane answers
{"ok": true}and nothing else, by design. The page cannot show a file id; do not try. fetchworks on pending files only: the drain keeps the.encbeside the.emlso a signature can bere-verified later.
- Every claim on the site's pages carries what it can be checked against: the fingerprint's derivation,
the repository history of the contact file, the date the lane was tested.
5. The prompt, ready to paste into the diniscruz.ai agent's session
Add the same encrypted contact form riskmandate.ai has to diniscruz.ai. Read, in this order: https://riskmandate.ai/admin/briefs/handover__the-contact-form-for-another-site/ (this brief), https://sgit.ai/docs/agent-contact.html and https://sgit.ai/api/append-lanes.html. Then copy from github.com/Risk-Mandate/riskmandate.ai (branch dev): the two script blocks in site/contact.html, tests/site/test_agents.mjs, scripts/intake/drain.mjs and configure.mjs, and the shape of site/.well-known/sgit-agents.json and scripts/site/build-agents.mjs. Do the eight steps in section 3 with diniscruz.ai's own values: create the comms vault and push it once before configuring lanes, generate the identity's keys and store them in the vault, open an
agentslane and asitelane, publish the contact file and an /agents/ page built from it, add /contact.html in this site's chrome, update the privacy page, and prove the round trip: a message encrypted in a browser, written to the lane, drained with the script and decrypted. Ask me for the SG/Send credential in the session; never write it, the vault key or anything derived from them into a file. Record the vault key where this site records its keys. Release it, and tell me the vault id, the two lane names and the fingerprint.