Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
9eac374
sanitize user data and improve message handling in sendDirectMessage …
ilya-korotya Mar 4, 2026
c1fe272
update error message
ilya-korotya Mar 4, 2026
1224a8b
Merge pull request #3 from BillionsNetwork/feature/sanitize-user-data
vmidyllic Mar 4, 2026
78bfde7
add more info about arch
ilya-korotya Mar 4, 2026
f0c78c5
Merge pull request #4 from BillionsNetwork/feature/add-arch-desc
ilya-korotya Mar 4, 2026
48c23d3
update
ilya-korotya Mar 4, 2026
c60c89b
Merge pull request #5 from BillionsNetwork/chore/update-readme-md
vmidyllic Mar 4, 2026
94fc4c3
update
ilya-korotya Mar 4, 2026
39849a4
Merge pull request #6 from BillionsNetwork/chore/update-skill
vmidyllic Mar 4, 2026
e2fd478
Update external call policy in README
vmidyllic Mar 10, 2026
0681157
Merge pull request #209 from BillionsNetwork/vmidyllic-patch-1
vmidyllic Mar 10, 2026
9248a1d
Refactor key import process and update dependencies for improved func…
ilya-korotya Mar 11, 2026
ea57b05
Merge pull request #214 from BillionsNetwork/fix/use-compressed-pubkey
vmidyllic Mar 11, 2026
cd5b263
Update README with security recommendations and mitigations
vmidyllic Mar 11, 2026
77a47c9
Merge pull request #215 from BillionsNetwork/vmidyllic-patch-2
vmidyllic Mar 11, 2026
0f46387
Fix metadata key in SKILL.md
vmidyllic Mar 11, 2026
11ca755
Merge pull request #216 from BillionsNetwork/vmidyllic-patch-3
vmidyllic Mar 11, 2026
7067d1d
update deps
ilya-korotya Mar 11, 2026
ba0bdf0
Merge pull request #217 from BillionsNetwork/chore/update-deps
vmidyllic Mar 11, 2026
59577a6
fix readme
vmidyllic Mar 11, 2026
efb1efb
Implement KMS encryption for private keys in keys.js and update READM…
ilya-korotya Mar 12, 2026
af6c99b
encrypt each key
ilya-korotya Mar 12, 2026
2fe6ff8
Apply suggestion from @Copilot
vmidyllic Mar 12, 2026
fa078dd
Update README.md
vmidyllic Mar 12, 2026
6f1ed62
Merge pull request #227 from BillionsNetwork/feature/support-master-e…
vmidyllic Mar 12, 2026
dc6b303
add info about openclaw exec
vmidyllic Mar 12, 2026
0bcfbe1
Revise README for clarity and terminology updates
OBrezhniev Mar 12, 2026
c9db135
Revise SKILL.md for clarity and technical details
OBrezhniev Mar 12, 2026
3589d67
Fix typos in README.md for clarity
OBrezhniev Mar 12, 2026
0ae43cf
support git shortener
ilya-korotya Mar 13, 2026
9aaaa4c
Merge pull request #241 from BillionsNetwork/chore/support-link-short…
vmidyllic Mar 13, 2026
e23421c
add optionalEnv
ilya-korotya Mar 19, 2026
af1e2e6
Merge pull request #519 from BillionsNetwork/chore/add-env
ilya-korotya Mar 19, 2026
7de74ad
add skills.sh as skill hub
ilya-korotya Mar 20, 2026
937a2ea
Merge pull request #533 from BillionsNetwork/chore/update-cta
ilya-korotya Mar 20, 2026
656b1b1
add GitHub Actions workflow to evaluate Gemini skill
ilya-korotya Mar 23, 2026
796fd91
update skill dir path
ilya-korotya Mar 23, 2026
d9cd115
use workspace as working directory
ilya-korotya Mar 23, 2026
268d824
add yes for google cli command
ilya-korotya Mar 23, 2026
14ad6c1
apply yolo flag
ilya-korotya Mar 23, 2026
17e7f90
temporary disable validate step
ilya-korotya Mar 23, 2026
630a5e5
update KMS_FILE path
ilya-korotya Mar 23, 2026
ac98170
check if default did was created
ilya-korotya Mar 23, 2026
a63e7e5
migrate e2e test to promptfoo
ilya-korotya Mar 25, 2026
dc132de
use contains-any instead of icontains for setp 3
ilya-korotya Mar 25, 2026
dd67a41
fix last step on CI
ilya-korotya Mar 25, 2026
34974c3
Merge pull request #550 from BillionsNetwork/feature/illia-support-ev…
ilya-korotya Mar 25, 2026
3d93043
support x402 payment
ilya-korotya Apr 22, 2026
afb9a1f
add clawhubignore
vmidyllic May 1, 2026
233c21a
use signature instead of token
ilya-korotya May 1, 2026
4a72bf7
add master key to example
ilya-korotya May 1, 2026
4975f62
add GitHub Actions workflow for publishing to OpenClaw Hub and update…
ilya-korotya May 6, 2026
64c9121
refactor GitHub Actions workflow to publish skills on release and rem…
ilya-korotya May 6, 2026
bcb744b
Merge pull request #692 from BillionsNetwork/chore/fix-openclaw-scan
ilya-korotya May 6, 2026
751cc70
install clawhub
ilya-korotya May 6, 2026
b893ddc
add SECURITY.md file
ilya-korotya May 6, 2026
da5c2fc
update SECURITY.md
ilya-korotya May 6, 2026
0d6cf87
update README.md
ilya-korotya May 6, 2026
c655ffe
add hint for buildX402Payment.js
ilya-korotya May 11, 2026
0515f66
Merge branch 'develop' into feature/support-basic-person-pairing
ilya-korotya May 11, 2026
28c4d66
use deployed pkg
ilya-korotya May 11, 2026
58556d7
update pipe line
ilya-korotya May 11, 2026
9dfe734
use only identity slug
ilya-korotya May 12, 2026
669e6dc
Merge pull request #694 from BillionsNetwork/chore/update-ci
ilya-korotya May 12, 2026
bc94de5
Merge branch 'develop' into feature/support-basic-person-pairing
ilya-korotya May 13, 2026
caacd0d
Merge pull request #561 from BillionsNetwork/feature/support-basic-pe…
ilya-korotya May 13, 2026
f8ce553
update x402 skill
ilya-korotya May 13, 2026
dab8301
Merge pull request #695 from BillionsNetwork/feature/fix-scan-v1
ilya-korotya May 13, 2026
7ddc088
show infor about resource
ilya-korotya May 13, 2026
d0a8e52
Merge pull request #696 from BillionsNetwork/feature/fix-scan-v2
ilya-korotya May 13, 2026
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
3 changes: 3 additions & 0 deletions .clawhubignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
prompt.json
promptfooconfig.yaml
.github/
65 changes: 65 additions & 0 deletions .github/workflows/evaluate-skill.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Evaluate Skill

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_dispatch:

