Skip to content
Open
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
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,27 @@ jobs:
- name: pytest
working-directory: sdk/python
run: python -m pytest -v

typescript:
name: TypeScript tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: sdk/typescript/package-lock.json

- name: npm ci
working-directory: sdk/typescript
run: npm ci

- name: build
working-directory: sdk/typescript
run: npm run build

- name: unit tests
working-directory: sdk/typescript
run: npm test --silent
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ make build && ./bin/acf-sidecar
pip install -e sdk/python
```

### 3b. Build the TypeScript SDK

```bash
cd sdk/typescript
npm ci
npm run build
```

### 4. Send your first request

```python
Expand All @@ -200,6 +208,16 @@ cd sidecar && go test ./... -v
# Python unit tests
cd sdk/python && python -m pytest -v

# TypeScript unit tests
cd sdk/typescript && npm test

# TypeScript sidecar integration tests
cd sdk/typescript && npm run test:e2e
# Prerequisites: export ACF_HMAC_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))") and start the sidecar in a separate shell (see step 2 above)

# TypeScript/Python parity tests (against live sidecar)
cd sdk/typescript && npm run test:parity

# Or both via make (from repo root)
make test # Go tests
make sdk-test-python # Python tests
Expand All @@ -224,7 +242,7 @@ acf-sdk/
├── sidecar/ Go enforcement kernel (PDP)
├── sdk/
│ ├── python/ Python SDK v1 — zero external dependencies
│ └── typescript/ TypeScript SDK v2deferred until v1 is stable
│ └── typescript/ TypeScript SDK v1frame/transport/firewall complete
├── policies/v1/ Rego policies + data
├── tests/integration/ 33-payload adversarial test suite
├── config/ Sidecar configuration
Expand Down Expand Up @@ -263,6 +281,7 @@ See [PHILOSOPHY.md](PHILOSOPHY.md) for the full design rationale. The short vers
- [Phase 1](docs/phase1.md) — wire protocol and crypto implementation
- [Phase 2](docs/phase2.md) — pipeline stages, strict_mode switch, scoring
- [Crypto](docs/crypto.md) — HMAC signing and nonce replay protection
- [TypeScript SDK](sdk/typescript/README.md) — usage, test commands, troubleshooting
- [Policy authoring](docs/policy-authoring.md) — how to write and test Rego policies
- [Philosophy](PHILOSOPHY.md) — design principles and threat model rationale

Expand Down
2 changes: 1 addition & 1 deletion sdk/python/acf/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def _build_payload(
"payload": content,
"state": None,
}
return json.dumps(ctx, separators=(",", ":")).encode("utf-8")
return json.dumps(ctx, separators=(",", ":"), sort_keys=True).encode("utf-8")

def _send(self, payload: bytes) -> Decision | SanitiseResult:
resp = self._transport.send(payload)
Expand Down
95 changes: 95 additions & 0 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ACF TypeScript SDK

TypeScript client for ACF-SDK (Agentic Cognitive Firewall).

This SDK sends signed risk-context payloads over IPC to the Go sidecar and
returns enforcement decisions:

- `ALLOW`
- `SANITISE`
- `BLOCK`

## Prerequisites

- Node.js 18+
- Go sidecar built and running

## Install

From `sdk/typescript`:

```bash
npm ci
npm run build
```

## Configuration

The firewall requires a shared HMAC key:

```bash
export ACF_HMAC_KEY=<64-hex-char-key>
```

Optional custom socket path:

```bash
export ACF_SOCKET_PATH=/tmp/acf.sock
```

## Quick Start

```ts
import { Firewall, Decision } from "@acf-sdk/acf"

const fw = new Firewall()
const result = await fw.onPrompt("hello world")

if (result === Decision.BLOCK) {
throw new Error("Blocked by firewall")
}
```

## Test Commands

From `sdk/typescript`:

```bash
# Unit tests (fast, default)
npm test

# Live sidecar E2E tests
npm run test:e2e

# TypeScript/Python parity tests against live sidecar
npm run test:parity
```

## Troubleshooting

### `No HMAC key provided`

Set `ACF_HMAC_KEY` or pass key bytes directly:

```ts
const fw = new Firewall(undefined, Buffer.from("<hex>", "hex"))
```

### Sidecar not reachable

- Ensure sidecar is running.
- Ensure `ACF_SOCKET_PATH` matches both sidecar and SDK.
- Ensure SDK and sidecar use the same `ACF_HMAC_KEY`.

### E2E tests skipped in sandboxed environments

Some environments disallow Unix socket bind/listen. E2E/parity tests will skip
with a clear reason when this restriction is detected.

## Compatibility Contract

TypeScript and Python SDKs must remain wire-compatible:

- canonical JSON signing
- identical frame structure
- decision-level parity for shared scenarios
50 changes: 50 additions & 0 deletions sdk/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions sdk/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "node --test tests/"
"test": "npm run build && node --test dist/tests/*.test.js",
"test:e2e": "npm run build && node --test dist/tests/integration.e2e.js",
"test:parity": "npm run build && node --test dist/tests/parity.e2e.js"
},
"engines": {
"node": ">=18"
},
"dependencies": {},
"devDependencies": {
"typescript": "^5.4.0"
"@types/node": "^25.5.2",
"typescript": "^5.9.3"
}
}
Loading