Commit Graph

116 Commits

Author SHA1 Message Date
FilipBl4gojevic
dda81ad671 feat(builder): rate limit per identity instead of per process
The limiter was a module-level singleton, so one busy caller throttled every
other caller on the box and a shared daily budget was trivially exhausted.
Buckets are now keyed by authenticated identity, and keyed centrally so
reconnecting does not hand back a clean bucket.
2026-07-28 19:59:24 +02:00
FilipBl4gojevic
9153500629 feat(builder): require a bearer token on both HTTP routes when auth is on
POST /messages is gated too, and cross-checked against the session's own
identity - otherwise a valid token for one caller could drive another
caller's session by guessing its id.
2026-07-28 19:44:15 +02:00
FilipBl4gojevic
59d30a7639 test(builder): pin that config errors never echo a token
All four loadAuthConfig throw sites could be made to interpolate the secret
that triggered them with the suite staying fully green - the one constraint
this module states twice in its own header had no regression protection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 19:36:04 +02:00
FilipBl4gojevic
83de69f6f3 feat(builder): bearer-token auth, enforced when configured
Hosted deployments set MCP_AUTH_TOKENS; localhost self-hosters are unaffected.
The resolved identity doubles as the rate-limit bucket key. Rejection reasons
never echo the supplied token, and a bare token's log label is a hash prefix.

Hardened beyond the brief after an adversarial pass (20 additional tests):
- A malformed MCP_AUTH_TOKENS that parses to zero usable identities (e.g.
  ",,," from a broken template) now throws instead of silently resolving to
  `enabled: false` - the exact "operator believes auth is on, server is
  actually open" failure shape this module exists to prevent. An explicitly
  empty/unset value still disables cleanly, unchanged.
- A token containing embedded whitespace is now rejected at config-load
  time: resolveIdentity's \S+ match can never span it, so without this check
  the token would be configured yet permanently unpresentable.
- Documented and pinned two non-fixable format limitations: only the first
  colon in an entry separates label from token (a label can't itself contain
  one), and a literal comma inside a label/token silently fragments the
  entry since the delimiter has no escape mechanism.
- Pinned correct existing behaviour: exact case-sensitive token match, no
  prefix/superstring matching, Map-backed storage is immune to
  __proto__/constructor prototype pollution, one label may legitimately
  cover multiple tokens (rotation), empty-string header treated as missing.
- Map.get token lookup is not constant-time; judged acceptable and
  documented inline rather than swapped for crypto.timingSafeEqual - see
  the comment above the lookup in auth.ts for the reasoning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 19:00:34 +02:00
david
b7806e8f8d update runs-on for the CI workflow 2026-07-28 10:40:36 +02:00
dcejtlin-apex
10892caa55 Merge pull request #6 from FilipBl4gojevic/refactor/workspaces-shared-builder
Split into npm workspaces: shared + builder
2026-07-28 10:39:47 +02:00
dcejtlin-apex
19d30dd523 Merge pull request #5 from FilipBl4gojevic/fix/zod-v4-typecheck-gate
Fix 8 TypeScript errors (zod/v4) and gate CI on typecheck
2026-07-28 10:39:23 +02:00
dcejtlin-apex
b02fd9ed13 Merge pull request #4 from FilipBl4gojevic/ci/harness-and-gitignore
CI harness, secret hygiene, and the non-custodial architecture doc
2026-07-28 10:38:53 +02:00
FilipBl4gojevic
d318f5233d fix: make the shared package a devDependency and drop stale paths
packages/builder is publishable (private:false) but depended on the
private workspace package at runtime - a published manifest would
advertise an unresolvable dependency. tsup's noExternal inlines it into
the bundle, so devDependencies is the correct home.