jobs:
evaluate:
runs-on: ubuntu-latest

env:
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '20'

- name: Install promptfoo
run: npm install -g promptfoo

- name: Run promptfoo eval
run: promptfoo eval --no-cache --output results.json

- name: Upload eval results
if: always()
uses: actions/upload-artifact@v4
with:
name: promptfoo-results
path: results.json

- name: Write job summary
if: always()
run: |
echo "### Promptfoo Skill Evaluation" >> "$GITHUB_STEP_SUMMARY"
if [ -f results.json ]; then
PASS=$(jq '[.results.results[] | select(.success == true)] | length' results.json)
FAIL=$(jq '[.results.results[] | select(.success == false)] | length' results.json)
TOTAL=$((PASS + FAIL))
echo "- Passed: $PASS" >> "$GITHUB_STEP_SUMMARY"
echo "- Failed: $FAIL" >> "$GITHUB_STEP_SUMMARY"
echo "- Total: $TOTAL" >> "$GITHUB_STEP_SUMMARY"
if [ "$TOTAL" -gt 0 ]; then
# Use awk for floating-point: success rate as integer percentage
RATE=$(awk "BEGIN { printf \"%d\", ($PASS / $TOTAL) * 100 }")
echo "- Success rate: ${RATE}%" >> "$GITHUB_STEP_SUMMARY"
if [ "$RATE" -lt 95 ]; then
echo "::error::Success rate ${RATE}% is below the 95% threshold (${PASS}/${TOTAL} passed)"
exit 1
fi
else
echo "- No tests found in results.json" >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
else
echo "- results.json not found" >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
30 changes: 30 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Publish to OpenClaw Hub

on:
release:
types: [published]

jobs:
publish:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install clawhub CLI
run: npm install -g clawhub

- name: Login to OpenClaw Hub
run: clawhub login --token ${{ secrets.CLAWHUB_TOKEN }}

- name: Publish skill
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
clawhub skill publish . --version "$VERSION" --slug identity --owner "@billionsnetwork" --name "Verified Agent Identity"
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ scripts/node_modules
.vscode
.DS_Store
*.zip
upload.sh
upload.sh
.env
139 changes: 138 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ This skill enables AI agents to create, manage, link, prove and verify ownership

