akprx 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.
akprx-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexey Floppa
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.
akprx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: akprx
3
+ Version: 0.1.0
4
+ Summary: Agent Key Proxy — credential broker daemon for AI agents on Linux
5
+ Author-email: Alexey Floppa <you@example.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Alexey Floppa
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/belka0fficial/akprx
29
+ Project-URL: Repository, https://github.com/belka0fficial/akprx
30
+ Project-URL: Issues, https://github.com/belka0fficial/akprx/issues
31
+ Project-URL: Changelog, https://github.com/belka0fficial/akprx/blob/main/CHANGELOG.md
32
+ Keywords: ai-agent,api-proxy,credentials,secrets,broker,daemon,linux
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Environment :: No Input/Output (Daemon)
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Intended Audience :: System Administrators
37
+ Classifier: License :: OSI Approved :: MIT License
38
+ Classifier: Operating System :: POSIX :: Linux
39
+ Classifier: Programming Language :: Python :: 3
40
+ Classifier: Programming Language :: Python :: 3.10
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3.12
43
+ Classifier: Topic :: Security
44
+ Classifier: Topic :: Internet :: Proxy Servers
45
+ Classifier: Topic :: System :: Systems Administration
46
+ Requires-Python: >=3.10
47
+ Description-Content-Type: text/markdown
48
+ License-File: LICENSE
49
+ Requires-Dist: fastapi>=0.110.0
50
+ Requires-Dist: uvicorn>=0.29.0
51
+ Requires-Dist: httpx>=0.27.0
52
+ Requires-Dist: pydantic>=2.0.0
53
+ Dynamic: license-file
54
+
55
+ # akprx
56
+
57
+ **Agent Key Proxy** — credential broker daemon for AI agents on Linux.
58
+
59
+ Your agent needs to call external APIs. You don't want it holding the keys.
60
+ `akprx` sits in the middle: the agent asks it to make calls, it injects the credentials, returns only the result. The agent never sees a token.
61
+
62
+ ```
63
+ agent → POST /call → akprx injects key → GitHub API
64
+
65
+ key never crosses this boundary
66
+ ```
67
+
68
+ ---
69
+
70
+ ## The problem
71
+
72
+ When an AI agent has direct access to API keys:
73
+
74
+ - Keys live in env vars or config files the agent can read
75
+ - A prompt injection attack can extract them
76
+ - A compromised plugin leaks every credential the agent holds
77
+ - You have no visibility into what the agent calls or with what
78
+
79
+ ## The solution
80
+
81
+ `akprx` runs as a systemd daemon on `127.0.0.1` only — unreachable from outside the machine. The agent user can talk to it over HTTP. It cannot read the secret files. It cannot read the environment of the broker process. It gets results, never credentials.
82
+
83
+ ---
84
+
85
+ ## Install
86
+
87
+ ```bash
88
+ pip install akprx
89
+ sudo akprx setup
90
+ ```
91
+
92
+ That's it. `setup` creates the system user, directories, permissions, and the systemd service. Nothing else to configure.
93
+
94
+ ---
95
+
96
+ ## Quickstart
97
+
98
+ ```bash
99
+ # check everything is running
100
+ akprx status
101
+
102
+ # store an API key (value is hidden input)
103
+ akprx key add GITHUB_TOKEN
104
+
105
+ # register a provider
106
+ akprx adaptor add
107
+
108
+ # make a call through the proxy
109
+ akprx call github /repos/youruser/yourrepo
110
+
111
+ # POST with a body
112
+ akprx call github /repos/youruser/yourrepo/issues \
113
+ --method POST \
114
+ --data '{"title": "bug report", "body": "details here"}'
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Commands
120
+
121
+ ```
122
+ akprx status service health, keys, adaptors
123
+
124
+ akprx adaptor list list registered adaptors
125
+ akprx adaptor add register a new adaptor (interactive)
126
+ akprx adaptor show <name> show full config of one adaptor
127
+ akprx adaptor remove <name> remove an adaptor
128
+
129
+ akprx key list list stored key names (no values shown)
130
+ akprx key add <name> store a new key (value prompted, hidden)
131
+ akprx key remove <name> remove a key
132
+ akprx key rotate <name> replace a key value (prompted, hidden)
133
+
134
+ akprx call <adaptor> <path> GET request through adaptor
135
+ akprx call <adaptor> <path> --method POST --data '{...}'
136
+
137
+ akprx setup first-time setup (run with sudo)
138
+ akprx version print version
139
+ ```
140
+
141
+ ---
142
+
143
+ ## How adaptors work
144
+
145
+ An adaptor is a registered API provider. It stores:
146
+
147
+ - `base_url` — the root URL of the API
148
+ - `secret_env` — which env var holds the token
149
+ - `auth_header` / `auth_prefix` — how to inject the credential
150
+ - `extra_headers` — any static headers the API requires
151
+
152
+ Once registered, the agent can call any endpoint on that provider freely.
153
+ To add a new provider, run `akprx adaptor add` and answer the prompts.
154
+
155
+ ### Example — GitHub
156
+
157
+ ```bash
158
+ akprx adaptor add
159
+ # name: github
160
+ # base url: https://api.github.com
161
+ # secret env var: GITHUB_TOKEN
162
+ # auth header: Authorization
163
+ # auth prefix: Bearer
164
+ # extra headers:
165
+ # Accept: application/vnd.github+json
166
+ # X-GitHub-Api-Version: 2022-11-28
167
+ ```
168
+
169
+ ---
170
+
171
+ ## File layout
172
+
173
+ ```
174
+ /srv/akprx/
175
+ ├── adaptors.json registered providers owner: akprx:akprx 600
176
+ └── secrets/
177
+ └── secrets.env API keys owner: akprx:akprx 600
178
+ ```
179
+
180
+ The `akprx` system user owns all files.
181
+ The agent user cannot read either file.
182
+ Root can manage everything via sudo.
183
+
184
+ ---
185
+
186
+ ## Security model
187
+
188
+ | What | Who can access |
189
+ |-----------------------------|----------------------------|
190
+ | `secrets.env` | `akprx` user only |
191
+ | `adaptors.json` | `akprx` user only |
192
+ | broker HTTP API | any local process |
193
+ | secret values via HTTP API | nobody — never returned |
194
+ | key names via HTTP API | any local process |
195
+ | port 8080 from outside VPS | nobody — loopback only |
196
+
197
+ The security guarantee is structural — enforced by Linux file permissions and network binding, not application-level checks.
198
+
199
+ ---
200
+
201
+ ## Who this is for
202
+
203
+ - Running a self-hosted AI agent (OpenClaw, Claude Code, or similar) on a Linux VPS
204
+ - Want the agent to call external APIs on your behalf
205
+ - Don't want the agent to hold or see raw credentials
206
+ - Want a simple CLI to manage everything without editing config files
207
+
208
+ ## Who this is NOT for
209
+
210
+ - Enterprise teams needing secret rotation, SSO, or audit compliance → use HashiCorp Vault
211
+ - Cloud-native setups → use AWS Secrets Manager
212
+ - Docker-only workflows → use OneCLI
213
+ - Multi-user access control → out of scope
214
+
215
+ ---
216
+
217
+ ## Requirements
218
+
219
+ - Linux (systemd-based)
220
+ - Python 3.10+
221
+ - Root access for initial setup
222
+
223
+ ---
224
+
225
+ ## License
226
+
227
+ MIT — see [LICENSE](LICENSE)
228
+
229
+ ---
230
+
231
+ ## Author
232
+
233
+ [Alexey Floppa](https://github.com/belka0fficial)
akprx-0.1.0/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # akprx
2
+
3
+ **Agent Key Proxy** — credential broker daemon for AI agents on Linux.
4
+
5
+ Your agent needs to call external APIs. You don't want it holding the keys.
6
+ `akprx` sits in the middle: the agent asks it to make calls, it injects the credentials, returns only the result. The agent never sees a token.
7
+
8
+ ```
9
+ agent → POST /call → akprx injects key → GitHub API
10
+
11
+ key never crosses this boundary
12
+ ```
13
+
14
+ ---
15
+
16
+ ## The problem
17
+
18
+ When an AI agent has direct access to API keys:
19
+
20
+ - Keys live in env vars or config files the agent can read
21
+ - A prompt injection attack can extract them
22
+ - A compromised plugin leaks every credential the agent holds
23
+ - You have no visibility into what the agent calls or with what
24
+
25
+ ## The solution
26
+
27
+ `akprx` runs as a systemd daemon on `127.0.0.1` only — unreachable from outside the machine. The agent user can talk to it over HTTP. It cannot read the secret files. It cannot read the environment of the broker process. It gets results, never credentials.
28
+
29
+ ---
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install akprx
35
+ sudo akprx setup
36
+ ```
37
+
38
+ That's it. `setup` creates the system user, directories, permissions, and the systemd service. Nothing else to configure.
39
+
40
+ ---
41
+
42
+ ## Quickstart
43
+
44
+ ```bash
45
+ # check everything is running
46
+ akprx status
47
+
48
+ # store an API key (value is hidden input)
49
+ akprx key add GITHUB_TOKEN
50
+
51
+ # register a provider
52
+ akprx adaptor add
53
+
54
+ # make a call through the proxy
55
+ akprx call github /repos/youruser/yourrepo
56
+
57
+ # POST with a body
58
+ akprx call github /repos/youruser/yourrepo/issues \
59
+ --method POST \
60
+ --data '{"title": "bug report", "body": "details here"}'
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Commands
66
+
67
+ ```
68
+ akprx status service health, keys, adaptors
69
+
70
+ akprx adaptor list list registered adaptors
71
+ akprx adaptor add register a new adaptor (interactive)
72
+ akprx adaptor show <name> show full config of one adaptor
73
+ akprx adaptor remove <name> remove an adaptor
74
+
75
+ akprx key list list stored key names (no values shown)
76
+ akprx key add <name> store a new key (value prompted, hidden)
77
+ akprx key remove <name> remove a key
78
+ akprx key rotate <name> replace a key value (prompted, hidden)
79
+
80
+ akprx call <adaptor> <path> GET request through adaptor
81
+ akprx call <adaptor> <path> --method POST --data '{...}'
82
+
83
+ akprx setup first-time setup (run with sudo)
84
+ akprx version print version
85
+ ```
86
+
87
+ ---
88
+
89
+ ## How adaptors work
90
+
91
+ An adaptor is a registered API provider. It stores:
92
+
93
+ - `base_url` — the root URL of the API
94
+ - `secret_env` — which env var holds the token
95
+ - `auth_header` / `auth_prefix` — how to inject the credential
96
+ - `extra_headers` — any static headers the API requires
97
+
98
+ Once registered, the agent can call any endpoint on that provider freely.
99
+ To add a new provider, run `akprx adaptor add` and answer the prompts.
100
+
101
+ ### Example — GitHub
102
+
103
+ ```bash
104
+ akprx adaptor add
105
+ # name: github
106
+ # base url: https://api.github.com
107
+ # secret env var: GITHUB_TOKEN
108
+ # auth header: Authorization
109
+ # auth prefix: Bearer
110
+ # extra headers:
111
+ # Accept: application/vnd.github+json
112
+ # X-GitHub-Api-Version: 2022-11-28
113
+ ```
114
+
115
+ ---
116
+
117
+ ## File layout
118
+
119
+ ```
120
+ /srv/akprx/
121
+ ├── adaptors.json registered providers owner: akprx:akprx 600
122
+ └── secrets/
123
+ └── secrets.env API keys owner: akprx:akprx 600
124
+ ```
125
+
126
+ The `akprx` system user owns all files.
127
+ The agent user cannot read either file.
128
+ Root can manage everything via sudo.
129
+
130
+ ---
131
+
132
+ ## Security model
133
+
134
+ | What | Who can access |
135
+ |-----------------------------|----------------------------|
136
+ | `secrets.env` | `akprx` user only |
137
+ | `adaptors.json` | `akprx` user only |
138
+ | broker HTTP API | any local process |
139
+ | secret values via HTTP API | nobody — never returned |
140
+ | key names via HTTP API | any local process |
141
+ | port 8080 from outside VPS | nobody — loopback only |
142
+
143
+ The security guarantee is structural — enforced by Linux file permissions and network binding, not application-level checks.
144
+
145
+ ---
146
+
147
+ ## Who this is for
148
+
149
+ - Running a self-hosted AI agent (OpenClaw, Claude Code, or similar) on a Linux VPS
150
+ - Want the agent to call external APIs on your behalf
151
+ - Don't want the agent to hold or see raw credentials
152
+ - Want a simple CLI to manage everything without editing config files
153
+
154
+ ## Who this is NOT for
155
+
156
+ - Enterprise teams needing secret rotation, SSO, or audit compliance → use HashiCorp Vault
157
+ - Cloud-native setups → use AWS Secrets Manager
158
+ - Docker-only workflows → use OneCLI
159
+ - Multi-user access control → out of scope
160
+
161
+ ---
162
+
163
+ ## Requirements
164
+
165
+ - Linux (systemd-based)
166
+ - Python 3.10+
167
+ - Root access for initial setup
168
+
169
+ ---
170
+
171
+ ## License
172
+
173
+ MIT — see [LICENSE](LICENSE)
174
+
175
+ ---
176
+
177
+ ## Author
178
+
179
+ [Alexey Floppa](https://github.com/belka0fficial)
@@ -0,0 +1,7 @@
1
+ """
2
+ akprx — Agent Key Proxy
3
+ Credential broker daemon for AI agents on Linux.
4
+ """
5
+
6
+ __version__ = "0.1.0"
7
+ __author__ = "Alexey Floppa"
File without changes
@@ -0,0 +1,96 @@
1
+ """
2
+ akprx.cli.adaptor
3
+ Subcommands for managing adaptors.
4
+ """
5
+
6
+ import sys
7
+
8
+ from akprx.cli import http
9
+
10
+ USAGE = """\
11
+ usage:
12
+ akprx adaptor list
13
+ akprx adaptor add
14
+ akprx adaptor show <name>
15
+ akprx adaptor remove <name>
16
+ """
17
+
18
+
19
+ def _list() -> None:
20
+ data = http.get("/adaptors")
21
+ if data["total"] == 0:
22
+ print("no adaptors registered.")
23
+ print("add one: akprx adaptor add")
24
+ return
25
+ for a in data["adaptors"]:
26
+ sym = "✓" if a["status"] == "ok" else "✗"
27
+ name = a["name"].ljust(16)
28
+ url = a["config"]["base_url"].ljust(40)
29
+ state = "ok" if a["config"]["secret_loaded"] else "MISSING SECRET"
30
+ print(f" {sym} {name} {url} {state}")
31
+ for issue in a.get("issues", []):
32
+ print(f" ! {issue}")
33
+
34
+
35
+ def _show(name: str) -> None:
36
+ http.pp(http.get(f"/adaptors/{name}"))
37
+
38
+
39
+ def _add() -> None:
40
+ print("register a new adaptor.\n")
41
+ name = input(" name (e.g. github): ").strip()
42
+ base_url = input(" base url: ").strip()
43
+ secret_env = input(" secret env var name: ").strip()
44
+ auth_header = input(" auth header [Authorization]: ").strip() or "Authorization"
45
+ auth_prefix = input(" auth prefix [Bearer]: ").strip() or "Bearer"
46
+
47
+ extra: dict[str, str] = {}
48
+ print(" extra headers (blank name to finish):")
49
+ while True:
50
+ k = input(" header name: ").strip()
51
+ if not k:
52
+ break
53
+ v = input(" header value: ").strip()
54
+ extra[k] = v
55
+
56
+ result = http.post("/adaptors", {
57
+ "name": name,
58
+ "base_url": base_url,
59
+ "secret_env": secret_env,
60
+ "auth_header": auth_header,
61
+ "auth_prefix": auth_prefix,
62
+ "extra_headers": extra,
63
+ })
64
+ http.pp(result)
65
+
66
+
67
+ def _remove(name: str) -> None:
68
+ result = http.delete(f"/adaptors/{name}")
69
+ http.pp(result)
70
+
71
+
72
+ def run(args: list[str]) -> None:
73
+ if not args or args[0] in ("-h", "--help", "help"):
74
+ print(USAGE)
75
+ sys.exit(0)
76
+
77
+ sub = args[0]
78
+
79
+ if sub == "list":
80
+ _list()
81
+ elif sub == "add":
82
+ _add()
83
+ elif sub == "show":
84
+ if len(args) < 2:
85
+ print("usage: akprx adaptor show <name>", file=sys.stderr)
86
+ sys.exit(1)
87
+ _show(args[1])
88
+ elif sub == "remove":
89
+ if len(args) < 2:
90
+ print("usage: akprx adaptor remove <name>", file=sys.stderr)
91
+ sys.exit(1)
92
+ _remove(args[1])
93
+ else:
94
+ print(f"akprx adaptor: unknown subcommand '{sub}'", file=sys.stderr)
95
+ print(USAGE, file=sys.stderr)
96
+ sys.exit(1)
@@ -0,0 +1,61 @@
1
+ """
2
+ akprx.cli.call
3
+ Fire an authenticated API call through a registered adaptor.
4
+ """
5
+
6
+ import json
7
+ import sys
8
+
9
+ from akprx.cli import http
10
+
11
+ USAGE = """\
12
+ usage:
13
+ akprx call <adaptor> <path>
14
+ akprx call <adaptor> <path> --method POST --data '{...}'
15
+
16
+ examples:
17
+ akprx call github /repos/belka0fficial/belka.life
18
+ akprx call github /repos/belka0fficial/belka.life/issues --method POST --data '{"title":"bug"}'
19
+ """
20
+
21
+
22
+ def run(args: list[str]) -> None:
23
+ if not args or args[0] in ("-h", "--help", "help"):
24
+ print(USAGE)
25
+ sys.exit(0)
26
+
27
+ if len(args) < 2:
28
+ print("usage: akprx call <adaptor> <path>", file=sys.stderr)
29
+ sys.exit(1)
30
+
31
+ adaptor = args[0]
32
+ path = args[1]
33
+ method = "GET"
34
+ data = None
35
+
36
+ i = 2
37
+ while i < len(args):
38
+ if args[i] == "--method" and i + 1 < len(args):
39
+ method = args[i + 1].upper()
40
+ i += 2
41
+ elif args[i] == "--data" and i + 1 < len(args):
42
+ data = args[i + 1]
43
+ i += 2
44
+ else:
45
+ i += 1
46
+
47
+ body: dict = {
48
+ "adaptor": adaptor,
49
+ "method": method,
50
+ "path": path,
51
+ }
52
+
53
+ if data:
54
+ try:
55
+ body["payload"] = json.loads(data)
56
+ except json.JSONDecodeError:
57
+ print("error: --data is not valid JSON", file=sys.stderr)
58
+ sys.exit(1)
59
+
60
+ result = http.post("/call", body)
61
+ http.pp(result)
@@ -0,0 +1,57 @@
1
+ """
2
+ akprx.cli.http
3
+ Shared HTTP client for CLI commands.
4
+ All requests go to the local broker.
5
+ """
6
+
7
+ import json
8
+ import sys
9
+ import urllib.error
10
+ import urllib.request
11
+
12
+ from akprx.config import BASE_URL
13
+
14
+
15
+ def request(method: str, path: str, body: dict | None = None) -> dict:
16
+ url = BASE_URL + path
17
+ data = json.dumps(body).encode() if body is not None else None
18
+ headers = {"Content-Type": "application/json"} if data else {}
19
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
20
+ try:
21
+ with urllib.request.urlopen(req) as resp:
22
+ return json.loads(resp.read())
23
+ except urllib.error.HTTPError as e:
24
+ raw = e.read().decode()
25
+ try:
26
+ detail = json.loads(raw).get("detail", raw)
27
+ except Exception:
28
+ detail = raw
29
+ print(f"error {e.code}: {detail}", file=sys.stderr)
30
+ sys.exit(1)
31
+ except urllib.error.URLError:
32
+ print(
33
+ f"error: cannot reach akprx at {BASE_URL}\n"
34
+ " is the service running? try: systemctl status akprx",
35
+ file=sys.stderr,
36
+ )
37
+ sys.exit(1)
38
+
39
+
40
+ def get(path: str) -> dict:
41
+ return request("GET", path)
42
+
43
+
44
+ def post(path: str, body: dict) -> dict:
45
+ return request("POST", path, body)
46
+
47
+
48
+ def put(path: str, body: dict) -> dict:
49
+ return request("PUT", path, body)
50
+
51
+
52
+ def delete(path: str) -> dict:
53
+ return request("DELETE", path)
54
+
55
+
56
+ def pp(data: dict) -> None:
57
+ print(json.dumps(data, indent=2))