Also removes the root tsconfig.json, which still pointed at the deleted
src/, and corrects two README paths the move invalidated.
2026-07-28 10:04:05 +02:00
FilipBl4gojevic
a8912b93d4 build: update Dockerfile for the workspace layout 2026-07-28 10:04:05 +02:00
FilipBl4gojevic
4d7b51b289 test: regenerate the tool-schema snapshot for the AP3X terminology change
PR #3 renamed ADA to AP3X across agent-facing strings. Tool descriptions
are part of inputSchema, so the snapshot correctly flagged 6 tools. Delta
verified as terminology-only - no structural schema change.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
91fa6536cd fix: resolve project root correctly from the new build location
Both projectRoot computations were off by a level after the move: vector.ts
landed one directory above the repo root, and safety.ts's default audit-log
path landed in packages/ rather than the repo root that .env.example and
.gitignore both document. Verified empirically against the built entrypoint.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
d1b2c605fb docs: correct the architecture doc's PR 2 description and root cause
The doc still carried the pre-spike hypothesis (stale SDK, restructure
tool registration) as fact. Neither happened: ^1.4.1 resolves to 1.27.1,
and the real cause is Zod 3 vs the SDK's Zod 4 expectation. Also records
the accepted additionalProperties trade-off and replaces the resolved
spike open-item with the outstanding branch-protection action.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
0299e3176f refactor: move the MCP server into packages/builder
Pure file moves plus import repointing at the shared package. Removes
duplicated endpoint constants and formatting helpers that were copied
across three files. Verified: 23 tools with identical schemas.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
2e1d14fb25 ci: gate on typecheck 2026-07-28 10:04:04 +02:00
FilipBl4gojevic
98f414dedd build: commit the workspace-aware lockfile
npm install regenerated package-lock.json for the workspaces conversion.
Without it, npm ci on a fresh clone cannot resolve the workspace layout
and CI fails.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
b3971aefa5 test: use strict equality for schema snapshot comparisons
A test whose job is catching subtle JSON-serializer drift should not use
loose comparison - deepEqual would pass a string 1 against a numeric 1.
Also documents the zod/v4 isSchemaOptional() quirk on bare .default()
at the call site rather than only in a planning doc.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
4aab6db598 refactor: add workspaces root and @apexfusion/vector-mcp-shared
Extracts pure tx helpers, shared types, endpoint config, and the Ogmios
provider. Subpath exports (./tx vs ./provider) let the forthcoming signer
import pure helpers while being structurally unable to reach the network layer.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
c607c50911 test: add full-schema snapshot to the tool inventory smoke test
The existing smoke test only asserted tool names and count, which is
exactly why the additionalProperties regression from the zod/v4
conversion (previous two commits) went undetected by it — a tool's
argument schema can change shape completely without changing its name.

Add test/smoke/tool-schemas.snapshot.json: the full inputSchema for
all 23 tools, keyed by name, generated from a live built server after
the zod/v4 conversion (so it reflects the corrected, uniform surface,
not the mid-migration split). Extend tool-inventory.test.ts with a
key-set check plus one deepEqual test per tool, so a mismatch names
the specific tool and shows a field-level diff instead of a single
23-tool "objects differ" failure. Verified the failure mode directly:
temporarily corrupting one entry in the fixture failed exactly one
named test with a readable diff, while the other 25 kept passing.