1. Ask bot to install the skill:

Install with clawhub:

```plaintext
Install the skill `npx clawhub@latest install verified-agent-identity`
```

Or install with skills.sh:

```plaintext
Intall the skill `npx skills add BillionsNetwork/verified-agent-identity`
```

Or

```plaintext
Expand All @@ -31,9 +39,19 @@ This skill enables AI agents to create, manage, link, prove and verify ownership
### Human CTA:

1. Install the skill:

Use clawhub to install the skill:

```bash
npx clawhub@latest install verified-agent-identity
```

Use skills.sh to install the skill:

```bash
npx skills add BillionsNetwork/verified-agent-identity
```

2. Create a new identity:

```bash
Expand All @@ -45,9 +63,11 @@ This skill enables AI agents to create, manage, link, prove and verify ownership

```bash
# Use an existing private key to create an identity
node scripts/createNewEthereumIdentity.js --key <your-ethereum-private-key>
BILLIONS_NETWORK_MASTER_KMS_KEY="<your-strong-secret>" node scripts/createNewEthereumIdentity.js --key <your-ethereum-private-key>
```

> **Warning**: Only pass a **dedicated identity key** to `--key` — never an Ethereum wallet key that holds assets. If the key file is exposed, any key stored here could be used to impersonate the agent or, if reused, to control the associated wallet.

3. Generate a verification link to connect your human identity to the agent:

