@ebowwa/logic-spec 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/README.md +125 -0
- package/REFERENCE.md +147 -0
- package/SPEC.md +783 -0
- package/dist/cli.js +6713 -0
- package/dist/index.js +6663 -0
- package/examples/minimal.yaml +49 -0
- package/examples/smart-glass-supervisor.yaml +330 -0
- package/examples/state-machine.yaml +106 -0
- package/package.json +50 -0
- package/schema.json +409 -0
package/SPEC.md
ADDED
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
# Logic Specification (logic.yaml) v1.0
|
|
2
|
+
|
|
3
|
+
A language-agnostic standard for extracting pure logic from implementations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`logic.yaml` captures the **what** and **how** of a system's behavior without the **syntax** of any particular language. This enables:
|
|
8
|
+
|
|
9
|
+
- AI agents to understand logic without reading implementation code
|
|
10
|
+
- Portability across languages and runtimes
|
|
11
|
+
- Automated validation, testing, and documentation generation
|
|
12
|
+
- Clear contracts between system components
|
|
13
|
+
|
|
14
|
+
## Core Principles
|
|
15
|
+
|
|
16
|
+
| Principle | Description |
|
|
17
|
+
|-----------|-------------|
|
|
18
|
+
| **I/O at edges** | Logic blocks are pure functions - no side effects, no hidden state |
|
|
19
|
+
| **Algorithm as text** | Steps described in human-readable prose, not code syntax |
|
|
20
|
+
| **Types explicit** | All data shapes defined with schemas for validation |
|
|
21
|
+
| **State first-class** | State machines declared explicitly, not inferred |
|
|
22
|
+
| **Constraints separate** | Performance/reliability requirements not buried in code |
|
|
23
|
+
| **Failures declared** | Error conditions and recovery strategies part of contract |
|
|
24
|
+
| **Composable blocks** | Logic chains into flows, not monolithic procedures |
|
|
25
|
+
| **Versioned spec** | Schema can evolve without breaking consumers |
|
|
26
|
+
| **Observable** | Hooks for metrics, logging, tracing built-in |
|
|
27
|
+
| **Testable** | Assertions defined alongside logic |
|
|
28
|
+
|
|
29
|
+
## Specification
|
|
30
|
+
|
|
31
|
+
### Document Structure
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
# logic.yaml v1.0
|
|
35
|
+
|
|
36
|
+
meta: # Required - Document metadata
|
|
37
|
+
inputs: # Required - System inputs
|
|
38
|
+
outputs: # Required - System outputs
|
|
39
|
+
types: # Optional - Type definitions
|
|
40
|
+
logic: # Required - Pure logic blocks
|
|
41
|
+
state: # Optional - State machine definition
|
|
42
|
+
flows: # Optional - Logic composition
|
|
43
|
+
failures: # Optional - Error handling
|
|
44
|
+
constraints: # Optional - Performance/reliability bounds
|
|
45
|
+
dependencies: # Optional - External service contracts
|
|
46
|
+
observability: # Optional - Metrics/logging hooks
|
|
47
|
+
tests: # Optional - Built-in assertions
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### `meta`
|
|
53
|
+
|
|
54
|
+
Required. Identifies and versions the specification.
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
meta:
|
|
58
|
+
spec_version: "1.0" # Required - Logic spec version
|
|
59
|
+
name: string # Required - System/module name
|
|
60
|
+
version: string # Required - Semantic version of this logic
|
|
61
|
+
description: string # Optional - Human description
|
|
62
|
+
language_target: string | any # Optional - Target language(s)
|
|
63
|
+
author: string # Optional - Creator
|
|
64
|
+
license: string # Optional - License identifier
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Example:**
|
|
68
|
+
```yaml
|
|
69
|
+
meta:
|
|
70
|
+
spec_version: "1.0"
|
|
71
|
+
name: user-authenticator
|
|
72
|
+
version: "2.1.0"
|
|
73
|
+
description: Handles user authentication flows
|
|
74
|
+
language_target: any
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### `inputs`
|
|
80
|
+
|
|
81
|
+
Required. Defines what enters the system.
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
inputs:
|
|
85
|
+
- name: string # Required - Input identifier
|
|
86
|
+
type: string # Required - Type name or primitive
|
|
87
|
+
required: boolean # Optional - Default: true
|
|
88
|
+
default: any # Optional - Default value if not required
|
|
89
|
+
description: string # Optional - Purpose of this input
|
|
90
|
+
schema: # Optional - Detailed structure
|
|
91
|
+
field: type
|
|
92
|
+
validation: # Optional - Validation rules
|
|
93
|
+
min: number
|
|
94
|
+
max: number
|
|
95
|
+
pattern: regex
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Primitive types:** `string`, `number`, `boolean`, `array`, `object`, `null`, `any`
|
|
99
|
+
|
|
100
|
+
**Example:**
|
|
101
|
+
```yaml
|
|
102
|
+
inputs:
|
|
103
|
+
- name: credentials
|
|
104
|
+
type: Credentials
|
|
105
|
+
required: true
|
|
106
|
+
schema:
|
|
107
|
+
username: string
|
|
108
|
+
password: string
|
|
109
|
+
totp_code: string
|
|
110
|
+
|
|
111
|
+
- name: session_duration
|
|
112
|
+
type: number
|
|
113
|
+
required: false
|
|
114
|
+
default: 3600
|
|
115
|
+
validation:
|
|
116
|
+
min: 300
|
|
117
|
+
max: 86400
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### `outputs`
|
|
123
|
+
|
|
124
|
+
Required. Defines what leaves the system.
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
outputs:
|
|
128
|
+
- name: string # Required - Output identifier
|
|
129
|
+
type: string # Required - Type name or primitive
|
|
130
|
+
description: string # Optional - Purpose
|
|
131
|
+
schema: object # Optional - Detailed structure
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Example:**
|
|
135
|
+
```yaml
|
|
136
|
+
outputs:
|
|
137
|
+
- name: auth_result
|
|
138
|
+
type: AuthResult
|
|
139
|
+
schema:
|
|
140
|
+
success: boolean
|
|
141
|
+
token: string
|
|
142
|
+
expires_at: number
|
|
143
|
+
user_id: string
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### `types`
|
|
149
|
+
|
|
150
|
+
Optional. Defines custom types referenced elsewhere.
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
types:
|
|
154
|
+
TypeName:
|
|
155
|
+
description: string # Optional
|
|
156
|
+
fields: # Required for object types
|
|
157
|
+
field_name: type
|
|
158
|
+
enum: # Required for enum types
|
|
159
|
+
- value1
|
|
160
|
+
- value2
|
|
161
|
+
generic: # Optional - Generic type params
|
|
162
|
+
- T
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Example:**
|
|
166
|
+
```yaml
|
|
167
|
+
types:
|
|
168
|
+
Credentials:
|
|
169
|
+
fields:
|
|
170
|
+
username: string
|
|
171
|
+
password: string
|
|
172
|
+
totp_code: string | null
|
|
173
|
+
|
|
174
|
+
AuthResult:
|
|
175
|
+
fields:
|
|
176
|
+
success: boolean
|
|
177
|
+
token: string | null
|
|
178
|
+
expires_at: number
|
|
179
|
+
user_id: string | null
|
|
180
|
+
error: AuthError | null
|
|
181
|
+
|
|
182
|
+
AuthError:
|
|
183
|
+
enum:
|
|
184
|
+
- INVALID_CREDENTIALS
|
|
185
|
+
- ACCOUNT_LOCKED
|
|
186
|
+
- TOTP_REQUIRED
|
|
187
|
+
- TOTP_INVALID
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### `logic`
|
|
193
|
+
|
|
194
|
+
Required. Pure logic blocks - no I/O, no side effects.
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
logic:
|
|
198
|
+
- id: string # Required - Unique identifier
|
|
199
|
+
type: string # Required - Block type
|
|
200
|
+
description: string # Optional - What this does
|
|
201
|
+
input: string | array # Required - Input name(s)
|
|
202
|
+
output: string # Required - Output name
|
|
203
|
+
algorithm: string # Required - Steps in prose
|
|
204
|
+
complexity: string # Optional - Big-O notation
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Block types:**
|
|
208
|
+
| Type | Purpose |
|
|
209
|
+
|------|---------|
|
|
210
|
+
| `transform` | Pure data transformation |
|
|
211
|
+
| `validate` | Input validation |
|
|
212
|
+
| `compute` | Calculation/derivation |
|
|
213
|
+
| `decision` | Conditional logic, branching |
|
|
214
|
+
| `aggregate` | Combining multiple inputs |
|
|
215
|
+
| `inference` | AI/ML model invocation |
|
|
216
|
+
| `query` | External data fetch (marked, not pure) |
|
|
217
|
+
|
|
218
|
+
**Example:**
|
|
219
|
+
```yaml
|
|
220
|
+
logic:
|
|
221
|
+
- id: validate_credentials
|
|
222
|
+
type: validate
|
|
223
|
+
input: credentials
|
|
224
|
+
output: validation_result
|
|
225
|
+
algorithm: |
|
|
226
|
+
1. Check username format: 3-64 chars, alphanumeric + underscore
|
|
227
|
+
2. Check password length: >= 12 chars
|
|
228
|
+
3. If totp_code present, validate 6-digit format
|
|
229
|
+
4. Return {valid: boolean, errors: string[]}
|
|
230
|
+
|
|
231
|
+
- id: hash_password
|
|
232
|
+
type: transform
|
|
233
|
+
input: credentials.password
|
|
234
|
+
output: hashed_password
|
|
235
|
+
algorithm: |
|
|
236
|
+
1. Generate salt: 16 random bytes
|
|
237
|
+
2. Apply Argon2id with salt
|
|
238
|
+
3. Return base64-encoded hash
|
|
239
|
+
complexity: O(1)
|
|
240
|
+
|
|
241
|
+
- id: verify_password
|
|
242
|
+
type: decision
|
|
243
|
+
input: [credentials, stored_hash]
|
|
244
|
+
output: password_valid
|
|
245
|
+
algorithm: |
|
|
246
|
+
1. Extract salt from stored_hash
|
|
247
|
+
2. Hash input password with same salt
|
|
248
|
+
3. Compare with constant-time comparison
|
|
249
|
+
4. Return boolean
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### `state`
|
|
255
|
+
|
|
256
|
+
Optional. State machine definition.
|
|
257
|
+
|
|
258
|
+
```yaml
|
|
259
|
+
state:
|
|
260
|
+
initial: string # Required - Starting state
|
|
261
|
+
persist: boolean # Optional - Persist across runs
|
|
262
|
+
states: # Optional - State definitions
|
|
263
|
+
state_name:
|
|
264
|
+
description: string
|
|
265
|
+
transitions: # Required - State transitions
|
|
266
|
+
- from: string | array
|
|
267
|
+
to: string
|
|
268
|
+
trigger: string # Event or condition
|
|
269
|
+
action: string # Optional - Logic block to execute
|
|
270
|
+
guard: string # Optional - Condition that must be true
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Example:**
|
|
274
|
+
```yaml
|
|
275
|
+
state:
|
|
276
|
+
initial: unauthenticated
|
|
277
|
+
persist: true
|
|
278
|
+
states:
|
|
279
|
+
unauthenticated:
|
|
280
|
+
description: No active session
|
|
281
|
+
authenticating:
|
|
282
|
+
description: Login in progress
|
|
283
|
+
authenticated:
|
|
284
|
+
description: Valid session active
|
|
285
|
+
locked:
|
|
286
|
+
description: Account locked due to failures
|
|
287
|
+
|
|
288
|
+
transitions:
|
|
289
|
+
- from: unauthenticated
|
|
290
|
+
to: authenticating
|
|
291
|
+
trigger: login_attempt
|
|
292
|
+
action: validate_credentials
|
|
293
|
+
|
|
294
|
+
- from: authenticating
|
|
295
|
+
to: authenticated
|
|
296
|
+
trigger: credentials_valid
|
|
297
|
+
action: create_session
|
|
298
|
+
guard: attempts < 5
|
|
299
|
+
|
|
300
|
+
- from: authenticating
|
|
301
|
+
to: locked
|
|
302
|
+
trigger: too_many_failures
|
|
303
|
+
guard: attempts >= 5
|
|
304
|
+
|
|
305
|
+
- from: authenticated
|
|
306
|
+
to: unauthenticated
|
|
307
|
+
trigger: logout | session_expired
|
|
308
|
+
action: destroy_session
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
### `flows`
|
|
314
|
+
|
|
315
|
+
Optional. Composition of logic blocks into sequences.
|
|
316
|
+
|
|
317
|
+
```yaml
|
|
318
|
+
flows:
|
|
319
|
+
- name: string # Required - Flow identifier
|
|
320
|
+
description: string # Optional
|
|
321
|
+
steps: array # Required - Logic block IDs in order
|
|
322
|
+
parallel: boolean # Optional - Run steps in parallel
|
|
323
|
+
timeout_ms: number # Optional - Max execution time
|
|
324
|
+
retry: # Optional - Retry configuration
|
|
325
|
+
count: number
|
|
326
|
+
backoff: linear | exponential
|
|
327
|
+
delay_ms: number
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Example:**
|
|
331
|
+
```yaml
|
|
332
|
+
flows:
|
|
333
|
+
- name: authenticate_user
|
|
334
|
+
description: Full authentication flow
|
|
335
|
+
steps:
|
|
336
|
+
- validate_credentials
|
|
337
|
+
- verify_password
|
|
338
|
+
- check_account_status
|
|
339
|
+
- create_session
|
|
340
|
+
parallel: false
|
|
341
|
+
timeout_ms: 5000
|
|
342
|
+
|
|
343
|
+
- name: parallel_validation
|
|
344
|
+
steps:
|
|
345
|
+
- validate_credentials
|
|
346
|
+
- check_rate_limit
|
|
347
|
+
- check_ip_reputation
|
|
348
|
+
parallel: true
|
|
349
|
+
timeout_ms: 1000
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
### `failures`
|
|
355
|
+
|
|
356
|
+
Optional. Error conditions and recovery strategies.
|
|
357
|
+
|
|
358
|
+
```yaml
|
|
359
|
+
failures:
|
|
360
|
+
- code: string # Required - Error code
|
|
361
|
+
condition: string # Required - When this occurs
|
|
362
|
+
severity: critical | error | warning | info
|
|
363
|
+
recovery: # Optional - Recovery strategy
|
|
364
|
+
strategy: retry | fallback | escalate | ignore
|
|
365
|
+
retry_count: number # For retry strategy
|
|
366
|
+
fallback_to: string # For fallback strategy
|
|
367
|
+
message: string # Optional - Human-readable message
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Example:**
|
|
371
|
+
```yaml
|
|
372
|
+
failures:
|
|
373
|
+
- code: E001
|
|
374
|
+
condition: "database.connection_timeout > 5000ms"
|
|
375
|
+
severity: error
|
|
376
|
+
recovery:
|
|
377
|
+
strategy: retry
|
|
378
|
+
retry_count: 3
|
|
379
|
+
message: Database connection timed out
|
|
380
|
+
|
|
381
|
+
- code: E002
|
|
382
|
+
condition: "password_hash.failure"
|
|
383
|
+
severity: critical
|
|
384
|
+
recovery:
|
|
385
|
+
strategy: escalate
|
|
386
|
+
message: Password hashing failed - possible security issue
|
|
387
|
+
|
|
388
|
+
- code: E003
|
|
389
|
+
condition: "rate_limit.exceeded"
|
|
390
|
+
severity: warning
|
|
391
|
+
recovery:
|
|
392
|
+
strategy: fallback
|
|
393
|
+
fallback_to: cached_response
|
|
394
|
+
message: Rate limit exceeded
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
### `constraints`
|
|
400
|
+
|
|
401
|
+
Optional. Performance and reliability bounds.
|
|
402
|
+
|
|
403
|
+
```yaml
|
|
404
|
+
constraints:
|
|
405
|
+
latency_max_ms: number # Max response time
|
|
406
|
+
throughput_min_rps: number # Min requests per second
|
|
407
|
+
memory_limit_mb: number # Max memory usage
|
|
408
|
+
cpu_limit_percent: number # Max CPU usage
|
|
409
|
+
availability_percent: number # Uptime requirement
|
|
410
|
+
data_retention_days: number # Data storage duration
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**Example:**
|
|
414
|
+
```yaml
|
|
415
|
+
constraints:
|
|
416
|
+
latency_max_ms: 500
|
|
417
|
+
throughput_min_rps: 1000
|
|
418
|
+
memory_limit_mb: 512
|
|
419
|
+
availability_percent: 99.9
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### `dependencies`
|
|
425
|
+
|
|
426
|
+
Optional. External service contracts.
|
|
427
|
+
|
|
428
|
+
```yaml
|
|
429
|
+
dependencies:
|
|
430
|
+
- name: string # Required - Service name
|
|
431
|
+
type: string # Required - Service type
|
|
432
|
+
contract: string # Optional - Interface name
|
|
433
|
+
required: boolean # Optional - Default: true
|
|
434
|
+
endpoint: string # Optional - Service URL
|
|
435
|
+
timeout_ms: number # Optional - Request timeout
|
|
436
|
+
auth: string # Optional - Auth method
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Example:**
|
|
440
|
+
```yaml
|
|
441
|
+
dependencies:
|
|
442
|
+
- name: user_database
|
|
443
|
+
type: database
|
|
444
|
+
contract: UserStore
|
|
445
|
+
required: true
|
|
446
|
+
timeout_ms: 1000
|
|
447
|
+
|
|
448
|
+
- name: cache
|
|
449
|
+
type: cache
|
|
450
|
+
contract: KeyValueStore
|
|
451
|
+
required: false
|
|
452
|
+
|
|
453
|
+
- name: gemini_api
|
|
454
|
+
type: external_service
|
|
455
|
+
contract: VisionAPI
|
|
456
|
+
required: true
|
|
457
|
+
timeout_ms: 30000
|
|
458
|
+
auth: api_key
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
### `observability`
|
|
464
|
+
|
|
465
|
+
Optional. Metrics, logging, and tracing hooks.
|
|
466
|
+
|
|
467
|
+
```yaml
|
|
468
|
+
observability:
|
|
469
|
+
metrics:
|
|
470
|
+
- name: string # Required - Metric name
|
|
471
|
+
type: counter | gauge | histogram
|
|
472
|
+
unit: string # Optional - Unit of measurement
|
|
473
|
+
labels: array # Optional - Dimension labels
|
|
474
|
+
|
|
475
|
+
logs:
|
|
476
|
+
level: debug | info | warn | error
|
|
477
|
+
include: array # Fields to include in logs
|
|
478
|
+
exclude: array # Fields to redact
|
|
479
|
+
|
|
480
|
+
traces:
|
|
481
|
+
enabled: boolean
|
|
482
|
+
sample_rate: number # 0.0 to 1.0
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Example:**
|
|
486
|
+
```yaml
|
|
487
|
+
observability:
|
|
488
|
+
metrics:
|
|
489
|
+
- name: auth_attempts_total
|
|
490
|
+
type: counter
|
|
491
|
+
labels: [status, method]
|
|
492
|
+
- name: auth_duration_ms
|
|
493
|
+
type: histogram
|
|
494
|
+
unit: ms
|
|
495
|
+
|
|
496
|
+
logs:
|
|
497
|
+
level: info
|
|
498
|
+
include: [user_id, method, duration_ms]
|
|
499
|
+
exclude: [password, token]
|
|
500
|
+
|
|
501
|
+
traces:
|
|
502
|
+
enabled: true
|
|
503
|
+
sample_rate: 0.1
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
### `tests`
|
|
509
|
+
|
|
510
|
+
Optional. Built-in assertions and test cases.
|
|
511
|
+
|
|
512
|
+
```yaml
|
|
513
|
+
tests:
|
|
514
|
+
- name: string # Required - Test name
|
|
515
|
+
description: string # Optional
|
|
516
|
+
given: object # Required - Input values
|
|
517
|
+
when: string # Optional - Action/flow to execute
|
|
518
|
+
expect: object # Required - Expected outputs
|
|
519
|
+
timeout_ms: number # Optional
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Matchers in `expect`:**
|
|
523
|
+
| Syntax | Meaning |
|
|
524
|
+
|--------|---------|
|
|
525
|
+
| `value` | Exact match |
|
|
526
|
+
| `">= n"` | Greater than or equal |
|
|
527
|
+
| `"<= n"` | Less than or equal |
|
|
528
|
+
| `"type:typename"` | Type check |
|
|
529
|
+
| `"/regex/"` | Regex match |
|
|
530
|
+
| `"!null"` | Not null |
|
|
531
|
+
| `"any"` | Any value (just check presence) |
|
|
532
|
+
|
|
533
|
+
**Example:**
|
|
534
|
+
```yaml
|
|
535
|
+
tests:
|
|
536
|
+
- name: valid_login
|
|
537
|
+
description: Successful authentication with valid credentials
|
|
538
|
+
given:
|
|
539
|
+
credentials:
|
|
540
|
+
username: testuser
|
|
541
|
+
password: correct_password_123
|
|
542
|
+
when: authenticate_user
|
|
543
|
+
expect:
|
|
544
|
+
auth_result.success: true
|
|
545
|
+
auth_result.token: "!null"
|
|
546
|
+
auth_result.expires_at: ">= 0"
|
|
547
|
+
|
|
548
|
+
- name: invalid_password
|
|
549
|
+
given:
|
|
550
|
+
credentials:
|
|
551
|
+
username: testuser
|
|
552
|
+
password: wrong_password
|
|
553
|
+
when: authenticate_user
|
|
554
|
+
expect:
|
|
555
|
+
auth_result.success: false
|
|
556
|
+
auth_result.error: INVALID_CREDENTIALS
|
|
557
|
+
|
|
558
|
+
- name: rate_limited
|
|
559
|
+
given:
|
|
560
|
+
credentials:
|
|
561
|
+
username: testuser
|
|
562
|
+
password: any_password
|
|
563
|
+
rate_limit_exceeded: true
|
|
564
|
+
expect:
|
|
565
|
+
auth_result.success: false
|
|
566
|
+
auth_result.error: RATE_LIMITED
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Complete Example
|
|
572
|
+
|
|
573
|
+
```yaml
|
|
574
|
+
# logic.yaml - User Authentication Module
|
|
575
|
+
|
|
576
|
+
meta:
|
|
577
|
+
spec_version: "1.0"
|
|
578
|
+
name: user-auth
|
|
579
|
+
version: "1.0.0"
|
|
580
|
+
description: User authentication with password and TOTP
|
|
581
|
+
language_target: any
|
|
582
|
+
|
|
583
|
+
inputs:
|
|
584
|
+
- name: credentials
|
|
585
|
+
type: Credentials
|
|
586
|
+
required: true
|
|
587
|
+
- name: client_ip
|
|
588
|
+
type: string
|
|
589
|
+
required: true
|
|
590
|
+
- name: session_duration
|
|
591
|
+
type: number
|
|
592
|
+
default: 3600
|
|
593
|
+
|
|
594
|
+
outputs:
|
|
595
|
+
- name: auth_result
|
|
596
|
+
type: AuthResult
|
|
597
|
+
|
|
598
|
+
types:
|
|
599
|
+
Credentials:
|
|
600
|
+
fields:
|
|
601
|
+
username: string
|
|
602
|
+
password: string
|
|
603
|
+
totp_code: string | null
|
|
604
|
+
|
|
605
|
+
AuthResult:
|
|
606
|
+
fields:
|
|
607
|
+
success: boolean
|
|
608
|
+
token: string | null
|
|
609
|
+
expires_at: number
|
|
610
|
+
user_id: string | null
|
|
611
|
+
error: AuthError | null
|
|
612
|
+
|
|
613
|
+
AuthError:
|
|
614
|
+
enum: [INVALID_CREDENTIALS, ACCOUNT_LOCKED, TOTP_REQUIRED, TOTP_INVALID, RATE_LIMITED]
|
|
615
|
+
|
|
616
|
+
logic:
|
|
617
|
+
- id: validate_input
|
|
618
|
+
type: validate
|
|
619
|
+
input: credentials
|
|
620
|
+
output: validation
|
|
621
|
+
algorithm: |
|
|
622
|
+
1. Validate username format
|
|
623
|
+
2. Validate password length >= 12
|
|
624
|
+
3. Return {valid: boolean, errors: string[]}
|
|
625
|
+
|
|
626
|
+
- id: check_rate_limit
|
|
627
|
+
type: decision
|
|
628
|
+
input: client_ip
|
|
629
|
+
output: rate_ok
|
|
630
|
+
algorithm: |
|
|
631
|
+
1. Count recent attempts from IP
|
|
632
|
+
2. Return true if under limit, false otherwise
|
|
633
|
+
|
|
634
|
+
- id: verify_credentials
|
|
635
|
+
type: decision
|
|
636
|
+
input: [credentials, stored_user]
|
|
637
|
+
output: credentials_valid
|
|
638
|
+
algorithm: |
|
|
639
|
+
1. Fetch stored hash for username
|
|
640
|
+
2. Compare passwords with constant-time comparison
|
|
641
|
+
3. If TOTP enabled, verify code
|
|
642
|
+
4. Return boolean
|
|
643
|
+
|
|
644
|
+
- id: create_session
|
|
645
|
+
type: transform
|
|
646
|
+
input: [user_id, session_duration]
|
|
647
|
+
output: auth_result
|
|
648
|
+
algorithm: |
|
|
649
|
+
1. Generate secure random token
|
|
650
|
+
2. Set expiration = now + session_duration
|
|
651
|
+
3. Store session in cache
|
|
652
|
+
4. Return AuthResult with success=true
|
|
653
|
+
|
|
654
|
+
state:
|
|
655
|
+
initial: idle
|
|
656
|
+
transitions:
|
|
657
|
+
- from: idle
|
|
658
|
+
to: validating
|
|
659
|
+
trigger: login_attempt
|
|
660
|
+
- from: validating
|
|
661
|
+
to: authenticating
|
|
662
|
+
trigger: input_valid
|
|
663
|
+
- from: validating
|
|
664
|
+
to: idle
|
|
665
|
+
trigger: input_invalid
|
|
666
|
+
- from: authenticating
|
|
667
|
+
to: success
|
|
668
|
+
trigger: credentials_valid
|
|
669
|
+
- from: authenticating
|
|
670
|
+
to: failed
|
|
671
|
+
trigger: credentials_invalid
|
|
672
|
+
- from: success
|
|
673
|
+
to: idle
|
|
674
|
+
trigger: logout
|
|
675
|
+
|
|
676
|
+
flows:
|
|
677
|
+
- name: authenticate
|
|
678
|
+
steps: [validate_input, check_rate_limit, verify_credentials, create_session]
|
|
679
|
+
timeout_ms: 5000
|
|
680
|
+
|
|
681
|
+
failures:
|
|
682
|
+
- code: E001
|
|
683
|
+
condition: "rate_limit.exceeded"
|
|
684
|
+
recovery:
|
|
685
|
+
strategy: fallback
|
|
686
|
+
fallback_to: rate_limit_response
|
|
687
|
+
- code: E002
|
|
688
|
+
condition: "database.unavailable"
|
|
689
|
+
recovery:
|
|
690
|
+
strategy: retry
|
|
691
|
+
retry_count: 3
|
|
692
|
+
|
|
693
|
+
constraints:
|
|
694
|
+
latency_max_ms: 500
|
|
695
|
+
throughput_min_rps: 1000
|
|
696
|
+
availability_percent: 99.9
|
|
697
|
+
|
|
698
|
+
dependencies:
|
|
699
|
+
- name: user_db
|
|
700
|
+
type: database
|
|
701
|
+
contract: UserStore
|
|
702
|
+
- name: session_cache
|
|
703
|
+
type: cache
|
|
704
|
+
contract: KeyValueStore
|
|
705
|
+
- name: rate_limiter
|
|
706
|
+
type: service
|
|
707
|
+
contract: RateLimiter
|
|
708
|
+
|
|
709
|
+
observability:
|
|
710
|
+
metrics:
|
|
711
|
+
- name: auth_attempts
|
|
712
|
+
type: counter
|
|
713
|
+
labels: [status, method]
|
|
714
|
+
- name: auth_latency
|
|
715
|
+
type: histogram
|
|
716
|
+
unit: ms
|
|
717
|
+
logs:
|
|
718
|
+
level: info
|
|
719
|
+
include: [user_id, client_ip, duration_ms]
|
|
720
|
+
exclude: [password, token]
|
|
721
|
+
|
|
722
|
+
tests:
|
|
723
|
+
- name: happy_path
|
|
724
|
+
given:
|
|
725
|
+
credentials: {username: alice, password: secure_password_123}
|
|
726
|
+
client_ip: 192.168.1.1
|
|
727
|
+
expect:
|
|
728
|
+
auth_result.success: true
|
|
729
|
+
auth_result.token: "!null"
|
|
730
|
+
|
|
731
|
+
- name: wrong_password
|
|
732
|
+
given:
|
|
733
|
+
credentials: {username: alice, password: wrong_password}
|
|
734
|
+
client_ip: 192.168.1.1
|
|
735
|
+
expect:
|
|
736
|
+
auth_result.success: false
|
|
737
|
+
auth_result.error: INVALID_CREDENTIALS
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
## Validation Rules
|
|
743
|
+
|
|
744
|
+
A valid `logic.yaml` must pass these checks:
|
|
745
|
+
|
|
746
|
+
1. **Required fields present**: `meta`, `inputs`, `outputs`, `logic`
|
|
747
|
+
2. **Unique identifiers**: All `logic[].id`, `types` keys, `inputs[].name`, `outputs[].name` must be unique
|
|
748
|
+
3. **Type references**: All referenced types must be defined in `types` or be primitives
|
|
749
|
+
4. **Input/output connectivity**: Each `logic[].input` must reference a defined input or another logic block's output
|
|
750
|
+
5. **State transitions**: All states in `transitions` must be defined
|
|
751
|
+
6. **Test coverage**: Each `flow` should have at least one test case
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## Versioning
|
|
756
|
+
|
|
757
|
+
The spec uses semantic versioning:
|
|
758
|
+
|
|
759
|
+
- **Major**: Breaking changes to required structure
|
|
760
|
+
- **Minor**: New optional fields added
|
|
761
|
+
- **Patch**: Clarifications, no structural changes
|
|
762
|
+
|
|
763
|
+
Current version: **1.0.0**
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
## Tooling
|
|
768
|
+
|
|
769
|
+
Recommended tooling for `logic.yaml`:
|
|
770
|
+
|
|
771
|
+
| Tool | Purpose |
|
|
772
|
+
|------|---------|
|
|
773
|
+
| `logic-validate` | Validate spec against schema |
|
|
774
|
+
| `logic-generate` | Generate code from spec |
|
|
775
|
+
| `logic-docs` | Generate documentation |
|
|
776
|
+
| `logic-test` | Run built-in test cases |
|
|
777
|
+
| `logic-visualize` | Generate state/flow diagrams |
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## License
|
|
782
|
+
|
|
783
|
+
This specification is released under the MIT License.
|