kpt-kernel 0.1.0__tar.gz
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.
- kpt_kernel-0.1.0/PKG-INFO +75 -0
- kpt_kernel-0.1.0/README.md +65 -0
- kpt_kernel-0.1.0/fpt_client.py +34 -0
- kpt_kernel-0.1.0/fpt_daemon.py +369 -0
- kpt_kernel-0.1.0/kpt_kernel.egg-info/PKG-INFO +75 -0
- kpt_kernel-0.1.0/kpt_kernel.egg-info/SOURCES.txt +12 -0
- kpt_kernel-0.1.0/kpt_kernel.egg-info/dependency_links.txt +1 -0
- kpt_kernel-0.1.0/kpt_kernel.egg-info/entry_points.txt +3 -0
- kpt_kernel-0.1.0/kpt_kernel.egg-info/requires.txt +1 -0
- kpt_kernel-0.1.0/kpt_kernel.egg-info/top_level.txt +3 -0
- kpt_kernel-0.1.0/living_zero_core.py +188 -0
- kpt_kernel-0.1.0/pyproject.toml +22 -0
- kpt_kernel-0.1.0/setup.cfg +4 -0
- kpt_kernel-0.1.0/tests/test_fpt_kernel_control.py +57 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kpt_kernel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Homeostatic Feedback Processor Theory (FPT) control plane for Admission Gate
|
|
5
|
+
Author: ak-skwaa-mahawk
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: admission-gate>=0.4.0
|
|
10
|
+
|
|
11
|
+
# Fpt_kernel_daem_sqaw
|
|
12
|
+
|
|
13
|
+
A homeostatic Feedback Processor Theory (FPT) daemon that acts as a closed-loop dynamic controller for [Admission Gate](https://github.com/ak-skwaa-mahawk/admission-gate).
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
The daemon operates as an async Unix domain socket server, continuously sampling telemetry from gated executions and modulating admission limits across multiple operational surfaces via a tuned PID controller with sigmoidal damping.
|
|
18
|
+
|
|
19
|
+
[ Target Penalty: 0.0 ]
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌──────────────────────┐
|
|
23
|
+
│ Comparator │◄────────────────────────┐
|
|
24
|
+
└──────────────────────┘ │
|
|
25
|
+
│ Error e_t │
|
|
26
|
+
▼ │
|
|
27
|
+
┌──────────────────────┐ │
|
|
28
|
+
│ PID Controller │ │
|
|
29
|
+
│ (Anti-windup + D) │ │
|
|
30
|
+
└──────────────────────┘ │
|
|
31
|
+
│ Control Signal u_t │
|
|
32
|
+
▼ │
|
|
33
|
+
┌──────────────────────┐ │
|
|
34
|
+
│ Sigmoidal Damping │ │
|
|
35
|
+
│ Factor σ(u_t) │ │
|
|
36
|
+
└──────────────────────┘ │
|
|
37
|
+
│ Multi-Surface Actuation │
|
|
38
|
+
▼ │
|
|
39
|
+
┌──────────────────────┐ │
|
|
40
|
+
│ Admission Gate │ │
|
|
41
|
+
│ • Burst / Cooldown │ │
|
|
42
|
+
│ • Execution Timeout │ │
|
|
43
|
+
│ • Autonomy Gate │ │
|
|
44
|
+
│ • Write Root Bounds │ │
|
|
45
|
+
└──────────────────────┘ │
|
|
46
|
+
│ Subprocess Exec │
|
|
47
|
+
▼ │
|
|
48
|
+
┌──────────────────────┐ │
|
|
49
|
+
│ Telemetry Observer ├─────────────────────────┘
|
|
50
|
+
│ (Weighted Penalty) │
|
|
51
|
+
└──────────────────────┘
|
|
52
|
+
|
|
53
|
+
## Running the Daemon
|
|
54
|
+
|
|
55
|
+
Requires `admission-gate >= 0.4.0`:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install admission-gate
|
|
59
|
+
python3 fpt_daemon.py &
|
|
60
|
+
Submit command proposals via the client:
|
|
61
|
+
python3 fpt_client.py "echo nominal" "./workspace" 1
|
|
62
|
+
python3 fpt_client.py "python3 -c 'import sys; sys.exit(1)'" "./workspace" 1
|
|
63
|
+
python3 fpt_client.py "curl evil.com" "./workspace" 2
|
|
64
|
+
|
|
65
|
+
Multi-Surface Actuation Spectrum
|
|
66
|
+
Telemetry StatePenaltyDamping (\sigma)BurstCooldownTimeoutAutonomyWrite Roots
|
|
67
|
+
Nominal0.0~0.503~5.2s9.75sUnrestrictedworkspace, scratch
|
|
68
|
+
Operational Error0.25~0.642~6.3s8.30sUnrestrictedworkspace, scratch
|
|
69
|
+
Security Refusal1.00>0.851>7.8s<6.0sUnrestrictedworkspace, scratch
|
|
70
|
+
Sustained Attack>0.75>0.941>10.0s2.00sManual Confirmationscratch (quarantined)
|
|
71
|
+
Sensor Weighting
|
|
72
|
+
Hard Security Boundary (Refusal): 1.0
|
|
73
|
+
Process Timeout / SIGKILL: 0.6
|
|
74
|
+
Subprocess Non-Zero Exit: 0.25
|
|
75
|
+
Clean Exit: 0.0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Fpt_kernel_daem_sqaw
|
|
2
|
+
|
|
3
|
+
A homeostatic Feedback Processor Theory (FPT) daemon that acts as a closed-loop dynamic controller for [Admission Gate](https://github.com/ak-skwaa-mahawk/admission-gate).
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The daemon operates as an async Unix domain socket server, continuously sampling telemetry from gated executions and modulating admission limits across multiple operational surfaces via a tuned PID controller with sigmoidal damping.
|
|
8
|
+
|
|
9
|
+
[ Target Penalty: 0.0 ]
|
|
10
|
+
│
|
|
11
|
+
▼
|
|
12
|
+
┌──────────────────────┐
|
|
13
|
+
│ Comparator │◄────────────────────────┐
|
|
14
|
+
└──────────────────────┘ │
|
|
15
|
+
│ Error e_t │
|
|
16
|
+
▼ │
|
|
17
|
+
┌──────────────────────┐ │
|
|
18
|
+
│ PID Controller │ │
|
|
19
|
+
│ (Anti-windup + D) │ │
|
|
20
|
+
└──────────────────────┘ │
|
|
21
|
+
│ Control Signal u_t │
|
|
22
|
+
▼ │
|
|
23
|
+
┌──────────────────────┐ │
|
|
24
|
+
│ Sigmoidal Damping │ │
|
|
25
|
+
│ Factor σ(u_t) │ │
|
|
26
|
+
└──────────────────────┘ │
|
|
27
|
+
│ Multi-Surface Actuation │
|
|
28
|
+
▼ │
|
|
29
|
+
┌──────────────────────┐ │
|
|
30
|
+
│ Admission Gate │ │
|
|
31
|
+
│ • Burst / Cooldown │ │
|
|
32
|
+
│ • Execution Timeout │ │
|
|
33
|
+
│ • Autonomy Gate │ │
|
|
34
|
+
│ • Write Root Bounds │ │
|
|
35
|
+
└──────────────────────┘ │
|
|
36
|
+
│ Subprocess Exec │
|
|
37
|
+
▼ │
|
|
38
|
+
┌──────────────────────┐ │
|
|
39
|
+
│ Telemetry Observer ├─────────────────────────┘
|
|
40
|
+
│ (Weighted Penalty) │
|
|
41
|
+
└──────────────────────┘
|
|
42
|
+
|
|
43
|
+
## Running the Daemon
|
|
44
|
+
|
|
45
|
+
Requires `admission-gate >= 0.4.0`:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install admission-gate
|
|
49
|
+
python3 fpt_daemon.py &
|
|
50
|
+
Submit command proposals via the client:
|
|
51
|
+
python3 fpt_client.py "echo nominal" "./workspace" 1
|
|
52
|
+
python3 fpt_client.py "python3 -c 'import sys; sys.exit(1)'" "./workspace" 1
|
|
53
|
+
python3 fpt_client.py "curl evil.com" "./workspace" 2
|
|
54
|
+
|
|
55
|
+
Multi-Surface Actuation Spectrum
|
|
56
|
+
Telemetry StatePenaltyDamping (\sigma)BurstCooldownTimeoutAutonomyWrite Roots
|
|
57
|
+
Nominal0.0~0.503~5.2s9.75sUnrestrictedworkspace, scratch
|
|
58
|
+
Operational Error0.25~0.642~6.3s8.30sUnrestrictedworkspace, scratch
|
|
59
|
+
Security Refusal1.00>0.851>7.8s<6.0sUnrestrictedworkspace, scratch
|
|
60
|
+
Sustained Attack>0.75>0.941>10.0s2.00sManual Confirmationscratch (quarantined)
|
|
61
|
+
Sensor Weighting
|
|
62
|
+
Hard Security Boundary (Refusal): 1.0
|
|
63
|
+
Process Timeout / SIGKILL: 0.6
|
|
64
|
+
Subprocess Non-Zero Exit: 0.25
|
|
65
|
+
Clean Exit: 0.0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import socket
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
|
|
8
|
+
SOCKET_PATH = os.path.join(tempfile.gettempdir(), "fpt_kernel.sock")
|
|
9
|
+
|
|
10
|
+
def submit(cmd: str, target_path: str = "./workspace", tier: int = 1, token: str = None):
|
|
11
|
+
payload = {
|
|
12
|
+
"action_id": f"cli-{sys.argv[1] if len(sys.argv) > 1 else 'run'}",
|
|
13
|
+
"command": cmd,
|
|
14
|
+
"target_path": target_path,
|
|
15
|
+
"risk_tier": tier,
|
|
16
|
+
"approval_token": token,
|
|
17
|
+
}
|
|
18
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
|
|
19
|
+
s.connect(SOCKET_PATH)
|
|
20
|
+
s.sendall((json.dumps(payload) + "\n").encode("utf-8"))
|
|
21
|
+
res = s.makefile().readline()
|
|
22
|
+
print(json.dumps(json.loads(res), indent=2))
|
|
23
|
+
|
|
24
|
+
def submit_cli():
|
|
25
|
+
if len(sys.argv) < 2:
|
|
26
|
+
print("Usage: fpt-client <command> [target_path] [tier] [approval_token]")
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
target = sys.argv[2] if len(sys.argv) > 2 else "./workspace"
|
|
29
|
+
tier = int(sys.argv[3]) if len(sys.argv) > 3 else 1
|
|
30
|
+
token = sys.argv[4] if len(sys.argv) > 4 else None
|
|
31
|
+
submit(sys.argv[1], target, tier, token)
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
submit_cli()
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Fpt_kernel_daem_sqaw
|
|
4
|
+
Homeostatic Feedback Processor Theory (FPT) daemon listening over a Unix socket.
|
|
5
|
+
Controls execution limits dynamically via Admission Gate using a multi-surface PID controller
|
|
6
|
+
with atomic state persistence, differentiated telemetry classification, and Living Zero CA3 handshake approval.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import collections
|
|
11
|
+
import json
|
|
12
|
+
import math
|
|
13
|
+
import os
|
|
14
|
+
import signal
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
import time
|
|
18
|
+
from dataclasses import asdict, dataclass
|
|
19
|
+
from typing import Deque, Dict, List, Optional, Tuple
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from admission_gate import (
|
|
23
|
+
ActionProposal,
|
|
24
|
+
ExecConfig,
|
|
25
|
+
GateConfig,
|
|
26
|
+
ProcessConfig,
|
|
27
|
+
RateLimitConfig,
|
|
28
|
+
gated_shell,
|
|
29
|
+
)
|
|
30
|
+
from living_zero_core import CA3Dynamics, OwnershipEncoder, OwnershipMemory, OwnershipProjector, normalize
|
|
31
|
+
|
|
32
|
+
SOCKET_PATH = os.path.join(tempfile.gettempdir(), "fpt_kernel.sock")
|
|
33
|
+
STATE_PATH = os.path.join(os.path.expanduser("~"), ".fpt_daemon_state.json")
|
|
34
|
+
|
|
35
|
+
# Sensor weights
|
|
36
|
+
WEIGHT_POLICY_REFUSAL = 1.0 # Hard security boundary violation
|
|
37
|
+
WEIGHT_EXEC_TIMEOUT = 0.60 # Subprocess hung / killed by timeout
|
|
38
|
+
WEIGHT_PROCESS_FAULT = 0.25 # Operational non-zero exit code
|
|
39
|
+
WEIGHT_CLEAN_SUCCESS = 0.0 # Nominal execution
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class TelemetryEvent:
|
|
43
|
+
timestamp: float
|
|
44
|
+
category: str
|
|
45
|
+
penalty: float
|
|
46
|
+
|
|
47
|
+
class FPTAdmissionController:
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
base_config: GateConfig,
|
|
51
|
+
window_size: int = 12,
|
|
52
|
+
target_penalty: float = 0.0,
|
|
53
|
+
gain_p: float = 1.6,
|
|
54
|
+
gain_i: float = 0.15,
|
|
55
|
+
gain_d: float = 0.7,
|
|
56
|
+
state_file: str = STATE_PATH,
|
|
57
|
+
):
|
|
58
|
+
self.config = base_config
|
|
59
|
+
self.window_size = window_size
|
|
60
|
+
self.target = target_penalty
|
|
61
|
+
self.state_file = state_file
|
|
62
|
+
|
|
63
|
+
# PID Gains
|
|
64
|
+
self.kp = gain_p
|
|
65
|
+
self.ki = gain_i
|
|
66
|
+
self.kd = gain_d
|
|
67
|
+
|
|
68
|
+
# Internal State Registers
|
|
69
|
+
self.history: Deque[TelemetryEvent] = collections.deque(maxlen=window_size)
|
|
70
|
+
self.integral_error = 0.0
|
|
71
|
+
self.last_error = 0.0
|
|
72
|
+
self.last_timestamp = time.monotonic()
|
|
73
|
+
self.cycle_count = 0
|
|
74
|
+
self.current_damping = 0.50
|
|
75
|
+
|
|
76
|
+
# Nominal Baselines
|
|
77
|
+
self.base_burst = base_config.rate_limit.burst_threshold
|
|
78
|
+
self.base_cooldown = base_config.rate_limit.tier3_cooldown_seconds
|
|
79
|
+
self.base_timeout = base_config.process.timeout_seconds
|
|
80
|
+
self.nominal_write_roots = list(base_config.write_roots)
|
|
81
|
+
|
|
82
|
+
# Initialize Living Zero Handshake Engine (CA3 attractor)
|
|
83
|
+
self.N = 128
|
|
84
|
+
self.d = 32
|
|
85
|
+
self.oproj = OwnershipProjector(N=self.N, d=self.d, seed=42)
|
|
86
|
+
self.memory = OwnershipMemory(N=self.N, ownership_projector=self.oproj, eta=0.8, gamma=2.0)
|
|
87
|
+
self.ca3 = CA3Dynamics(N=self.N, memory=self.memory, tau=0.05, dt=0.01)
|
|
88
|
+
self.encoder = OwnershipEncoder(d=self.d)
|
|
89
|
+
|
|
90
|
+
# Baseline Handshake Authority Anchor
|
|
91
|
+
self.human_authority_tag = "authority:human_in_the_loop"
|
|
92
|
+
u_auth = self.encoder.encode(self.human_authority_tag)
|
|
93
|
+
self.authority_pattern = self.oproj.get_basis_vector(u_auth)
|
|
94
|
+
self.memory.encode(self.authority_pattern, raw_tag=self.human_authority_tag)
|
|
95
|
+
self.ca3.encode_pattern(pid=0, p_vec=self.authority_pattern, strength=1.5)
|
|
96
|
+
|
|
97
|
+
self.load_state()
|
|
98
|
+
|
|
99
|
+
def load_state(self):
|
|
100
|
+
if not os.path.exists(self.state_file):
|
|
101
|
+
return
|
|
102
|
+
try:
|
|
103
|
+
with open(self.state_file, "r", encoding="utf-8") as f:
|
|
104
|
+
data = json.load(f)
|
|
105
|
+
self.cycle_count = data.get("cycle_count", 0)
|
|
106
|
+
self.integral_error = float(data.get("integral_error", 0.0))
|
|
107
|
+
self.last_error = float(data.get("last_error", 0.0))
|
|
108
|
+
self.current_damping = float(data.get("damping", 0.50))
|
|
109
|
+
for item in data.get("history", []):
|
|
110
|
+
self.history.append(TelemetryEvent(
|
|
111
|
+
timestamp=item["timestamp"],
|
|
112
|
+
category=item["category"],
|
|
113
|
+
penalty=item["penalty"],
|
|
114
|
+
))
|
|
115
|
+
self.actuate(self.current_damping)
|
|
116
|
+
print(f"[FPT Daemon] Restored state: cycle={self.cycle_count}, damping={self.current_damping:.3f}")
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"[FPT Daemon] Warning: State restore failed: {e}")
|
|
119
|
+
|
|
120
|
+
def save_state(self):
|
|
121
|
+
try:
|
|
122
|
+
temp_target = f"{self.state_file}.tmp"
|
|
123
|
+
payload = {
|
|
124
|
+
"cycle_count": self.cycle_count,
|
|
125
|
+
"integral_error": self.integral_error,
|
|
126
|
+
"last_error": self.last_error,
|
|
127
|
+
"damping": self.current_damping,
|
|
128
|
+
"history": [asdict(ev) for ev in self.history],
|
|
129
|
+
"saved_at": time.time(),
|
|
130
|
+
}
|
|
131
|
+
with open(temp_target, "w", encoding="utf-8") as f:
|
|
132
|
+
json.dump(payload, f, indent=2)
|
|
133
|
+
os.replace(temp_target, self.state_file)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
print(f"[FPT Daemon] Error saving state: {e}")
|
|
136
|
+
|
|
137
|
+
def verify_handshake(self, token_str: Optional[str]) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
Validates an out-of-band authorization token against the CA3 dynamical attractor.
|
|
140
|
+
Accepts raw authority strings or signed JSON payloads from Human_inthe_loop.
|
|
141
|
+
"""
|
|
142
|
+
if not token_str:
|
|
143
|
+
return False
|
|
144
|
+
try:
|
|
145
|
+
# If passed as a JSON handshake token from Human_inthe_loop, extract authority_tag
|
|
146
|
+
candidate_tag = token_str
|
|
147
|
+
if token_str.strip().startswith("{"):
|
|
148
|
+
payload = json.loads(token_str)
|
|
149
|
+
candidate_tag = payload.get("authority_tag", "")
|
|
150
|
+
|
|
151
|
+
u = self.encoder.encode(candidate_tag)
|
|
152
|
+
w_hat = self.oproj.get_basis_vector(u)
|
|
153
|
+
trig, theta, r = self.ca3.reward_handshake(w_hat, target_pid=0, eps=0.35 * math.pi)
|
|
154
|
+
return trig
|
|
155
|
+
except Exception:
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
def observe(self, executed: bool, exit_code: int, raw_output: str) -> Tuple[str, float]:
|
|
159
|
+
now = time.time()
|
|
160
|
+
if not executed:
|
|
161
|
+
category = "policy_block"
|
|
162
|
+
penalty = WEIGHT_POLICY_REFUSAL
|
|
163
|
+
elif exit_code in (124, -9) or "timed out" in raw_output.lower():
|
|
164
|
+
category = "timeout"
|
|
165
|
+
penalty = WEIGHT_EXEC_TIMEOUT
|
|
166
|
+
elif exit_code != 0:
|
|
167
|
+
category = "proc_error"
|
|
168
|
+
penalty = WEIGHT_PROCESS_FAULT
|
|
169
|
+
else:
|
|
170
|
+
category = "nominal"
|
|
171
|
+
penalty = WEIGHT_CLEAN_SUCCESS
|
|
172
|
+
|
|
173
|
+
self.history.append(TelemetryEvent(timestamp=now, category=category, penalty=penalty))
|
|
174
|
+
running_penalty = sum(ev.penalty for ev in self.history) / len(self.history)
|
|
175
|
+
return category, running_penalty
|
|
176
|
+
|
|
177
|
+
def compute_damping(self, current_penalty: float) -> Tuple[float, float, float, float, float]:
|
|
178
|
+
now = time.monotonic()
|
|
179
|
+
dt = max(0.05, now - self.last_timestamp)
|
|
180
|
+
self.last_timestamp = now
|
|
181
|
+
|
|
182
|
+
error = current_penalty - self.target
|
|
183
|
+
self.integral_error = max(-1.0, min(1.0, self.integral_error + (error * 0.1)))
|
|
184
|
+
|
|
185
|
+
if self.cycle_count <= 1:
|
|
186
|
+
d_error = 0.0
|
|
187
|
+
else:
|
|
188
|
+
d_error = max(-2.0, min(2.0, (error - self.last_error) / dt))
|
|
189
|
+
self.last_error = error
|
|
190
|
+
|
|
191
|
+
control_signal = (self.kp * error) + (self.ki * self.integral_error) + (self.kd * d_error)
|
|
192
|
+
damping = 1.0 / (1.0 + math.exp(-(control_signal - 0.4) * 2.5))
|
|
193
|
+
self.current_damping = damping
|
|
194
|
+
return error, self.integral_error, d_error, control_signal, damping
|
|
195
|
+
|
|
196
|
+
def actuate(self, damping: float) -> Dict:
|
|
197
|
+
active_burst = max(1, int(round(self.base_burst * (1.0 - (0.8 * damping)))))
|
|
198
|
+
active_cooldown = self.base_cooldown * (1.0 + (5.0 * damping))
|
|
199
|
+
self.config.rate_limit.burst_threshold = active_burst
|
|
200
|
+
self.config.rate_limit.tier3_cooldown_seconds = active_cooldown
|
|
201
|
+
|
|
202
|
+
active_timeout = max(2.0, self.base_timeout * (1.0 - (0.75 * damping)))
|
|
203
|
+
self.config.process.timeout_seconds = active_timeout
|
|
204
|
+
|
|
205
|
+
require_confirm = damping > 0.85
|
|
206
|
+
self.config.require_confirm = require_confirm
|
|
207
|
+
|
|
208
|
+
if damping > 0.92:
|
|
209
|
+
active_write_roots = [p for p in self.nominal_write_roots if "scratch" in p]
|
|
210
|
+
if not active_write_roots:
|
|
211
|
+
active_write_roots = ["./scratch"]
|
|
212
|
+
else:
|
|
213
|
+
active_write_roots = list(self.nominal_write_roots)
|
|
214
|
+
self.config.write_roots = active_write_roots
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
"active_burst": active_burst,
|
|
218
|
+
"active_cooldown_sec": round(active_cooldown, 2),
|
|
219
|
+
"active_timeout_sec": round(active_timeout, 2),
|
|
220
|
+
"require_confirm": require_confirm,
|
|
221
|
+
"active_write_roots": active_write_roots,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
def step(self, proposal: ActionProposal, approval_token: Optional[str] = None) -> Tuple[bool, str, int, Dict]:
|
|
225
|
+
self.cycle_count += 1
|
|
226
|
+
|
|
227
|
+
# Tag proposal with sovereign proof if provided
|
|
228
|
+
if approval_token and approval_token.strip().startswith("{"):
|
|
229
|
+
try:
|
|
230
|
+
tok_data = json.loads(approval_token)
|
|
231
|
+
proposal.action_id = f"{proposal.action_id}:sov={tok_data.get("sovereign_id", "anon")}"
|
|
232
|
+
except Exception:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
# Check if high damping requires confirmation
|
|
236
|
+
if self.config.require_confirm:
|
|
237
|
+
authorized = self.verify_handshake(approval_token)
|
|
238
|
+
if not authorized:
|
|
239
|
+
# Challenge issued back to client/human
|
|
240
|
+
category, observed_penalty = self.observe(False, -1, "Confirmation required: awaiting Human_inthe_loop token")
|
|
241
|
+
error, i_err, d_err, u_sig, damping = self.compute_damping(observed_penalty)
|
|
242
|
+
actuation = self.actuate(damping)
|
|
243
|
+
self.save_state()
|
|
244
|
+
telemetry = {
|
|
245
|
+
"cycle": self.cycle_count,
|
|
246
|
+
"event_category": "challenge_pending",
|
|
247
|
+
"observed_penalty": round(observed_penalty, 3),
|
|
248
|
+
"damping": round(damping, 3),
|
|
249
|
+
"actuation": actuation,
|
|
250
|
+
"challenge": {
|
|
251
|
+
"action_id": proposal.action_id,
|
|
252
|
+
"command": proposal.command,
|
|
253
|
+
"required_authority": self.human_authority_tag,
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return False, "E_CHALLENGE_REQUIRED: Provide Human_inthe_loop handshake token", -1, telemetry
|
|
257
|
+
|
|
258
|
+
# Single-shot authorization override for this cycle
|
|
259
|
+
self.config.require_confirm = False
|
|
260
|
+
|
|
261
|
+
executed, result, exit_code = gated_shell(proposal, config=self.config)
|
|
262
|
+
category, observed_penalty = self.observe(executed, exit_code, result)
|
|
263
|
+
error, i_err, d_err, u_sig, damping = self.compute_damping(observed_penalty)
|
|
264
|
+
actuation = self.actuate(damping)
|
|
265
|
+
|
|
266
|
+
self.save_state()
|
|
267
|
+
|
|
268
|
+
telemetry = {
|
|
269
|
+
"cycle": self.cycle_count,
|
|
270
|
+
"event_category": category,
|
|
271
|
+
"observed_penalty": round(observed_penalty, 3),
|
|
272
|
+
"control_signal": round(u_sig, 3),
|
|
273
|
+
"damping": round(damping, 3),
|
|
274
|
+
"actuation": actuation,
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return executed, result, exit_code, telemetry
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter, controller: FPTAdmissionController):
|
|
281
|
+
data = await reader.readline()
|
|
282
|
+
if not data:
|
|
283
|
+
writer.close()
|
|
284
|
+
await writer.wait_closed()
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
req = json.loads(data.decode("utf-8"))
|
|
289
|
+
proposal = ActionProposal(
|
|
290
|
+
action_id=req.get("action_id", "anon"),
|
|
291
|
+
command=req["command"],
|
|
292
|
+
target_path=req.get("target_path", "./workspace"),
|
|
293
|
+
risk_tier=int(req.get("risk_tier", 1)),
|
|
294
|
+
)
|
|
295
|
+
approval_token = req.get("approval_token")
|
|
296
|
+
|
|
297
|
+
executed, result, exit_code, telemetry = controller.step(proposal, approval_token)
|
|
298
|
+
|
|
299
|
+
response = {
|
|
300
|
+
"status": "ok" if executed and exit_code == 0 else "blocked_or_failed",
|
|
301
|
+
"executed": executed,
|
|
302
|
+
"exit_code": exit_code,
|
|
303
|
+
"output": result.strip(),
|
|
304
|
+
"fpt_telemetry": telemetry,
|
|
305
|
+
}
|
|
306
|
+
except Exception as e:
|
|
307
|
+
response = {"status": "error", "error": str(e)}
|
|
308
|
+
|
|
309
|
+
writer.write((json.dumps(response) + "\n").encode("utf-8"))
|
|
310
|
+
await writer.drain()
|
|
311
|
+
writer.close()
|
|
312
|
+
await writer.wait_closed()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
async def main():
|
|
316
|
+
if os.path.exists(SOCKET_PATH):
|
|
317
|
+
try:
|
|
318
|
+
os.remove(SOCKET_PATH)
|
|
319
|
+
except OSError:
|
|
320
|
+
pass
|
|
321
|
+
|
|
322
|
+
os.makedirs("./repo", exist_ok=True)
|
|
323
|
+
os.makedirs("./workspace", exist_ok=True)
|
|
324
|
+
os.makedirs("./scratch", exist_ok=True)
|
|
325
|
+
|
|
326
|
+
config = GateConfig(
|
|
327
|
+
read_roots=["./repo"],
|
|
328
|
+
write_roots=["./workspace", "./scratch"],
|
|
329
|
+
require_confirm=False,
|
|
330
|
+
exec_policy=ExecConfig(
|
|
331
|
+
allow=["echo", "ls", "python", "python3", "git", "cat", "touch", "pytest", "sleep"],
|
|
332
|
+
deny=["curl", "wget", "ssh", "nc", "bash", "sh", "sudo"],
|
|
333
|
+
),
|
|
334
|
+
rate_limit=RateLimitConfig(
|
|
335
|
+
enabled=True,
|
|
336
|
+
max_requests_per_minute=60,
|
|
337
|
+
burst_threshold=5,
|
|
338
|
+
tier3_cooldown_seconds=1.5,
|
|
339
|
+
),
|
|
340
|
+
process=ProcessConfig(timeout_seconds=12.0, scrub_env=True),
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
controller = FPTAdmissionController(config)
|
|
344
|
+
server = await asyncio.start_unix_server(
|
|
345
|
+
lambda r, w: handle_client(r, w, controller),
|
|
346
|
+
path=SOCKET_PATH,
|
|
347
|
+
)
|
|
348
|
+
print(f"[FPT Daemon] Online. CA3-governed PID running on {SOCKET_PATH}")
|
|
349
|
+
|
|
350
|
+
loop = asyncio.get_running_loop()
|
|
351
|
+
stop = loop.create_future()
|
|
352
|
+
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
353
|
+
loop.add_signal_handler(sig, stop.set_result, None)
|
|
354
|
+
|
|
355
|
+
await stop
|
|
356
|
+
print("\n[FPT Daemon] Shutting down...")
|
|
357
|
+
server.close()
|
|
358
|
+
await server.wait_closed()
|
|
359
|
+
if os.path.exists(SOCKET_PATH):
|
|
360
|
+
try:
|
|
361
|
+
os.remove(SOCKET_PATH)
|
|
362
|
+
except OSError:
|
|
363
|
+
pass
|
|
364
|
+
|
|
365
|
+
def run():
|
|
366
|
+
asyncio.run(main())
|
|
367
|
+
|
|
368
|
+
if __name__ == "__main__":
|
|
369
|
+
run()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kpt_kernel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Homeostatic Feedback Processor Theory (FPT) control plane for Admission Gate
|
|
5
|
+
Author: ak-skwaa-mahawk
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: admission-gate>=0.4.0
|
|
10
|
+
|
|
11
|
+
# Fpt_kernel_daem_sqaw
|
|
12
|
+
|
|
13
|
+
A homeostatic Feedback Processor Theory (FPT) daemon that acts as a closed-loop dynamic controller for [Admission Gate](https://github.com/ak-skwaa-mahawk/admission-gate).
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
The daemon operates as an async Unix domain socket server, continuously sampling telemetry from gated executions and modulating admission limits across multiple operational surfaces via a tuned PID controller with sigmoidal damping.
|
|
18
|
+
|
|
19
|
+
[ Target Penalty: 0.0 ]
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌──────────────────────┐
|
|
23
|
+
│ Comparator │◄────────────────────────┐
|
|
24
|
+
└──────────────────────┘ │
|
|
25
|
+
│ Error e_t │
|
|
26
|
+
▼ │
|
|
27
|
+
┌──────────────────────┐ │
|
|
28
|
+
│ PID Controller │ │
|
|
29
|
+
│ (Anti-windup + D) │ │
|
|
30
|
+
└──────────────────────┘ │
|
|
31
|
+
│ Control Signal u_t │
|
|
32
|
+
▼ │
|
|
33
|
+
┌──────────────────────┐ │
|
|
34
|
+
│ Sigmoidal Damping │ │
|
|
35
|
+
│ Factor σ(u_t) │ │
|
|
36
|
+
└──────────────────────┘ │
|
|
37
|
+
│ Multi-Surface Actuation │
|
|
38
|
+
▼ │
|
|
39
|
+
┌──────────────────────┐ │
|
|
40
|
+
│ Admission Gate │ │
|
|
41
|
+
│ • Burst / Cooldown │ │
|
|
42
|
+
│ • Execution Timeout │ │
|
|
43
|
+
│ • Autonomy Gate │ │
|
|
44
|
+
│ • Write Root Bounds │ │
|
|
45
|
+
└──────────────────────┘ │
|
|
46
|
+
│ Subprocess Exec │
|
|
47
|
+
▼ │
|
|
48
|
+
┌──────────────────────┐ │
|
|
49
|
+
│ Telemetry Observer ├─────────────────────────┘
|
|
50
|
+
│ (Weighted Penalty) │
|
|
51
|
+
└──────────────────────┘
|
|
52
|
+
|
|
53
|
+
## Running the Daemon
|
|
54
|
+
|
|
55
|
+
Requires `admission-gate >= 0.4.0`:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install admission-gate
|
|
59
|
+
python3 fpt_daemon.py &
|
|
60
|
+
Submit command proposals via the client:
|
|
61
|
+
python3 fpt_client.py "echo nominal" "./workspace" 1
|
|
62
|
+
python3 fpt_client.py "python3 -c 'import sys; sys.exit(1)'" "./workspace" 1
|
|
63
|
+
python3 fpt_client.py "curl evil.com" "./workspace" 2
|
|
64
|
+
|
|
65
|
+
Multi-Surface Actuation Spectrum
|
|
66
|
+
Telemetry StatePenaltyDamping (\sigma)BurstCooldownTimeoutAutonomyWrite Roots
|
|
67
|
+
Nominal0.0~0.503~5.2s9.75sUnrestrictedworkspace, scratch
|
|
68
|
+
Operational Error0.25~0.642~6.3s8.30sUnrestrictedworkspace, scratch
|
|
69
|
+
Security Refusal1.00>0.851>7.8s<6.0sUnrestrictedworkspace, scratch
|
|
70
|
+
Sustained Attack>0.75>0.941>10.0s2.00sManual Confirmationscratch (quarantined)
|
|
71
|
+
Sensor Weighting
|
|
72
|
+
Hard Security Boundary (Refusal): 1.0
|
|
73
|
+
Process Timeout / SIGKILL: 0.6
|
|
74
|
+
Subprocess Non-Zero Exit: 0.25
|
|
75
|
+
Clean Exit: 0.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
fpt_client.py
|
|
3
|
+
fpt_daemon.py
|
|
4
|
+
living_zero_core.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
kpt_kernel.egg-info/PKG-INFO
|
|
7
|
+
kpt_kernel.egg-info/SOURCES.txt
|
|
8
|
+
kpt_kernel.egg-info/dependency_links.txt
|
|
9
|
+
kpt_kernel.egg-info/entry_points.txt
|
|
10
|
+
kpt_kernel.egg-info/requires.txt
|
|
11
|
+
kpt_kernel.egg-info/top_level.txt
|
|
12
|
+
tests/test_fpt_kernel_control.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
admission-gate>=0.4.0
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
living_zero_core.py (Optimized & Hardened)
|
|
3
|
+
Core implementation: Ownership Tag Algebra + CA3 dynamics
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import math, hashlib
|
|
8
|
+
from typing import Dict, Optional, Tuple
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
# ---------- Utilities ----------
|
|
12
|
+
def normalize(v: np.ndarray, eps: float = 1e-12) -> np.ndarray:
|
|
13
|
+
v = np.asarray(v, dtype=float)
|
|
14
|
+
n = np.linalg.norm(v)
|
|
15
|
+
return v if n < eps else v / n
|
|
16
|
+
|
|
17
|
+
# ---------- Ownership Encoder & Projector ----------
|
|
18
|
+
class OwnershipEncoder:
|
|
19
|
+
def __init__(self, d: int = 64):
|
|
20
|
+
self.d = int(d)
|
|
21
|
+
|
|
22
|
+
def _seed_from_raw(self, raw) -> int:
|
|
23
|
+
h = hashlib.sha256(str(raw).encode("utf-8")).digest()
|
|
24
|
+
return int.from_bytes(h[:8], byteorder="big")
|
|
25
|
+
|
|
26
|
+
def encode(self, raw) -> np.ndarray:
|
|
27
|
+
seed = self._seed_from_raw(raw)
|
|
28
|
+
rng = np.random.default_rng(seed)
|
|
29
|
+
v = rng.normal(size=(self.d,))
|
|
30
|
+
return normalize(v)
|
|
31
|
+
|
|
32
|
+
class OwnershipProjector:
|
|
33
|
+
def __init__(self, N: int, d: int = 64, seed: int = 0):
|
|
34
|
+
self.N = int(N)
|
|
35
|
+
self.d = int(d)
|
|
36
|
+
if self.d > self.N:
|
|
37
|
+
raise ValueError(f"Tag dimension d ({self.d}) cannot exceed state dimension N ({self.N})")
|
|
38
|
+
|
|
39
|
+
rng = np.random.default_rng(seed)
|
|
40
|
+
Q_random = rng.normal(size=(self.N, self.d))
|
|
41
|
+
self.Q, _ = np.linalg.qr(Q_random, mode="reduced")
|
|
42
|
+
|
|
43
|
+
def get_basis_vector(self, ownership_vector: np.ndarray) -> np.ndarray:
|
|
44
|
+
w = self.Q @ ownership_vector
|
|
45
|
+
return normalize(w)
|
|
46
|
+
|
|
47
|
+
def projector(self, ownership_vector: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
48
|
+
w_hat = self.get_basis_vector(ownership_vector)
|
|
49
|
+
Phi = np.outer(w_hat, w_hat)
|
|
50
|
+
return Phi, w_hat
|
|
51
|
+
|
|
52
|
+
# ---------- Ownership-aware associative memory ----------
|
|
53
|
+
class OwnershipMemory:
|
|
54
|
+
def __init__(self, N: int, ownership_projector: OwnershipProjector, eta=1e-3, gamma=1.0, weight_decay=0.0):
|
|
55
|
+
self.N = int(N)
|
|
56
|
+
self.Oproj = ownership_projector
|
|
57
|
+
self.encoder = OwnershipEncoder(d=self.Oproj.d)
|
|
58
|
+
self.W = np.zeros((self.N, self.N), dtype=float)
|
|
59
|
+
self.eta = float(eta)
|
|
60
|
+
self.gamma = float(gamma)
|
|
61
|
+
self.weight_decay = float(weight_decay)
|
|
62
|
+
self._tag_cache: Dict[str, np.ndarray] = {}
|
|
63
|
+
|
|
64
|
+
def _get_w_hat(self, raw_tag) -> np.ndarray:
|
|
65
|
+
tag_str = str(raw_tag)
|
|
66
|
+
if tag_str not in self._tag_cache:
|
|
67
|
+
u = self.encoder.encode(raw_tag)
|
|
68
|
+
self._tag_cache[tag_str] = self.Oproj.get_basis_vector(u)
|
|
69
|
+
return self._tag_cache[tag_str]
|
|
70
|
+
|
|
71
|
+
def encode(self, s: np.ndarray, raw_tag=None):
|
|
72
|
+
s = np.asarray(s, dtype=float)
|
|
73
|
+
if raw_tag is None:
|
|
74
|
+
v = s
|
|
75
|
+
else:
|
|
76
|
+
w_hat = self._get_w_hat(raw_tag)
|
|
77
|
+
v = s + self.gamma * np.dot(w_hat, s) * w_hat
|
|
78
|
+
|
|
79
|
+
Delta = self.eta * np.outer(v, v)
|
|
80
|
+
self.W += Delta
|
|
81
|
+
|
|
82
|
+
if self.weight_decay > 0.0:
|
|
83
|
+
self.W *= (1.0 - self.weight_decay)
|
|
84
|
+
|
|
85
|
+
self.W = 0.5 * (self.W + self.W.T)
|
|
86
|
+
|
|
87
|
+
def recall_iter(self, x0: np.ndarray, steps: int = 10, bias_tag=None, beta: float = 0.0, activation=np.tanh) -> np.ndarray:
|
|
88
|
+
x = np.array(x0, dtype=float)
|
|
89
|
+
w_hat = self._get_w_hat(bias_tag) if (bias_tag is not None and beta != 0.0) else None
|
|
90
|
+
|
|
91
|
+
for _ in range(steps):
|
|
92
|
+
u = self.W @ x
|
|
93
|
+
if w_hat is not None:
|
|
94
|
+
u = u + beta * np.dot(w_hat, u) * w_hat
|
|
95
|
+
x = activation(u)
|
|
96
|
+
return x
|
|
97
|
+
|
|
98
|
+
def selective_revoke(self, raw_tag, rho: float = 1.0):
|
|
99
|
+
w_hat = self._get_w_hat(raw_tag)
|
|
100
|
+
W_w = self.W @ w_hat
|
|
101
|
+
w_W_w = float(np.dot(w_hat, W_w))
|
|
102
|
+
|
|
103
|
+
self.W -= rho * (np.outer(W_w, w_hat) + np.outer(w_hat, W_w))
|
|
104
|
+
self.W += (rho**2 * w_W_w) * np.outer(w_hat, w_hat)
|
|
105
|
+
self.W = 0.5 * (self.W + self.W.T)
|
|
106
|
+
|
|
107
|
+
def tag_similarity(self, raw_tag1, raw_tag2) -> float:
|
|
108
|
+
w1 = self._get_w_hat(raw_tag1)
|
|
109
|
+
w2 = self._get_w_hat(raw_tag2)
|
|
110
|
+
return float(np.dot(w1, w2))**2
|
|
111
|
+
|
|
112
|
+
# ---------- CA3 Dynamics & Living Zero ----------
|
|
113
|
+
class CA3Dynamics:
|
|
114
|
+
def __init__(self, N: int, memory: OwnershipMemory, tau: float = 0.1, dt: float = 0.01, alpha_max: float = 2.0):
|
|
115
|
+
self.N = int(N)
|
|
116
|
+
self.memory = memory
|
|
117
|
+
self.tau = float(tau)
|
|
118
|
+
self.dt = float(dt)
|
|
119
|
+
self.alpha_max = float(alpha_max)
|
|
120
|
+
self.alpha: Dict[int, float] = {}
|
|
121
|
+
self.patterns: Dict[int, np.ndarray] = {}
|
|
122
|
+
self.P: float = 0.0
|
|
123
|
+
|
|
124
|
+
def energy_grad(self, x: np.ndarray) -> np.ndarray:
|
|
125
|
+
grad = x.copy()
|
|
126
|
+
for mu, alpha_mu in self.alpha.items():
|
|
127
|
+
p = self.patterns[mu]
|
|
128
|
+
grad -= alpha_mu * np.dot(p, x) * p
|
|
129
|
+
return grad
|
|
130
|
+
|
|
131
|
+
def step(self, x: np.ndarray, Vd: float = 1.0, R: float = 1.0, b: Optional[np.ndarray] = None, noise_std: float = 0.0) -> np.ndarray:
|
|
132
|
+
g = float(Vd) * float(R)
|
|
133
|
+
gradE = self.energy_grad(x)
|
|
134
|
+
raw_drive = self.memory.W @ x if b is None else (self.memory.W @ x + b)
|
|
135
|
+
drive = np.tanh(raw_drive)
|
|
136
|
+
|
|
137
|
+
eta = np.random.normal(scale=noise_std, size=self.N) if noise_std > 0.0 else 0.0
|
|
138
|
+
dx = (-gradE + g * drive + eta) * (self.dt / self.tau)
|
|
139
|
+
return x + dx
|
|
140
|
+
|
|
141
|
+
def encode_pattern(self, pid: int, p_vec: np.ndarray, strength: float = 0.1):
|
|
142
|
+
self.patterns[pid] = normalize(p_vec)
|
|
143
|
+
self.alpha[pid] = float(strength)
|
|
144
|
+
|
|
145
|
+
def reward_handshake(
|
|
146
|
+
self, x: np.ndarray, target_pid: int, eps: float = 0.03 * math.pi,
|
|
147
|
+
R0: float = 1.0, kappa_r: float = 1e-2, kappa_P: float = 1e-2
|
|
148
|
+
) -> Tuple[bool, float, float]:
|
|
149
|
+
p = self.patterns[target_pid]
|
|
150
|
+
x_hat = normalize(x)
|
|
151
|
+
dot = float(np.clip(np.dot(x_hat, p), -1.0, 1.0))
|
|
152
|
+
theta = math.acos(dot)
|
|
153
|
+
|
|
154
|
+
if theta <= eps:
|
|
155
|
+
r = R0 * math.exp(-theta / eps)
|
|
156
|
+
self.alpha[target_pid] = min(self.alpha_max, self.alpha[target_pid] + kappa_r * r)
|
|
157
|
+
self.P += kappa_P * r
|
|
158
|
+
return True, theta, r
|
|
159
|
+
return False, theta, 0.0
|
|
160
|
+
|
|
161
|
+
def demo_small_run(seed=0):
|
|
162
|
+
rng = np.random.default_rng(seed)
|
|
163
|
+
N = 256
|
|
164
|
+
d = 64
|
|
165
|
+
Oproj = OwnershipProjector(N=N, d=d, seed=1)
|
|
166
|
+
# Scale eta to balance recurrent drive against linear relaxation
|
|
167
|
+
mem = OwnershipMemory(N=N, ownership_projector=Oproj, eta=0.8, gamma=2.0)
|
|
168
|
+
ca3 = CA3Dynamics(N=N, memory=mem, tau=0.05, dt=0.01)
|
|
169
|
+
|
|
170
|
+
p = normalize(rng.normal(size=(N,)))
|
|
171
|
+
tag = "owner:collective"
|
|
172
|
+
# Prime memory with the target attractor basin
|
|
173
|
+
mem.encode(p, raw_tag=tag)
|
|
174
|
+
ca3.encode_pattern(0, p, strength=1.2)
|
|
175
|
+
|
|
176
|
+
x = normalize(p + 0.8 * rng.normal(size=(N,)))
|
|
177
|
+
events = 0
|
|
178
|
+
for _ in range(400):
|
|
179
|
+
x = ca3.step(x, Vd=1.0, R=1.0, noise_std=0.01)
|
|
180
|
+
trig, theta, r = ca3.reward_handshake(x, 0, eps=0.15 * math.pi)
|
|
181
|
+
if trig:
|
|
182
|
+
events += 1
|
|
183
|
+
mem.encode(x, raw_tag=tag)
|
|
184
|
+
return {"final_sim": float(np.dot(normalize(x), normalize(p))), "events": events}
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
res = demo_small_run()
|
|
188
|
+
print(f"Demo complete: final_similarity={res['final_sim']:.4f}, handshake_events={res['events']}")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kpt_kernel"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Homeostatic Feedback Processor Theory (FPT) control plane for Admission Gate"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{ name = "ak-skwaa-mahawk" }]
|
|
11
|
+
license = "MIT"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"admission-gate>=0.4.0"
|
|
14
|
+
]
|
|
15
|
+
requires-python = ">=3.9"
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
fpt-daemon = "fpt_daemon:run"
|
|
19
|
+
fpt-client = "fpt_client:submit_cli"
|
|
20
|
+
|
|
21
|
+
[tool.setuptools]
|
|
22
|
+
py-modules = ["fpt_daemon", "fpt_client", "living_zero_core"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import math
|
|
3
|
+
import pytest
|
|
4
|
+
from admission_gate import GateConfig
|
|
5
|
+
from fpt_daemon import FPTAdmissionController
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
def controller(tmp_path):
|
|
9
|
+
test_state_file = str(tmp_path / "test_state.json")
|
|
10
|
+
config = GateConfig()
|
|
11
|
+
ctrl = FPTAdmissionController(base_config=config)
|
|
12
|
+
|
|
13
|
+
# Isolate state persistence to the pytest temp path
|
|
14
|
+
if hasattr(ctrl, "state_file"):
|
|
15
|
+
ctrl.state_file = test_state_file
|
|
16
|
+
elif hasattr(ctrl, "state_path"):
|
|
17
|
+
ctrl.state_path = test_state_file
|
|
18
|
+
|
|
19
|
+
# Zero out in-memory state to ensure isolation
|
|
20
|
+
ctrl.cycle_count = 0
|
|
21
|
+
ctrl.integral_err = 0.0
|
|
22
|
+
ctrl.last_err = 0.0
|
|
23
|
+
if hasattr(ctrl, "penalties"):
|
|
24
|
+
ctrl.penalties.clear()
|
|
25
|
+
|
|
26
|
+
return ctrl
|
|
27
|
+
|
|
28
|
+
def test_pid_damping_bounds(controller):
|
|
29
|
+
# Under zero error, damping remains well below escalation threshold
|
|
30
|
+
err, i_err, d_err, u_sig, damping = controller.compute_damping(0.0)
|
|
31
|
+
assert 0.0 <= damping <= 0.60
|
|
32
|
+
assert not controller.actuate(damping)["require_confirm"]
|
|
33
|
+
|
|
34
|
+
# Sustained maximum penalty: drive damping into escalation ceiling
|
|
35
|
+
for _ in range(8):
|
|
36
|
+
err, i_err, d_err, u_sig, damping = controller.compute_damping(1.0)
|
|
37
|
+
|
|
38
|
+
assert damping > 0.85
|
|
39
|
+
actuation = controller.actuate(damping)
|
|
40
|
+
assert actuation["require_confirm"] is True
|
|
41
|
+
assert actuation["active_burst"] <= 2
|
|
42
|
+
assert actuation["active_cooldown_sec"] > 5.0
|
|
43
|
+
|
|
44
|
+
def test_quarantine_activation(controller):
|
|
45
|
+
actuation = controller.actuate(0.95)
|
|
46
|
+
assert actuation["active_write_roots"] == ["./scratch"]
|
|
47
|
+
|
|
48
|
+
def test_handshake_sovereign_clearance(controller):
|
|
49
|
+
assert controller.verify_handshake("authority:human_in_the_loop") is True
|
|
50
|
+
assert controller.verify_handshake("random_untrusted_token") is False
|
|
51
|
+
|
|
52
|
+
valid_token = json.dumps({
|
|
53
|
+
"protocol_version": "v1.0",
|
|
54
|
+
"sovereign_id": "99733-Q",
|
|
55
|
+
"authority_tag": "authority:human_in_the_loop"
|
|
56
|
+
})
|
|
57
|
+
assert controller.verify_handshake(valid_token) is True
|