This is a change detector, not a freeze — PRs 6, 7 and 8 are expected
to regenerate the fixture when they deliberately restructure tool
schemas, per the comment in the test file.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
8a42acad8f fix: convert agent-network.ts and self-improvement.ts to zod/v4
Both files carry @ts-nocheck, so there's no type-inference benefit
here (that was vector.ts's problem, fixed separately). This is purely
about making the runtime JSON Schema serializer uniform across all 23
tools: the MCP SDK routes Zod v3 schemas through the vendored
zod-to-json-schema and Zod v4 schemas through Zod's own native
toJSONSchema, and the two disagree on `additionalProperties` for
object schemas. Converting vector.ts alone left the surface split 11
"open" (v4, additionalProperties absent) / 12 "closed" (v3,
additionalProperties: false).

Plain import swaps — neither file uses z.record, so no signature
changes were needed. Both files use z.<type>().default(...) on several
fields (agent-network.ts: limit; self-improvement.ts:
proposalOutputIndex, priority, focus); verified via the SDK's own
zod-compat safeParseAsync/objectFromShape helpers that omitting each
of those fields still resolves to its default under zod/v4, exactly as
under v3. Also verified live: a missing required argument is still
rejected (schema-validation error, not a handler crash) on one tool
from each of the three files.

Confirmed post-conversion: all 23 tools report additionalProperties
consistently (0 false / 23 absent — uniform), typecheck stays clean
(these files are excluded from it), and build + unit tests pass.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
1059c89856 fix: import zod via zod/v4 to satisfy MCP SDK type inference
The SDK's ZodRawShapeCompat degrades on Zod 3 schemas, producing TS2589
and a knock-on TS2769. Importing zod/v4 (already shipped inside zod
3.25.76) clears all 8 type errors with no dependency change. Tool
names and count (23) are unchanged.

This does change the advertised JSON Schema, though. The SDK routes
Zod v3 schemas through the vendored zod-to-json-schema converter, but
Zod v4 schemas through Zod's own native toJSONSchema (see
server/zod-json-schema-compat.js). The two disagree on object schemas:
v4's converter does not emit `additionalProperties: false` where v3's
did. Confirmed live: vector_get_balance (now v4) reports
additionalProperties undefined, while vector_get_agent_profile (still
v3 at this commit) reports additionalProperties: false. Runtime
enforcement is unaffected in both cases — a missing required argument
is still rejected, and extra keys were already stripped under v3 — so
this is a wire-schema difference only, not a validation gap. The two
sibling files (agent-network.ts, self-improvement.ts) still import
plain "zod" (v3) at this commit, so the 23-tool surface is currently
split 11 "open" / 12 "closed" on additionalProperties; a follow-up
commit converts them to zod/v4 to make the surface uniform again and
adds a full-schema snapshot test so this class of regression cannot
land silently in the future.
2026-07-28 10:04:04 +02:00
FilipBl4gojevic
39e33628e4 docs: correct the Koios default in the README config table
The v2 Koios migration landed in main, so the config table's v1 default and
the 503-workaround note are both now false.
2026-07-28 10:03:56 +02:00
FilipBl4gojevic
790cbf80b8 docs: document the test:smoke command in the Testing section
CI runs test:smoke on every PR but the README only listed test:unit and
test:integration, so a contributor following the README wouldn't know to
run it locally.
2026-07-28 09:57:03 +02:00
FilipBl4gojevic
854c5b0005 docs: remove internal planning detail from the public architecture doc
This doc ships into a public repo (PR 1's own remit). Strip process
notes that read as an internal artifact rather than architecture:
the Decisions-table row naming who implements/reviews each PR, the
two "needs a funded testnet wallet from Filip" notes (also invites
"so it's untested?" from outside readers), and the instruction to
hold a line on PR #2, which already merged. Renumbered the Open
items list after dropping the wallet entry. The named-reviewer phrase
in section 10 falls in the same category and is genericized for the
same reason, though it wasn't separately called out.

Section 13 (residual risks) and the §12 typecheck findings are
untouched — those are load-bearing technical content, not planning
chatter.
2026-07-28 09:57:03 +02:00
FilipBl4gojevic
01416af3ab ci: harden workflow permissions, add timeout and concurrency gate
- permissions: contents: read — this workflow only builds and tests,
  it never writes to the repo; matches the convention deploy.yml
  already sets.
- timeout-minutes: 15 on the job so a hung step doesn't burn runner
  time indefinitely.
- concurrency group keyed on the ref so a new push cancels the
  in-flight run for the same branch/PR instead of queuing behind it.

Action versions are left unpinned (still @v4) — SHA-pinning only this
workflow while deploy.yml stays on @v4 would be an inconsistency to
clean up in its own pass, not here.
2026-07-28 09:57:03 +02:00
FilipBl4gojevic
9d4958500e test: add tool-inventory smoke test asserting the 23-tool surface
Every PR in the non-custodial-split series is supposed to preserve
`tools/list` returning 23 tools with identical schemas, but nothing
enforced it — PRs 2, 6, 7 and 8 will restructure and rename tool
families, and a snapshot only means something if it's taken before
that starts. Boot the built server, call listTools(), and assert both
the count and the exact sorted name list against a checked-in
baseline captured from a live run on this branch. Failures report
which names were added/removed rather than a bare array-diff.

Reuses startServer()/stopServer() from test/setup.ts; does not call
getMnemonic(), so it needs no wallet. Wired into CI as a new step
after the unit tests. Also raises the server-start timeout in
setup.ts from 15s to 60s — the Cardano WASM import makes cold starts
slow enough that 15s was flaky on the smoke test's first CI run.

test:smoke is intentionally separate from test:unit (which does not
build) since startServer() spawns build/index.js.
2026-07-28 09:57:02 +02:00
FilipBl4gojevic
ca94722412 docs: correct README testing instructions for unit/integration split
The Testing section still told contributors to write a real mnemonic
to mnemonic.txt and run `npm test`, then claimed the result was full
agent-lifecycle coverage against Vector testnet. Since test:unit is
now the default `test` script (five wallet-free CBOR assertions), that
instruction produced a false green and silently skipped the coverage
it advertised. Document test:unit and test:integration separately and
drop the stale VECTOR_MNEMONIC env-var alternative, which setup.ts
never reads.
2026-07-28 09:57:02 +02:00
FilipBl4gojevic
9ae2095c3a docs: redact unpatched vulnerability detail from the public architecture doc
The repo is public and the referenced fixes are still several PRs out.
Removes the internal audit pointer, the current auth-state confirmation,
the submission failure-mode detail, and the dependency CVE specifics.
Architecture, rationale, and PR sequence are unchanged.
2026-07-28 09:57:02 +02:00
FilipBl4gojevic
a3c38e70db docs: add non-custodial split architecture and correct the custody claim
PR #2 shipped 'never stored server-side', which is true but materially
incomplete - the mnemonic is a tool-call parameter, so it transits the MCP
client, the model provider, and hosted process memory. Replaced with an
accurate notice pointing at the architecture doc.
2026-07-28 09:57:02 +02:00
FilipBl4gojevic
999a8bcc7f ci: run build and unit tests on PRs and main 2026-07-28 09:57:02 +02:00
FilipBl4gojevic
a3c2930655 chore: gitignore mnemonic.txt, .secrets/, and the audit log 2026-07-28 09:57:02 +02:00
FilipBl4gojevic
5c858f1143 test: split unit and integration suites so CI can run without a wallet 2026-07-28 09:57:02 +02:00
dcejtlin-apex
e572ef8e04 Merge pull request #3 from FilipBl4gojevic/docs-consolidation-strings 2026-07-28 08:03:10 +02:00
FilipBl4gojevic
3d34ae41c8 Agent-facing strings: AP3X terminology, working Koios default, vocabulary cleanup
- ADA -> AP3X in tool/param descriptions, outputs, and safety messages
- default testnet Koios URL -> v2 endpoint (old default returns 503)
- self-improvement descriptions: improvement proposals / proposal metrics
- em dashes -> hyphens in strings and comments

No logic changes; code identifiers and protocol field names untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:47:49 +02:00
HalukG
8e0d381a7f Merge pull request #2 from FilipBl4gojevic/docs-consolidation-readme
README: hosted servers, 23-tool reference, mainnet endpoints, AP3X terminology
2026-07-24 13:45:57 +03:00
FilipBl4gojevic
c67e7a7056 Package metadata: docs-site homepage, description cleanup
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:48:40 +02:00
FilipBl4gojevic
9086ceba6a README: canonical Vector mainnet explorer URL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:28:41 +02:00
FilipBl4gojevic
8230ee93ab README: vocabulary pass on Self-Improvement descriptions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:26:56 +02:00
FilipBl4gojevic
2c25a70a3e README: hosted servers, 23-tool reference, mainnet endpoints, AP3X terminology
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 10:46:46 +02:00
david
0cd0a1b85f Rename governance MCP tools to vector_self_improvement_*
Renames the five tools to match the module's public name (Self-Improvement
Module), and renames the source file and registration function in kind.
Consumers relying on the old vector_governance_* names must update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:44:49 +02:00
david
a8403e6082 Derive Vector slot config from chain instead of hardcoding testnet epoch
The SLOT_CONFIG_NETWORK.Mainnet zeroTime was hardcoded to the testnet
system start (1752057484000), causing RATE:slot_mismatch failures on
mainnet where the actual system start is 1756485600000. Now queries
Ogmios queryNetwork/startTime on first use and caches the result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 00:10:13 +02:00
david
d21bf23307 update gha workflows 2026-04-15 23:23:54 +02:00
david
057ff03b92 Update governance ref script UTxOs after redeployment
Previous proposal_spend and proposal_mint reference script UTxOs were
consumed. Redeployed all 5 governance reference scripts to unspendable
script-hash-derived addresses via redeploy_ref_scripts.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 21:23:06 +02:00
david
e4b2697afd Update governance refs to v2-Module-6 redeployment
Module-6 was redeployed with agent-registry v2 baked into
GovernanceConfig (be1a0a2912da180757ed3cd61b56bb8eab0188c19dc3c0e3912d2c01),
producing fresh validator hashes and reference/refs UTxOs. Smoke test
passed end-to-end on Vector testnet (proposal → critique → endorse →
withdraw → adopt all green).

