cfgit 0.1.0__py3-none-any.whl
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.
- cfg/__init__.py +13 -0
- cfg/adapters/__init__.py +25 -0
- cfg/adapters/base.py +127 -0
- cfg/adapters/mongo.py +570 -0
- cfg/adapters/postgres.py +756 -0
- cfg/approval/__init__.py +5 -0
- cfg/approval/base.py +29 -0
- cfg/cli/__init__.py +2 -0
- cfg/cli/main.py +665 -0
- cfg/core/__init__.py +13 -0
- cfg/core/authz.py +58 -0
- cfg/core/config.py +324 -0
- cfg/core/diff.py +43 -0
- cfg/core/engine.py +1388 -0
- cfg/core/hashing.py +102 -0
- cfg/core/identity.py +213 -0
- cfg/interfaces/__init__.py +2 -0
- cfg/interfaces/actions.py +598 -0
- cfg/mcp/__init__.py +10 -0
- cfg/mcp/server.py +452 -0
- cfg/ui/__init__.py +2 -0
- cfg/ui/server.py +1066 -0
- cfgit-0.1.0.dist-info/METADATA +744 -0
- cfgit-0.1.0.dist-info/RECORD +28 -0
- cfgit-0.1.0.dist-info/WHEEL +4 -0
- cfgit-0.1.0.dist-info/entry_points.txt +3 -0
- cfgit-0.1.0.dist-info/licenses/LICENSE +201 -0
- cfgit-0.1.0.dist-info/licenses/NOTICE +10 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cfgit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Git-style history, diff, drift detection, and rollback for live database records without migrating or owning your datastore
|
|
5
|
+
Project-URL: Homepage, https://github.com/AusafMo/cfgit
|
|
6
|
+
Project-URL: Repository, https://github.com/AusafMo/cfgit
|
|
7
|
+
Project-URL: Issues, https://github.com/AusafMo/cfgit/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/AusafMo/cfgit#readme
|
|
9
|
+
Author: Mohammad Ausaf
|
|
10
|
+
License: Apache License
|
|
11
|
+
Version 2.0, January 2004
|
|
12
|
+
http://www.apache.org/licenses/
|
|
13
|
+
|
|
14
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
15
|
+
|
|
16
|
+
1. Definitions.
|
|
17
|
+
|
|
18
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
19
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
20
|
+
|
|
21
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
22
|
+
the copyright owner that is granting the License.
|
|
23
|
+
|
|
24
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
25
|
+
other entities that control, are controlled by, or are under common
|
|
26
|
+
control with that entity. For the purposes of this definition,
|
|
27
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
28
|
+
direction or management of such entity, whether by contract or
|
|
29
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
30
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
31
|
+
|
|
32
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
33
|
+
exercising permissions granted by this License.
|
|
34
|
+
|
|
35
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
36
|
+
including but not limited to software source code, documentation
|
|
37
|
+
source, and configuration files.
|
|
38
|
+
|
|
39
|
+
"Object" form shall mean any form resulting from mechanical
|
|
40
|
+
transformation or translation of a Source form, including but
|
|
41
|
+
not limited to compiled object code, generated documentation,
|
|
42
|
+
and conversions to other media types.
|
|
43
|
+
|
|
44
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
45
|
+
Object form, made available under the License, as indicated by a
|
|
46
|
+
copyright notice that is included in or attached to the work
|
|
47
|
+
(an example is provided in the Appendix below).
|
|
48
|
+
|
|
49
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
50
|
+
form, that is based on (or derived from) the Work and for which the
|
|
51
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
52
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
53
|
+
of this License, Derivative Works shall not include works that remain
|
|
54
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
55
|
+
the Work and Derivative Works thereof.
|
|
56
|
+
|
|
57
|
+
"Contribution" shall mean any work of authorship, including
|
|
58
|
+
the original version of the Work and any modifications or additions
|
|
59
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
60
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
61
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
62
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
63
|
+
means any form of electronic, verbal, or written communication sent
|
|
64
|
+
to the Licensor or its representatives, including but not limited to
|
|
65
|
+
communication on electronic mailing lists, source code control systems,
|
|
66
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
67
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
68
|
+
excluding communication that is conspicuously marked or otherwise
|
|
69
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
70
|
+
|
|
71
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
72
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
73
|
+
subsequently incorporated within the Work.
|
|
74
|
+
|
|
75
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
76
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
77
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
78
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
79
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
80
|
+
Work and such Derivative Works in Source or Object form.
|
|
81
|
+
|
|
82
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
83
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
84
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
85
|
+
(except as stated in this section) patent license to make, have made,
|
|
86
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
87
|
+
where such license applies only to those patent claims licensable
|
|
88
|
+
by such Contributor that are necessarily infringed by their
|
|
89
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
90
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
91
|
+
institute patent litigation against any entity (including a
|
|
92
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
93
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
94
|
+
or contributory patent infringement, then any patent licenses
|
|
95
|
+
granted to You under this License for that Work shall terminate
|
|
96
|
+
as of the date such litigation is filed.
|
|
97
|
+
|
|
98
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
99
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
100
|
+
modifications, and in Source or Object form, provided that You
|
|
101
|
+
meet the following conditions:
|
|
102
|
+
|
|
103
|
+
(a) You must give any other recipients of the Work or
|
|
104
|
+
Derivative Works a copy of this License; and
|
|
105
|
+
|
|
106
|
+
(b) You must cause any modified files to carry prominent notices
|
|
107
|
+
stating that You changed the files; and
|
|
108
|
+
|
|
109
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
110
|
+
that You distribute, all copyright, patent, trademark, and
|
|
111
|
+
attribution notices from the Source form of the Work,
|
|
112
|
+
excluding those notices that do not pertain to any part of
|
|
113
|
+
the Derivative Works; and
|
|
114
|
+
|
|
115
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
116
|
+
distribution, then any Derivative Works that You distribute must
|
|
117
|
+
include a readable copy of the attribution notices contained
|
|
118
|
+
within such NOTICE file, excluding those notices that do not
|
|
119
|
+
pertain to any part of the Derivative Works, in at least one
|
|
120
|
+
of the following places: within a NOTICE text file distributed
|
|
121
|
+
as part of the Derivative Works; within the Source form or
|
|
122
|
+
documentation, if provided along with the Derivative Works; or,
|
|
123
|
+
within a display generated by the Derivative Works, if and
|
|
124
|
+
wherever such third-party notices normally appear. The contents
|
|
125
|
+
of the NOTICE file are for informational purposes only and
|
|
126
|
+
do not modify the License. You may add Your own attribution
|
|
127
|
+
notices within Derivative Works that You distribute, alongside
|
|
128
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
129
|
+
that such additional attribution notices cannot be construed
|
|
130
|
+
as modifying the License.
|
|
131
|
+
|
|
132
|
+
You may add Your own copyright statement to Your modifications and
|
|
133
|
+
may provide additional or different license terms and conditions
|
|
134
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
135
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
136
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
137
|
+
the conditions stated in this License.
|
|
138
|
+
|
|
139
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
140
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
141
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
142
|
+
this License, without any additional terms or conditions.
|
|
143
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
144
|
+
the terms of any separate license agreement you may have executed
|
|
145
|
+
with Licensor regarding such Contributions.
|
|
146
|
+
|
|
147
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
148
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
149
|
+
except as required for reasonable and customary use in describing the
|
|
150
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
151
|
+
|
|
152
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
153
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
154
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
155
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
156
|
+
implied, including, without limitation, any warranties or conditions
|
|
157
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
158
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
159
|
+
appropriateness of using or redistributing the Work and assume any
|
|
160
|
+
risks associated with Your exercise of permissions under this License.
|
|
161
|
+
|
|
162
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
163
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
164
|
+
unless required by applicable law (such as deliberate and grossly
|
|
165
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
166
|
+
liable to You for damages, including any direct, indirect, special,
|
|
167
|
+
incidental, or consequential damages of any character arising as a
|
|
168
|
+
result of this License or out of the use or inability to use the
|
|
169
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
170
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
171
|
+
other commercial damages or losses), even if such Contributor
|
|
172
|
+
has been advised of the possibility of such damages.
|
|
173
|
+
|
|
174
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
175
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
176
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
177
|
+
or other liability obligations and/or rights consistent with this
|
|
178
|
+
License. However, in accepting such obligations, You may act only
|
|
179
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
180
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
181
|
+
defend, and hold each Contributor harmless for any liability
|
|
182
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
183
|
+
of your accepting any such warranty or additional liability.
|
|
184
|
+
|
|
185
|
+
END OF TERMS AND CONDITIONS
|
|
186
|
+
|
|
187
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
188
|
+
|
|
189
|
+
To apply the Apache License to your work, attach the following
|
|
190
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
191
|
+
replaced with your own identifying information. (Don't include
|
|
192
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
193
|
+
comment syntax for the file format. We also recommend that a
|
|
194
|
+
file or class name and description of purpose be included on the
|
|
195
|
+
same "printed page" as the copyright notice for easier
|
|
196
|
+
identification within third-party archives.
|
|
197
|
+
|
|
198
|
+
Copyright 2026 Mohammad Ausaf
|
|
199
|
+
|
|
200
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
201
|
+
you may not use this file except in compliance with the License.
|
|
202
|
+
You may obtain a copy of the License at
|
|
203
|
+
|
|
204
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
205
|
+
|
|
206
|
+
Unless required by applicable law or agreed to in writing, software
|
|
207
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
208
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
209
|
+
See the License for the specific language governing permissions and
|
|
210
|
+
limitations under the License.
|
|
211
|
+
License-File: LICENSE
|
|
212
|
+
License-File: NOTICE
|
|
213
|
+
Keywords: agents,configuration,database,database-versioning,datastore,drift-detection,mcp,mongodb,postgres,rollback,version-control
|
|
214
|
+
Requires-Python: >=3.11
|
|
215
|
+
Requires-Dist: tomli; python_version < '3.11'
|
|
216
|
+
Provides-Extra: cli
|
|
217
|
+
Requires-Dist: click>=8.1; extra == 'cli'
|
|
218
|
+
Requires-Dist: rich>=13.0; extra == 'cli'
|
|
219
|
+
Provides-Extra: dev
|
|
220
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
221
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
222
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
223
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
224
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
225
|
+
Provides-Extra: impact
|
|
226
|
+
Provides-Extra: mcp
|
|
227
|
+
Requires-Dist: mcp>=1.0; extra == 'mcp'
|
|
228
|
+
Provides-Extra: mongo
|
|
229
|
+
Requires-Dist: pymongo>=4.6; extra == 'mongo'
|
|
230
|
+
Provides-Extra: postgres
|
|
231
|
+
Requires-Dist: psycopg[binary]>=3.2; extra == 'postgres'
|
|
232
|
+
Description-Content-Type: text/markdown
|
|
233
|
+
|
|
234
|
+
# cfgit
|
|
235
|
+
|
|
236
|
+
Non-custodial version control for live datastore records.
|
|
237
|
+
|
|
238
|
+
A clean tool for dirty workflows. Git that does not make you move in.
|
|
239
|
+
|
|
240
|
+
cfgit gives git-shaped history, diff, rollback, tags, and drift reconciliation to
|
|
241
|
+
records that already live in your database. Your application keeps reading the
|
|
242
|
+
same database. Your scripts and admin tools can still write it. cfgit sits beside
|
|
243
|
+
the store, records what changed, and refuses to clobber changes it did not record.
|
|
244
|
+
|
|
245
|
+
<p align="center">
|
|
246
|
+
<img src="docs/screenshots/01-diff.png" alt="Side-by-side line diff of a live agent config, with a sticky field header and collapsed context" width="32%" />
|
|
247
|
+
<img src="docs/screenshots/02-impact.png" alt="System-impact panel: deterministic facts plus opt-in LLM narration of what the change does downstream" width="32%" />
|
|
248
|
+
<img src="docs/screenshots/03-scoped-impact.png" alt="Scoped impact: select records on the left and reason the change against only those" width="32%" />
|
|
249
|
+
</p>
|
|
250
|
+
|
|
251
|
+
<p align="center">
|
|
252
|
+
<sub>Line-aligned diff of a live record · system-impact panel · impact scoped to the records you select (demo data)</sub>
|
|
253
|
+
</p>
|
|
254
|
+
|
|
255
|
+
## Why cfgit exists
|
|
256
|
+
|
|
257
|
+
Many teams keep runtime behavior in live database records: model routing, agent
|
|
258
|
+
prompts, provider settings, pricing tables, policy config, workflow definitions,
|
|
259
|
+
feature controls, and other control-plane data. These records are often edited by
|
|
260
|
+
people, scripts, admin APIs, and AI coding agents. The edits take effect
|
|
261
|
+
immediately, but the workflow usually lacks the things engineers expect from code:
|
|
262
|
+
|
|
263
|
+
- a useful history
|
|
264
|
+
- a readable diff
|
|
265
|
+
- a safe commit path
|
|
266
|
+
- rollback to a known good point
|
|
267
|
+
- a way to see when someone changed the database outside the tool
|
|
268
|
+
|
|
269
|
+
Existing "git for data" tools usually solve a different problem: they want to
|
|
270
|
+
own the database or sit in front of storage. cfgit is for the case where you
|
|
271
|
+
cannot move the data and cannot put a gateway in the runtime path.
|
|
272
|
+
|
|
273
|
+
## Core idea
|
|
274
|
+
|
|
275
|
+
cfgit versions opaque JSON records identified by a stable id. It stores history
|
|
276
|
+
beside the live datastore, not inside your application code and not in a hosted
|
|
277
|
+
prompt registry.
|
|
278
|
+
|
|
279
|
+
The important state is drift:
|
|
280
|
+
|
|
281
|
+
- `cfg status` detects live records that changed outside cfgit.
|
|
282
|
+
- `cfg diff <record> =HEAD =live` shows what changed.
|
|
283
|
+
- `cfg adopt <record>` folds that out-of-band change into history.
|
|
284
|
+
- `cfg commit` refuses to overwrite un-adopted drift.
|
|
285
|
+
|
|
286
|
+
That drift reconciliation is the main reason cfgit exists.
|
|
287
|
+
|
|
288
|
+
## Status
|
|
289
|
+
|
|
290
|
+
cfgit is pre-1.0 software. The current implementation includes:
|
|
291
|
+
|
|
292
|
+
- CLI with JSON output
|
|
293
|
+
- MongoDB adapter
|
|
294
|
+
- Postgres adapter
|
|
295
|
+
- local author permission checks
|
|
296
|
+
- per-environment identity modes with hashed token or DB-principal verification
|
|
297
|
+
- opt-in branch and PR workflow for draft changes before runtime merge
|
|
298
|
+
- system restore by tag or timestamp
|
|
299
|
+
- localhost web UI
|
|
300
|
+
- MCP server
|
|
301
|
+
- portable Codex or Claude Code skill
|
|
302
|
+
- optional `cfgit-impact` plugin for deterministic impact summaries and opt-in LLM narration
|
|
303
|
+
|
|
304
|
+
The engine is intentionally DB-neutral. Mongo and Postgres are the first two
|
|
305
|
+
adapters to prove the storage seam.
|
|
306
|
+
|
|
307
|
+
## When to use cfgit
|
|
308
|
+
|
|
309
|
+
Good fit:
|
|
310
|
+
|
|
311
|
+
- control-plane collections or tables
|
|
312
|
+
- low to moderate record counts
|
|
313
|
+
- records edited by a small team or agents
|
|
314
|
+
- changes where "who changed what and why" matters
|
|
315
|
+
- data where rollback to a known good state is a real operation
|
|
316
|
+
|
|
317
|
+
Examples:
|
|
318
|
+
|
|
319
|
+
- agent configs
|
|
320
|
+
- model routing records
|
|
321
|
+
- provider templates
|
|
322
|
+
- pricing or policy config
|
|
323
|
+
- workflow definitions
|
|
324
|
+
- feature or runtime behavior config
|
|
325
|
+
|
|
326
|
+
Bad fit:
|
|
327
|
+
|
|
328
|
+
- user-generated content
|
|
329
|
+
- events, logs, analytics, metrics
|
|
330
|
+
- high-write transactional tables
|
|
331
|
+
- append-only data
|
|
332
|
+
- rows written by traffic rather than curated by people
|
|
333
|
+
|
|
334
|
+
cfgit stores full document versions. It is not a warehouse, event log, backup
|
|
335
|
+
system, or schema migration tool.
|
|
336
|
+
|
|
337
|
+
## Install
|
|
338
|
+
|
|
339
|
+
From a checkout:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
python -m venv .venv
|
|
343
|
+
. .venv/bin/activate
|
|
344
|
+
pip install -e '.[mongo,postgres,mcp,dev]'
|
|
345
|
+
pip install -e plugins/cfg_impact
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Minimal install for Mongo only:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
pip install -e '.[mongo]'
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Minimal install for Postgres only:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
pip install -e '.[postgres]'
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Quick start
|
|
361
|
+
|
|
362
|
+
Create `.cfg.toml` in the repo or working directory where you want to operate:
|
|
363
|
+
|
|
364
|
+
```toml
|
|
365
|
+
[project]
|
|
366
|
+
name = "runtime-control-plane"
|
|
367
|
+
|
|
368
|
+
[history]
|
|
369
|
+
history_collection = "config_history"
|
|
370
|
+
heads_collection = "config_heads"
|
|
371
|
+
|
|
372
|
+
[branches]
|
|
373
|
+
enabled = false
|
|
374
|
+
refs_collection = "cfgit_refs"
|
|
375
|
+
default_branch = "main"
|
|
376
|
+
|
|
377
|
+
[[collection]]
|
|
378
|
+
name = "agent_configs"
|
|
379
|
+
id_field = "config_id"
|
|
380
|
+
live_when = { is_active = true }
|
|
381
|
+
ignore_fields = ["_id", "is_active", "updated_at", "updated_by"]
|
|
382
|
+
secret_fields = []
|
|
383
|
+
|
|
384
|
+
[env.dev]
|
|
385
|
+
database = "mongo"
|
|
386
|
+
uri = "env:DEV_MONGODB_URI"
|
|
387
|
+
db = "my-dev-db"
|
|
388
|
+
needs_approval = false
|
|
389
|
+
|
|
390
|
+
[env.dev.identity]
|
|
391
|
+
mode = "open"
|
|
392
|
+
|
|
393
|
+
[env.dev.permissions]
|
|
394
|
+
mode = "open"
|
|
395
|
+
admins = []
|
|
396
|
+
writers = []
|
|
397
|
+
admin_actions = ["restore_system"]
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
You can define multiple `[env.<name>]` blocks, but one cfgit command opens one
|
|
401
|
+
env at a time:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
cfg --env dev status
|
|
405
|
+
cfg --env prod log agent_configs:agent_planner
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Keep each physical history store under one stable env name. If the same database
|
|
409
|
+
is later addressed as a different env, cfgit will report that history exists
|
|
410
|
+
under the original env instead of returning an empty log.
|
|
411
|
+
|
|
412
|
+
Point it at a local or staging database first:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
export DEV_MONGODB_URI='mongodb://localhost:27017/?replicaSet=rs0'
|
|
416
|
+
cfg init
|
|
417
|
+
cfg doctor
|
|
418
|
+
cfg import --all -m "initial import"
|
|
419
|
+
cfg status
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
`cfg doctor` is read-only. Run it before the first import for a new database or
|
|
423
|
+
`.cfg.toml`; it reports secret-deny matches, large fields, and live-rule/key
|
|
424
|
+
issues in one pass, with paste-ready `secret_fields` and `ignore_fields`
|
|
425
|
+
suggestions.
|
|
426
|
+
|
|
427
|
+
Check drift:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
cfg status
|
|
431
|
+
cfg diff agent_configs:agent_planner =HEAD =live
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Commit a full JSON document:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
cfg commit agent_configs:agent_planner --from planner.json -m "tune planner routing"
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Commit multiple records as one batch intent:
|
|
441
|
+
|
|
442
|
+
```json
|
|
443
|
+
[
|
|
444
|
+
{"record": "agent_configs:planner", "doc": {"config_id": "planner", "model": "fast"}},
|
|
445
|
+
{"record": "modelgarden_models:openai/gpt-4o-mini", "doc": {"model_path": "openai/gpt-4o-mini"}}
|
|
446
|
+
]
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
cfg commit --bulk-from batch.json -m "switch planner routing"
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Bulk commit preflights the whole batch before writing. If any target has
|
|
454
|
+
un-adopted drift, is missing, duplicates another target, or trips the secret
|
|
455
|
+
policy, cfgit applies none of the batch.
|
|
456
|
+
|
|
457
|
+
Draft changes on a branch before mutating runtime:
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
cfg branch create router-test --from main
|
|
461
|
+
cfg --branch router-test commit agent_configs:agent_planner --from planner.json -m "try router change"
|
|
462
|
+
cfg diff main..router-test
|
|
463
|
+
cfg pr create --base main --head router-test -m "review router change"
|
|
464
|
+
cfg pr merge <pr-id>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Branching is opt-in with `[branches] enabled = true`. `branch`, branch commits,
|
|
468
|
+
branch diff, and PR creation write only cfgit draft/review refs. The only branch
|
|
469
|
+
command that mutates runtime is `cfg pr merge`, and it refuses stale main heads
|
|
470
|
+
or un-adopted live drift.
|
|
471
|
+
|
|
472
|
+
`commit`, `import`, and `adopt` scan the would-be-stored document for secret-like
|
|
473
|
+
field names and values from `[secrets]`. Fields listed in `secret_fields` are
|
|
474
|
+
stripped before history. Use `--allow-secret` only for intentional fixtures or
|
|
475
|
+
known false positives; cfgit records the override in history metadata.
|
|
476
|
+
|
|
477
|
+
Adopt an out-of-band database write:
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
cfg adopt agent_configs:agent_planner -m "adopt admin console edit"
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Tag and restore:
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
cfg tag june7-good
|
|
487
|
+
cfg restore --tag june7-good --dry-run -m "preview rollback"
|
|
488
|
+
cfg restore --tag june7-good -m "restore known good state"
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Open the local UI:
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
cfg ui
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Run the MCP server:
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
cfg-mcp
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Record syntax
|
|
504
|
+
|
|
505
|
+
Records are addressed as:
|
|
506
|
+
|
|
507
|
+
```text
|
|
508
|
+
collection:id
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Examples:
|
|
512
|
+
|
|
513
|
+
```text
|
|
514
|
+
agent_configs:agent_planner
|
|
515
|
+
modelgarden_models:openai/gpt-4o-mini
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The collection and id field are configured in `.cfg.toml`.
|
|
519
|
+
|
|
520
|
+
## Commands
|
|
521
|
+
|
|
522
|
+
Common commands:
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
cfg init
|
|
526
|
+
cfg doctor [record]
|
|
527
|
+
cfg import --all -m "initial import"
|
|
528
|
+
cfg status [record]
|
|
529
|
+
cfg diff <record> [from] [to]
|
|
530
|
+
cfg impact <record> [from] [to]
|
|
531
|
+
cfg commit <record> --from <file.json> -m "message"
|
|
532
|
+
cfg commit --bulk-from <batch.json> -m "message"
|
|
533
|
+
cfg branch list
|
|
534
|
+
cfg branch create <name> --from main
|
|
535
|
+
cfg branch delete <name>
|
|
536
|
+
cfg switch <name>
|
|
537
|
+
cfg --branch <name> commit <record> --from <file.json> -m "message"
|
|
538
|
+
cfg --branch <name> commit --bulk-from <batch.json> -m "message"
|
|
539
|
+
cfg diff main..<branch>
|
|
540
|
+
cfg --branch <branch> log
|
|
541
|
+
cfg pr create --base main --head <branch> -m "message"
|
|
542
|
+
cfg pr list
|
|
543
|
+
cfg pr show <id>
|
|
544
|
+
cfg pr close <id>
|
|
545
|
+
cfg pr merge <id>
|
|
546
|
+
cfg log <record>
|
|
547
|
+
cfg show <record> <ref>
|
|
548
|
+
cfg adopt <record> -m "message"
|
|
549
|
+
cfg adopt --all -m "message"
|
|
550
|
+
cfg tag <name>
|
|
551
|
+
cfg restore <record> <ref> -m "message"
|
|
552
|
+
cfg restore --as-of <date> --dry-run -m "message"
|
|
553
|
+
cfg restore --tag <name> --dry-run -m "message"
|
|
554
|
+
cfg fsck
|
|
555
|
+
cfg whoami
|
|
556
|
+
cfg ui
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
Every command supports `--json` for scripts and agents.
|
|
560
|
+
|
|
561
|
+
Refs:
|
|
562
|
+
|
|
563
|
+
- `=HEAD` or `HEAD`: last cfgit-recorded version
|
|
564
|
+
- `=live` or `live`: current live database record
|
|
565
|
+
- `@<seq>`: history entry number
|
|
566
|
+
- `<oid-prefix>`: content hash prefix
|
|
567
|
+
- `tag:<name>`: tagged version
|
|
568
|
+
|
|
569
|
+
## Local UI
|
|
570
|
+
|
|
571
|
+
`cfg ui` starts a localhost-only web UI over the same action layer as the CLI and
|
|
572
|
+
MCP server. It reads like a git client: a collection-and-record tree on the left,
|
|
573
|
+
a commit-graph history rail, and a line-aligned side-by-side diff that collapses
|
|
574
|
+
unchanged context (expandable in place) and keeps the field name pinned while you
|
|
575
|
+
scroll. It can run status, diff, impact, commit, branch draft commits, PR open
|
|
576
|
+
and merge, log, show, adopt, restore, tag, init, import, and fsck, and ships
|
|
577
|
+
dark and light themes.
|
|
578
|
+
|
|
579
|
+
By default it binds to `127.0.0.1:8765` and tries the next free ports if needed:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
cfg ui
|
|
583
|
+
cfg ui --port 9000 --no-open
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
If you omit `--port`, cfgit will try the next free local ports. If you pass
|
|
587
|
+
`--port` explicitly, cfgit treats that port as intentional and fails if it is
|
|
588
|
+
already in use.
|
|
589
|
+
|
|
590
|
+
## MCP and agent usage
|
|
591
|
+
|
|
592
|
+
The MCP server exposes the same operations with a uniform envelope:
|
|
593
|
+
|
|
594
|
+
```json
|
|
595
|
+
{
|
|
596
|
+
"status": "ok",
|
|
597
|
+
"code": 0,
|
|
598
|
+
"message": "",
|
|
599
|
+
"data": {}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Tools include:
|
|
604
|
+
|
|
605
|
+
- `cfg_status`
|
|
606
|
+
- `cfg_doctor`
|
|
607
|
+
- `cfg_diff`
|
|
608
|
+
- `cfg_impact`
|
|
609
|
+
- `cfg_commit`
|
|
610
|
+
- `cfg_bulk_commit`
|
|
611
|
+
- `cfg_branch_list`
|
|
612
|
+
- `cfg_branch_create`
|
|
613
|
+
- `cfg_branch_delete`
|
|
614
|
+
- `cfg_branch_diff`
|
|
615
|
+
- `cfg_branch_log`
|
|
616
|
+
- `cfg_pr_create`
|
|
617
|
+
- `cfg_pr_list`
|
|
618
|
+
- `cfg_pr_show`
|
|
619
|
+
- `cfg_pr_close`
|
|
620
|
+
- `cfg_pr_merge`
|
|
621
|
+
- `cfg_log`
|
|
622
|
+
- `cfg_show`
|
|
623
|
+
- `cfg_adopt`
|
|
624
|
+
- `cfg_restore`
|
|
625
|
+
- `cfg_tag`
|
|
626
|
+
- `cfg_fsck`
|
|
627
|
+
- `cfg_whoami`
|
|
628
|
+
- `cfg_init`
|
|
629
|
+
- `cfg_identity_hash`
|
|
630
|
+
|
|
631
|
+
A portable skill lives at `skills/cfgit/SKILL.md`.
|
|
632
|
+
`cfg_impact` accepts the same `against` list/string as the CLI `--against` flag,
|
|
633
|
+
so MCP clients can request scoped narration without shelling out.
|
|
634
|
+
|
|
635
|
+
If `cfg_log` or `cfg_show` returns `bad_config` with a message saying history
|
|
636
|
+
exists under another env, the same database has been addressed with two
|
|
637
|
+
different `.cfg.toml` env names. Re-run with the env that wrote the history, or
|
|
638
|
+
standardize the config so that database always uses one env name.
|
|
639
|
+
|
|
640
|
+
## Impact summaries
|
|
641
|
+
|
|
642
|
+
`cfg impact` runs deterministic local analysis by default. It categorizes changed
|
|
643
|
+
paths, finds static references to changed values across configured records, and
|
|
644
|
+
reports a risk level.
|
|
645
|
+
|
|
646
|
+
Optional LLM narration lives in the separate `cfgit-impact` plugin. It reads the
|
|
647
|
+
real before/after of the change plus a map of the surrounding records, then
|
|
648
|
+
explains in plain language what the change does, what it ripples into, and how to
|
|
649
|
+
roll it back:
|
|
650
|
+
|
|
651
|
+
```bash
|
|
652
|
+
pip install -e plugins/cfg_impact
|
|
653
|
+
cfg impact agent_configs:agent_planner --llm --json
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
By default the narration reasons against the whole system. To scope it to a few
|
|
657
|
+
records you care about, pass `--against` (repeat it, or comma-separate). The model
|
|
658
|
+
then reasons about the changed record against only those selected records; no
|
|
659
|
+
unselected sibling record text leaves your machine:
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
cfg impact agent_configs:brief_classifier \
|
|
663
|
+
--against agent_configs:critic --against agent_configs:shot_breakdown --llm
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
In the web UI the same scoping is a click: select records in the left tree and the
|
|
667
|
+
button reads `Impact (2)`, scoping the analysis to that set.
|
|
668
|
+
|
|
669
|
+
Provider selection comes from `[connections].ai_provider` in `.cfg.toml`, unless
|
|
670
|
+
you pass `--provider`. `--llm` is gated by `[connections].share_with_ai`; add
|
|
671
|
+
the exact record id, `collection:*`, or `*` before any provider call. The plugin
|
|
672
|
+
supports `claude`, `openai`, and `gemini`. API keys are read from the environment
|
|
673
|
+
only (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY` or `GOOGLE_API_KEY`),
|
|
674
|
+
never from the config file.
|
|
675
|
+
|
|
676
|
+
The core package never imports LLM provider code or vendor SDKs. That boundary is
|
|
677
|
+
tested.
|
|
678
|
+
|
|
679
|
+
## Storage adapters
|
|
680
|
+
|
|
681
|
+
Mongo:
|
|
682
|
+
|
|
683
|
+
- requires a replica set or sharded cluster for transactional writes
|
|
684
|
+
- versions documents in configured collections
|
|
685
|
+
- stores history in configured history and heads collections
|
|
686
|
+
|
|
687
|
+
Postgres:
|
|
688
|
+
|
|
689
|
+
- uses ordinary ACID transactions
|
|
690
|
+
- expects each live table to have:
|
|
691
|
+
- an id column named by `id_field`
|
|
692
|
+
- optional scalar columns used by `live_when`
|
|
693
|
+
- a `doc jsonb` column containing the full record
|
|
694
|
+
|
|
695
|
+
See [docs/ADAPTERS.md](docs/ADAPTERS.md).
|
|
696
|
+
|
|
697
|
+
## Safety model
|
|
698
|
+
|
|
699
|
+
cfgit is non-custodial. It does not stop other writers from changing the
|
|
700
|
+
database. It detects and reconciles those changes.
|
|
701
|
+
|
|
702
|
+
Important safety properties:
|
|
703
|
+
|
|
704
|
+
- mutating operations use adapter-level compare-and-swap checks
|
|
705
|
+
- commits refuse to clobber live drift
|
|
706
|
+
- system restore supports dry runs
|
|
707
|
+
- per-environment identity can stay open or require verified token/DB-principal identity
|
|
708
|
+
- local permissions can restrict high-blast-radius actions
|
|
709
|
+
- secret fields can be stripped from stored history
|
|
710
|
+
- core imports no DB drivers and no LLM providers
|
|
711
|
+
|
|
712
|
+
Start on a local or staging database. Do not point a new config at production
|
|
713
|
+
until you have run `cfg status`, `cfg import`, `cfg diff`, and restore dry runs
|
|
714
|
+
against a safe environment.
|
|
715
|
+
|
|
716
|
+
## Documentation
|
|
717
|
+
|
|
718
|
+
- [docs/USAGE.md](docs/USAGE.md): command flows and examples
|
|
719
|
+
- [docs/CONFIGURATION.md](docs/CONFIGURATION.md): `.cfg.toml` reference
|
|
720
|
+
- [docs/IDENTITY_AND_ATTRIBUTION.md](docs/IDENTITY_AND_ATTRIBUTION.md): identity modes and attribution limits
|
|
721
|
+
- [docs/ADAPTERS.md](docs/ADAPTERS.md): Mongo and Postgres adapter details
|
|
722
|
+
- [docs/AGENTS.md](docs/AGENTS.md): MCP, skill, and impact plugin usage
|
|
723
|
+
- [docs/SPEC_CORE.md](docs/SPEC_CORE.md): project framing and v1 scope
|
|
724
|
+
- [docs/SPEC.md](docs/SPEC.md): deeper engine reference
|
|
725
|
+
- [docs/README.md](docs/README.md): full documentation index, including archived project notes and historical specs
|
|
726
|
+
- [docs/project-notes/findings.md](docs/project-notes/findings.md): implementation findings from the origin build
|
|
727
|
+
- [docs/project-notes/handoff.md](docs/project-notes/handoff.md): archived handoff notes
|
|
728
|
+
- [docs/archive/spec-v0.1.md](docs/archive/spec-v0.1.md): original historical spec
|
|
729
|
+
|
|
730
|
+
## Development
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
python -m venv .venv
|
|
734
|
+
. .venv/bin/activate
|
|
735
|
+
pip install -e '.[mongo,postgres,mcp,dev]'
|
|
736
|
+
pip install -e plugins/cfg_impact
|
|
737
|
+
ruff check src tests plugins/cfg_impact/cfg_impact
|
|
738
|
+
pytest tests/ -q
|
|
739
|
+
git diff --check
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
## License
|
|
743
|
+
|
|
744
|
+
Apache-2.0. See [LICENSE](LICENSE), [NOTICE](NOTICE), and [CREDITS.md](CREDITS.md).
|