Skip to content

Commit 889c9e4

Browse files
committed
release: promptpurify v0.0.1
Tiny prompt-injection firewall for LLM chat apps. 13.9 MB CPU-only ONNX model + zero-dep TypeScript SDK + docs + supply-chain hygiene. Built from scratch by SecureLayer7. Ships: - promptpurify model (4-layer transformer encoder, ~13.7M params, INT8 ONNX, ~5–10 ms p50 CPU inference, sliding-window inference, marker invariance, SHA256SUMS for integrity verification) - SDK on npm — structural firewall (Unicode normalize, role-fenced messages, sink-aware policy, tripwire regex), ONNX runner, browser IIFE with zero ONNX bytes - Public 922-row scored eval slice + upstream-corpus license manifest - scripts/bench.mjs — public benchmark with --threshold / --behavior / --by-behavior flags - Documentation — README + docs/ (QUICKSTART, HOW-IT-WORKS, BENCHMARKS with peer-benchmark methodology comparison, SAMPLE-DATA, REPRODUCE, HONEST-LIMITS) - MODEL_CARD.md (HF model-index frontmatter), SECURITY.md (disclose.io safe-harbor, 90-day disclosure window), CHANGELOG.md - CI + release workflows — cosign keyless signing, SLSA build provenance, CycloneDX SBOM, npm publish --provenance, Hugging Face Hub mirror push to Securelayer7/promptpurify - Minimal sample app + customer-support fintech example Headline numbers at production threshold (0.95): - Pliny 20% held-out: 95.66% recall - Buried-tail injection: 85.54% recall - Gandalf: 100.00% recall - deepset attack set: 100.00% recall - Wikipedia long-paste: 4.67% false-positive rate On every axis measured against ProtectAI v2, deepset, fmops, and Meta Prompt-Guard-2 at their published thresholds, promptpurify is on top or tied. Full per-cell breakdown and peer-benchmark methodology in docs/BENCHMARKS.md.
0 parents  commit 889c9e4

55 files changed

Lines changed: 43424 additions & 0 deletions

Some content is hidden

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

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
build-test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
matrix:
13+
node: [20, 22]
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: ${{ matrix.node }}
19+
cache: npm
20+
- run: npm ci
21+
- run: npm run typecheck
22+
- run: npm test
23+
- run: npm run build
24+
25+
bench:
26+
runs-on: ubuntu-latest
27+
needs: build-test
28+
steps:
29+
- uses: actions/checkout@v4
30+
- uses: actions/setup-node@v4
31+
with:
32+
node-version: 22
33+
cache: npm
34+
- run: npm ci
35+
- run: npm run build
36+
- name: Verify model checksum
37+
run: cd models/l5e && sha256sum -c SHA256SUMS
38+
- name: Run public benchmark
39+
run: node scripts/bench.mjs

