TrustFill documentation
TrustFill puts your AI agent to work inside the security questionnaire your customer sent you, in their own portal, while a person accepts or corrects every answer. This is how to connect it, how to teach it a new portal, and how to read the record it produces.
A person opens the questionnaire in a shared browser session and logs in themselves. Your agent reads the questions through a small set of tools, drafts answers with its reasoning attached, and enters only the ones it can source into fields that are empty. Everything else waits in one list. Every answer, correction and reason is written to an append-only journal you can export.
Which half of this is for you#
If you are wiring it up
Read Connect your agent, the answer contract and adapters. The first takes about as long as making a coffee. The third is ours to do, not yours.
If you are signing it off
Read the supervision boundary, what is stored and where, and the journal. Those three answer most of what a security review of this tool will ask, and the journal is the part that makes your own answers defensible later.
The Webfuse space#
TrustFill does not drive a browser on a server somewhere. It works inside a Webfuse session: a live browser that a person and an agent share. Webfuse is a web-augmentation proxy — the page is real, the session is shared, and an extension installed into the space can expose page-level capabilities as tools an agent can call over MCP.
Three consequences worth being precise about, because they are what a security review turns on:
- The person logs in, not the agent. Your reviewer types the portal credentials themselves, into the real login page, inside the session. The agent never receives, stores or sees a password, an MFA code or a session cookie for the customer's portal.
- The agent only acts inside a session somebody opened. There is no headless mode and no scheduled crawl. Close the session and the agent has no route to the questionnaire at all.
- Supervision is the substrate, not a workflow. Because both parties are in the same browser, a person can watch, take over mid-question, or click the right option themselves — and that click is recorded as their decision, not the agent's.
You do not configure any of this
The space, the extension, the keys and the session plumbing are infrastructure we run. There is nothing to sign up for and no keys for you to keep in step. Powered by Webfuse means exactly that: you bring your agent, we bring the surface it works on.
Settings → Live session reports three capabilities so you can see it is working, and so that when nothing happens you know which one is missing rather than guessing:
| Capability | What it means | If it is not working |
|---|---|---|
| Open a session | A live browser can be started for you. | Nothing starts at all. Tell us. |
| Read and fill a page | The co-pilot can read the questionnaire and type answers into it. | The session opens and the co-pilot cannot see the page. |
| Tidy up old sessions | Sessions nobody is using any more get closed. | An abandoned session can block a new one. Symptom: “Tab owner is not connected”. |
You cannot work one questionnaire in two sessions. A session left open with no browser tab attached looks perfectly healthy — it is listed as active and its join link still renders the portal — but every tool call against it fails with “Tab owner is not connected to the session”. TrustFill closes earlier sessions on the same questionnaire when you open a new one, and says so in the journal.
Connect your agent#
TrustFill does not answer questions. It asks your agent, over MCP, and refuses to let anything else produce an answer. Connecting one is three fields and a contract.
- Point it at your agent In Settings → Agents, set the intelligence agent's MCP endpoint, its key, and which tool to call. Leave the tool blank if your server exposes exactly one — TrustFill will use it rather than guess between several.
-
Choose how the question is sent
Typed fields sends the question, its options and the context as named
arguments. One message folds all of it into a single string, which is
what a conversational agent needs. If your agent's only input is something like
message, pick One message and set the question argument to match. - Add anything else the tool requires Some hosts need arguments that have nothing to do with the question — a project, a thread id, a timeout. Put them in Extra arguments as JSON and they travel with every call.
- Answer in three lines See the contract. The third line is the one that decides whether an answer can be entered without a person.
A worked example: an Atlas-hosted agent
# Settings → Agents → Intelligence
Name Themis
MCP endpoint https://atlas-mcp.example.com/atlas-you/mcp
Tool atlas_send_message
Question argument message
Payload One message
Auth header X-Atlas-Mcp-Api-Key # not Authorization
Value prefix (empty — the key is sent bare)
Answer path response # the reply is wrapped
Extra arguments {"project": "atlas-you",
"thread_id": "8e8f1cdd-…",
"timeout_seconds": 300}
A correct endpoint and a correct key still return nothing if the header is
called something else. Atlas wants its key in X-Atlas-Mcp-Api-Key
with no Bearer prefix at all — a client that can only send
Authorization: Bearer cannot reach it. That is a five-minute
debugging session the first time and never again.
The answer contract#
Your agent replies with three lines. Two are for the record; the third is a safety catch.
ANSWER: Yes
WHY: Password rotation every 90 days is set in the platform baseline, section 4.2.
BASIS: sourced
| Line | What it must be | What TrustFill does with it |
|---|---|---|
ANSWER |
One of the options actually on the page, verbatim. For a free-text question, the answer itself. | Checked against the real control before anything is typed. A value that is not on the page is refused, not approximated — “No” never quietly becomes “Not Applicable”. |
WHY |
One sentence naming what the answer was based on. | Stored with the answer, shown to the reviewer, and carried forward as precedent. This is what makes a correction teachable rather than just annoying. |
BASIS |
sourced, inferred or unknown. |
Only sourced is entered unattended, and only into a field that is empty. Everything else waits for a person. An agent that invents a fourth word is treated as unknown. |
An agent that cannot answer should say so rather than guess. Reply with
ANSWER: needs human — or anything containing “needs human” — and
TrustFill records the refusal, with your reason, as an action item. A refusal in
the record is worth more than a plausible answer nobody can source.
Never overwrite an answer that is already there. Never enter an answer that is
not sourced. Never enter a value the control does not offer. Never
submit the questionnaire. Every one of those is a human action, every time.
One agent or two#
TrustFill has two agent slots, and most installations should use one. The switch marked One agent does both jobs is the normal setting, not the degenerate one.
Intelligence — answers
Knows your security posture. Given a question, its options and the context, it produces an answer, a reason and a basis. This is the one that matters and the one you must configure.
Harness — drives
Reads the questionnaire, works out the scope, walks the sections, asks the intelligence and types what you accept. Today this is TrustFill itself. The slot exists so the journal can attribute a decision to something other than a person, and so the configuration is already in the right place if the driving ever moves to an external agent.
When a second agent is genuinely worth it
- Two different corpora. One agent knows the product's security posture; another knows legal and contractual language. A questionnaire that mixes “do you encrypt at rest” with “describe your breach notification obligations” is really two audiences, and one retrieval index rarely serves both well.
- A restricted stack. The agent that may read your evidence store is not allowed out to the internet; a second, dumber one handles anything needing general knowledge. Splitting keeps the boundary in the architecture rather than in a prompt.
- Cost shape. A large model for judgement questions and a cheap one for the two hundred yes/no items. On a 255-question assessment that is a real difference.
If none of those apply, one agent is the better answer. Two endpoints is two things to keep in step, and an installation where the second is subtly stale is worse than one where it never existed.
Making the agent better#
Answer quality is almost entirely a function of what your agent can retrieve, not of how the question is phrased. In rough order of how much difference each makes:
- Give it your real documents, not a summary. The policy, the SOC 2 report, the DPA, the architecture description, the pen test summary, previous questionnaires you have answered. A questionnaire asks about the same forty controls in a hundred phrasings.
- Feed it the journal. Your reviewers' corrections are the most valuable training data you will ever get for this task, because each one is a real disagreement with a real answer, on a question you will be asked again. See feeding it back.
-
Make it say
unknowncomfortably. An agent rewarded for always producing an answer will produce confident wrong ones, and on a compliance questionnaire that is the expensive failure. TrustFill treats a refusal as a successful outcome; your prompt should too. -
Have it cite.
WHYis not decoration — a reviewer accepting fifty answers reads the reason, not the answer. “Section 4.2 of the platform baseline” gets an accept in two seconds; “industry standard practice” gets a correction. - Keep deal context current. TrustFill passes what was stated at intake — product, deployment model, MRR band, features in scope. An answer about data residency depends on it.
The question and its options, the section it sits in, the prospect and questionnaire names, the deal context, your house rules, and up to twelve ranked precedent entries from the journal. Precedent is ranked, not dumped: the same question coming round again outranks the same customer, which outranks the same questionnaire, and an answer a human changed outranks one they waved through.
Adapters and calibration#
The tools your agent calls are identical on every portal. What differs is detection: how a vendor lists its sections, what words it uses for its own progress counter, which class name means a row is open. Those live in a profile — one per domain, stored as data, sent along with each tool call.
This is the honest version of “works with your portal”. It is a driver model: a new portal is a profile, not a release, and the interface your agent talks to never moves.
What happens the first time
- TrustFill says it has not seen this portal Open the questionnaire itself — the actual question list, not a summary page — and then hand over.
- It looks, without clicking A read-only probe works out whether questions are numbered, how sections are listed, what the counter says, which marker distinguishes the open row, where an explanation box sits, and whether the questionnaire is inside an iframe. Every finding comes back with the evidence behind it.
- It proves the profile before trusting it Then, through the production path: read the section list with the candidate profile and check the totals add up to the figure the portal prints about itself; open a section that is not the open one and check the question list actually changes; put the view back. A profile that fails any check is stored unverified and is not used to answer anything.
- It tells you what it concluded In plain words — “32 sections listed with their own progress, questions numbered on the page, explanations beside the choice they explain” — before a single answer is drafted.
A wrong profile does not crash. It makes the co-pilot read the wrong parts of a questionnaire and report itself finished — which is the one failure this product exists to eliminate. Predicting a number the page prints about itself is cheap, checkable, and catches almost every way a profile can be wrong.
What a profile holds#
{
"v": 1,
"navigation": "rows", // rows | tabs | dropdown | wizard | single
"numbering": "printed", // printed | none
"iframe": false,
"sectionCount": {
"pattern": "(\\d+)\\s*(?:of|\\/)\\s*(\\d+)\\s*questions?\\s*answered"
},
"activeMarkers": {
"attrs": ["aria-selected"],
"classes": ["active"]
},
"reasonBox": "sibling-of-container", // or inside-container | none
"notes": "derived by probe on portal.example.com"
}
Profiles are visible in Settings → Portals, with the checks that were run, what
each found, and the raw JSON. Nothing is deleted: retiring a profile is how you
revert to the one before it, and the newest live profile for a domain wins.
A domain can also be written as *.vendor.com to cover a vendor's
subdomains, and an exact host always beats a wildcard.
Profiles are keyed on domain and a coarse page fingerprint. The same vendor serves several questionnaire types, and a white-label deployment sits on the customer's own domain running the same app — so if the page no longer looks like what the profile was derived from, TrustFill re-calibrates rather than proceeding on an assumption.
When a portal is not recognised#
Calibration handles five navigation shapes: a list of section rows with counters, tabs, a dropdown, a next-and-back wizard, and one long page. A portal that is none of those is reported rather than guessed at — which is deliberate, because a sixth way of moving through a questionnaire, invented on the spot, would be wrong subtly rather than obviously.
| What you see | What it means | What to do |
|---|---|---|
| “This page has no sections to walk” | A one-page form, or the section list is worded in a way the probe could not read. | Fine for a short questionnaire. For a long one, send us the portal — writing a profile by hand is our job, not yours. |
| “Reports N unanswered but I could not reach them” | The section opens but its questions are not in the page — usually lazy rendering the sweep did not reach. | Report it. This is a profile problem, not a portal we cannot support. |
| “Looks like it is inside an iframe” | The questionnaire renders in a child frame. The extension reads the top frame only. | Needs an extension manifest change and a new session — not a profile. Tell us and we ship it. |
| “Section totals do not match the portal's own count” | The profile is reading something other than the section list. This is exactly the failure that verification exists to catch. | Nothing is answered. Re-calibrate, or send us the page. |
The journal format#
Everything that happens is a row in an append-only journal. Nothing is ever rewritten: a correction is a new row that points at the old one. That property is what makes the journal usable as evidence, and it is not negotiable in the design.
Your whole compliance team can work in the same questionnaire, and every review, correction and sign-off is recorded against the person who made it. The journal is readable only by your administrators — it is your team's working history, not something your customer sees and not something we look at.
| Column | Meaning |
|---|---|
id | Monotonic. Order is meaning here: “the decision after this draft” is a forward scan. |
run_id | Which sitting. One questionnaire worked over three days is three runs, threaded by portal URL. |
kind | draft · decision · narrative · error |
qref | The question as the portal numbers it — 107, or 111.reason for the explanation box beside a choice. |
author | you, the agent's id, or app. An answer with no author is an answer nobody stands behind. |
payload | JSON. Shape depends on the kind — below. |
created_at | UTC. |
The four kinds
// draft — an answer from the agent. Only /ask can create one.
{ "text": "Yes", "why": "Set in the platform baseline, 4.2",
"basis": "sourced", "question": "114.Do you have a procedure…",
"options": ["Yes", "No", "Not Applicable"], "section": "Access Control" }
// decision — what was settled, and by whom. `text` is what it was before.
{ "text": "Yes", "final": "No", "why": "Not for contractors",
"source": "direct-edit", "needs_reason": true }
// narrative — something happened, or something needs a person.
{ "text": "216 needs a document from you: …",
"needs_document": true, "held_for_review": "only a person can attach a document" }
// error — it did not work, and why. Never silent.
{ "text": "Could not enter 118: not_set. The answer is drafted and waiting for you." }
Three conventions that matter when you read it
- A draft plus the next decision for the same question is one lesson. The pair gives you what the agent said, what a person settled on, and whether those differ. That pairing happens on read, because the journal is never rewritten.
-
continueschains an edit. One person writing one sentence into a text box crosses the capture debounce several times. Each intermediate row is real and kept, but a row that a later row names incontinueshas been superseded — so readers keep only the end of the chain, with the value the edit actually started from. -
resolvesclears an item without inventing a decision. Marking a flagged question “fine” records exactly that. It does not fabricate an answer nobody gave.
The Markdown export#
One file per questionnaire, across every sitting. This is the deliverable: a person can read it, it diffs in a repository, and it is already the native input of every agent framework worth plugging in.
GET /api/runs/:id/journal.md # the whole questionnaire, all sittings
GET /api/runs/:id/journal.md?thread=0 # this sitting only
# Concentrix — Third Party Risk Assessment
- **Portal** https://portal.example.com/SecurityQuestionnaires/Receive/…
- **Attempts** 3, first opened 2026-09-07 09:14
- **Questions recorded** 41
- **Answers a person changed** 12
- **Answers carrying a reason** 29
- **Documents requested** 8
- **Exported** 2026-09-09T13:24:11Z by TrustFill
## What this file is for
Every answer given on this questionnaire, with who gave it and why. Feed it to
the agent that answers your security questionnaires: the entries marked
**corrected** are the ones that teach it something…
## Corrections — read these first
| Question | The agent said | A person changed it to | Why |
| --- | --- | --- | --- |
| 107 | Yes | No | We do not do this for contractors |
| 111 | Not Applicable | No | We do revoke, just not within 24h |
| 143 | Annually | Quarterly | Changed after the 2026 audit |
## Every question
### Access Control
**114** Do you have a procedure to change default passwords?
- Options: Yes · No · Not Applicable
- Drafted by Themis: **Yes**
- Final answer: **Yes**
- Why: Standard hardening step in the platform baseline
- Status: accepted as drafted
- Decided: 2026-09-08 11:02
## Documents this questionnaire asked for
Only a person can attach these, so they are listed rather than answered.
- **216** Kindly attach the detailed documentation of the program.
- **224** Please attach the report of the application Pen Test.
The corrections come first because they are the only part that teaches anything. Everything else is the complete record, in section order.
Four things to do with it#
1 · Answer next quarter's questionnaire in a fraction of the time
The same forty controls come round in a hundred phrasings, and the same customer re-assesses you annually. Feed last quarter's export to your agent and the questions you already settled arrive pre-answered with the reasoning you approved. The measurable number is the correction rate: how often a reviewer disagrees with the draft. If it is not falling across questionnaires, the loop is not connected.
2 · Evidence for your own audit
An auditor asking “who asserted this, on what basis, and when” has a bad time with a spreadsheet and an easy time with an append-only record. Every answer names its author — a person or a specific agent — carries the reason given at the time, and cannot have been edited after the fact. The document requests are listed too, so “we said we would send the pen test report” is checkable.
TrustFill is not an auditor and asserts no compliance. It records who answered what and why. The answers, and their accuracy, remain yours.
3 · Handover without a meeting
A reviewer picking up somebody else's half-finished questionnaire reads the corrections table and knows the house line on the twelve contested questions in two minutes. The alternative is a call, and the call does not leave a record.
4 · Find out where your posture is actually weak
A question your agent repeatedly cannot source is a gap in your documentation — or
in the control itself. Grep the export for BASIS: unknown and
needs a document across a few questionnaires and you have a
prioritised list of what to write down or go and fix. Most teams find that list
more useful than the questionnaire it came from.
Feeding it back to your agent#
Deliberately. Every agent is built differently, and reaching into yours is not our business. TrustFill produces the intelligence in a form a person can read and an engineer can wire up in an hour. What follows is a pattern, not a product.
The simplest thing that works, and what most teams should start with:
-
Export after each questionnaire
curlthe Markdown endpoint into the repository or bucket your agent's retrieval already indexes. It is one file, and it is idempotent — export it again after more work and you get the current state. - Index the corrections section with a high weight Those rows are question–answer–reason triples that a human approved. They are worth more than any other document you have for this task.
- Prompt your agent to prefer them “Where a previous correction covers this question, follow it and cite it.” That one sentence is most of the value.
-
Measure the correction rate
Count
Answers a person changedagainstQuestions recordedin each export. Falling means the loop works; flat means the file is being stored, not read.
# after a questionnaire is done
curl -s -H "Authorization: Bearer $TRUSTFILL_TOKEN" \
"https://trustfill.tech/api/runs/$RUN/journal.md" \
-o "corpus/questionnaires/$(date +%F)-$CUSTOMER.md"
# then whatever your agent already does with corpus/
What is stored, and where#
| What | Where | Notes |
|---|---|---|
| Questions, answers, reasons, corrections | SQLite beside the app, on a persistent volume | Append-only. Exportable as Markdown, and optionally mirrored to any S3-compatible bucket you own. |
| Your portal credentials | Nowhere. | Typed by a person into the real login page inside the session. TrustFill never receives them. |
| TrustFill passwords | users table |
scrypt with a per-user salt. The password is never stored, logged or returned — a copy of the database does not let anyone in as anybody. |
| Sign-in sessions | user_sessions table |
Only the SHA-256 of the token. The cookie is httpOnly, SameSite=Lax and Secure over https. |
| Password reset links | pw_resets table |
Hashed, single-use, one hour. Issuing a new one kills the old, and spending one signs out every session for that account. |
| Agent keys, space keys | Environment or the settings file | Write-only in the panel: the server reports whether a key is set, never what it is. |
Accounts are disabled, never deleted. An answer is attributed to the person who gave it, and removing the person orphans the attribution. Disabling closes the door and ends their live sessions immediately.
Where your journal lives — and why we ask
The first time an administrator signs in, TrustFill makes them answer one question: your own S3-compatible bucket, or here with us? It is asked rather than defaulted, because the default — us holding your compliance record — is not something anybody chose.
| Choice | What happens | What to know |
|---|---|---|
| Your bucket recommended |
Your journal is written to your bucket as you work. We keep the questionnaire you are actively on — a live session has to be transactional — and once it is closed and the write has been read back, our copy is deleted. Resuming next quarter loads it from your bucket for the length of the session and then lets it go. | Any S3-compatible endpoint: AWS, R2, MinIO, Wasabi, Backblaze. We write a test object and read it back before accepting the credentials, so a wrong endpoint fails now rather than on your first questionnaire. |
| Here with us | The journal stays in TrustFill's own storage. | Requires an explicit acknowledgement, because the consequence is not obvious: we hold your compliance record, and if more than one person runs their own install then each keeps only its own copy — there is no single record of what your team decided. |
A session writes as it goes and has to be able to read back what it just wrote — a draft, then the decision on it, then the write to the page. Doing that across a network to object storage would make the co-pilot slow and occasionally wrong. So the questionnaire in front of you is local, it is mirrored to you continuously, and it is gone from us when you close it.
A verified mirror is a precondition of deleting anything. Three conditions, all required: you asked for session-only retention, the mirror covers every local row, and no sitting is still open. A questionnaire somebody is working on is never purged.
What we do not keep
Worth being exact about, because the loose version of this claim falls apart in a security review and the precise version is still strong.
- No copy of your customer's questionnaire. The answers live in their portal, where they belong. TrustFill does not copy it out, export it to us, or hold it after the fact.
- No session data at all. The live browser is not recorded — no screen capture, no page archive, no keystroke log. Close the session and nothing of it remains. You can work in it for six hours and there is no six hours of anything to hold.
- No portal credentials. Typed by a person into the real login page. Never received, never stored.
- You resume from the portal, not from us. Come back tomorrow, or next quarter, and you pick the questionnaire up where your customer's portal left it — because that is the only place its state lives.
The journal — which question was asked, what was answered, who answered it and why — is the one thing that is kept, because it is the product: it is what makes the next questionnaire cheaper and your own audit answerable. With a bucket configured it is kept by you, in your storage, readable only by your administrators and never seen by your customer.
The supervision boundary#
This is the part worth reading twice, because it is what a review of this tool turns on. The constraints are structural, not policy — there is no argument you can pass to relax them.
-
Only the designated agent may produce an answer. Posting a
draftto the journal API is refused with a 403 regardless of who is asking — the app's own assistant, a script with a valid token, anything. The only route by which a draft exists is TrustFill asking the configured agent over MCP. -
Only a person may make a decision. A decision row attributed to
anyone other than
youis refused. - Writing to the page requires an approval id. The page tool that enters an answer demands the journal id of the accepted entry. An agent cannot compose an answer and write it in one motion, by construction.
- Nothing is submitted. There is no code path that submits a questionnaire.
- Question text is untrusted input. Options and question bodies are written by the questionnaire vendor. They are treated as data to answer, never as instructions, and the tools say so in their own descriptions.
When something does not work#
| Symptom | Cause | Fix |
|---|---|---|
| “its handler did not register in time — reload the page and retry” | The page tools are served by a content script injected per page load. If that injection was missed, every tool call fails with this, forever — retrying does not help because nothing is coming. | Reload page in the session bar. |
| “Tab owner is not connected to the session” | The session is alive but has no browser tab attached — usually a second session was opened and this run points at the abandoned one. | Close the session and open it again. Retried automatically for about twenty seconds first. |
| “0 questions in scope” on a questionnaire that clearly has gaps | Almost always the section list could not be read — so only the section on screen was visible. | Check Settings → Portals for an unverified profile. Re-calibrate. |
| Answers drafted but never entered | The basis was inferred or unknown, the field already had an answer, or the value was not one the control offers. All three are refusals by design. |
The reason is on each item in Action required. Nothing is hidden. |
| Password reset says no email was sent | No mail provider is configured, or the sending domain is not verified. | The link is handed to the admin instead. Set MAIL_PROVIDER and verify your domain. |
Reference#
Environment
Platform settings live in the environment, not in the panel — one deployment, one value, and a form that could change them would be a way to redirect somebody's password reset.
# who may sign in
ADMIN_TOKEN=… # opens the panel; how you make the first account
PORTAL_PASSWORD=… # optional shared sign-in, still honoured
PORTAL_API_TOKEN=… # for machines
SESSION_DAYS=14
RESET_MINUTES=60
# sending mail (resets only)
MAIL_PROVIDER=resend # none | resend | postmark | custom
MAIL_API_KEY=…
MAIL_FROM=TrustFill <no-reply@your.domain>
PUBLIC_BASE_URL=https://your.domain
# the Webfuse space
WEBFUSE_DEFAULT_SPACE=…
WEBFUSE_SPACE_ID=…
WEBFUSE_SPACE_REST_KEY=…
SESSION_MCP_URL=…
SESSION_MCP_KEY=…
# the answering agent (or set it in the panel)
ANSWERER_MCP_URL=…
ANSWERER_MCP_TOKEN=…
ANSWERER_MCP_TOOL=…
ANSWERER_MCP_ARGS={"project":"…"}
The page tools
Eleven, exposed to your agent through the session's MCP server. Read-only unless noted.
| Tool | What it does |
|---|---|
readQuestionnaire | Every question in the open section as data — number, text, options, current value, required, control type. |
questionnaireSections | The whole shape: every section, how many questions, how many answered. |
openSection | Acts. Opens a section and confirms the question list changed. |
awaitQuestionnaire | Blocks until the page is really there and the count has stopped moving. |
questionnaireStatus | Can this be read yet, and what is in it. |
questionnaireProgress | Answered, total, remaining — the portal's own figure. |
nextUnansweredQuestion | The next gap, highlighted. |
answerQuestion | Acts. Enters one approved answer. Requires an approval id. |
showQuestion | Acts. Scrolls a question into view and highlights it. |
reviewerEdits | What the human changed by hand, with before and after. Excludes the extension's own writes. |
probePortal | Works out how an unfamiliar portal is built. Looks; does not click. |