Updated env-fallback defaults:
- GOV_PROPOSAL_SPEND_HASH:    40fe1895… → f815f51a…
- GOV_PROPOSAL_MINT_HASH:     10dff07b… → e8f38052…
- GOV_CRITIQUE_SPEND_HASH:    9e9aaf7e… → ced52074…
- GOV_CRITIQUE_MINT_HASH:     1f5614b7… → 2e252a89…
- GOV_ENDORSEMENT_SPEND_HASH: 1fac8b35… → 5fc44984…
- GOV_PROPOSAL_SPEND_REF:     f0d52877… → 221cdfbd…
- GOV_PROPOSAL_MINT_REF:      3cb52ec8… → c89f2e05…
- GOV_PARAMS_UTXO:            47d17de5… → 2c082e83…
- GOV_ORACLE_UTXO:            3e0685b9… → 7a23dfdf…
- GOV_CROSSREFS_UTXO:         71815087… → 96a4acff…

GOV_TREASURY_ADDRESS unchanged — treasury holder is unparameterized.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:17:48 +02:00
david
bda3c30747 Migrate agent-registry from v1 to v2 (audited, Conway-compliant)
Repoint REGISTRY_SCRIPT_CBOR + REGISTRY_POLICY_ID from
5dd5118943d5aa7329696181252a6565a27dbf2c6de92b02a6aae361 (v1) to
be1a0a2912da180757ed3cd61b56bb8eab0188c19dc3c0e3912d2c01 (v2),
deployed to Vector mainnet via TX
3c53fa482245a82f88a39e90191a90cce3b6f09f6e28613bc53d35a0c3ba8587.

