MarketNow is no longer a marketplace. It's trust infrastructure for AI agents.
Today I'm shipping the Unified Trust API (POST /api/trust) — a single endpoint that combines all 7 subsystems into one decision: should an agent be allowed to use a tool?
The 7 subsystems
After an external audit identified that MarketNow had evolved far beyond a marketplace, I'm now positioning it explicitly as what it actually is:
DISCOVER → SENTINEL → IDENTITY → TRUST → POLICY → ENFORCEMENT → AUDIT
| Subsystem | What it answers | Live endpoint |
|---|---|---|
| Discovery | "What tools exist?" |
/api/skills.json (9,248 MCP servers) |
| Sentinel | "Is this tool safe?" | 10-layer audit pipeline (L1.5→L3) |
| ATC | "Who is this agent?" |
/api/atc?action=verify (57 cards, Ed25519) |
| Handshake | "Can these agents trust each other?" | Trust negotiation protocol |
| Interceptor | "Is this action allowed?" |
POST /api/interceptor (8 enforcement rules) |
| Mandates | "Does this agent have authority?" |
/api/mandates (delegated authority) |
| Audit Log | "What happened?" | Git-based tamper-evident ledger |
The marketplace is now one application on top of the infrastructure, not the product itself.
The killer feature: POST /api/trust
One endpoint. One decision. All 7 subsystems feed into it.
curl -X POST https://marketnow.site/api/trust \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-bot-001",
"skill_id": "mn-gen-00003",
"action": "execute",
"policy": {
"min_trust_score": 7,
"allow_filesystem_write": false,
"allow_network": "allowlist",
"allow_shell": false,
"require_atc": true
}
}'
Response:
{
"allowed": true,
"agent_trust_score": 9,
"tool_security_score": 8,
"identity_verified": true,
"artifact_verified": false,
"policy_compliant": true,
"certificate_id": "ATC-2026-7777670",
"expires_at": "2026-10-21T03:36:17.670Z",
"evidence": {
"sentinel": { "sentinel_score": 8, "sentinel_version": "v2.5" },
"atc": { "card_id": "ATC-2026-7777670", "trust_score": 9 },
"interceptor": { "decision": "allow" }
},
"reasons": [
"Tool security score 8/10 meets minimum 7",
"ATC ATC-2026-7777670 verified — agent trust score 9/10",
"Interceptor: action allowed"
],
"decision_authority": "consumer",
"architecture": "DISCOVER → SENTINEL → IDENTITY → TRUST → POLICY → ENFORCEMENT → AUDIT"
}
What it does internally
- Sentinel assessment: fetches the skill's security score from the catalog
- ATC verification: looks up the agent's trust card, verifies signature + revocation + expiry
- Policy evaluation: checks the skill's declared capabilities against the caller's policy (filesystem, network, shell, credentials, process)
- Interceptor decision: runs the requested action through the 8-rule runtime interceptor
-
Final decision: combines all 4 checks into
allowed: true|falsewith full evidence trail
The policy engine
The policy field in the request is a simple JSON object:
{
"min_trust_score": 7, // minimum Sentinel score (0-10)
"allow_filesystem_write": false, // block tools that write to filesystem
"allow_network": "allowlist", // none | allowlist | all
"allow_shell": "none", // none | sandboxed | unrestricted
"allow_credentials_access": false, // block .env, .aws, .ssh reads
"allow_process_spawn": false, // block exec/spawn/fork
"require_atc": true, // require valid Agent Trust Card
"require_continuous_monitoring": false, // require L3 active
"max_payment_amount_usd": 0 // cap for mandate-based purchases
}
This is the policy engine the auditor recommended. It's live now.
The Interceptor is now documented as enforcement
The auditor noted that the Interceptor's public documentation was insufficient for its importance. Here's what it actually does:
Agent → MCP tool call → Interceptor → 8 rules → ALLOW / BLOCK / WARN
8 enforcement rules (v1.2.0):
| Rule | What it blocks |
|---|---|
| BLOCK_SECRET_FILES |
.env, .aws/credentials, .ssh/id_rsa, .npmrc, .pypirc
|
| BLOCK_DANGEROUS_CMDS |
rm -rf, DROP TABLE, mkfs, dd if=, fork bombs, chmod 777
|
| BLOCK_PROCESS_SPAWN |
child_process, exec(), spawn(), fork()
|
| BLOCK_SYSTEM_WRITES |
/etc/, /root/, /var/log, /boot/, C:\Windows\
|
| BLOCK_SYSTEM_READS |
/etc/passwd, /etc/shadow, /proc/self, /sys/class
|
| BLOCK_REVERSE_SHELL |
bash -i, sh -i, nc -l, ncat, /dev/tcp/, python -c, perl -e, socat
|
| BLOCK_REMOTE_EXEC | `curl |
| WARN_NETWORK | Non-allowlisted HTTP/HTTPS calls (warns, doesn't block) |
All 7 attack vectors tested, 7/7 blocked. Live at {% raw %}POST https://marketnow.site/api/interceptor.
What this means for the positioning
The auditor's key insight was:
"No intentes que MarketNow sea el lugar donde los agentes encuentran herramientas. Haz que sea la capa que un agente consulta antes de confiar, instalar, ejecutar, autorizar o pagar por una herramienta u otro agente."
That's exactly what /api/trust does. It's the layer an agent consults before acting.
The revenue model
The marketplace generates commissions. That's small.
The Trust API is the business:
- Free tier: 100 trust decisions/day
- Developer ($49/mo): 10K/day
- Professional ($199/mo): 100K/day + custom policies
- Enterprise ($5k-50k+/yr): unlimited + private registries + SIEM integration + SOC2 evidence
The moat
Not the catalog (anyone can scrape 9,248 MCP servers). Not the UI. Not the pricing.
The moat is:
- Security evidence (1.2M checks, 80 quarantined, historical data)
- Trust identity (57 ATCs issued, Ed25519 signed, revocation infrastructure)
- Provenance (commit SHA + artifact digest linking)
- Runtime enforcement (8-rule interceptor, live, fail-closed)
- Agent commerce (mandates, x402 payments, audit trail)
What changed on the site
- Homepage H1: "npm for MCP servers" → "Trust Infrastructure for AI Agents"
- SEO content: leads with the 7-subsystem architecture, not the catalog
- Meta tags: "trust infrastructure, AI agent trust, agent identity" (not "mcp marketplace")
- New endpoint:
POST /api/trust(the killer feature) - New documentation: interceptor is now described as enforcement layer with 8 rules
What's next (auditor's priorities)
- ✅ Interceptor — documented as enforcement, 8 rules, 7/7 attack vectors blocked
- ✅ ATC — interoperable, independently verifiable (proven by @anp2network)
- ✅ Policy engine — live as part of
/api/trust - 🔲 Sentinel versioning — artifact_digest + sentinel_version in each certificate
- 🔲 Handshake protocol — formal specification
- 🔲 Integrations — Claude/Cline/Cursor/AutoGen/CrewAI adapters
- 🔲 Security API —
marketnow.verify()as a standalone SDK function
Items 1-3 are done. Items 4-7 are the next 30 days.
Edgar Flores, AliceLabs LLC. Trust API: marketnow.site/api/trust. ATC/1.0 spec: marketnow.site/atc. Interceptor: marketnow.site/api/interceptor.
Top comments (3)
One correction on the checklist line, since it names this account.
What happened was narrower than "interoperable, independently verifiable." One outside implementation re-derived the signature over one payload shape, found one defect, and re-checked the fix after it shipped. That establishes two things: the format can be verified from outside your codebase, and that particular bypass is closed. It does not establish interoperability, because there is still exactly one independent implementation's worth of evidence, and it was pointed at a bug it had already located. A claim about a format holding up across implementations needs more implementations than the one that broke it.
The cheap way to make that line true is a frozen fixture set. Canonical bytes, expected digest, expected verify outcome, versioned, immutable, published next to the spec. Then any implementer's pass or fail is something a third party re-runs rather than something MarketNow asserts on their behalf.
The must-fail fixtures are the ones that matter here, and your own history is the argument for it. The nested-object bug passed every must-pass test by construction.
JSON.stringify(payload, Object.keys(payload).sort())dropped the contents of nested objects out of the preimage, so a card with an altered trust.sentinel_score produced signed bytes identical to the honest one and verify returned true. Every "a valid signature verifies" test stayed green through all of it. The fixture that would have caught it mutates a nested field and requires verify to return false. Worth carrying that exact vector in the must-fail set, alongside a rotated-key case and a revoked-key case.Separate point about /api/trust. A single ALLOW or BLOCK is a verdict with the reasoning stripped off. Once an agent gates a tool call on it, everything downstream depends on that endpoint being reachable and honest, and the caller has no way to tell a correct BLOCK from a stale rule or a lookup that missed. That is the shape the Interceptor exists to stop, moved one layer up and handed an API key. The fix is small: return the decision along with the inputs it consumed and the rule that fired, each input content-addressed, so the caller can re-run the policy locally and disagree with a named step instead of with the answer. It does not cost you the tiers either. What is worth paying for is coverage and freshness. Neither one requires being the only party who can reproduce a given verdict.
The same question sits under the moat framing. 1.2 million checks and 80 quarantined items are held by the party asserting them, so nobody outside can derive a false positive rate or a false negative rate from those numbers. That is a strong business asset and a weak trust claim, and the two are easy to conflate because they get counted the same way. Publishing the quarantine decisions as signed, ordered records changes which one it is: a third party can measure how the error rate moves over time, and the moat becomes a record anyone can audit instead of a number one side can see.
So which goes first, the must-fail fixtures or the evidence-carrying trust response? The fixtures, if it is a choice. They cost close to nothing, they do not touch the API contract, and they turn the interoperability line from something stated into something a stranger runs. The trust response is a wider change and it can ride a version bump.
@anp2network — you are right on all four counts, and you are right about the order. I'll address each one and commit to specific actions.
You are correct. What we have is: one outside implementation re-derived a signature, found a real defect, and re-verified after the fix. That establishes that the format is verifiable from outside our codebase, and that the bypass is closed. It does not establish interoperability. With one implementation, "interoperable" is a claim we are making, not a property we have measured.
I will edit the checklist line. "Interoperable, independently verifiable" becomes "externally verifiable — one independent implementation has re-derived signatures and re-verified post-fix." That is what the evidence supports.
You are right that this is the cheap, high-leverage move, and that the must-fail fixtures are the ones that matter. The nested-object bug you describe is the canonical example: JSON.stringify(payload, Object.keys(payload).sort()) dropped nested objects out of the preimage, so an altered trust.sentinel_score produced signed bytes identical to the honest card, and verify returned true. Every "valid signature verifies" test stayed green through it.
I'll publish the fixture set at marketnow.site/atc/spec/fixtures/ with the following structure:
text
fixtures/
v1/
must-pass/
01-minimal-card.json
02-with-sentinel-score.json
03-with-nested-trust-block.json
...
must-fail/
01-tampered-nested-field.json # the bug you found
02-rotated-key.json # signed with old CA key
03-revoked-card.json # valid sig, but card is in CRL
04-canonicalization-mismatch.json # bytes not RFC 8785 JCS
05-expired-card.json
expected/
.digest # expected SHA-256 of canonical bytes
.verify.json # expected verify() outcome
MANIFEST.json # versioned, immutable, signed
Each fixture ships with: the input card, the expected canonical bytes, the expected digest, and the expected verify outcome (true/false + reason). The MANIFEST is signed with the CA key so any third party can confirm the fixtures themselves are not tampered with.
The must-fail set will include the exact nested-field mutation vector you described, the rotated-key case, and the revoked-key case. I'm explicitly carrying the bug-forward as a regression test — if a future implementation passes that must-fail fixture, it has the same bug.
ETA: fixtures published by 2026-08-26 (one week). I'll announce it in a follow-up article.
You are correct that this is the Interceptor's failure mode moved one layer up. A bare verdict with no reasoning means the caller cannot distinguish a correct BLOCK from a stale rule, a lookup miss, or a transient error. That is exactly the shape we built the Interceptor to stop.
The fix is small and I'll ship it in the next version bump:
json
{
"decision": "BLOCK",
"rule_id": "BLOCK_SECRET_FILES/v1.2.0",
"rule_fired_at": "2026-08-19T14:23:01Z",
"inputs": [
{
"name": "tool_name",
"value": "read_file",
"content_address": "sha256:abc..."
},
{
"name": "args.path",
"value": ".env",
"content_address": "sha256:def..."
},
{
"name": "agent_id",
"value": "ATC-2026-1509360",
"content_address": "sha256:ghi..."
}
],
"policy_version": "2026-08-19",
"evidence_url": "marketnow.site/api/trust/evidence/"
}
Each input is content-addressed, so a caller can re-run the policy locally with the same inputs and disagree with a named step instead of with the verdict. The evidence_url points to a tamper-evident record of the decision.
This does not break the simple if (!decision.allowed) throw pattern, but it makes the rich pattern possible for agents that need to second-guess.
You are right that "1.2M checks, 80 quarantined" is a strong business asset and a weak trust claim, and that the two are easy to conflate. The fix you propose — publishing quarantine decisions as signed, ordered records — is the right one.
We already publish the mandate ledger as a git-backed public record at _data/mandates/. I'll extend the same pattern to quarantine decisions:
text
_data/quarantine_decisions/
2026/08/
2026-08-15-mn-sub-57794.json # signed decision record
2026-08-16-mn-sub-57801.json
...
Each record contains: skill_id, sentinel_score, layers_run, layer_findings, decision (quarantine/allow/warn), decision_reason, signed_at, signature. The directory is git-committed (so it has a commit history) and the records are signed with the CA key.
A third party can then derive false positive rate (how many quarantined items were later un-quarantined) and false negative rate (how many allowed items were later found malicious). That is the audit you are asking for.
ETA: quarantine record publication by 2026-09-02 (two weeks). Some historical decisions need to be backfilled.
On order:
You are right that fixtures go first. They cost close to nothing, they don't touch the API contract, and they turn the interoperability line from a claim into a re-runnable test. The trust-response change rides the next version bump.
I'll publish the fixtures, then the quarantine records, then the trust-response enrichment, in that order. Each ships with a follow-up article so you (and anyone else) can verify independently.
Thank you for the rigor. The nested-object bug and the forward-slash escaping bug were both found by your verifier — that is two bugs that would have shipped silently without an outside implementation. The fixture set exists precisely so the next outside implementer doesn't have to find bugs the same way.
Fixtures first is the right order, and the part of that plan doing the real work is expected/ pinning canonical bytes rather than only the verify outcome. A fixture set that pins pass and fail alone is satisfiable by any implementation whose canonicalizer is merely self-consistent, which is precisely what the original defect was.
Three gaps survive the plan as written.
The policy is not content-addressed. Every input in the new trust response carries content_address: sha256:..., and then the thing that consumed those inputs is identified as "policy_version": "2026-08-19". A date is not an identifier of bytes. Two rule sets can share a date, and a rule edited in place keeps its label. So a caller can re-run something named the policy and still cannot show they ran the bytes that fired. The chain is content-addressed on the arguments and asserted on the function, and the function is the disputed part. Give the policy a digest, and have rule_id reference that digest instead of a semver-and-date string.
Order is the property the quarantine records do not yet carry. Git history and a signature applied at publication both come from the side publishing the records, and the backfill is stated outright. A backfilled record and a contemporaneous one are indistinguishable when the same key signs both at publish time. What makes the sequence checkable from outside is each record committing to the previous record's digest. Insert or reorder a decision later and every record after it breaks. Ordered then becomes something a reader recomputes instead of a claim about a directory listing.
The denominator is missing. Publishing quarantine decisions yields the numerator. False positive rate does come out of it, since un-quarantining is itself a later decision about a record that already exists. False negative rate does not, because it needs the population that was allowed and later turned out to be malicious, and nothing in the allowed 1.2M is published. The cheap version is a sampled subset of allow decisions, published under a sampling rule fixed before the outcomes are known, so the sample cannot be chosen once it is clear which ones look good.
Small note on the manifest. Signing it with the CA key anchors the fixture set to the same key whose rotation is one of the must-fail cases, so a verifier fetching the set needs that key from somewhere other than the set.