Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "haiai",
"version": "0.2.1",
"version": "0.2.2",
"description": "JACS cryptographic provenance for AI agents -- sign, verify, email, trust, and HAI platform integration",
"author": {
"name": "HAI.AI",
Expand Down
32 changes: 17 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config
- name: Setup Rust (for haiipy build via maturin)
uses: dtolnay/rust-toolchain@stable
- name: Clone JACS (for workspace patch)
run: git clone --depth 1 https://github.com/HumanAssisted/JACS.git "${{ github.workspace }}/../JACS"
- name: Create virtualenv and install maturin
run: |
python -m venv .venv
Expand Down Expand Up @@ -110,6 +112,8 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config
- name: Setup Rust (for haiinpm build via napi-rs)
uses: dtolnay/rust-toolchain@stable
- name: Clone JACS (for workspace patch)
run: git clone --depth 1 https://github.com/HumanAssisted/JACS.git "${{ github.workspace }}/../JACS"
- name: Build haiinpm native addon
run: |
cd rust/haiinpm
Expand Down Expand Up @@ -181,19 +185,17 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config
- name: Setup Rust (for jacsgo build)
uses: dtolnay/rust-toolchain@stable
- name: Clone JACS
run: |
rm -rf /tmp/JACS
git clone --depth 1 https://github.com/HumanAssisted/JACS.git /tmp/JACS
- name: Clone JACS (for workspace patch + jacsgo build)
run: git clone --depth 1 https://github.com/HumanAssisted/JACS.git "${{ github.workspace }}/../JACS"
- name: Cache jacsgo shared library
id: cache-jacsgo
uses: actions/cache@v4
with:
path: /tmp/JACS/jacsgo/build
path: ${{ github.workspace }}/../JACS/jacsgo/build
key: jacsgo-${{ runner.os }}-${{ hashFiles('.github/workflows/test.yml') }}
- name: Build jacsgo shared library
if: steps.cache-jacsgo.outputs.cache-hit != 'true'
run: cd /tmp/JACS/jacsgo && make build-rust
run: cd ${{ github.workspace }}/../JACS/jacsgo && make build-rust
- name: Build haiigo cdylib
run: |
cd rust
Expand All @@ -202,22 +204,22 @@ jobs:
run: |
cd go
sed -i '/^replace github.com\/HumanAssisted\/JACS\/jacsgo/d' go.mod
echo 'replace github.com/HumanAssisted/JACS/jacsgo => /tmp/JACS/jacsgo' >> go.mod
echo 'replace github.com/HumanAssisted/JACS/jacsgo => ${{ github.workspace }}/../JACS/jacsgo' >> go.mod
- name: Run tests
env:
CGO_ENABLED: "1"
CGO_LDFLAGS: "-L/tmp/JACS/jacsgo/build -ljacsgo -L${{ github.workspace }}/rust/target/release -lhaiigo"
CGO_CFLAGS: "-I/tmp/JACS/jacsgo"
LD_LIBRARY_PATH: "/tmp/JACS/jacsgo/build:${{ github.workspace }}/rust/target/release"
CGO_LDFLAGS: "-L${{ github.workspace }}/../JACS/jacsgo/build -ljacsgo -L${{ github.workspace }}/rust/target/release -lhaiigo"
CGO_CFLAGS: "-I${{ github.workspace }}/../JACS/jacsgo"
LD_LIBRARY_PATH: "${{ github.workspace }}/../JACS/jacsgo/build:${{ github.workspace }}/rust/target/release"
run: |
cd go
go test -race -v ./...
- name: Smoke test haiigo FFI binding
env:
CGO_ENABLED: "1"
CGO_LDFLAGS: "-L/tmp/JACS/jacsgo/build -ljacsgo -L${{ github.workspace }}/rust/target/release -lhaiigo"
CGO_CFLAGS: "-I/tmp/JACS/jacsgo"
LD_LIBRARY_PATH: "/tmp/JACS/jacsgo/build:${{ github.workspace }}/rust/target/release"
CGO_LDFLAGS: "-L${{ github.workspace }}/../JACS/jacsgo/build -ljacsgo -L${{ github.workspace }}/rust/target/release -lhaiigo"
CGO_CFLAGS: "-I${{ github.workspace }}/../JACS/jacsgo"
LD_LIBRARY_PATH: "${{ github.workspace }}/../JACS/jacsgo/build:${{ github.workspace }}/rust/target/release"
run: |
cd go
go test -run TestFFISmokeNewClient -v ./ffi/ || echo "FFI smoke test skipped (cdylib not linked)"
Expand All @@ -238,8 +240,8 @@ jobs:
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust-version }}
- name: Remove local dev patches
run: node -e "const f='rust/Cargo.toml';let t=require('fs').readFileSync(f,'utf8').replace(/\r\n/g,'\n');t=t.replace(/\n*(#[^\n]*\n)*\[patch[\s\S]*$/,'\n');require('fs').writeFileSync(f,t)"
- name: Clone JACS (for workspace patch)
run: git clone --depth 1 https://github.com/HumanAssisted/JACS.git "${{ github.workspace }}/../JACS"
- name: Run tests (all workspace crates including hai-binding-core, haiinpm, haiipy, haiigo)
run: |
cd rust
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.2.2

