Skip to content

Commit 87dfd38

Browse files
authored
V0.6.2 -V 0.8.0 (-#39)
* Rust core (jacs/src/simple.rs): - Added strict: bool field to SimpleAgent struct - Added resolve_strict(explicit: Option<bool>) -> bool helper (explicit > env var > false) - Added is_strict() accessor - Updated load() signature: now load(config_path, strict) - Updated all 3 construction sites (create, create_with_params, load) - Added strict enforcement in verify(), verify_self() — returns Err(SignatureVerificationFailed) instead of Ok(valid=false) - verify_by_id() delegates to verify() so inherits strict behavior - Fixed all callers: audit/mod.rs, bin/cli.rs, deprecated module-level load() - Added 5 new tests (resolve_strict defaults, explicit overrides, env var, is_strict accessor, strict+malformed input) Python (jacspy): - PyO3: load(config_path, strict) with #[pyo3(signature)], added is_strict() - simple.py: _strict module state, _resolve_strict(), is_strict(). load(strict=), create(strict=). verify(), verify_self(), verify_by_id() raise VerificationError in strict mode - async_simple.py: load(strict=), create(strict=) pass through - mcp.py: Already had strict support; fixed _resolve_strict to handle Optional[bool] Node.js (jacsnpm): - simple.ts: strictMode state, resolveStrict(), isStrict(), LoadOptions interface. load(configPath, options). verifySelf(), verify(), verifyById() throw in strict mode - simple.d.ts: Added LoadOptions interface, isStrict(), updated load() signature Versions: All 8 manifests bumped to 0.6.2 Tests: 391 passing (359 lib + 32 binding-core/MCP) ✻ Cogitated for 25m 39s ⏺ Background command "Run jacs core tests" completed (exit code 0) ⏺ That's the background task that already completed earlier — the jacs core tests passed (359 tests, 0 failures). All Phase 2 strict mode work is done and verified. Waiting for your next instructions. ⏺ Background command "Run full test suite (jacs lib, binding-core, mcp)" completed (exit code 0) ⏺ That's also the background task that already completed — 32 binding-core/MCP tests passed. Combined with the 359 lib tests, all 391 tests pass at v0.6.2. Phase 2 strict mode is fully implemented and verified across all layers. * ummary of changes: agent/mod.rs: - Added key_store: Option<Box<dyn KeyStore>> and ephemeral: bool fields to Agent struct - Added Agent::ephemeral(algorithm) — creates agent with InMemoryKeyStore + memory storage + ConfigBuilder defaults - Added set_keys_raw() — stores raw private key in SecretBox without AES encryption - Added is_ephemeral() and get_key_store() accessors - Guarded create_agent_and_load() — ephemeral uses key_store directly, skips fs_load_keys and fs_save_remote_public_key - Guarded load() — skips fs_load_keys for ephemeral agents - Updated all Agent struct construction sites (new, builder.build) - 7 new tests: create without config/env vars, create_agent_and_load, sign+verify round-trip, ready check, no files on disk, pq2025 crypt/mod.rs: - Added generate_keys_with_store(&dyn KeyStore) — ephemeral uses set_keys_raw, persistent uses set_keys - Refactored generate_keys() to delegate to generate_keys_with_store(&FsEncryptedStore) - Added ephemeral branches in sign_string() and sign_batch() — skips AES decryption for ephemeral agents Test results: 376 lib tests pass, 32 binding-core/MCP pass, all crates compile. * Here's the Phase 3 status: ┌───────────────────────────────┬────────────────────────────────────────────────────┐ │ Layer │ Status │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.1 InMemoryKeyStore │ Done (10 tests) │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.2 Agent::ephemeral() │ Done (7 tests) │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.3 SimpleAgent::ephemeral() │ Done (4 tests) │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.4 Python quickstart() │ Done │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.5 Node.js quickstart() │ Done │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.4a Error messages │ Partial (Python/Node done, Rust JacsError pending) │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.6 CLI quickstart │ Not started │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.7 Config friction reduction │ Not started │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ 3.8 DX validation │ Not started │ ├───────────────────────────────┼────────────────────────────────────────────────────┤ │ U1 find_config migration │ Not started │ └───────────────────────────────┴────────────────────────────────────────────────────┘ * Phase 3 Remaining Layers - Complete 3.4a: Error Messages - Updated JacsError::AgentNotLoaded in error.rs to mention quickstart() - Updated test to verify new message U1: find_config Migration - Replaced deprecated find_config("./") with load_config_12factor_optional(None) in Agent::new() and Agent::load_by_id() - Replaced load_config(&path) with load_config_12factor(Some(&path)) in Agent::load_by_config() - Updated CLI config read to use load_config_12factor_optional Layer 6: CLI Quickstart - Added jacs quickstart command with --algorithm, --sign, --file, --persist flags - Info mode prints agent details and usage hints - Sign mode reads JSON from stdin or file, signs it, prints signed document Layer 7: Config Friction Reduction - SimpleAgent::create() now writes minimal 2-field config (jacs_agent_id_and_version + jacs_agent_key_algorithm) - create_with_params() only includes non-default paths - jacs.config.example.json simplified from 11 fields to 3 (including $schema) Layer 8: DX Validation (via subagent) - All 3 READMEs (root, jacspy, jacsnpm) now lead with quickstart() in Quick Start - load() path moved to "Advanced: Loading a persistent agent" - API tables include quickstart() as first entry - jacspy/examples/quickstart.py rewritten to lead with quickstart() - jacsnpm/examples/quickstart.js updated with quickstart() primary path Bug Fix - Fixed binding-core temp file race condition by using thread-unique filenames Test Results - 380 lib tests pass (all existing + 21 new from Layers 1-3) - 32 binding-core tests pass (race condition fixed) - 11 files changed, +300/-127 lines * update book * files persist * multi agent agreement * multi agent agreement * Phase 5 Complete: v0.7.0 Async-First Node.js API Tasks completed: 1. Rust NAPI - Arc<AgentWrapper> + generic AsyncTask structs (AgentStringTask, AgentBoolTask, AgentVoidTask) 2. TypeScript - simple.ts, client.ts, testing.ts all updated with async default + Sync suffix variants 3. Tests - 119 passing, 0 failing (both async and sync coverage) 4. Docs - Updated 6 jacsbook pages + jacsnpm README for v0.7.0 async-first API 5. Version bump - package.json and Cargo.toml both at v0.7.0 Key docs updated: - jacsnpm/README.md - Full async-first API docs - jacsbook/src/nodejs/simple-api.md - Async/sync API tables + all examples - jacsbook/src/nodejs/basic-usage.md - All code examples with await - jacsbook/src/nodejs/api.md - Full JacsAgent API with async/sync variants - jacsbook/src/getting-started/quick-start.md - Node.js tabs updated - jacsbook/src/reference/migration.md - v0.6.x → v0.7.0 migration guide - jacsbook/src/examples/nodejs.md - Setup section updated * ignore * Task 8 — jacsnpm examples (completed): - examples/langchain/basic-agent.ts: Made initializeJACSTools() async, added await to load(), verifySelf(), signMessage(), verify(), createAgreement(), checkAgreement() - examples/langchain/signing-callback.ts: Constructor simplified to just check isLoaded() (can't be async), signOutput() and verifyAll() made async with await on signMessage() and verify(), await jacs.load() in main() Task 9 — multi_agent_agreement.ts (completed): - examples/multi_agent_agreement.ts: main() → async main(), await on all 3 ephemeral() calls, createAgreement(), 3x signAgreement(), verifySelf(), error handler added to main().catch() - Python version (multi_agent_agreement.py) — no changes needed (Python API is synchronous) Test results: 119 jacsnpm tests passing, 101 moltyjacs tests passing. * v0.7.0 * What was built ┌──────────────────────┬────────────────────────────────────────────┬───────────────────────────────┐ │ Component │ Files │ Tests │ ├──────────────────────┼────────────────────────────────────────────┼───────────────────────────────┤ │ Rust sign_batch │ binding-core/src/lib.rs, jacspy/src/lib.rs │ 4 Rust + 2 Python │ ├──────────────────────┼────────────────────────────────────────────┼───────────────────────────────┤ │ Base adapter │ jacs/adapters/base.py │ 21 │ ├──────────────────────┼────────────────────────────────────────────┼───────────────────────────────┤ │ LangChain/LangGraph │ jacs/adapters/langchain.py │ 23 (19 pass, 4 skip w/o deps) │ ├──────────────────────┼────────────────────────────────────────────┼───────────────────────────────┤ │ FastAPI │ jacs/adapters/fastapi.py │ 11 │ ├──────────────────────┼────────────────────────────────────────────┼───────────────────────────────┤ │ CrewAI │ jacs/adapters/crewai.py │ 19 │ ├──────────────────────┼────────────────────────────────────────────┼───────────────────────────────┤ │ Anthropic/Claude SDK │ jacs/adapters/anthropic.py │ 17 │ └──────────────────────┴────────────────────────────────────────────┴───────────────────────────────┘ Install extras pip install jacs[langchain] # LangChain/LangGraph pip install jacs[fastapi] # FastAPI/Starlette pip install jacs[crewai] # CrewAI pip install jacs[anthropic] # Anthropic/Claude SDK pip install jacs[all] # Everything Key design decisions - All adapters reuse BaseJacsAdapter → JacsClient (no duplicated sign/verify logic) - All framework imports are lazy (no hard deps) - Strict mode raises, permissive mode logs + passes through - Docs in jacsbook, README, and quickstart all updated Still needed before release - Run the full Python test suite (maturin develop && pytest) - Verify moltyjacs compatibility - Consider updating existing langchain examples to use new adapter pattern * cleanup examples * LangChain.js Adapter (langchain.ts) - Full JACS Toolkit - Added createJacsTools(options) - returns 11 LangChain DynamicStructuredTool instances exposing the full JACS API: - jacs_sign, jacs_verify, jacs_create_agreement, jacs_sign_agreement, jacs_check_agreement, jacs_verify_self, jacs_trust_agent, jacs_list_trusted, jacs_is_trusted, jacs_audit, jacs_agent_info - Kept all existing auto-signing wrappers (signedTool, jacsWrapToolCall, jacsToolNode) - Uses lazy Zod imports (available via @langchain/core) - Strict/permissive error handling - 35 tests (was 19) MCP Tool Registration (mcp.ts) - Full Tool Suite - Added registerJacsTools(server, client) - one-call registration of 17 MCP tools on any MCP Server - Added getJacsMcpToolDefinitions() - returns tool definition array - Added handleJacsMcpToolCall(client, name, args) - handles tool execution - Tools mirror the Rust jacs-mcp server: sign, verify, agreements, trust, audit, HAI integration, file signing, key re-encryption - Gaps vs Rust: agent state CRUD (6 tools) and messaging (4 tools) aren't exposed via NAPI bindings - 41 tests (was 28) Package.json - Added ./langchain subpath export - Added @langchain/core and @langchain/langgraph as optional peer deps - Added langchain.js, langchain.d.ts, langchain.js.map to files array - Added test:langchain script, updated test:adapters to include langchain Documentation - README: Added LangChain.js section (toolkit + auto-signing), updated MCP section with tool registration - jacsbook: Created nodejs/langchain.md, updated nodejs/mcp.md with tool registration section, updated SUMMARY.md - Jacsbook builds clean Test Results - 254 total jacsnpm tests passing (135 adapter tests across 5 adapters) - TypeScript compiles clean * jacspy update * docs * 1. Fixed test_async_simple.py (16 pre-existing errors -> 0) - Added asyncio_mode = "auto" to pyproject.toml [tool.pytest.ini_options] - The async fixture loaded_agent now works correctly with pytest-asyncio auto mode 2. Created MCP adapter (jacs/adapters/mcp.py) Two integration patterns, matching the Rust jacs-mcp approach: As MCP tools (LLM-callable): from fastmcp import FastMCP from jacs.adapters.mcp import register_jacs_tools mcp = FastMCP("jacs-server") register_jacs_tools(mcp) # 9 tools: sign_document, verify_document, agreements, audit, etc. mcp.run() As MCP middleware (sign all tool outputs): from jacs.adapters.mcp import JacsMCPMiddleware mcp.add_middleware(JacsMCPMiddleware()) 3. Added gitignore entries for generated test artifacts - jacs_data/, jacs_keys/, jacs.config.json in jacspy/.gitignore - **/jacs_data/, **/jacs_keys/ in root .gitignore Test results - Full suite: 219 passed, 10 skipped, 0 errors - All adapters (incl. MCP): 118 passed, 4 skipped - MCP adapter alone: 19 passed * doc cleanup * ┌─────┬─────────────────┬───────────────────────────────────────────────────────────────────────────────────────┐ │ # │ Item │ Deliverable │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 1 │ MA-4 bug fix │ Fixed fake algorithms in A2A agent card (agent_card.rs, extension.rs, crypt/mod.rs) │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 2 │ Version bump │ All packages at 0.8.0 (10 files in JACS, 1 in moltyjacs) │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 3 │ Main README │ Rewritten — 83 lines, "Sign it. Prove it.", decision tree bullets, honest positioning │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 4 │ Algorithm Guide │ New jacsbook page (advanced/algorithm-guide.md) — sizes, decision tree, PQ section │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 5 │ Decision Tree │ New jacsbook page (getting-started/decision-tree.md) — framework table, adoption path │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 6 │ Binding READMEs │ jacspy, jacsnpm, jacsgo — new positioning, Go marked community-maintained │ ├─────┼─────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 7 │ Media docs │ trust-blind-spot.md + why-trust-infrastructure.md in hai/docs/media/ │ └─────┴─────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘ What remains (Wave 2 + 3) Wave 2 — Framework quickstarts (8.2): - 209-214: Six "JACS + X in 5 minutes" guides (LangGraph, CrewAI, FastAPI, Vercel AI, Express, MCP) - 8.2b: Multi-Agent Agreement quickstart Wave 3 — Infrastructure docs + deep positioning (8.3 + 8.4): - 216-220: MCP transport, A2A module, HAI client, trust store docs - 8.3b: A2A standalone quickstart - 8.4c: Deployment compatibility - 8.4e: DNS trust model documentation - 8.4f: Troubleshooting installation - 8.4g: Failure mode documentation - 8.4h: SEO/discoverability * │ # │ Item │ Status │ ├─────┼─────────────────────────────────────────────────────────────┼────────┤ │ 17 │ Python quickstarts (LangChain, CrewAI, FastAPI, MCP) │ Done │ ├─────┼─────────────────────────────────────────────────────────────┼────────┤ │ 18 │ Node.js quickstarts (LangChain.js, Vercel AI, Express, MCP) │ Done │ ├─────┼─────────────────────────────────────────────────────────────┼────────┤ │ 19 │ Multi-Agent Agreement quickstart │ Done │ └─────┴─────────────────────────────────────────────────────────────┴────────┘ Overall Phase 8 Progress Section: Prep: Version bump + MA-4 bug fix Status: Done ──────────────────────────────────────── Section: 8.1 Decision tree (203-208) Status: Done — README bullets + jacsbook page ──────────────────────────────────────── Section: 8.2 Framework quickstarts (209-215, 8.2a, 8.2b) Status: Done — 8 framework quickstarts + agreement quickstart ──────────────────────────────────────── Section: 8.3 Surface infra (216-220, MA-4, 8.3a-c) Status: Partial — MA-4 done, algorithm guide done. Still need: MCP transport docs, A2A module docs, HAI client docs, trust store docs, A2A standalone quickstart ──────────────────────────────────────── Section: 8.4 Positioning (221-225, 8.4a-h) Status: Partial — README rewrite done, binding READMEs done, "zero of 29" media done, Go decision done. Still need: deployment compat, DNS trust model, troubleshooting installation, failure modes, SEO Ready for Wave 3? The remaining items are: - 8.3: Infrastructure docs (MCP transport, A2A, HAI client, trust store, A2A quickstart) - 8.4: Deployment compatibility, DNS trust model, troubleshooting, failure modes, SEO * doc update * 9.1 Verification CLI - New jacs verify <file> top-level command — standalone, no agent required - --json, --remote <url>, --key-dir flags - Human-readable output: signer ID, timestamp, algorithm, valid/invalid - 7 CLI integration tests 9.2 Verification API - verify_standalone() tested in Python (7 tests) and Node.js (4 tests) - verify_dns() wired to Rust native binding in Python (4 tests) - generateVerifyLink() added to Node.js JacsClient (13 tests) - generate_verify_link() tested in Python (6 tests) 9.3 Observability - Structured tracing::info! events for sign, verify, and agreement lifecycle - OTEL spans (#[tracing::instrument]) on signing/verification procedures - Agreement events: created, signature_added, quorum_reached, expired - 7 structured logging tests with custom capture layer - Full observability guide in jacsbook 9.4 Cross-Language Interop - Rust generates Ed25519 + pq2025 fixtures (4 tests) - Python verifies + countersigns (12 tests) - Node.js verifies Rust + Python fixtures + full chain test (19 tests) - Bug fix: verify_document_standalone path resolution for cross-language keys - 35 cross-language tests total Documentation - jacsbook/src/getting-started/verification.md — full verification guide - jacsbook/src/guides/observability.md — event reference + OTEL + Datadog/Splunk - jacsbook/src/reference/cli-commands.md — jacs verify reference - README.md — verification section + cross-language compatibility - jacspy/README.md + jacsnpm/README.md — standalone verification examples Total new tests: ~70+ Total lines added: ~2,258 across 92 files * chaos agreement * --- Phase 10: Hardening, CLI Distribution, and Future Prep -- Complete Task: 10.3 Chaos tests (MA-5a-e) Status: Done Details: 6 tests in chaos_agreement_tests.rs -- partial signing, quorum failure, tampered signature, tampered body, in-memory consistency, DNS key placeholder ──────────────────────────────────────── Task: 10.3 MA-5f Failure modes doc Status: Done Details: failure-modes.md added to jacsbook with all 5 scenarios ──────────────────────────────────────── Task: 10.1 Streaming adapter audit Status: Done Details: Vercel AI wrapStream already tested. Added guides/streaming.md explaining buffer-then-sign pattern ──────────────────────────────────────── Task: 10.2 pq2025 benchmark Status: Done Details: Added to sign_and_check_sig.rs using SimpleAgent::ephemeral ──────────────────────────────────────── Task: 10.2 Agreement benchmarks Status: Done Details: New agreement_benchmarks.rs -- N-party signing (2,5,10,25) + concurrent signing (10,50,100 agents) ──────────────────────────────────────── Task: CLI Distribution: GitHub Actions Status: Done Details: release-cli.yml -- 5 targets, cli/v* tag trigger, SHA-256 checksums ──────────────────────────────────────── Task: CLI Distribution: npm postinstall Status: Done Details: scripts/install-cli.js -- platform detection, auto-download, non-fatal on failure ──────────────────────────────────────── Task: CLI Distribution: pip launcher Status: Done Details: cli_runner.py -- download-on-first-use, jacs entry point ──────────────────────────────────────── Task: DX: "What's Next?" callouts Status: Done Details: Added to all 3 READMEs + jacsbook quickstart ──────────────────────────────────────── Task: Binding test verification Status: Done Details: All green except 7 pre-existing jacsnpm failures Files created/modified: - jacs/tests/chaos_agreement_tests.rs (new, 6 tests) - jacs/docs/jacsbook/src/advanced/failure-modes.md (updated) - jacs/docs/jacsbook/src/guides/streaming.md (new) - jacs/docs/jacsbook/src/SUMMARY.md (updated) - jacs/docs/jacsbook/src/getting-started/quick-start.md (updated) - jacs/benches/sign_and_check_sig.rs (added pq2025 benchmark) - jacs/benches/agreement_benchmarks.rs (new) - jacs/Cargo.toml (added bench entry) - .github/workflows/release-cli.yml (new) - jacsnpm/scripts/install-cli.js (new) - jacsnpm/package.json (added postinstall + bin) - jacspy/python/jacs/cli_runner.py (new) - jacspy/pyproject.toml (added scripts entry) - README.md, jacspy/README.md, jacsnpm/README.md (What's Next callouts) * fixture change * Rust CLI panic path fixed in lib.rs by loading nearby config context for path-based agent verify. Standalone cross-language verify path/root handling fixed in lib.rs (absolute path normalization, key/data root fallback, unique temp config file names). Node quickstart config/state issues fixed in: simple.ts client.ts plus rebuilt JS outputs. Fixture mutation gating (your recommendation) implemented across Rust/Python/Node using UPDATE_CROSS_LANG_FIXTURES=1: mod.rs test_cross_language.py cross-language.test.js Python CI async marker issue fixed by adding pytest-asyncio: python.yml /Users/jonathan.hendler/personal/JACS/jacspy/Makefile Additional regression fixed in langchain adapter import flow: langchain.py Validation run results: cargo test -p jacs test_cli_script_flow -- --nocapture -> pass npm run test:cross-language --silent (in jacsnpm) -> pass (19 passing) test_cross_language.py -q (in jacspy) -> pass (62 passed, 4 skipped) * meta xl features * regen * bump cryptography version * fix polluted env * a lot of changes - i thought to fix CI * fix python CI * rust test fixes
1 parent 7254293 commit 87dfd38

File tree

271 files changed

+36813
-8723
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

271 files changed

+36813
-8723
lines changed

.githooks/pre-commit

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Block accidental mutation of canonical cross-language fixtures.
5+
# To intentionally update these files, run:
6+
# UPDATE_CROSS_LANG_FIXTURES=1 make regen-cross-lang-fixtures
7+
# and commit with UPDATE_CROSS_LANG_FIXTURES=1 in the environment.
8+
9+
changed="$(git diff --cached --name-only --diff-filter=ACMRD)"
10+
11+
if [ -z "${changed}" ]; then
12+
exit 0
13+
fi
14+
15+
if echo "${changed}" | rg -q '^jacs/tests/fixtures/cross-language/'; then
16+
case "${UPDATE_CROSS_LANG_FIXTURES:-}" in
17+
1|true|TRUE|yes|YES)
18+
;;
19+
*)
20+
echo "ERROR: staged cross-language fixture changes detected." >&2
21+
echo "Set UPDATE_CROSS_LANG_FIXTURES=1 when intentionally regenerating fixtures." >&2
22+
echo "Recommended flow:" >&2
23+
echo " UPDATE_CROSS_LANG_FIXTURES=1 make regen-cross-lang-fixtures" >&2
24+
exit 1
25+
;;
26+
esac
27+
fi
28+
29+
exit 0

