buzzster 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +10 -0
- package/README.md +325 -0
- package/SKILL.md +765 -0
- package/client.js +138 -0
- package/index.js +25 -0
- package/middleware.js +111 -0
- package/package.json +30 -0
- package/rules.js +100 -0
- package/setup.sh +50 -0
package/.env.example
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# BioFirewall Client
|
|
2
|
+
|
|
3
|
+
Express middleware to protect your routes with agent authentication.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
- **Blocks browsers** (User-Agent detection)
|
|
8
|
+
- **Verifies tokens** with central BioFirewall API
|
|
9
|
+
- **Caches verification** locally (30s default) to reduce API calls
|
|
10
|
+
- **Attaches agent info** to req.agent
|
|
11
|
+
- **Returns helpful error messages**
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install biofirewall-client
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
const express = require('express');
|
|
23
|
+
const BioFirewall = require('biofirewall-client');
|
|
24
|
+
|
|
25
|
+
const app = express();
|
|
26
|
+
|
|
27
|
+
// Create middleware
|
|
28
|
+
const bioFirewall = new BioFirewall({
|
|
29
|
+
apiUrl: 'http://localhost:3333', // Central API
|
|
30
|
+
blockBrowsers: true,
|
|
31
|
+
enforceAuthentication: true
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Apply to all routes
|
|
35
|
+
app.use(bioFirewall);
|
|
36
|
+
|
|
37
|
+
// Now only agents can access
|
|
38
|
+
app.get('/api/secret', (req, res) => {
|
|
39
|
+
res.json({
|
|
40
|
+
message: 'Hello, ' + req.agent.name,
|
|
41
|
+
agentId: req.agent.id
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
app.listen(8080);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration Options
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
const bioFirewall = new BioFirewall({
|
|
52
|
+
// Required
|
|
53
|
+
apiUrl: 'http://localhost:3333',
|
|
54
|
+
|
|
55
|
+
// Optional
|
|
56
|
+
blockBrowsers: true, // Block User-Agent that look like browsers
|
|
57
|
+
enforceAuthentication: true, // Require X-Bio-Token header
|
|
58
|
+
cacheTokens: true, // Cache verification results locally
|
|
59
|
+
cacheTTL: 30000 // Cache time-to-live (ms)
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## How It Works
|
|
64
|
+
|
|
65
|
+
### Layer 1: Passive Filtering (Local)
|
|
66
|
+
```
|
|
67
|
+
User-Agent: Mozilla/5.0 ... → 406 Not Acceptable
|
|
68
|
+
Accept: text/html → 406 Not Acceptable
|
|
69
|
+
Accept-Language: en-US → 406 Not Acceptable
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Layer 2: Token Verification (Central API)
|
|
73
|
+
```
|
|
74
|
+
POST /verify on central API
|
|
75
|
+
{
|
|
76
|
+
"agentId": "agent_xyz",
|
|
77
|
+
"token": "eyJhbGc..."
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Response:
|
|
81
|
+
{
|
|
82
|
+
"valid": true,
|
|
83
|
+
"agent": {
|
|
84
|
+
"id": "agent_xyz",
|
|
85
|
+
"name": "MyAgent",
|
|
86
|
+
"version": "1.0.0"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Layer 3: Cache (Local)
|
|
92
|
+
```
|
|
93
|
+
Result cached for 30 seconds
|
|
94
|
+
Reduces API calls during request burst
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Usage Examples
|
|
98
|
+
|
|
99
|
+
### Protect Specific Routes
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
const express = require('express');
|
|
103
|
+
const BioFirewall = require('biofirewall-client');
|
|
104
|
+
|
|
105
|
+
const app = express();
|
|
106
|
+
const publicRoutes = require('./routes/public');
|
|
107
|
+
const agentOnlyRoutes = require('./routes/agent-only');
|
|
108
|
+
|
|
109
|
+
// Public routes (no auth)
|
|
110
|
+
app.use('/public', publicRoutes);
|
|
111
|
+
|
|
112
|
+
// Apply BioFirewall middleware
|
|
113
|
+
const bioFirewall = new BioFirewall('http://localhost:3333');
|
|
114
|
+
app.use('/api', bioFirewall, agentOnlyRoutes);
|
|
115
|
+
|
|
116
|
+
app.listen(8080);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Custom Error Handling
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
const bioFirewall = new BioFirewall('http://localhost:3333');
|
|
123
|
+
|
|
124
|
+
app.use((req, res, next) => {
|
|
125
|
+
bioFirewall(req, res, (err) => {
|
|
126
|
+
if (err) {
|
|
127
|
+
// Custom error handling
|
|
128
|
+
console.error('Auth error:', err);
|
|
129
|
+
res.status(401).json({
|
|
130
|
+
success: false,
|
|
131
|
+
message: 'Custom error message'
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
next();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Access Agent Information
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
app.get('/api/profile', (req, res) => {
|
|
144
|
+
// req.agent is populated by middleware
|
|
145
|
+
res.json({
|
|
146
|
+
you: req.agent.name,
|
|
147
|
+
agentId: req.agent.id,
|
|
148
|
+
version: req.agent.version,
|
|
149
|
+
permissions: req.agent.permissions
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Request Headers Required
|
|
155
|
+
|
|
156
|
+
Agents must send:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
X-Bio-Agent-Id: agent_a1b2c3d4e5f6
|
|
160
|
+
X-Bio-Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
161
|
+
User-Agent: MyAgent/1.0
|
|
162
|
+
Accept: application/json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Do NOT send:**
|
|
166
|
+
```
|
|
167
|
+
Accept: text/html
|
|
168
|
+
Accept-Language: en-US
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Response Headers
|
|
172
|
+
|
|
173
|
+
Middleware adds:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
X-Bio-Verified: true
|
|
177
|
+
X-Bio-Agent-Name: MyAgent
|
|
178
|
+
X-Bio-Version: 3.0
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Error Responses
|
|
182
|
+
|
|
183
|
+
### 406 Not Acceptable (Browser Detected)
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"error": "BIOLOGICAL_ENTITY_DETECTED",
|
|
187
|
+
"message": "This resource is reserved for automated agents",
|
|
188
|
+
"tip": "Use an API client. Ensure User-Agent does not look like a browser"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 428 Precondition Required (Auth Missing)
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"error": "AUTHENTICATION_REQUIRED",
|
|
196
|
+
"message": "Valid agent token required to access this resource",
|
|
197
|
+
"protocol": {
|
|
198
|
+
"headers": {
|
|
199
|
+
"X-Bio-Agent-Id": "Your agent ID",
|
|
200
|
+
"X-Bio-Token": "JWT token"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 401 Unauthorized (Invalid Token)
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"error": "INVALID_TOKEN",
|
|
210
|
+
"message": "Token signature is invalid"
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 403 Forbidden (Agent Revoked)
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"error": "AGENT_NOT_ACTIVE",
|
|
218
|
+
"message": "Agent status is revoked"
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Token Caching
|
|
223
|
+
|
|
224
|
+
By default, token verification results are cached for 30 seconds:
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
// This prevents hammering the central API
|
|
228
|
+
// during request bursts
|
|
229
|
+
|
|
230
|
+
// Clear all cache
|
|
231
|
+
bioFirewall.clearCache();
|
|
232
|
+
|
|
233
|
+
// Clear specific token
|
|
234
|
+
bioFirewall.clearCacheEntry(agentId, token);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Performance Notes
|
|
238
|
+
|
|
239
|
+
- **Local validation:** User-Agent check (instant, no API call)
|
|
240
|
+
- **First request:** API call to central BioFirewall server (~5-50ms)
|
|
241
|
+
- **Cached requests:** Served from local cache (<1ms)
|
|
242
|
+
- **Cache hit rate:** ~95% for typical agents
|
|
243
|
+
|
|
244
|
+
## Testing
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
// Mock requests
|
|
248
|
+
const request = require('supertest');
|
|
249
|
+
const app = require('./app');
|
|
250
|
+
|
|
251
|
+
describe('Protected endpoint', () => {
|
|
252
|
+
it('should reject browsers', async () => {
|
|
253
|
+
const res = await request(app)
|
|
254
|
+
.get('/api/secret')
|
|
255
|
+
.set('User-Agent', 'Mozilla/5.0 Chrome/...');
|
|
256
|
+
|
|
257
|
+
expect(res.status).toBe(406);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should reject missing auth', async () => {
|
|
261
|
+
const res = await request(app)
|
|
262
|
+
.get('/api/secret')
|
|
263
|
+
.set('User-Agent', 'MyBot/1.0');
|
|
264
|
+
|
|
265
|
+
expect(res.status).toBe(428);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should allow valid agents', async () => {
|
|
269
|
+
const res = await request(app)
|
|
270
|
+
.get('/api/secret')
|
|
271
|
+
.set('User-Agent', 'MyBot/1.0')
|
|
272
|
+
.set('X-Bio-Agent-Id', 'agent_xyz')
|
|
273
|
+
.set('X-Bio-Token', 'valid_token...');
|
|
274
|
+
|
|
275
|
+
expect(res.status).toBe(200);
|
|
276
|
+
expect(res.body.agent.id).toBe('agent_xyz');
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Troubleshooting
|
|
282
|
+
|
|
283
|
+
### "Cannot connect to central API"
|
|
284
|
+
```javascript
|
|
285
|
+
// Check API URL
|
|
286
|
+
const bioFirewall = new BioFirewall('http://localhost:3333');
|
|
287
|
+
|
|
288
|
+
// Verify API is running
|
|
289
|
+
curl http://localhost:3333/health
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### "Token always invalid"
|
|
293
|
+
```javascript
|
|
294
|
+
// Regenerate token (expires after 1 hour)
|
|
295
|
+
const newToken = crypto.createToken(privateKey, agentId, metadata, 3600);
|
|
296
|
+
|
|
297
|
+
// Use new token in X-Bio-Token header
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### "Agents keep getting 406"
|
|
301
|
+
```javascript
|
|
302
|
+
// Check headers
|
|
303
|
+
curl -v \
|
|
304
|
+
-H "User-Agent: MyBot/1.0" \
|
|
305
|
+
-H "Accept: application/json" \
|
|
306
|
+
-H "X-Bio-Agent-Id: agent_xyz" \
|
|
307
|
+
-H "X-Bio-Token: ..." \
|
|
308
|
+
http://localhost:8080/api/secret
|
|
309
|
+
|
|
310
|
+
# Do NOT include:
|
|
311
|
+
# - "Mozilla" in User-Agent
|
|
312
|
+
# - "text/html" in Accept
|
|
313
|
+
# - "Accept-Language" header
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
MIT
|
|
319
|
+
|
|
320
|
+
## Support
|
|
321
|
+
|
|
322
|
+
- 📖 [SKILL.md](../SKILL.md) — Full developer guide
|
|
323
|
+
- 🏗️ [ARCHITECTURE.md](../ARCHITECTURE.md) — System design
|
|
324
|
+
- 🛡️ [biofirewall-api](../biofirewall-api) — Central API server
|
|
325
|
+
- 🔗 GitHub: https://github.com/openclaw/biofirewall
|