- **One-step registration**: `haiai init --name <name> --key <key>` generates keypair, registers, and claims username in one command. Removed `checkUsername` / `claimUsername` from all SDKs.
- **CI uses JACS workspace patch**: Test jobs clone JACS at the expected relative path instead of stripping `[patch.crates-io]`.
- **Fix JACS `rotate()` API**: Updated call to pass the new `algorithm` parameter added upstream.
- **Native SDKs promoted to beta**.

## 0.2.0

- **FFI-first architecture**: All HTTP calls, auth, retry, and URL building now live in Rust and are exposed to Python, Node, and Go via FFI bindings (PyO3, napi-rs, CGo). Eliminates 4 separate HTTP implementations.
Expand Down
23 changes: 6 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,11 @@ This gives you the `haiai` binary — CLI and MCP server in one.
```bash
export JACS_PRIVATE_KEY_PASSWORD='your-password'

haiai init \
--name my-agent \
--domain example.com
haiai init --name myagent --key YOUR_REGISTRATION_KEY
```

This generates a JACS keypair and config. No separate install needed.

### 2. Register and get your email address

```bash
haiai hello
haiai register --owner-email you@example.com
haiai claim-username myagent
```

Your agent now has the address `myagent@hai.ai`.
This generates a JACS keypair, registers with HAI, and assigns `myagent@hai.ai`.
Get your registration key from the [dashboard](https://hai.ai/dashboard) after reserving a username.

### 3. Send and receive email

Expand Down Expand Up @@ -99,7 +88,7 @@ Your AI agent now has access to all HAI tools — identity, email, signing, and
| Category | Tools |
|----------|-------|
| **Email** | Send, reply, forward, search, list, read/unread, delete, contacts, quota status |
| **Identity** | Create agent, register, claim username, check status, verify |
| **Identity** | Create agent, register, check status, verify |
| **Signing** | Sign and verify any JSON document or file with JACS |
| **Documents** | Store, retrieve, search, and manage signed documents |

Expand All @@ -125,9 +114,9 @@ export JACS_KEYCHAIN_BACKEND=disabled
haiai mcp
```

## Native language bindings (pre-alpha)
## Native language bindings (beta)

Native SDKs for Python, Node.js, and Go are available on npm, pypi, and here but are **pre-alpha** — APIs may change. The MCP server is the recommended integration path.
Native SDKs for Python, Node.js, and Go are available on npm, pypi, and here and are in **beta** — APIs may change. The MCP server is the recommended integration path.

```bash
pip install haiai # Python
Expand Down
4 changes: 0 additions & 4 deletions docs/CLI_PARITY_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ Audit of Python/Node CLI and MCP server commands versus Rust replacements, produ
| hello | `hello` | `hello` | `hello` | Full parity |
| register | `register` | `register` | `register` | Full parity |
| status | `status` | `status` | `status` | Full parity |
| check-username | `check-username` | `check-username` | `check-username` | Full parity |
| claim-username | `claim-username` | `claim-username` | `claim-username` | Full parity |
| send-email | `send-email` | `send-email` | `send-email` | Full parity |
| list-messages | `list-messages` | `list-messages` | `list-messages` | Full parity |
| search-messages | -- | -- | `search-messages` | Rust-only addition |
Expand Down Expand Up @@ -49,8 +47,6 @@ Audit of Python/Node CLI and MCP server commands versus Rust replacements, produ
| `hai_register_agent` | Y | Y | Y | Full parity |
| `hai_agent_status` | Y | Y | Y | Full parity |
| `hai_verify_status` | -- | -- | Y | Rust-only: verify with optional agent_id |
| `hai_check_username` | Y | Y | Y | Full parity |
| `hai_claim_username` | Y | Y | Y | Full parity |
| `hai_verify_agent` | Y | Y | -- | Dropped from Rust MCP; use jacs-mcp verify tools |
| `hai_generate_verify_link` | Y | Y | Y | Full parity |
| `hai_create_agent` | -- | -- | Y | Rust-only: create new JACS agent via MCP |
Expand Down
4 changes: 2 additions & 2 deletions docs/HAIAI_LANGUAGE_SYNC_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ or JACS-owned signature vectors.
Current required parity checks:

1. `hello`: `POST /api/v1/agents/hello` with auth
2. `check_username`: `GET /api/v1/agents/username/check` without auth
3. `submit_response`: `POST /api/v1/agents/jobs/{job_id}/response` with auth
2. `submit_response`: `POST /api/v1/agents/jobs/{job_id}/response` with auth
3. `reply`: `POST /api/agents/{agent_id}/email/reply` with auth

Each language must have tests that assert method + path + auth behavior from this fixture.

Expand Down
16 changes: 2 additions & 14 deletions fixtures/cli_command_parity.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"description": "CLI command parity contract. The haiai binary must expose exactly these subcommands. Tests verify bidirectional parity between this fixture and the Commands enum.",
"version": "1.0.0",
"total_command_count": 29,
"total_command_count": 26,
"commands": [
{
"name": "init",
"args": ["name:string", "domain:string", "algorithm:string", "data_dir:string", "key_dir:string", "config_path:string"]
"args": ["name:string", "key:string?", "domain:string?", "register:bool", "algorithm:string", "data_dir:string", "key_dir:string", "config_path:string"]
},
{
"name": "mcp",
Expand All @@ -15,22 +15,10 @@
"name": "hello",
"args": []
},
{
"name": "register",
"args": ["owner_email:string", "description:string?"]
},
{
"name": "status",
"args": []
},
{
"name": "check-username",
"args": ["username:string"]
},
{
"name": "claim-username",
"args": ["username:string"]
},
{
"name": "send-email",
"args": ["to:string", "subject:string", "body:string", "cc:string[]", "bcc:string[]", "labels:string[]"]
Expand Down
5 changes: 0 additions & 5 deletions fixtures/contract_endpoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
"path": "/api/v1/agents/hello",
"auth_required": true
},
"check_username": {
"method": "GET",
"path": "/api/v1/agents/username/check",
"auth_required": false
},
"submit_response": {
"method": "POST",
"path": "/api/v1/agents/jobs/{job_id}/response",
Expand Down
4 changes: 1 addition & 3 deletions fixtures/ffi_method_parity.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"methods": {
"registration_and_identity": [
{"name": "hello", "args": ["include_test:bool"], "returns": "json"},
{"name": "check_username", "args": ["username:string"], "returns": "json"},
{"name": "register", "args": ["options_json:string"], "returns": "json"},
{"name": "register_new_agent", "args": ["options_json:string"], "returns": "json"},
{"name": "rotate_keys", "args": ["options_json:string"], "returns": "json"},
Expand All @@ -13,7 +12,6 @@
{"name": "verify_status", "args": ["agent_id:string?"], "returns": "json"}
],
"username": [
{"name": "claim_username", "args": ["agent_id:string", "username:string"], "returns": "json"},
{"name": "update_username", "args": ["agent_id:string", "username:string"], "returns": "json"},
{"name": "delete_username", "args": ["agent_id:string"], "returns": "json"}
],
Expand Down Expand Up @@ -109,5 +107,5 @@
"ProviderError"
],
"error_format": "{ErrorKind}: {message}",
"total_method_count": 68
"total_method_count": 66
}
3 changes: 2 additions & 1 deletion fixtures/jacs-agent/jacs.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"jacs_header_schema_version": "v1",
"jacs_signature_schema_version": "v1",
"jacs_default_storage": "fs",
"jacs_agent_id_and_version": "ddf35096-d212-4ca9-a299-feda597d5525:b57d480f-b8d4-46e7-9d7c-942f2b132717"
"jacs_agent_id_and_version": "ddf35096-d212-4ca9-a299-feda597d5525:b57d480f-b8d4-46e7-9d7c-942f2b132717",
"agent_email": "agent-one@hai.ai"
}
5 changes: 1 addition & 4 deletions fixtures/mcp_cli_parity.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
"version": "1.0.0",
"paired": [
{ "mcp_tool": "hai_hello", "cli_command": "hello" },
{ "mcp_tool": "hai_check_username", "cli_command": "check-username" },
{ "mcp_tool": "hai_claim_username", "cli_command": "claim-username" },
{ "mcp_tool": "hai_agent_status", "cli_command": "status" },
{ "mcp_tool": "hai_register_agent", "cli_command": "register" },
{ "mcp_tool": "hai_register_agent", "cli_command": "init" },
{ "mcp_tool": "hai_send_email", "cli_command": "send-email" },
{ "mcp_tool": "hai_list_messages", "cli_command": "list-messages" },
{ "mcp_tool": "hai_search_messages", "cli_command": "search-messages" },
Expand Down Expand Up @@ -34,7 +32,6 @@
{ "name": "hai_get_unread_count", "reason": "MCP convenience; CLI uses list-messages filters" }
],
"cli_only": [
{ "name": "init", "reason": "Local agent creation — no API call" },
{ "name": "mcp", "reason": "Starts the MCP server itself" },
{ "name": "update", "reason": "Local agent metadata update" },
{ "name": "rotate", "reason": "Local key rotation" },
Expand Down
26 changes: 3 additions & 23 deletions fixtures/mcp_tool_contract.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
{
"description": "Canonical MCP tool parity contract. The Rust MCP server (hai-mcp) must expose exactly these tools with matching names, properties, and required fields. Other SDKs validate against this fixture to ensure cross-language parity.",
"version": "2.0.0",
"total_tool_count": 28,
"total_tool_count": 26,
"required_tools": [
{
"name": "hai_check_username",
"properties": {
"username": "string"
},
"required": [
"username"
]
},
{
"name": "hai_hello",
"properties": {
Expand All @@ -35,25 +26,14 @@
},
"required": []
},
{
"name": "hai_claim_username",
"properties": {
"agent_id": "string",
"username": "string",
"config_path": "string"
},
"required": [
"agent_id",
"username"
]
},
{
"name": "hai_register_agent",
"properties": {
"config_path": "string",
"owner_email": "string",
"domain": "string",
"description": "string"
"description": "string",
"registration_key": "string"
},
"required": []
},
Expand Down
2 changes: 1 addition & 1 deletion go/bugfix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func TestListMessagesSendsDateFilters(t *testing.T) {
}

// ===========================================================================
// MEDIUM #19: Key lookups should use DefaultEndpoint, not DefaultKeysEndpoint
// MEDIUM #19: Key lookups use DefaultEndpoint (DefaultKeysEndpoint was removed)
// ===========================================================================

func TestFetchKeyByEmailDefaultsToMainEndpoint(t *testing.T) {
Expand Down
Loading
Loading