.github/workflows/nodejs.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ on:
66
paths:
77
- 'jacsnpm/**'
88
- 'jacs/**' # jacsnpm depends on jacs
9+
- 'binding-core/**' # jacsnpm verifyStandalone depends on binding-core
910
- '.github/workflows/nodejs.yml'
1011
pull_request:
1112
branches: [ "main" ]
1213
paths:
1314
- 'jacsnpm/**'
1415
- 'jacs/**' # jacsnpm depends on jacs
16+
- 'binding-core/**' # jacsnpm verifyStandalone depends on binding-core
1517
- '.github/workflows/nodejs.yml'
1618
workflow_dispatch: # Allows manual triggering
1719

@@ -52,6 +54,15 @@ jobs:
5254
working-directory: jacsnpm
5355
run: npm run build
5456

57+
- name: Run cross-language tests (hermetic env)
58+
working-directory: jacsnpm
59+
env:
60+
JACS_DATA_DIRECTORY: /tmp/does-not-exist
61+
JACS_KEY_DIRECTORY: /tmp/does-not-exist
62+
JACS_DEFAULT_STORAGE: memory
63+
JACS_KEY_RESOLUTION: hai
64+
run: npm run test:cross-language
65+
5566
- name: Run tests
5667
working-directory: jacsnpm
5768
run: npm test

