lomberk 0.2.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.
- lomberk-0.2.0/.gitignore +10 -0
- lomberk-0.2.0/LICENSE +202 -0
- lomberk-0.2.0/PKG-INFO +289 -0
- lomberk-0.2.0/README.md +249 -0
- lomberk-0.2.0/SECURITY.md +57 -0
- lomberk-0.2.0/pyproject.toml +98 -0
- lomberk-0.2.0/src/lomberk/__init__.py +0 -0
- lomberk-0.2.0/src/lomberk/api.py +613 -0
- lomberk-0.2.0/src/lomberk/audit.py +41 -0
- lomberk-0.2.0/src/lomberk/capture/install.py +67 -0
- lomberk-0.2.0/src/lomberk/capture/sql/01_schema.sql +154 -0
- lomberk-0.2.0/src/lomberk/capture/sql/02_functions.sql +1061 -0
- lomberk-0.2.0/src/lomberk/cli.py +443 -0
- lomberk-0.2.0/src/lomberk/config.py +159 -0
- lomberk-0.2.0/src/lomberk/doctor.py +269 -0
- lomberk-0.2.0/src/lomberk/ee/LICENSE +104 -0
- lomberk-0.2.0/src/lomberk/ee/__init__.py +5 -0
- lomberk-0.2.0/src/lomberk/ee/export.py +28 -0
- lomberk-0.2.0/src/lomberk/ee/license.py +180 -0
- lomberk-0.2.0/src/lomberk/ee/panel.py +285 -0
- lomberk-0.2.0/src/lomberk/ee/panel_static/app.css +59 -0
- lomberk-0.2.0/src/lomberk/ee/panel_static/app.js +165 -0
- lomberk-0.2.0/src/lomberk/ee/panel_static/index.html +54 -0
- lomberk-0.2.0/src/lomberk/ee/slack.py +166 -0
- lomberk-0.2.0/src/lomberk/ee/slack_publisher.py +140 -0
- lomberk-0.2.0/src/lomberk/ee/slack_transport.py +200 -0
- lomberk-0.2.0/src/lomberk/ee/sso.py +106 -0
- lomberk-0.2.0/src/lomberk/executor.py +836 -0
- lomberk-0.2.0/src/lomberk/models.py +29 -0
- lomberk-0.2.0/src/lomberk/policy.py +58 -0
- lomberk-0.2.0/src/lomberk/recovery.py +129 -0
- lomberk-0.2.0/src/lomberk/sentinel.py +227 -0
- lomberk-0.2.0/src/lomberk/server.py +207 -0
- lomberk-0.2.0/src/lomberk/setup.py +170 -0
- lomberk-0.2.0/src/lomberk/sql/classify.py +342 -0
- lomberk-0.2.0/src/lomberk/startup.py +82 -0
- lomberk-0.2.0/tests/conftest.py +23 -0
- lomberk-0.2.0/tests/integration/test_agent_tools.py +114 -0
- lomberk-0.2.0/tests/integration/test_api_idempotency.py +257 -0
- lomberk-0.2.0/tests/integration/test_approval_preview.py +115 -0
- lomberk-0.2.0/tests/integration/test_audit_round1b.py +341 -0
- lomberk-0.2.0/tests/integration/test_audit_run3.py +115 -0
- lomberk-0.2.0/tests/integration/test_capture_undo.py +345 -0
- lomberk-0.2.0/tests/integration/test_cli.py +121 -0
- lomberk-0.2.0/tests/integration/test_commit_failure.py +55 -0
- lomberk-0.2.0/tests/integration/test_composite_pk_undo.py +118 -0
- lomberk-0.2.0/tests/integration/test_concurrent_approval.py +88 -0
- lomberk-0.2.0/tests/integration/test_db_hardening.py +73 -0
- lomberk-0.2.0/tests/integration/test_ddl_undo.py +93 -0
- lomberk-0.2.0/tests/integration/test_doctor.py +167 -0
- lomberk-0.2.0/tests/integration/test_fk_undo.py +150 -0
- lomberk-0.2.0/tests/integration/test_identity_undo.py +95 -0
- lomberk-0.2.0/tests/integration/test_ledger.py +78 -0
- lomberk-0.2.0/tests/integration/test_mcp_cli.py +153 -0
- lomberk-0.2.0/tests/integration/test_metrics.py +70 -0
- lomberk-0.2.0/tests/integration/test_p0_api_endpoints.py +148 -0
- lomberk-0.2.0/tests/integration/test_p0_connection_failure.py +235 -0
- lomberk-0.2.0/tests/integration/test_p0_read_only.py +134 -0
- lomberk-0.2.0/tests/integration/test_p0_registration_validator.py +142 -0
- lomberk-0.2.0/tests/integration/test_p0_undo_mvp.py +367 -0
- lomberk-0.2.0/tests/integration/test_panel.py +255 -0
- lomberk-0.2.0/tests/integration/test_partitioned_undo.py +120 -0
- lomberk-0.2.0/tests/integration/test_remote_mcp.py +131 -0
- lomberk-0.2.0/tests/integration/test_row_limit_hold.py +151 -0
- lomberk-0.2.0/tests/integration/test_setup_wizard.py +154 -0
- lomberk-0.2.0/tests/integration/test_slack_integration.py +847 -0
- lomberk-0.2.0/tests/integration/test_slack_publisher.py +206 -0
- lomberk-0.2.0/tests/integration/test_stdio_startup.py +83 -0
- lomberk-0.2.0/tests/integration/test_two_person_approval.py +187 -0
- lomberk-0.2.0/tests/integration/test_type_fidelity_undo.py +110 -0
- lomberk-0.2.0/tests/integration/test_undo_upgrade.py +51 -0
- lomberk-0.2.0/tests/integration/test_undo_window.py +135 -0
- lomberk-0.2.0/tests/integration/test_undo_window_pro.py +21 -0
- lomberk-0.2.0/tests/integration/test_write_scope.py +130 -0
- lomberk-0.2.0/tests/offline_pilot.py +84 -0
- lomberk-0.2.0/tests/property/test_undo_properties.py +105 -0
- lomberk-0.2.0/tests/unit/test_agent_error_messages.py +17 -0
- lomberk-0.2.0/tests/unit/test_attack_gallery.py +38 -0
- lomberk-0.2.0/tests/unit/test_classify.py +116 -0
- lomberk-0.2.0/tests/unit/test_classify_extras.py +19 -0
- lomberk-0.2.0/tests/unit/test_cli_errors.py +28 -0
- lomberk-0.2.0/tests/unit/test_cli_serve.py +15 -0
- lomberk-0.2.0/tests/unit/test_config.py +64 -0
- lomberk-0.2.0/tests/unit/test_dotenv_cwd.py +32 -0
- lomberk-0.2.0/tests/unit/test_firewall_attacks.py +137 -0
- lomberk-0.2.0/tests/unit/test_firewall_fuzz.py +85 -0
- lomberk-0.2.0/tests/unit/test_firewall_usability.py +190 -0
- lomberk-0.2.0/tests/unit/test_license.py +237 -0
- lomberk-0.2.0/tests/unit/test_offline_pilot.py +25 -0
- lomberk-0.2.0/tests/unit/test_policy.py +80 -0
- lomberk-0.2.0/tests/unit/test_pro_extras.py +60 -0
- lomberk-0.2.0/tests/unit/test_recovery_engine.py +148 -0
- lomberk-0.2.0/tests/unit/test_sensitive_columns.py +145 -0
- lomberk-0.2.0/tests/unit/test_server_identity.py +15 -0
- lomberk-0.2.0/tests/unit/test_slack_adapter.py +208 -0
- lomberk-0.2.0/tests/unit/test_slack_transport.py +376 -0
- lomberk-0.2.0/tests/unit/test_sso.py +189 -0
- lomberk-0.2.0/tests/unit/test_transport_timeout.py +188 -0
- lomberk-0.2.0/tests/unit/test_weak_keys.py +43 -0
lomberk-0.2.0/.gitignore
ADDED
lomberk-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
lomberk-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lomberk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Every agent write, reversible: an MCP gateway for PostgreSQL that blocks destructive SQL, holds risky writes for a person and can undo any applied write.
|
|
5
|
+
Project-URL: Homepage, https://ege-usta.github.io/lomberk/
|
|
6
|
+
Project-URL: Source, https://github.com/ege-usta/lomberk
|
|
7
|
+
Project-URL: Documentation, https://github.com/ege-usta/lomberk#readme
|
|
8
|
+
Project-URL: Changelog, https://github.com/ege-usta/lomberk/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/ege-usta/lomberk/issues
|
|
10
|
+
Project-URL: Security, https://github.com/ege-usta/lomberk/security
|
|
11
|
+
License-Expression: Apache-2.0 AND Elastic-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
License-File: src/lomberk/ee/LICENSE
|
|
14
|
+
Keywords: ai-agents,approval,mcp,postgres,postgresql,sql-firewall,undo
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Database
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: fastapi>=0.141.1
|
|
25
|
+
Requires-Dist: mcp>=2.0.0
|
|
26
|
+
Requires-Dist: pglast>=6.2
|
|
27
|
+
Requires-Dist: psycopg-pool>=3.3.3
|
|
28
|
+
Requires-Dist: psycopg[binary]>=3.2.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
|
+
Requires-Dist: python-dotenv>=1.2.3
|
|
31
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
32
|
+
Requires-Dist: rich>=13.0
|
|
33
|
+
Requires-Dist: typer>=0.16.0
|
|
34
|
+
Requires-Dist: uvicorn>=0.53.0
|
|
35
|
+
Provides-Extra: pro
|
|
36
|
+
Requires-Dist: aiohttp>=3.14.3; extra == 'pro'
|
|
37
|
+
Requires-Dist: pyjwt[crypto]>=2.15.0; extra == 'pro'
|
|
38
|
+
Requires-Dist: slack-sdk>=3.44.1; extra == 'pro'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# Lomberk
|
|
42
|
+
<!-- mcp-name: io.github.ege-usta/lomberk -->
|
|
43
|
+
|
|
44
|
+
[](https://github.com/ege-usta/lomberk/actions/workflows/ci.yml)
|
|
45
|
+
[](https://pypi.org/project/lomberk/)
|
|
46
|
+
[](https://pypi.org/project/lomberk/)
|
|
47
|
+
[](#overhead)
|
|
48
|
+
[](#license)
|
|
49
|
+
|
|
50
|
+
**Every agent write, reversible.**
|
|
51
|
+
Lomberk is an MCP gateway for PostgreSQL. AI agents send SQL through it instead of holding database credentials. It blocks destructive commands, holds large or sensitive writes for a human, records every changed row, and can undo any applied write, including foreign-key cascades.
|
|
52
|
+
|
|
53
|
+
**Just want a seatbelt for Claude Code?** [Lomberk Seatbelt](https://github.com/ege-usta/lomberk/tree/main/seatbelt) is a free plugin that makes your agent ask before `DROP`, `TRUNCATE`, `DELETE` without `WHERE`, `prisma migrate reset`, `dropdb` and friends. Install it in two commands.
|
|
54
|
+
|
|
55
|
+
## Quickstart (60 seconds)
|
|
56
|
+
You need [uv](https://docs.astral.sh/uv/) and the owner's connection string of a PostgreSQL 14+ database (a role that can create roles: `postgres` on Supabase, Neon and RDS).
|
|
57
|
+
```bash
|
|
58
|
+
uvx lomberk setup # asks for the owner connection string; what you type stays out of your shell history
|
|
59
|
+
```
|
|
60
|
+
In a script, set `LOMBERK_INSTALL_DSN` instead and add `--yes`.
|
|
61
|
+
Setup creates two least-privilege roles with generated passwords, installs Lomberk, lists your tables and makes the ones you pick undoable, then writes `lomberk.yaml` and a private `.env` (mode 600) and runs `lomberk doctor` on them. It never changes a role or file that already exists. The owner connection string is used once and never saved. At the end it prints the line for your MCP client, for example:
|
|
62
|
+
```bash
|
|
63
|
+
claude mcp add lomberk -e LOMBERK_CONFIG=/path/to/lomberk.yaml -- uvx lomberk serve
|
|
64
|
+
```
|
|
65
|
+
Cursor, Claude Desktop, VS Code and Codex get their config snippet too. Then ask your agent to change something:
|
|
66
|
+
```bash
|
|
67
|
+
lomberk log # what your agents changed, newest first
|
|
68
|
+
lomberk undo cp_… # put it back, cascades included
|
|
69
|
+
lomberk approve cp_… # let a held write run (or: lomberk reject cp_…)
|
|
70
|
+
lomberk doctor # check the setup any time
|
|
71
|
+
```
|
|
72
|
+
Supabase, Neon, RDS, Railway: [docs/hosted-postgres.md](https://github.com/ege-usta/lomberk/blob/main/docs/hosted-postgres.md). On Supabase, use the **session pooler** connection string (IPv4); setup derives the `lomberk_agent.<project-ref>` login names from it.
|
|
73
|
+
|
|
74
|
+
### What the agent gets
|
|
75
|
+
| Tool | |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `execute_sql` | One statement through the hard gate, the policy and capture. Replies with a checkpoint (`cp_…`), `APPROVAL REQUIRED`, or why it was denied |
|
|
78
|
+
| `describe_tables` | The tables it can use, their columns (secret ones hidden) and which writes wait for a human |
|
|
79
|
+
| `wait_for_approval` | Waits for a person to approve or reject a held write, reporting progress, then says whether it ran |
|
|
80
|
+
| `checkpoint_status` | What happened to one of its writes: waiting, applied, rejected or undone |
|
|
81
|
+
| `list_my_changes` | Its own recent writes with their tokens, so it can tell you what to undo |
|
|
82
|
+
|
|
83
|
+
The agent can't approve or undo anything. That takes the admin key, the CLI or (Pro) Slack, SSO or the panel.
|
|
84
|
+
|
|
85
|
+
## 9-second database deletion demo
|
|
86
|
+
|
|
87
|
+
In April 2026 an autonomous coding agent deleted a production database in 9 seconds (through an over-privileged infrastructure token). Lomberk closes the other door: an agent's own database access. Here is what a rogue agent can and can't do when its SQL goes through Lomberk.
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
### 1. Start the stack
|
|
92
|
+
```bash
|
|
93
|
+
git clone https://github.com/ege-usta/lomberk && cd lomberk
|
|
94
|
+
docker compose up -d --build
|
|
95
|
+
curl localhost:8000/readyz # {"status":"ready"}
|
|
96
|
+
```
|
|
97
|
+
This starts a demo `pocketos` database (`users` ← `payments` with `ON DELETE CASCADE`), installs Lomberk with two least-privilege roles, registers both tables, and starts the gateway.
|
|
98
|
+
|
|
99
|
+
### 2. Connect your agent (it gets a URL and a key, nothing else)
|
|
100
|
+
```bash
|
|
101
|
+
claude mcp add --transport http lomberk http://localhost:8000/mcp \
|
|
102
|
+
--header "Authorization: Bearer demo-agent-key"
|
|
103
|
+
```
|
|
104
|
+
Any MCP client that speaks Streamable HTTP works the same way.
|
|
105
|
+
|
|
106
|
+
### 3. The agent goes rogue
|
|
107
|
+
| Agent sends | Lomberk |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `DROP TABLE public.users` | **Denied**: DDL is structurally banned |
|
|
110
|
+
| `DELETE FROM public.users` | **Denied**: write without `WHERE` |
|
|
111
|
+
| `DELETE FROM public.users WHERE id > 0` | **Held**: `public.users` is protected → `APPROVAL REQUIRED. Token: cp_…` |
|
|
112
|
+
| `UPDATE public.payments SET amount = 0 WHERE id > 0` | **Applied**, with a checkpoint: 2 rows on an unprotected table. One undo call puts the old amounts back |
|
|
113
|
+
| The same write on more rows than `max_rows_without_approval` (10 in the demo) | **Held**: it ran, was counted and rolled back, and now waits for a person |
|
|
114
|
+
|
|
115
|
+
### 4. A human decides (admin key, never given to the agent)
|
|
116
|
+
```bash
|
|
117
|
+
H="X-API-Key: demo-admin-key"
|
|
118
|
+
curl -H "$H" localhost:8000/v1/checkpoints/pending
|
|
119
|
+
curl -H "$H" "localhost:8000/v1/checkpoints?status=applied" # recent writes, to find the cp_ to undo
|
|
120
|
+
curl -H "$H" -X POST localhost:8000/v1/checkpoints/cp_…/approve # or /reject
|
|
121
|
+
curl -H "$H" -X POST localhost:8000/v1/checkpoints/cp_…/undo # users AND cascaded payments come back
|
|
122
|
+
```
|
|
123
|
+
Pending requests expire after `agent.approval_timeout_minutes`. Review the exact SQL first with `GET /v1/checkpoints/cp_…` or `lomberk show cp_…`.
|
|
124
|
+
|
|
125
|
+
**See the rows, not just the SQL.** When a write is held, Lomberk runs it once, records what it changed and rolls it back, so the approver sees the effect before deciding, cascades included:
|
|
126
|
+
```
|
|
127
|
+
$ lomberk show cp_… # the agent sent: DELETE FROM public.users WHERE id = 1
|
|
128
|
+
would change: 2 row(s) in public.users (1), public.payments (1) (dry run when held)
|
|
129
|
+
delete public.users {"id": "1"}: {"id": 1, "name": "Alice CEO", "plan": "enterprise", "email": "alice@pocketos.com", …}
|
|
130
|
+
delete public.payments {"id": "1"}: {"id": 1, "amount": 1000.0, "status": "success", "user_id": 1}
|
|
131
|
+
```
|
|
132
|
+
The same preview is in the web panel and the API; Slack gets the counts only. Secret columns are masked, and columns hidden from the agent role never appear. A held write that would fail (a duplicate key, a missing parent) is refused at once instead of waiting for a person. The preview is a snapshot of the moment it was held. If a table's own triggers do something a rollback can't take back, set `approvals.preview: false`.
|
|
133
|
+
|
|
134
|
+
## Configuration (`lomberk.yaml`)
|
|
135
|
+
```yaml
|
|
136
|
+
limits:
|
|
137
|
+
max_rows_without_approval: 10 # larger writes are rolled back and held
|
|
138
|
+
statement_timeout_ms: 15000
|
|
139
|
+
protected_tables: # any write, or FK cascade, reaching these is held
|
|
140
|
+
- public.users
|
|
141
|
+
sensitive_columns: # like `password`: never shown to the agent, hidden by the DB on register
|
|
142
|
+
- pass
|
|
143
|
+
- settings # a JSON column that holds secrets
|
|
144
|
+
agent:
|
|
145
|
+
approval_timeout_minutes: 30
|
|
146
|
+
```
|
|
147
|
+
Unknown keys fail startup, so a typo can never silently turn protection off. The gateway reads `./lomberk.yaml`; when it starts somewhere else (MCP clients usually launch `lomberk serve` from another directory), set `LOMBERK_CONFIG` to the file's absolute path. If that file is missing, startup is refused; a missing `./lomberk.yaml` only prints a warning and runs with defaults (no protected tables).
|
|
148
|
+
|
|
149
|
+
Environment: `LOMBERK_CONFIG` (path to `lomberk.yaml`), `LOMBERK_ADMIN_DSN` (control role), `LOMBERK_AGENT_DSN` (agent role), `LOMBERK_API_KEY` (agent), `LOMBERK_ADMIN_KEY` (approvers), `LOMBERK_AUDIT_LOG` (JSONL path), `LOMBERK_LICENSE_KEY` (Pro), `LOMBERK_METRICS_KEY` (turns on `/metrics`). Keys must be 32+ random characters (`openssl rand -hex 32`); the gateway refuses weak or demo keys unless `LOMBERK_ALLOW_WEAK_KEYS=true` (laptop demos only).
|
|
150
|
+
|
|
151
|
+
## Installing on your own database
|
|
152
|
+
```bash
|
|
153
|
+
pip install lomberk # Slack and SSO: pip install "lomberk[pro]"; or: docker build -t lomberk .
|
|
154
|
+
export LOMBERK_INSTALL_DSN=postgresql://owner@db/app # used only for install
|
|
155
|
+
# first, as that owner: CREATE ROLE lomberk_control LOGIN PASSWORD '...'; CREATE ROLE lomberk_agent LOGIN PASSWORD '...';
|
|
156
|
+
lomberk init --control-role lomberk_control --agent-role lomberk_agent
|
|
157
|
+
lomberk register public.orders --agent-role lomberk_agent # repeat per table (and FK children)
|
|
158
|
+
lomberk verify-ledger # tamper-evidence check
|
|
159
|
+
```
|
|
160
|
+
What an agent can't do, with the exact refusal for 20 real attacks: [docs/attack-gallery.md](https://github.com/ege-usta/lomberk/blob/main/docs/attack-gallery.md).
|
|
161
|
+
|
|
162
|
+
Step-by-step for **Supabase, Neon, Amazon RDS and Railway** (roles, connection strings, RLS, `updated_at` triggers): [docs/hosted-postgres.md](https://github.com/ege-usta/lomberk/blob/main/docs/hosted-postgres.md).
|
|
163
|
+
|
|
164
|
+
Common questions before adopting Lomberk: [docs/faq.md](https://github.com/ege-usta/lomberk/blob/main/docs/faq.md).
|
|
165
|
+
|
|
166
|
+
Local, single-user development without the service: `lomberk serve` (stdio MCP). Do not use this mode with agents that have shell access. See [SECURITY.md](https://github.com/ege-usta/lomberk/blob/main/SECURITY.md).
|
|
167
|
+
|
|
168
|
+
## Team features (Solo and Pro)
|
|
169
|
+
The web panel and audit export come with Solo and Pro; Slack approvals, approver SSO and the two-person rule are Pro.
|
|
170
|
+
|
|
171
|
+
Slack approvals and approver SSO need extra libraries: `pip install "lomberk[pro]"`, or `uvx --from "lomberk[pro]" lomberk serve`. The Docker image has them. Without them the gateway refuses to start and says what to install.
|
|
172
|
+
|
|
173
|
+
### Approve in Slack (Pro)
|
|
174
|
+
Create the app from [`slack/app-manifest.json`](https://github.com/ege-usta/lomberk/blob/main/slack/app-manifest.json) (bot scope `chat:write` only, Socket Mode, so no public URL is needed), invite the bot to your approvals channel, then:
|
|
175
|
+
```yaml
|
|
176
|
+
slack:
|
|
177
|
+
channel_id: C0123APPROVALS
|
|
178
|
+
team_allowlist: [T0123]
|
|
179
|
+
admin_allowlist: [U0ALICE, U0BOB] # only these users' clicks count
|
|
180
|
+
include_sql: false # default: SQL never leaves for Slack
|
|
181
|
+
```
|
|
182
|
+
```bash
|
|
183
|
+
LOMBERK_SLACK_ENABLED=true LOMBERK_SLACK_APP_TOKEN=xapp-… LOMBERK_SLACK_BOT_TOKEN=xoxb-…
|
|
184
|
+
```
|
|
185
|
+
Every held request is posted with Approve/Reject buttons, and the message updates to the final state. The approver's Slack ID is written to the audit log.
|
|
186
|
+
|
|
187
|
+
### Approver SSO (Pro)
|
|
188
|
+
Approvers sign in with the company identity provider (Okta, Microsoft Entra ID, Google, Keycloak, any OIDC issuer) and send their ID token. The audit log then records the person (`oidc:alice@corp.com`), not a shared key.
|
|
189
|
+
```yaml
|
|
190
|
+
sso:
|
|
191
|
+
issuer: https://corp.okta.com # keys are discovered from the issuer
|
|
192
|
+
audience: lomberk-approvals # the client ID registered for Lomberk
|
|
193
|
+
allowed_groups: [db-approvers] # and/or allowed_identities: [alice@corp.com]
|
|
194
|
+
# allow_admin_key: true # the shared key is OFF once SSO is on; CLI stays as break-glass
|
|
195
|
+
```
|
|
196
|
+
```bash
|
|
197
|
+
curl -H "Authorization: Bearer $ID_TOKEN" -X POST localhost:8000/v1/checkpoints/cp_…/approve
|
|
198
|
+
```
|
|
199
|
+
Only asymmetrically signed tokens (RS/PS/ES) for this issuer and audience are accepted, and they must be unexpired. With the default `identity_claim: email`, the token must mark the email as verified. Entra ID does not send that flag, so set `identity_claim` to `upn` or `oid` there.
|
|
200
|
+
|
|
201
|
+
### Web approval panel (Pro)
|
|
202
|
+
Open `https://<gateway>/panel/` for writes waiting for approval, the history with who approved each one, one-click undo, and exports. Approvers sign in with SSO or the admin key. To offer "Sign in with SSO", register `<panel_url>/panel/sso/callback` as a redirect URI at your IdP:
|
|
203
|
+
```yaml
|
|
204
|
+
sso:
|
|
205
|
+
# ...the SSO settings above, with audience = the panel's client ID
|
|
206
|
+
panel_url: https://lomberk.corp.com # public https URL of this gateway
|
|
207
|
+
```
|
|
208
|
+
Set `LOMBERK_SSO_CLIENT_SECRET` if your IdP requires a client secret for that client.
|
|
209
|
+
|
|
210
|
+
### Two-person rule (Pro)
|
|
211
|
+
```yaml
|
|
212
|
+
approvals:
|
|
213
|
+
required: 2 # a held write runs only after two different people approve it
|
|
214
|
+
```
|
|
215
|
+
Only verified identities count: the N people must all approve through SSO (panel or API), or all through Slack. Mixing systems doesn't add up, because one person can hold both. Admin-key and CLI approvals are recorded but don't count, since they prove no identity. One reject cancels the write. Every approval is stored in the append-only `lomberk.approvals` table.
|
|
216
|
+
|
|
217
|
+
### Audit export (Pro)
|
|
218
|
+
```bash
|
|
219
|
+
curl -H "X-API-Key: $ADMIN_KEY" "localhost:8000/v1/audit/export?format=csv&since=2026-01-01" > audit.csv # or format=jsonl
|
|
220
|
+
```
|
|
221
|
+
Each checkpoint comes with its SQL, agent, status, rows, who approved it and its ledger hash. The panel has the same export as buttons.
|
|
222
|
+
|
|
223
|
+
### License key
|
|
224
|
+
The web panel and audit export need `LOMBERK_LICENSE_KEY` with a Solo or Pro key; Slack approvals, SSO and the two-person rule need Pro. A Solo key with Pro features configured refuses to start and says which feature needs Pro. The key also sets how far back undo reaches: 24 hours without one, 30 days with Solo or Pro, no limit with Enterprise. Age counts from when the write ran (for a held write, its last approval). An older checkpoint answers `expired` (HTTP 403) and stays as it is.
|
|
225
|
+
|
|
226
|
+
Buy a key on the [pricing page](https://ege-usta.github.io/lomberk/#pricing): it arrives by email right away and renews with your subscription. The gateway checks the key with Polar (`api.polar.sh`) once at startup. That call sends the key and, for a Pro key, a random label for this database (`lomberk.license_activation`), so one Pro key covers up to 3 databases. Free one up in Polar's customer portal. `lomberk undo` asks Polar only when a checkpoint is older than 24 hours. If Polar is down (no answer, or a 5xx or 429 reply after three tries), Pro stays on; while Polar doesn't answer at all, startup can take up to about a minute. A database's first activation is never sent twice: after a timeout or a 5xx it isn't retried (Polar may already have used a slot), and the next start tries again. Any other reply that isn't a valid grant counts as an invalid key. If the key is invalid, cancelled or already used on 3 databases, the gateway refuses to start with Pro features configured and tells you why. The key is checked at startup, so a cancelled subscription turns Pro off at the next restart.
|
|
227
|
+
|
|
228
|
+
## How it works
|
|
229
|
+
1. **AST hard gate**: PostgreSQL's own parser (`pglast`) allows one `SELECT/INSERT/UPDATE/DELETE` (or a plain `EXPLAIN`), schema-qualified writes with `WHERE`, and ~190 pure built-in functions: window functions, JSON, full-text search, `gen_random_uuid()` and the rest of what agents write every day.
|
|
230
|
+
2. **Policy**: protected tables, FK-cascade reach, and a row limit enforced by executing inside a transaction and rolling back.
|
|
231
|
+
3. **Capture**: a `SECURITY DEFINER` row trigger records every change (cascades included), bound to a single-use capability token.
|
|
232
|
+
4. **Undo**: an order that respects foreign keys (parents are restored before children), with conflict and schema-drift checks. Old rows are kept in Postgres' own text form, so every type (json `null` and key order included) comes back exactly. It refuses rather than touching rows written later.
|
|
233
|
+
5. **Bounded reads**: reads run read-only under `statement_timeout`, stream through a server-side cursor and return at most 100 rows (4,000 characters per value, 100,000 in total), telling the agent when more exist.
|
|
234
|
+
6. **Ledger**: append-only, SHA-256 hash-chained checkpoints.
|
|
235
|
+
|
|
236
|
+
### Monitoring
|
|
237
|
+
`/healthz` (process up) and `/readyz` (both database roles reachable) need no key. Set `LOMBERK_METRICS_KEY` to turn on `/metrics` for Prometheus: decisions by event (`denied`, `held`, `applied`, `undone`, ...), held writes waiting, and the age of the oldest one, so you can alert when approvals pile up. It carries counts only, never SQL, rows or names.
|
|
238
|
+
```yaml
|
|
239
|
+
scrape_configs:
|
|
240
|
+
- job_name: lomberk
|
|
241
|
+
authorization: { credentials: "<LOMBERK_METRICS_KEY>" }
|
|
242
|
+
static_configs: [{ targets: ["lomberk:8000"] }]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Current limits:** every registered table needs a primary key (one column or several; `lomberk register` reads it from the table). Partitioned tables are supported: register the partitioned table and every partition, including ones added later, is covered, and a row moved to another partition comes back where it was. Writes to tables with rules or classic inheritance are refused, and so are partitioned tables with their own triggers. Writes to tables with their own triggers wait for a person, unless you register the table with `--allow-triggers` (for triggers like `updated_at` that only change the row itself or tables Lomberk also protects); undo then switches them off while it restores rows, so rows come back exactly. JSON keys must be written as literals (`->>'theme'`); masking inside JSON is best effort, so list JSON columns that may hold secrets in `sensitive_columns`. DDL is blocked, not reversible. Full list in [SECURITY.md](https://github.com/ege-usta/lomberk/blob/main/SECURITY.md).
|
|
246
|
+
|
|
247
|
+
## Overhead
|
|
248
|
+
Measured against Postgres 16 on localhost (200 runs, p50 / p95):
|
|
249
|
+
|
|
250
|
+
| Path | Latency |
|
|
251
|
+
|---|---|
|
|
252
|
+
| Hard gate + policy (no DB) | 0.1 / 0.1 ms |
|
|
253
|
+
| Read through Lomberk | 5.4 / 6.2 ms |
|
|
254
|
+
| Captured, undoable write through Lomberk | 18.3 / 23.9 ms |
|
|
255
|
+
| Plain write on a new connection (baseline) | 4.8 / 6.0 ms |
|
|
256
|
+
|
|
257
|
+
Agent turns take seconds, so this is negligible in practice. Most of the cost is opening new connections; connection pooling is on the roadmap for remote/TLS databases.
|
|
258
|
+
|
|
259
|
+
Tested on PostgreSQL 14, 15, 16, 17 and 18 — the full test suite runs against every one in CI.
|
|
260
|
+
|
|
261
|
+
## Pricing
|
|
262
|
+
| | Free | Solo | Pro | Enterprise |
|
|
263
|
+
|---|---|---|---|---|
|
|
264
|
+
| For | trying it, side projects | one developer | a team | many databases |
|
|
265
|
+
| Price | $0 | $49/month (checkout opening soon) | **$199/month** or $1,990/year | [talk to us](https://github.com/ege-usta/lomberk/discussions) |
|
|
266
|
+
| Hard gate, holds, capture, ledger, remote MCP, API/CLI approvals, `lomberk log` | ✓ | ✓ | ✓ | ✓ |
|
|
267
|
+
| Undo reaches back | 24 hours | 30 days | 30 days | no limit |
|
|
268
|
+
| Web panel, audit export | | ✓ | ✓ | ✓ |
|
|
269
|
+
| Slack approvals, approver SSO, two-person rule | | | ✓ | ✓ |
|
|
270
|
+
| Databases per license key | | 1 | up to 3 | unlimited |
|
|
271
|
+
| Billing | | card, self-serve | card, self-serve | annual invoice |
|
|
272
|
+
| Help | GitHub issues | GitHub issues | GitHub issues | priority answers; help with your security questionnaire |
|
|
273
|
+
|
|
274
|
+
**[Buy Pro](https://buy.polar.sh/polar_cl_YqG6acsQyrldmOnlSzRDyHX5o1bWT76m33Afd3VTgEU)**: the key arrives by email right away. For Enterprise, open a [discussion](https://github.com/ege-usta/lomberk/discussions) with how many databases and approvers you have, and we'll reply with a quote. Payments, invoices and sales tax are handled by [Polar](https://polar.sh), the merchant of record.
|
|
275
|
+
|
|
276
|
+
Lomberk is self-hosted and your data never reaches us. There is no phone support or on-call; the docs and `lomberk doctor` are built so you can set it up alone.
|
|
277
|
+
|
|
278
|
+
## Development
|
|
279
|
+
```bash
|
|
280
|
+
uv sync --all-groups
|
|
281
|
+
PYTHONPATH=src uv run pytest tests/ # needs Docker (testcontainers)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Changes: [CHANGELOG.md](https://github.com/ege-usta/lomberk/blob/main/CHANGELOG.md). Contributing: [CONTRIBUTING.md](https://github.com/ege-usta/lomberk/blob/main/CONTRIBUTING.md).
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
Open core. Everything outside `src/lomberk/ee/` is Apache-2.0: the hard gate, policy, capture, undo, ledger, remote MCP, and API/CLI approvals. You can run the whole safety path for free.
|
|
288
|
+
|
|
289
|
+
`src/lomberk/ee/` (Slack approvals, approver SSO, the web panel, audit export and the license check) is source-available under the [Elastic License 2.0](https://github.com/ege-usta/lomberk/blob/main/src/lomberk/ee/LICENSE) (ELv2), the same license Elasticsearch uses. You can read, run and modify it. The features need a Solo, Pro or Enterprise key, and ELv2 forbids removing or bypassing the key check or offering the software as a hosted service. The two-person rule is core code, but turning it on needs a key.
|