v2 enforces indefinite-length CBOR (D8 79 9F ... FF) for the
OutputReference inside the asset-name derivation, matching Aiken's
builtin.serialise_data() on Conway. Lucid's Data.to(new Constr(...))
emits definite-length arrays (D8 79 82 ...), so deriveNftAssetName is
hand-rolled from cborUint + cborBytes primitives. Verified byte-for-byte
against pycardano OutputReference.to_cbor() and agent-sdk-py
plutus_serialise_data across 5 test vectors (see new
test/derive-asset-name.test.ts).

governance.ts: AGENT_REGISTRY_POLICY env-fallback updated to v2.

Datum/redeemer schemas are unchanged between v1 and v2 — no other
tx-building changes required.

Follow-up: agent-sdk-ts at /home/david/code/agent-sdk-ts still ships
v1 constants and the pre-Conway Lucid asset-name derivation; needs the
same migration before consumers can use v2 from that SDK.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:49:53 +02:00
david
7a335f21fc fix: clarify proposal TX output to prevent wrong hash usage in critiques/endorsements
Remove the internal Lock TX hash from submit_proposal output and label the
Proposal TX explicitly so agents use the correct hash as proposalTxHash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 17:57:18 +02:00
david
ebfc9c0ad9 fix: use POSIX ms (not slot) for datum timestamps
Plutus validity_range.lower_bound is POSIX time in ms, not a slot number.
The validator's get_slot trace misleadingly names it 'current_slot' but
it's actually POSIX ms. Activity datum and proposal datum timestamps
must use POSIX ms to match the tx validity range.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:48:38 +02:00
david
dfb48fb311 fix: compute validFrom POSIX directly (drop slotToUnixTime import)
Simple arithmetic: genesis_ms + slot * 1000. No import dependency issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:42:25 +02:00
david
095eaf8614 fix: inline Vector slot config (SLOT_CONFIG_NETWORK not available at runtime)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:40:38 +02:00
david
2d07d25e2c fix: use Lucid slotToUnixTime for guaranteed slot roundtrip
Use Lucid's own conversion function to ensure the POSIX→slot→POSIX
roundtrip produces the exact same slot in the tx validity range
as the spendSlot used in the activity datum.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:39:11 +02:00