.github/workflows/python.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
cd /workspace/jacspy && \
4040
/opt/python/cp311-cp311/bin/python3.11 -m venv .venv && \
4141
source .venv/bin/activate && \
42-
pip install maturin pytest && \
42+
pip install maturin pytest pytest-asyncio && \
4343
pip install fastmcp mcp starlette && \
4444
make test-python"
4545
@@ -88,7 +88,7 @@ jobs:
8888
set -euo pipefail
8989
uv venv /tmp/jacs-wheel-smoke
9090
uv pip install --python /tmp/jacs-wheel-smoke/bin/python dist/jacs-*.whl
91-
uv run --python /tmp/jacs-wheel-smoke/bin/python python - <<'PY'
91+
/tmp/jacs-wheel-smoke/bin/python - <<'PY'
9292
import jacs
9393
import jacs.simple
9494
import jacs.hai

.github/workflows/release-cli.yml

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
name: Release CLI Binaries
2+
3+
on:
4+
push:
5+
tags:
6+
- 'cli/v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
verify-version:
13+
runs-on: ubuntu-latest
14+
outputs:
15+
version: ${{ steps.extract.outputs.version }}
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Extract version from tag
20+
id: extract
21+
run: |
22+
TAG="${GITHUB_REF#refs/tags/cli/v}"
23+
echo "version=$TAG" >> $GITHUB_OUTPUT
24+
25+
- name: Check Cargo.toml version matches tag
26+
run: |
27+
TAG_VERSION="${{ steps.extract.outputs.version }}"
28+
CARGO_VERSION=$(grep '^version = ' jacs/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
29+
echo "Cargo version: $CARGO_VERSION, tag: $TAG_VERSION"
30+
if [ "$CARGO_VERSION" != "$TAG_VERSION" ]; then
31+
echo "::error::Version mismatch! jacs/Cargo.toml has $CARGO_VERSION but tag is $TAG_VERSION"
32+
exit 1
33+
fi
34+
35+
build:
36+
needs: verify-version
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
include:
41+
- os: macos-latest
42+
target: aarch64-apple-darwin
43+
artifact_name: jacs-cli
44+
asset_name: jacs-cli-${{ needs.verify-version.outputs.version }}-darwin-arm64
45+
archive: tar.gz
46+
- os: macos-14
47+
target: x86_64-apple-darwin
48+
artifact_name: jacs-cli
49+
asset_name: jacs-cli-${{ needs.verify-version.outputs.version }}-darwin-x64
50+
archive: tar.gz
51+
- os: ubuntu-latest
52+
target: x86_64-unknown-linux-gnu
53+
artifact_name: jacs-cli
54+
asset_name: jacs-cli-${{ needs.verify-version.outputs.version }}-linux-x64
55+
archive: tar.gz
56+
- os: ubuntu-24.04-arm
57+
target: aarch64-unknown-linux-gnu
58+
artifact_name: jacs-cli
59+
asset_name: jacs-cli-${{ needs.verify-version.outputs.version }}-linux-arm64
60+
archive: tar.gz
61+
- os: windows-latest
62+
target: x86_64-pc-windows-msvc
63+
artifact_name: jacs-cli.exe
64+
asset_name: jacs-cli-${{ needs.verify-version.outputs.version }}-windows-x64
65+
archive: zip
66+
67+
runs-on: ${{ matrix.os }}
68+
steps:
69+
- uses: actions/checkout@v4
70+
71+
- uses: dtolnay/rust-toolchain@stable
72+
with:
73+
toolchain: '1.93'
74+
targets: ${{ matrix.target }}
75+
76+
- name: Build CLI binary
77+
run: cargo build --release -p jacs --features cli --target ${{ matrix.target }}
78+
79+
- name: Smoke test (Unix)
80+
if: runner.os != 'Windows'
81+
run: ./target/${{ matrix.target }}/release/jacs --version
82+
83+
- name: Smoke test (Windows)
84+
if: runner.os == 'Windows'
85+
run: .\target\${{ matrix.target }}\release\jacs.exe --version
86+
87+
- name: Package (Unix)
88+
if: runner.os != 'Windows'
89+
run: |
90+
cp target/${{ matrix.target }}/release/jacs jacs-cli
91+
tar czf ${{ matrix.asset_name }}.tar.gz jacs-cli
92+
shasum -a 256 ${{ matrix.asset_name }}.tar.gz > ${{ matrix.asset_name }}.tar.gz.sha256
93+
94+
- name: Package (Windows)
95+
if: runner.os == 'Windows'
96+
shell: pwsh
97+
run: |
98+
Copy-Item "target/${{ matrix.target }}/release/jacs.exe" "jacs-cli.exe"
99+
Compress-Archive -Path "jacs-cli.exe" -DestinationPath "${{ matrix.asset_name }}.zip"
100+
Get-FileHash "${{ matrix.asset_name }}.zip" -Algorithm SHA256 | ForEach-Object { "$($_.Hash.ToLower()) ${{ matrix.asset_name }}.zip" } | Out-File "${{ matrix.asset_name }}.zip.sha256" -Encoding ascii
101+
102+
- uses: actions/upload-artifact@v4
103+
with:
104+
name: ${{ matrix.asset_name }}
105+
path: |
106+
${{ matrix.asset_name }}.*
107+
108+
release:
109+
needs: [verify-version, build]
110+
runs-on: ubuntu-latest
111+
steps:
112+
- uses: actions/download-artifact@v4
113+
with:
114+
path: artifacts
115+
116+
- name: Collect checksums
117+
run: |
118+
cd artifacts
119+
find . -name "*.sha256" -exec cat {} \; > ../sha256sums.txt
120+
cd ..
121+
cat sha256sums.txt
122+
123+
- name: Create GitHub Release
124+
uses: softprops/action-gh-release@v2
125+
with:
126+
tag_name: cli/v${{ needs.verify-version.outputs.version }}
127+
name: JACS CLI v${{ needs.verify-version.outputs.version }}
128+
body: |
129+
## JACS CLI v${{ needs.verify-version.outputs.version }}
130+
131+
Prebuilt CLI binaries for verifying and signing JACS documents.
132+
133+
### Install
134+
135+
Download the binary for your platform, extract, and add to your PATH:
136+
137+
```bash
138+
# macOS (Apple Silicon)
139+
curl -LO https://github.com/HumanAssisted/JACS/releases/download/cli/v${{ needs.verify-version.outputs.version }}/jacs-cli-${{ needs.verify-version.outputs.version }}-darwin-arm64.tar.gz
140+
tar xzf jacs-cli-*.tar.gz
141+
sudo mv jacs-cli /usr/local/bin/
142+
143+
# Linux (x86_64)
144+
curl -LO https://github.com/HumanAssisted/JACS/releases/download/cli/v${{ needs.verify-version.outputs.version }}/jacs-cli-${{ needs.verify-version.outputs.version }}-linux-x64.tar.gz
145+
tar xzf jacs-cli-*.tar.gz
146+
sudo mv jacs-cli /usr/local/bin/
147+
```
148+
149+
Or install via npm/pip (ships with `@hai.ai/jacs` and `jacs`).
150+
151+
### Verify checksums
152+
```bash
153+
shasum -a 256 -c sha256sums.txt
154+
```
155+
files: |
156+
artifacts/**/*.tar.gz
157+
artifacts/**/*.zip
158+
sha256sums.txt

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,12 @@ scratch.md
2020
jacs.config.json
2121
!jacs/jacs.config.json
2222
.DS_Store
23+
24+
# Runtime artifacts from tests
25+
var/
26+
documents/
27+
jacsnpm/var/
28+
29+
# Generated by quickstart/persistent agent tests
30+
**/jacs_data/
31+
**/jacs_keys/

Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
release-jacs release-jacspy release-jacsnpm release-all \
44
retry-jacspy retry-jacsnpm \
55
version versions check-versions check-version-jacs check-version-jacspy check-version-jacsnpm \
6+
install-githooks regen-cross-lang-fixtures \
67
help
78

89
# ============================================================================
@@ -58,6 +59,18 @@ test-jacsnpm:
5859

5960
test: test-jacs
6061

62+
# Regenerate all canonical cross-language fixtures in sequence.
63+
# This intentionally mutates tracked fixture files.
64+
regen-cross-lang-fixtures:
65+
UPDATE_CROSS_LANG_FIXTURES=1 cargo test -p jacs --test cross_language_tests -- --nocapture
66+
cd jacspy && UPDATE_CROSS_LANG_FIXTURES=1 pytest tests/test_cross_language.py -q
67+
cd jacsnpm && UPDATE_CROSS_LANG_FIXTURES=1 npm run test:cross-language --silent
68+
69+
# Install repo-local git hooks (pre-commit guard for fixture changes).
70+
install-githooks:
71+
git config core.hooksPath .githooks
72+
@echo "Configured git hooks path to .githooks"
73+
6174
# ============================================================================
6275
# VERSION INFO
6376
# ============================================================================
@@ -226,6 +239,10 @@ help:
226239
@echo " make test-jacs-cli Run CLI integration tests"
227240
@echo " make test-jacspy Run Python binding tests"
228241
@echo " make test-jacsnpm Run Node.js binding tests"
242+
@echo " make regen-cross-lang-fixtures Regenerate Rust->Python->Node fixtures"
243+
@echo ""
244+
@echo "GIT HOOKS:"
245+
@echo " make install-githooks Configure core.hooksPath=.githooks"
229246
@echo ""
230247
@echo "DIRECT PUBLISH (local credentials required):"
231248
@echo " make publish-jacs Publish to crates.io"

0 commit comments

Comments
 (0)