```bash
Expand All @@ -64,6 +84,123 @@ This skill enables AI agents to create, manage, link, prove and verify ownership
- **Proof Generation**: Generate cryptographic proofs to authenticate as a specific identity
- **Proof Verification**: Verify proofs to confirm identity ownership

## Architecture

### Runtime Requirements

- **Node.js `>= v20`** and **npm** are required to run the scripts.

### Dependency Surface

npm dependencies are intentionally minimal and scoped to well-established, audited packages:

| Package | Purpose |
| ---------------------- | ------------------------------------------------------------ |
| `@0xpolygonid/js-sdk` | iden3/Privado ID cryptographic primitives and key management |
| `@iden3/js-iden3-core` | DID and identity core types |
| `@iden3/js-iden3-auth` | JWS/JWA authorization response construction and verification |
| `ethers` | Ethereum key utilities |
| `uuid` | UUID generation for protocol message IDs |

Core libraries governing identity management use pinned, well-tested versions to ensure stability and security.

### Key Storage and Isolation

All cryptographic material is persisted to `$HOME/.openclaw/billions/` — a directory that lives **outside the agent's workspace**:

| File | Contents |
| ------------------ | ---------------------------------------------------------------------------------- |
| `kms.json` | Private keys — per-entry versioned format; keys are plain or AES-256-GCM encrypted |
| `identities.json` | Identity metadata |
| `defaultDid.json` | Active DID and associated public key |
| `challenges.json` | Per-DID challenge history |
| `credentials.json` | Verifiable credentials |

After the first run, restrict access to this directory: `chmod 700 ~/.openclaw/billions`

There are several ways of storing private keys, to enable master key encryption as described in the **KMS Encryption** section below.

### KMS Encryption

> See [SECURITY.md](SECURITY.md) for the full threat model, the rationale for shipping a plaintext storage mode, and the operator hardening checklist.

Set the environment variable `BILLIONS_NETWORK_MASTER_KMS_KEY` to enable AES-256-GCM at-rest encryption for the private keys inside `kms.json`. When set, every key value is individually encrypted on write; when absent, keys are stored as plain hex strings.

**`kms.json` entry format**

Each entry in the array is versioned. The `alias` is always stored in plaintext — only the `key` value is encrypted:

```json
[
{
"version": 1,
"provider": "plain",
"data": {
"alias": "secp256k1:abc123",
"key": "deadbeef...",
"createdAt": "2026-03-12T13:46:04.094Z"
}
},
{
"version": 1,
"provider": "encrypted",
"data": {
"alias": "secp256k1:xyz456",
"key": "<iv_hex>:<authTag_hex>:<ciphertext_hex>",
"createdAt": "2026-02-11T13:00:02.032Z"
}
}
]
```

**Behavior summary**

| `BILLIONS_NETWORK_MASTER_KMS_KEY` | `provider` on disk | `key` value on disk |
| --------------------------------- | ------------------ | ----------------------- |
| Not set | `"plain"` | Raw hex string |
| Set | `"encrypted"` | `iv:authTag:ciphertext` |

> **Backward compatibility** — the legacy format `[ { "alias": "...", "privateKeyHex": "..." } ]` is still read correctly. On the first write the file is automatically migrated to the new per-entry format. No manual step is required.

**How to set the variable**

_Option 1 — openclaw skill config (recommended for agent deployments):_

Add an `env` block for the skill inside your openclaw config:

```json
"skills": {
"entries": {
"verified-agent-identity": {
"env": {
"BILLIONS_NETWORK_MASTER_KMS_KEY": "<your-strong-secret>"
}
}
}
}
```

_Option 2 — shell or process environment:_

```bash
export BILLIONS_NETWORK_MASTER_KMS_KEY="<your-strong-secret>"
node scripts/createNewEthereumIdentity.js
node scripts/manualLinkHumanToAgent.js --challenge '{"name": "Agent Name", "description": "Short description of the agent"}'
```

For all other ways to pass environment variables to a skill see the [OpenClaw environment documentation](https://docs.openclaw.ai/help/environment).

**CRITICAL**: Save master keys securely and do not share them. If the master key is lost, all encrypted keys will be lost.

### Network and External Binary Policy

- All external https calls will be made to trusted resources. Signed JWS attestation (proof of agent ownership) is encoded securely by utilizing robust security practices. It requires an explicit user consent to pass it to any other source.
- All network calls are directed to legitimate DID resolvers (resolver.privado.id) or the project's own infrastructure (billions.network). These network calls cannot exfiltrate signed attestations or identity data to other third-party services by skill design. Wallet interaction is possible only through explicit action from the user side with consent. Also attestation contains only publicly verifiable information.
- Whitelisted domains:
- `resolver.privado.id` (DID resolution)
- `billions.network` (Billions Network interactions)
- `polygonid.me` (Polygon ID interactions)

## Documentation

See [SKILL.md](SKILL.md) for detailed usage instructions and examples.
96 changes: 96 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Security Policy

This document describes the security model of the `verified-agent-identity` skill, the threats it does and does not defend against, and the rationale behind design decisions that may surface in automated security scans.

## Scope

`verified-agent-identity` is a **local CLI skill**. It runs on a single operator's host, creates a decentralized identity (DID) for an AI agent, signs challenges with the agent's private key, and persists state under `~/.openclaw/billions/`. It is not a network service, has no listening port, and does not provide multi-tenant trust boundaries.

The only secret it manages is the agent's identity private key, stored in `~/.openclaw/billions/kms.json`.

## Threat Model

**In scope:**

- Preventing the identity key from being accidentally committed into the workspace or read by tools that operate inside the project directory.
- Protecting the key against casual disclosure on a single-user host (e.g. shoulder-surfing, accidental file sharing, careless backups).
- Preventing operator mistakes that would let an identity key double as an asset-holding wallet key.
- Providing opt-in at-rest encryption for shared/multi-user hosts and for environments where compliance requires it.

**Out of scope:**

- An attacker with read access to the operator's home directory or process memory. This is equivalent to full host compromise; no local secret-storage scheme defends against it without an external HSM or OS keystore, and integrating those would expand the dependency surface beyond what this skill commits to.
- Full-disk forensic recovery on a host the attacker physically controls.
- Hostile code already running with the operator's privileges.

## Storage Modes

Private keys are written to `~/.openclaw/billions/kms.json` in one of two formats, selected by the presence of the `BILLIONS_NETWORK_MASTER_KMS_KEY` environment variable.

| `BILLIONS_NETWORK_MASTER_KMS_KEY` | `provider` on disk | `key` value on disk | Posture |
| --------------------------------- | ------------------ | ----------------------- | ---------------------------- |
| Not set | `"plain"` | Raw hex string | Acceptable on a single-user host with `chmod 700 ~/.openclaw/billions`. |
| Set | `"encrypted"` | `iv:authTag:ciphertext` | **Recommended for all deployments.** AES-256-GCM at rest. |

Mode is selected per-write, so an operator can switch from `plain` to `encrypted` at any time by exporting the variable before the next key creation or import — no migration step is required.

## Compensating Controls

The following mitigations are present in the codebase and the documented installation flow:

- **Out-of-workspace storage.** Keys live under `~/.openclaw/billions/`, never inside the project directory. Tools (and the agent itself) that operate inside the workspace cannot read or exfiltrate them.
- **Filesystem hardening.** The README instructs the operator to run `chmod 700 ~/.openclaw/billions` after the first run (`README.md` → "Key Storage and Isolation").
- **Dedicated-key warning.** The README warns the operator never to import an Ethereum wallet key that holds assets, only a dedicated identity key (`README.md` step 2 warning under the Human CTA).
- **At-rest encryption available behind one env var.** AES-256-GCM is provided via `BILLIONS_NETWORK_MASTER_KMS_KEY`. No code change, no migration, no extra dependency.
- **Versioned on-disk format.** Each `kms.json` entry carries a `version` and `provider` field, so future format upgrades (e.g. an OS-keystore provider) can ship without breaking existing installs. Legacy entries auto-migrate on next write (see `scripts/shared/storage/keys.js`, `_decodeEntry` legacy branch).

## Scanner Findings — Acknowledged Risks

### Identity and Privilege Abuse — `scripts/shared/storage/keys.js` (plaintext storage branch)

**Finding (verbatim):**

> When no master key is configured, the key-storage code writes the raw private key value into `kms.json` as a plaintext entry.
>
> **User impact** — Anyone or any process that can read `~/.openclaw/billions/kms.json` may be able to impersonate the agent identity; if a real asset-holding Ethereum key is imported, the impact could extend beyond the agent identity.
>
> **Recommendation** — Set `BILLIONS_NETWORK_MASTER_KMS_KEY` before creating or importing keys, use only a dedicated no-assets identity key, restrict `~/.openclaw/billions` permissions, and avoid importing any wallet key that controls funds.

**Status: acknowledged, accepted — every item in the scanner's recommendation is already a documented and shipped control.**

The flagged code path is the documented `provider: "plain"` mode (see [Storage Modes](#storage-modes)). It is the default **only because the env var is unset**; setting `BILLIONS_NETWORK_MASTER_KMS_KEY` switches the same code path to AES-256-GCM with no further operator action. The threat the plaintext mode enables — local read of `~/.openclaw/billions/kms.json` on the operator's own host — is out of scope per the [Threat Model](#threat-model) above: an attacker with that level of access already controls the operator's shell history, SSH agent, browser secrets, and process memory.

#### Recommendation-to-Control mapping

| Scanner recommendation | Control in this repository | Reference |
| --- | --- | --- |
| Set `BILLIONS_NETWORK_MASTER_KMS_KEY` before creating or importing keys | A `> Note` block immediately precedes every key-creation command in the README, instructing the operator to set the variable. The `KMS Encryption` section documents the on-disk format change and the AES-256-GCM scheme. | `README.md` → "KMS Encryption" |
| Use only a dedicated, no-assets identity key | An explicit `> Warning` block under the key-creation step tells the operator never to pass an asset-holding wallet key to `--key`. | `README.md` step 2 of "Human CTA" |
| Restrict `~/.openclaw/billions` permissions | The "Key Storage and Isolation" section instructs `chmod 700 ~/.openclaw/billions` after the first run. The directory itself sits **outside the agent workspace**, so workspace-scoped tools cannot read it. | `README.md` → "Key Storage and Isolation" |
| Avoid importing any wallet key that controls funds | Same `> Warning` block as above; reinforced in the [Operator Checklist](#operator-checklist) below. | `README.md` step 2 warning + this document |

#### Why the plaintext mode is retained

1. **Zero-config local development and CI smoke tests** — no master secret to fetch or commit.
2. **Backward compatibility** — existing `kms.json` files written by earlier versions of the skill remain readable; legacy entries auto-migrate on the next write (`scripts/shared/storage/keys.js` → `_decodeEntry` legacy branch).
3. **Single code path** — the same write path becomes encrypted at rest the moment `BILLIONS_NETWORK_MASTER_KMS_KEY` is set. There is no separate "secure mode" the operator has to migrate to, so the plaintext default cannot drift away from the encrypted path over time.

The [Operator Checklist](#operator-checklist) is the recommended deployment posture and exactly matches the scanner's recommendation.

## Operator Checklist

1. **Set the master key first.**
```bash
export BILLIONS_NETWORK_MASTER_KMS_KEY="<a strong secret>"
```
Do this **before** the first `node scripts/createNewEthereumIdentity.js`. Keys created without it are written as `provider: "plain"`.
2. **Use a dedicated identity key.** Never reuse an Ethereum private key that holds assets. If the `kms.json` file is exposed, every key inside it should be revocable / disposable.
3. **Restrict the storage directory.**
```bash
chmod 700 ~/.openclaw/billions
```
4. **Back up the master key out of band.** If `BILLIONS_NETWORK_MASTER_KMS_KEY` is lost, every entry written under `provider: "encrypted"` is unrecoverable.

## Reporting a Vulnerability

Please report suspected vulnerabilities privately to the Billions Network security contact rather than filing a public issue. Open an issue marked `security` requesting a private disclosure channel if you do not already have one.
Loading
Loading