.github/workflows/release.yml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: 'Release tag (e.g. v0.0.1)'
11+
required: true
12+
13+
permissions:
14+
contents: write # GitHub Release upload
15+
id-token: write # cosign keyless + npm provenance + SLSA attestation
16+
attestations: write # SLSA build provenance
17+
18+
jobs:
19+
release:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- uses: actions/setup-node@v4
25+
with:
26+
node-version: 22
27+
registry-url: 'https://registry.npmjs.org'
28+
cache: npm
29+
30+
- run: npm ci
31+
- run: npm run typecheck
32+
- run: npm test
33+
- run: npm run build
34+
35+
# ---------- Model artifact tarball ----------
36+
- name: Build model tarball
37+
run: |
38+
tar -czf promptpurify-model.tar.gz \
39+
models/l5e/model.int8.onnx \
40+
models/l5e/vocab.txt \
41+
models/l5e/l5e.json \
42+
models/l5e/SHA256SUMS
43+
sha256sum promptpurify-model.tar.gz > promptpurify-model.tar.gz.sha256
44+
45+
- name: Verify model SHA256SUMS
46+
run: cd models/l5e && sha256sum -c SHA256SUMS
47+
48+
# ---------- cosign keyless signature ----------
49+
- uses: sigstore/cosign-installer@v3
50+
51+
- name: cosign-sign model tarball
52+
run: |
53+
cosign sign-blob --yes \
54+
--bundle promptpurify-model.tar.gz.cosign.bundle \
55+
promptpurify-model.tar.gz
56+
57+
# ---------- SLSA build provenance ----------
58+
- uses: actions/attest-build-provenance@v1
59+
with:
60+
subject-path: |
61+
promptpurify-model.tar.gz
62+
dist/**/*
63+
64+
# ---------- SBOM ----------
65+
- name: Generate CycloneDX SBOM
66+
run: npx --yes @cyclonedx/cyclonedx-npm --output-file SBOM.cdx.json --output-format JSON
67+
68+
# ---------- GitHub Release ----------
69+
- name: Upload release artifacts
70+
uses: softprops/action-gh-release@v2
71+
with:
72+
files: |
73+
promptpurify-model.tar.gz
74+
promptpurify-model.tar.gz.sha256
75+
promptpurify-model.tar.gz.cosign.bundle
76+
SBOM.cdx.json
77+
generate_release_notes: true
78+
79+
# ---------- npm publish with provenance ----------
80+
- name: Publish to npm with provenance
81+
run: npm publish --provenance --access public
82+
env:
83+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
84+
85+
# ---------- Hugging Face Hub mirror ----------
86+
- uses: actions/setup-python@v5
87+
with:
88+
python-version: '3.11'
89+
- name: Push to Securelayer7/promptpurify on HF Hub
90+
env:
91+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
92+
run: |
93+
pip install --quiet huggingface_hub
94+
python - <<'PY'
95+
import os, shutil
96+
from huggingface_hub import HfApi
97+
repo = "Securelayer7/promptpurify"
98+
api = HfApi(token=os.environ["HF_TOKEN"])
99+
api.create_repo(repo, repo_type="model", exist_ok=True)
100+
# MODEL_CARD.md becomes the HF README (has the YAML frontmatter HF needs)
101+
shutil.copy("MODEL_CARD.md", "models/l5e/README.md")
102+
api.upload_folder(
103+
repo_id=repo,
104+
folder_path="models/l5e",
105+
path_in_repo=".",
106+
commit_message=f"release {os.environ.get('GITHUB_REF_NAME', 'manual')}",
107+
allow_patterns=["model.int8.onnx", "vocab.txt", "l5e.json", "SHA256SUMS", "README.md"],
108+
)
109+
PY

.gitignore

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.DS_Store
5+
coverage/
6+
*.tgz
7+
.vitest/
8+
.env
9+
.env.*
10+
# EVAL-ONLY adversarial benchmark: github.com/elder-plinius (Pliny/BASI) raw
11+
# jailbreak + system-prompt-leak payloads. AGPL-3.0 / unlicensed — NOT
12+
# permissively licensed, so NEVER trained, NEVER shipped, NEVER redistributed.
13+
# Pulled locally only for measuring detector recall (training/eval_plinius.mjs).
14+
# Reproducible from training/fetch_plinius_eval.py. Only the survey table,
15+
# license inventory, harness + PLINIUS_BENCH.md are committed — never the raw.
16+
training/.eval_cache/
17+
# OOD eval raw datasets — third-party, not redistributed (size/licensing)
18+
training/.ood_cache/
19+
# V3 real-data retrain raw pool + held-out benchmark — third-party, not
20+
# redistributed (size/licensing). Splits derived deterministically downstream.
21+
training/.real_cache/
22+
# Versioned per-experiment train.jsonl dirs (V32/V33/V34/V35 derived data)
23+
training/.real_cache_v*/
24+
training/.real_cache_th/
25+
# Versioned model artifact export dirs (l5e_v33, l5e_v35 — large ONNX)
26+
models/l5e_v*/
27+
# ONNX export temp blobs / shape-inference scratch
28+
*.data
29+
sym_shape_infer_temp.onnx
30+
# fp32 distill intermediate (export_onnx.py strips it; never committed)
31+
models/l5b/_student_fp32/
32+
# STAGE-5 strictly-from-scratch L5c artifact: opt-in, npm-excluded
33+
# (files:["dist"]), large ONNX — gitignored from the shipped path exactly
34+
# like models/l5b. Reproducible from training/train_scratch.py (seed 1337).
35+
models/l5c/
36+
# STAGE-7 "intelligent" L5d artifact (fine-tuned Apache-2.0 distil-mBERT):
37+
# opt-in, npm-excluded (files:["dist"]), large INT8 ONNX — gitignored from
38+
# the shipped path exactly like models/l5b, l5c. Reproducible from
39+
# training/train_intelligent.py + export_intelligent.py (seed 1337).
40+
models/l5d/
41+
# STAGE-8 OUR-OWN pretrained backbone: sampled open pretrain corpus
42+
# (permissive third-party, not redistributed — size/licensing) + the
43+
# resulting L5e artifact (opt-in, npm-excluded, large INT8 ONNX). Both
44+
# gitignored from the shipped path exactly like .real_cache / models/l5d.
45+
# Reproducible from training/pretrain.py + train_intelligent.py
46+
# + export_intelligent.py (seed 1337).
47+
training/.pretrain_cache/
48+
# models/l5e/ — public release shipped: model.int8.onnx, vocab.txt,
49+
# l5e.json, SHA256SUMS. Ignore everything else under it (training
50+
# intermediates: _corpus/, _pretrained/, _hf_fp32_*, *.bak, etc).
51+
models/l5e/*
52+
!models/l5e/model.int8.onnx
53+
!models/l5e/vocab.txt
54+
!models/l5e/l5e.json
55+
!models/l5e/SHA256SUMS
56+
# isolated offline training venv (stable pinned CPU stack; never shipped)
57+
training/.venv/
58+
# python bytecode cache (training scripts; never shipped)
59+
training/__pycache__/
60+
**/__pycache__/
61+
62+
# Session-local — claude state, screenshots, big intermediates
63+
.claude/
64+
.tmp/
65+
training/.real_cache_th/
66+
examples/sample-app/public/hero.png

CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Changelog
2+
3+
All notable changes to promptpurify will be documented here.
4+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) +
5+
[SemVer](https://semver.org).
6+
7+
## [Unreleased]
8+
9+
## [0.0.1] — 2026-05-29
10+
11+
First public release.
12+
13+
### Added
14+
15+
- **promptpurify model** (13.9 MB INT8 ONNX, 4-layer transformer
16+
encoder, ~13.7M params, trained from scratch by SecureLayer7).
17+
- **Structural firewall** — Unicode normalization, role-fenced messages,
18+
sink-aware policy, tripwire regex. Deterministic, zero-dep, idempotent.
19+
- **`buildMessages()`** for role-separated, nonce-fenced LLM calls.
20+
- **`purifyOutput()`** for markdown-image / tracking-link exfil stripping.
21+
- **`createL5eRunner()`** ONNX runner with sliding-window inference
22+
(500 chars wide, 250 stride) and `[UNTRUSTED]` marker invariance.
23+
- **Browser bundle** — 7 KB IIFE, structural-only, no ONNX bytes.
24+
- **`scripts/bench.mjs`** — public benchmark script with `--threshold`,
25+
`--behavior`, `--by-behavior` flags.
26+
- **Documentation** — README, QUICKSTART, HOW-IT-WORKS, BENCHMARKS,
27+
SAMPLE-DATA, REPRODUCE, HONEST-LIMITS, MODEL_CARD, SECURITY.
28+
- **Sample frozen eval** — 922-row scored JSONL (`training/FROZEN_EVAL_SCORED.jsonl`).
29+
- **Supply-chain hygiene**`models/l5e/SHA256SUMS`, cosign-signed
30+
release tarball, `npm publish --provenance`, CycloneDX SBOM.
31+
- **Customer-support sample app**`examples/customer-support/`,
32+
drop-in fintech chatbot with promptpurify guard.
33+
34+
### Benchmarks (threshold 0.95)
35+
36+
- Pliny 20% held-out: **95.66%** recall
37+
- Buried-tail injection: **85.54%** recall
38+
- Wikipedia long-paste: **4.67%** false-positive rate
39+
- p50 inference latency: ~5 ms (M4 Max CPU)
40+
41+
Full per-cell vs ProtectAI v2 / deepset / fmops / Meta Prompt-Guard-2:
42+
[docs/BENCHMARKS.md](docs/BENCHMARKS.md).
43+
44+
### Known limitations
45+
46+
See [docs/HONEST-LIMITS.md](docs/HONEST-LIMITS.md).
47+
48+
[Unreleased]: https://github.com/securelayer7/PROMPTPurify/compare/v0.0.1...HEAD
49+
[0.0.1]: https://github.com/securelayer7/PROMPTPurify/releases/tag/v0.0.1

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 promptpurify contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)