evlet 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.
- evlet-0.1.0/AGENTS.md +38 -0
- evlet-0.1.0/LICENSE +21 -0
- evlet-0.1.0/MANIFEST.in +8 -0
- evlet-0.1.0/PKG-INFO +290 -0
- evlet-0.1.0/README.md +270 -0
- evlet-0.1.0/docs/application-generation.md +141 -0
- evlet-0.1.0/docs/architecture.md +247 -0
- evlet-0.1.0/docs/benchmarks/2026-10-29-postgresql.json +72 -0
- evlet-0.1.0/docs/benchmarks/2026-10-29-sqlite.json +70 -0
- evlet-0.1.0/docs/business-capabilities.md +231 -0
- evlet-0.1.0/docs/connector.md +198 -0
- evlet-0.1.0/docs/handoff.md +83 -0
- evlet-0.1.0/docs/jobs-realtime.md +279 -0
- evlet-0.1.0/docs/model-engine.md +193 -0
- evlet-0.1.0/docs/performance.md +144 -0
- evlet-0.1.0/docs/postgresql.md +142 -0
- evlet-0.1.0/docs/production-operations.md +102 -0
- evlet-0.1.0/docs/specification.md +1121 -0
- evlet-0.1.0/docs/tenancy-deployment.md +254 -0
- evlet-0.1.0/evlet/__init__.py +48 -0
- evlet-0.1.0/evlet/__main__.py +4 -0
- evlet-0.1.0/evlet/app.py +221 -0
- evlet-0.1.0/evlet/auth.py +224 -0
- evlet-0.1.0/evlet/business.py +402 -0
- evlet-0.1.0/evlet/cli.py +147 -0
- evlet-0.1.0/evlet/connector.py +161 -0
- evlet-0.1.0/evlet/database.py +199 -0
- evlet-0.1.0/evlet/deployment.py +63 -0
- evlet-0.1.0/evlet/discovery.py +125 -0
- evlet-0.1.0/evlet/events.py +65 -0
- evlet-0.1.0/evlet/fields.py +331 -0
- evlet-0.1.0/evlet/jobs.py +245 -0
- evlet-0.1.0/evlet/mcp.py +198 -0
- evlet-0.1.0/evlet/metadata.py +64 -0
- evlet-0.1.0/evlet/migrations.py +326 -0
- evlet-0.1.0/evlet/models.py +121 -0
- evlet-0.1.0/evlet/object_storage.py +111 -0
- evlet-0.1.0/evlet/permissions.py +108 -0
- evlet-0.1.0/evlet/postgres.py +212 -0
- evlet-0.1.0/evlet/postgres_schema.py +202 -0
- evlet-0.1.0/evlet/query.py +132 -0
- evlet-0.1.0/evlet/serverless.py +141 -0
- evlet-0.1.0/evlet/storage.py +134 -0
- evlet-0.1.0/evlet/tenancy.py +284 -0
- evlet-0.1.0/evlet.egg-info/PKG-INFO +290 -0
- evlet-0.1.0/evlet.egg-info/SOURCES.txt +120 -0
- evlet-0.1.0/evlet.egg-info/dependency_links.txt +1 -0
- evlet-0.1.0/evlet.egg-info/entry_points.txt +2 -0
- evlet-0.1.0/evlet.egg-info/requires.txt +13 -0
- evlet-0.1.0/evlet.egg-info/top_level.txt +1 -0
- evlet-0.1.0/examples/playground/app.py +48 -0
- evlet-0.1.0/examples/playground/manage.py +131 -0
- evlet-0.1.0/examples/playground/migrate.py +97 -0
- evlet-0.1.0/examples/playground/models.py +67 -0
- evlet-0.1.0/examples/playground/tenants.py +36 -0
- evlet-0.1.0/examples/playground/worker.py +58 -0
- evlet-0.1.0/frontend/.node-version +1 -0
- evlet-0.1.0/frontend/.npmrc +1 -0
- evlet-0.1.0/frontend/index.html +13 -0
- evlet-0.1.0/frontend/package-lock.json +2136 -0
- evlet-0.1.0/frontend/package.json +25 -0
- evlet-0.1.0/frontend/src/Accounts.vue +39 -0
- evlet-0.1.0/frontend/src/App.test.js +514 -0
- evlet-0.1.0/frontend/src/App.vue +216 -0
- evlet-0.1.0/frontend/src/FieldInput.vue +23 -0
- evlet-0.1.0/frontend/src/LinkInput.vue +51 -0
- evlet-0.1.0/frontend/src/QueryControls.vue +51 -0
- evlet-0.1.0/frontend/src/RecordDetail.vue +13 -0
- evlet-0.1.0/frontend/src/RecordExtras.vue +94 -0
- evlet-0.1.0/frontend/src/RecordForm.vue +88 -0
- evlet-0.1.0/frontend/src/SessionApp.vue +59 -0
- evlet-0.1.0/frontend/src/api.js +64 -0
- evlet-0.1.0/frontend/src/api.test.js +30 -0
- evlet-0.1.0/frontend/src/business.test.js +181 -0
- evlet-0.1.0/frontend/src/child-permissions.test.js +103 -0
- evlet-0.1.0/frontend/src/events.js +43 -0
- evlet-0.1.0/frontend/src/events.test.js +157 -0
- evlet-0.1.0/frontend/src/main.js +21 -0
- evlet-0.1.0/frontend/src/recovery.test.js +194 -0
- evlet-0.1.0/frontend/src/schema.js +44 -0
- evlet-0.1.0/frontend/src/style.css +93 -0
- evlet-0.1.0/frontend/src/test-business-schema.json +604 -0
- evlet-0.1.0/frontend/src/test-schema.json +535 -0
- evlet-0.1.0/frontend/src/test-setup.js +15 -0
- evlet-0.1.0/frontend/vite.config.js +21 -0
- evlet-0.1.0/pyproject.toml +28 -0
- evlet-0.1.0/requirements-dev.lock +54 -0
- evlet-0.1.0/requirements-mcp.in +2 -0
- evlet-0.1.0/requirements-mcp.lock +593 -0
- evlet-0.1.0/requirements-postgres.lock +77 -0
- evlet-0.1.0/requirements-s3.lock +16 -0
- evlet-0.1.0/requirements-server.lock +13 -0
- evlet-0.1.0/scripts/benchmark.py +180 -0
- evlet-0.1.0/scripts/check_package.py +96 -0
- evlet-0.1.0/scripts/playground.sh +156 -0
- evlet-0.1.0/scripts/smoke.py +106 -0
- evlet-0.1.0/scripts/smoke_tenants.py +210 -0
- evlet-0.1.0/setup.cfg +4 -0
- evlet-0.1.0/tests/conftest.py +16 -0
- evlet-0.1.0/tests/test_api.py +121 -0
- evlet-0.1.0/tests/test_async_tenants.py +45 -0
- evlet-0.1.0/tests/test_business.py +329 -0
- evlet-0.1.0/tests/test_business_regressions.py +94 -0
- evlet-0.1.0/tests/test_cli.py +103 -0
- evlet-0.1.0/tests/test_connector.py +253 -0
- evlet-0.1.0/tests/test_database.py +88 -0
- evlet-0.1.0/tests/test_deployment.py +55 -0
- evlet-0.1.0/tests/test_discovery.py +86 -0
- evlet-0.1.0/tests/test_events.py +89 -0
- evlet-0.1.0/tests/test_generation.py +149 -0
- evlet-0.1.0/tests/test_hardening.py +71 -0
- evlet-0.1.0/tests/test_input_regressions.py +68 -0
- evlet-0.1.0/tests/test_jobs.py +152 -0
- evlet-0.1.0/tests/test_mcp_sdk.py +59 -0
- evlet-0.1.0/tests/test_migrations.py +264 -0
- evlet-0.1.0/tests/test_model_engine.py +187 -0
- evlet-0.1.0/tests/test_models.py +93 -0
- evlet-0.1.0/tests/test_object_storage.py +117 -0
- evlet-0.1.0/tests/test_postgres.py +365 -0
- evlet-0.1.0/tests/test_serverless.py +82 -0
- evlet-0.1.0/tests/test_storage.py +136 -0
- evlet-0.1.0/tests/test_tenancy.py +210 -0
evlet-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Working on Evlet
|
|
2
|
+
|
|
3
|
+
Read `docs/specification.md` and `docs/architecture.md` before changing the public API.
|
|
4
|
+
The current scope is Milestone 8: optional PostgreSQL persistence, performance
|
|
5
|
+
measurement, concurrency and tenant security tests, and deployment operations.
|
|
6
|
+
Milestones 1 through 7 are merged. Keep changes within this milestone. Read
|
|
7
|
+
`docs/business-capabilities.md`, `docs/tenancy-deployment.md`,
|
|
8
|
+
`docs/jobs-realtime.md`, and `docs/connector.md` for public APIs and permission
|
|
9
|
+
boundaries. Read `docs/postgresql.md`, `docs/performance.md`, and
|
|
10
|
+
`docs/production-operations.md` for adapter limits and validation. Connector writes must use the existing
|
|
11
|
+
authenticated HTTP routes;
|
|
12
|
+
never bypass account, tenant, row, field, or action checks.
|
|
13
|
+
|
|
14
|
+
- Use Python 3.12+ and Node 24. Python definitions are authoritative; frontend
|
|
15
|
+
components consume a neutral schema and never appear in Python model definitions.
|
|
16
|
+
- Prefer the standard library. Justify every dependency. No mandatory Redis,
|
|
17
|
+
Docker, external database, ORM, or background worker.
|
|
18
|
+
- Keep public APIs explicit, small, and readable. Use snake_case in Python,
|
|
19
|
+
PascalCase for model and Vue component names. Do not create unused modules.
|
|
20
|
+
- `evlet/` contains the framework; `examples/playground/` is a consumer using only
|
|
21
|
+
public exports; `frontend/` is the Vue/PrimeVue Playground; `tests/` covers the
|
|
22
|
+
core, SQLite integration, and ASGI interface.
|
|
23
|
+
- Parameterize SQL values and validate SQL identifiers. Validate on the backend.
|
|
24
|
+
Preserve transaction boundaries, exact currency values, and foreign keys.
|
|
25
|
+
- Keep the Playground authenticated and development servers on loopback. Backend
|
|
26
|
+
permission checks cover all HTTP surfaces. Python sessions and action handlers
|
|
27
|
+
are trusted code. Do not describe the application as production ready.
|
|
28
|
+
- Add meaningful tests for features and regressions. Run `.venv/bin/python -m
|
|
29
|
+
pytest`, then `npm test` and `npm run build` in `frontend/`. Verify package build
|
|
30
|
+
and installation when changing packaging. Never substitute mocks for SQLite.
|
|
31
|
+
- Update documentation and demonstrate applicable public APIs in the Playground.
|
|
32
|
+
A feature is done only when its tests and all existing tests pass.
|
|
33
|
+
- Work on feature branches and prepare PRs; never merge automatically or push
|
|
34
|
+
experimental implementation into `main`.
|
|
35
|
+
- Cloud tasks are already isolated. Use the existing checkout; create no Git
|
|
36
|
+
worktree unless explicitly requested by the user.
|
|
37
|
+
|
|
38
|
+
See the README for complete installation, startup, and smoke-check commands.
|
evlet-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aslam
|
|
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.
|
evlet-0.1.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
include README.md AGENTS.md requirements-dev.lock requirements-server.lock requirements-s3.lock requirements-mcp.in requirements-mcp.lock requirements-postgres.lock
|
|
2
|
+
recursive-include docs *.md *.json
|
|
3
|
+
recursive-include examples *.py
|
|
4
|
+
recursive-include tests *.py
|
|
5
|
+
recursive-include scripts *.py *.sh
|
|
6
|
+
include frontend/package.json frontend/package-lock.json frontend/index.html frontend/vite.config.js
|
|
7
|
+
include frontend/.node-version frontend/.npmrc
|
|
8
|
+
recursive-include frontend/src *.js *.vue *.css *.json
|
evlet-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evlet
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A small Python application framework with SQLite and a neutral UI schema
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Provides-Extra: postgres
|
|
10
|
+
Requires-Dist: psycopg[binary]<4,>=3.3.6; extra == "postgres"
|
|
11
|
+
Requires-Dist: psycopg-pool<4,>=3.3.3; extra == "postgres"
|
|
12
|
+
Provides-Extra: s3
|
|
13
|
+
Requires-Dist: boto3<2,>=1.43.119; extra == "s3"
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest<10,>=8; extra == "dev"
|
|
16
|
+
Requires-Dist: uvicorn<1,>=0.34; extra == "dev"
|
|
17
|
+
Requires-Dist: build<2,>=1; extra == "dev"
|
|
18
|
+
Requires-Dist: setuptools>=75; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Evlet
|
|
22
|
+
|
|
23
|
+
A small Python application framework for business applications. This repository
|
|
24
|
+
implements **Milestone 8**: optional PostgreSQL persistence, large-data and
|
|
25
|
+
concurrency measurements, tenant security regression tests, and deployment
|
|
26
|
+
operations documentation. SQLite remains the default; PostgreSQL is optional.
|
|
27
|
+
|
|
28
|
+
Python model definitions remain authoritative. The core has **zero third-party
|
|
29
|
+
runtime dependencies**. No database server, Redis, Docker, or worker is required.
|
|
30
|
+
|
|
31
|
+
## Install the Python package
|
|
32
|
+
|
|
33
|
+
Requires Python 3.12 or newer:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install evlet
|
|
37
|
+
evlet --help
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Optional adapters are available with `python -m pip install 'evlet[postgres]'`
|
|
41
|
+
or `python -m pip install 'evlet[s3]'`. To serve an application, install an ASGI
|
|
42
|
+
server such as Uvicorn separately. The installed Python package contains the
|
|
43
|
+
framework and command-line tools. The Vue Playground requires the source
|
|
44
|
+
distribution or a repository checkout and the setup below.
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
Evlet is available under the [MIT License](https://github.com/dxb-aslam/evlet/blob/main/LICENSE).
|
|
49
|
+
|
|
50
|
+
## Open the Playground on macOS
|
|
51
|
+
|
|
52
|
+
Install Git, Python 3.12+, and Node 24.15+ (24.x). If you use Homebrew:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
brew install git python@3.12 node@24
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then, in Terminal:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/dxb-aslam/evlet.git
|
|
62
|
+
cd evlet
|
|
63
|
+
bash scripts/playground.sh --setup
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This private repository requires your GitHub account to have access. You can also
|
|
67
|
+
download the feature branch as a ZIP while signed in to GitHub, extract it, open
|
|
68
|
+
Terminal in the extracted folder, and run the same launcher command.
|
|
69
|
+
|
|
70
|
+
The launcher installs the locked dependencies, starts both servers and an optional
|
|
71
|
+
job worker, checks the API, and opens the Playground in your default browser. Keep Terminal open while
|
|
72
|
+
using the app. Press **Control-C** to stop its processes; your SQLite data remains.
|
|
73
|
+
On later runs, use `bash scripts/playground.sh` from the same folder. The launcher
|
|
74
|
+
activates Homebrew's versioned Node and Python paths for its own process only.
|
|
75
|
+
|
|
76
|
+
Logs are in `examples/playground/.data/logs/5173/` by default. If ports 8000 or 5173 are already
|
|
77
|
+
occupied, the launcher exits without stopping any existing service. Linux can
|
|
78
|
+
use the same launcher with Python and Node already installed. Set
|
|
79
|
+
`EVLET_OPEN_BROWSER=0` for noninteractive validation.
|
|
80
|
+
|
|
81
|
+
For an existing Milestone 1, 2, or 3 database, stop the launcher and run
|
|
82
|
+
`.venv/bin/python -m examples.playground.migrate` before restarting it. SQLite upgrades create
|
|
83
|
+
a backup and preserve existing records. When `EVLET_POSTGRES_DSN` is set, the
|
|
84
|
+
command upgrades PostgreSQL instead; take an operator-managed PostgreSQL backup
|
|
85
|
+
first, as described in the [PostgreSQL guide](docs/postgresql.md). See the [model engine guide](docs/model-engine.md)
|
|
86
|
+
for the API, migration operations, and current limits.
|
|
87
|
+
|
|
88
|
+
## Install for development
|
|
89
|
+
|
|
90
|
+
Requires Python 3.12+ with `venv`, Node **24.15+ in the 24.x line**, and npm.
|
|
91
|
+
The cloud environment was verified with Python 3.12.14 and Node 24.19.0.
|
|
92
|
+
Run from the repository root:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python3 -m venv .venv
|
|
96
|
+
.venv/bin/python -m pip install --require-hashes -r requirements-dev.lock
|
|
97
|
+
.venv/bin/python -m pip install --no-build-isolation --no-deps -e .
|
|
98
|
+
npm --prefix frontend ci
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
In a managed cloud checkout, use the existing checkout at `/workspace/evlet` and set writable
|
|
102
|
+
package caches if the default home directory is read-only:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
export PIP_CACHE_DIR=/workspace/.cache/pip
|
|
106
|
+
export npm_config_cache=/workspace/.cache/npm
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The lockfiles pin development dependencies and preserve package-integrity checks.
|
|
110
|
+
Do not regenerate them during routine setup. To intentionally update the Python
|
|
111
|
+
lock with `uv` installed, run `uv pip compile pyproject.toml --extra dev --universal
|
|
112
|
+
--generate-hashes --output-file requirements-dev.lock`, then rerun validation.
|
|
113
|
+
`uv` is only a lock-maintenance tool; ordinary setup uses pip.
|
|
114
|
+
|
|
115
|
+
## Run the Playground
|
|
116
|
+
|
|
117
|
+
From the root, start the backend in one terminal:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
.venv/bin/python -m uvicorn examples.playground.app:create_app --factory --host 127.0.0.1 --port 8000
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
In another terminal, start Vite:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm --prefix frontend run dev
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The backend listens on port 8000; Vite listens on port 5173 and proxies `/api` and
|
|
130
|
+
`/health` to it. Both bind to loopback. Use your development environment's normal
|
|
131
|
+
browser access where supported. The onboarding UI itself does not provide a web
|
|
132
|
+
preview; local HTTP requests still verify the running application.
|
|
133
|
+
|
|
134
|
+
Create the first administrator account when prompted. Use Accounts to add Editor
|
|
135
|
+
or Viewer accounts. Then create a customer, an invoice referencing it, and a payment
|
|
136
|
+
referencing that invoice. Refresh or restart the backend to verify persistence.
|
|
137
|
+
The default database is `examples/playground/.data/playground.sqlite3`, ignored
|
|
138
|
+
by Git. `EVLET_DATABASE` can select another SQLite file. Each database has its own accounts.
|
|
139
|
+
Projects demonstrate child tasks, Start/Complete actions, private attachments, and
|
|
140
|
+
a queued **Recalculate progress** action. The launcher starts its worker; with
|
|
141
|
+
manual startup, run `.venv/bin/python -m examples.playground.worker` separately.
|
|
142
|
+
|
|
143
|
+
Check the live workflow from the root:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
curl --fail http://127.0.0.1:8000/health
|
|
147
|
+
.venv/bin/python scripts/smoke.py --base-url http://127.0.0.1:5173
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The smoke check prompts for an existing Admin or Editor account. It creates a
|
|
151
|
+
uniquely named customer, invoice, payment, and Project, checks
|
|
152
|
+
updates, queries, validation, protected deletes, then removes its own records. Use a disposable database via `EVLET_DATABASE` for validation runs when you
|
|
153
|
+
do not want sample records in your working database. It also checks validation,
|
|
154
|
+
foreign-key errors, and exact decimal serialization through real HTTP requests.
|
|
155
|
+
|
|
156
|
+
## Use the Python API
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from evlet import Application, Currency, Database, Link, Model, Registry, Text
|
|
160
|
+
|
|
161
|
+
class Customer(Model):
|
|
162
|
+
name = Text(required=True)
|
|
163
|
+
|
|
164
|
+
class Invoice(Model):
|
|
165
|
+
customer = Link(Customer, required=True)
|
|
166
|
+
reference = Text(required=True, unique=True)
|
|
167
|
+
total = Currency(required=True, minimum="0.01")
|
|
168
|
+
|
|
169
|
+
database = Database(".data/example.sqlite3", Registry(Customer, Invoice))
|
|
170
|
+
database.initialize()
|
|
171
|
+
|
|
172
|
+
with database.transaction() as transaction:
|
|
173
|
+
customer = transaction.create(Customer, name="Acme")
|
|
174
|
+
invoice = transaction.create(
|
|
175
|
+
Invoice, customer=customer.id, reference="INV-001", total="100.10"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
assert database.get(Invoice, invoice.id).to_dict()["total"] == "100.10"
|
|
179
|
+
# Configure LocalAuth before serving protected records over HTTP.
|
|
180
|
+
app = Application(database)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Transactions commit on success and roll back together on failure. Use decimal
|
|
184
|
+
strings for currency. `Integer` is also available; `Text` supports length bounds,
|
|
185
|
+
required values, defaults, and uniqueness. `Link` uses actual SQLite foreign keys.
|
|
186
|
+
An application registers models explicitly, so unrelated classes are not exposed.
|
|
187
|
+
When shipping migrations, declare the complete history with
|
|
188
|
+
`Database(path, registry, migrations=migrations)` so fresh initialization records
|
|
189
|
+
its baseline. Apply later schema changes explicitly with `database.migrate(migrations)`.
|
|
190
|
+
See the [migration guide](docs/model-engine.md#explicit-migrations).
|
|
191
|
+
|
|
192
|
+
## Test and build
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
.venv/bin/python -m pytest
|
|
196
|
+
npm --prefix frontend test
|
|
197
|
+
npm --prefix frontend run build
|
|
198
|
+
.venv/bin/python -m build --no-isolation
|
|
199
|
+
python3 -m venv /tmp/evlet-wheel-check
|
|
200
|
+
/tmp/evlet-wheel-check/bin/python -m pip install --no-deps dist/evlet-0.1.0-py3-none-any.whl
|
|
201
|
+
/tmp/evlet-wheel-check/bin/python -I scripts/check_package.py
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The Python suite exercises real SQLite files, validation, rollback, constraints,
|
|
205
|
+
concurrent short writes, and full ASGI requests. The frontend suite uses real
|
|
206
|
+
PrimeVue components and substitutes only HTTP responses and missing DOM layout
|
|
207
|
+
APIs. The live smoke check covers the actual server and Vite proxy. Package checks
|
|
208
|
+
verify the installed wheel outside the source tree. GitHub Actions runs Python
|
|
209
|
+
tests on 3.12 and 3.13, distribution checks, frontend tests/build, and a live API
|
|
210
|
+
smoke check. Cloud validation does not establish that remote CI has run.
|
|
211
|
+
|
|
212
|
+
Build output is in `dist/` and `frontend/dist/`. `npm --prefix frontend run preview`
|
|
213
|
+
serves the production frontend on port 4173 with the same local backend proxy.
|
|
214
|
+
This is a development preview, not a deployment recipe.
|
|
215
|
+
|
|
216
|
+
## Current boundaries
|
|
217
|
+
|
|
218
|
+
- Create, read, update, delete, typed queries, and explicit additive/rename/index
|
|
219
|
+
migrations are supported. Destructive migrations and arbitrary field-type
|
|
220
|
+
conversions are not supported. Startup rejects schema drift.
|
|
221
|
+
- Local accounts and backend model/record/field permissions are implemented.
|
|
222
|
+
The Playground uses them; a bare `Application(database)` stays unauthenticated
|
|
223
|
+
for compatibility. Keep development servers on loopback. SSO and email recovery
|
|
224
|
+
remain outside the implemented scope.
|
|
225
|
+
- Currency has two decimal places and no currency-code conversion. The Playground
|
|
226
|
+
is a persistence example, not a ledger or payment processor.
|
|
227
|
+
- Email is a bounded text field; browser email validation is convenience only.
|
|
228
|
+
- Lists, counts, search, filters, and relationship choices use bounded server queries.
|
|
229
|
+
Text search uses SQLite ASCII case folding. Browser integer actions require safe integers.
|
|
230
|
+
- Forms, lists, and details are generated from renderer-neutral Python metadata.
|
|
231
|
+
Full PrimeVue controls and the theme currently produce a JavaScript bundle above
|
|
232
|
+
Vite's 500 kB advisory threshold. The build succeeds; bundle optimization is
|
|
233
|
+
follow-up work. No performance or scalability guarantees are claimed.
|
|
234
|
+
|
|
235
|
+
Read [architecture decisions](docs/architecture.md), the [full specification](docs/specification.md),
|
|
236
|
+
and [agent instructions](AGENTS.md). The [model engine guide](docs/model-engine.md)
|
|
237
|
+
covers the model engine. See [application generation](docs/application-generation.md)
|
|
238
|
+
for Milestone 3 metadata, defaults, generated screens, and upgrade instructions.
|
|
239
|
+
See [business capabilities](docs/business-capabilities.md) for Milestone 4 APIs,
|
|
240
|
+
account setup, permissions, actions, children, attachments, and limits.
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
## Milestone 5: tenants and deployment
|
|
244
|
+
|
|
245
|
+
The [tenant and deployment guide](docs/tenancy-deployment.md) covers separate tenant
|
|
246
|
+
SQLite files, hostname routing, DNS ownership verification, private file storage,
|
|
247
|
+
and the Caddy/systemd reference deployment. The ordinary Playground retains its
|
|
248
|
+
existing database and startup command. Tenant setup uses a separate catalog and
|
|
249
|
+
prompts for its first administrator through the local management command.
|
|
250
|
+
|
|
251
|
+
Run `python scripts/smoke_tenants.py` with the virtual environment active to check
|
|
252
|
+
two disposable tenants over HTTP. Add `--frontend` to test Vite or `--caddy` to test
|
|
253
|
+
local HTTPS after building the frontend. No deployment runs automatically.
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
## Milestone 6: jobs, live updates, and adapters
|
|
257
|
+
|
|
258
|
+
The [jobs and live-update guide](docs/jobs-realtime.md) covers transactional jobs,
|
|
259
|
+
retries, schedules, permission-filtered SSE and polling, and the optional S3/R2
|
|
260
|
+
storage and Lambda HTTP adapters. No Redis or distributed queue is required.
|
|
261
|
+
Use `EVLET_RUN_JOBS=0` to run the Playground without a worker.
|
|
262
|
+
|
|
263
|
+
The Lambda adapter supplies HTTP transport only. It does not make the SQLite
|
|
264
|
+
Playground deployable on an ephemeral filesystem. No cloud resources are created,
|
|
265
|
+
and live cloud operation has not been verified. WebSockets and broader production
|
|
266
|
+
hardening remain deferred.
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
## Milestone 7: discovery and connector interfaces
|
|
270
|
+
|
|
271
|
+
The [connector guide](docs/connector.md) covers compact discovery, model input
|
|
272
|
+
schemas, a local MCP stdio connector, private session files, and starter generation.
|
|
273
|
+
Run `python -m evlet new ./my-app` to create a small authenticated application.
|
|
274
|
+
The connector starts with read tools; `--allow-writes` explicitly enables permitted
|
|
275
|
+
writes through the existing HTTP API. Every request retains backend permission
|
|
276
|
+
checks. No external provider or additional runtime dependency is required.
|
|
277
|
+
|
|
278
|
+
Install `requirements-mcp.lock` with `--require-hashes` to include the optional
|
|
279
|
+
official-client interoperability test. CI includes it; ordinary Playground setup
|
|
280
|
+
does not need it. Remote MCP hosting and production hardening remain separate work.
|
|
281
|
+
|
|
282
|
+
## Milestone 8: performance and hardening
|
|
283
|
+
|
|
284
|
+
- [PostgreSQL setup, transaction behavior, and migration limits](docs/postgresql.md)
|
|
285
|
+
- [Reproducible workload and measured adapter comparison](docs/performance.md)
|
|
286
|
+
- [Deployment, backup, recovery, and monitoring](docs/production-operations.md)
|
|
287
|
+
|
|
288
|
+
The existing tenant catalog remains SQLite-only. PostgreSQL supports authenticated
|
|
289
|
+
single-application deployments and conservatively serializes writes per database.
|
|
290
|
+
No production scale or parallel-write capacity is implied by this release.
|
evlet-0.1.0/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Evlet
|
|
2
|
+
|
|
3
|
+
A small Python application framework for business applications. This repository
|
|
4
|
+
implements **Milestone 8**: optional PostgreSQL persistence, large-data and
|
|
5
|
+
concurrency measurements, tenant security regression tests, and deployment
|
|
6
|
+
operations documentation. SQLite remains the default; PostgreSQL is optional.
|
|
7
|
+
|
|
8
|
+
Python model definitions remain authoritative. The core has **zero third-party
|
|
9
|
+
runtime dependencies**. No database server, Redis, Docker, or worker is required.
|
|
10
|
+
|
|
11
|
+
## Install the Python package
|
|
12
|
+
|
|
13
|
+
Requires Python 3.12 or newer:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python -m pip install evlet
|
|
17
|
+
evlet --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Optional adapters are available with `python -m pip install 'evlet[postgres]'`
|
|
21
|
+
or `python -m pip install 'evlet[s3]'`. To serve an application, install an ASGI
|
|
22
|
+
server such as Uvicorn separately. The installed Python package contains the
|
|
23
|
+
framework and command-line tools. The Vue Playground requires the source
|
|
24
|
+
distribution or a repository checkout and the setup below.
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
Evlet is available under the [MIT License](https://github.com/dxb-aslam/evlet/blob/main/LICENSE).
|
|
29
|
+
|
|
30
|
+
## Open the Playground on macOS
|
|
31
|
+
|
|
32
|
+
Install Git, Python 3.12+, and Node 24.15+ (24.x). If you use Homebrew:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
brew install git python@3.12 node@24
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then, in Terminal:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/dxb-aslam/evlet.git
|
|
42
|
+
cd evlet
|
|
43
|
+
bash scripts/playground.sh --setup
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This private repository requires your GitHub account to have access. You can also
|
|
47
|
+
download the feature branch as a ZIP while signed in to GitHub, extract it, open
|
|
48
|
+
Terminal in the extracted folder, and run the same launcher command.
|
|
49
|
+
|
|
50
|
+
The launcher installs the locked dependencies, starts both servers and an optional
|
|
51
|
+
job worker, checks the API, and opens the Playground in your default browser. Keep Terminal open while
|
|
52
|
+
using the app. Press **Control-C** to stop its processes; your SQLite data remains.
|
|
53
|
+
On later runs, use `bash scripts/playground.sh` from the same folder. The launcher
|
|
54
|
+
activates Homebrew's versioned Node and Python paths for its own process only.
|
|
55
|
+
|
|
56
|
+
Logs are in `examples/playground/.data/logs/5173/` by default. If ports 8000 or 5173 are already
|
|
57
|
+
occupied, the launcher exits without stopping any existing service. Linux can
|
|
58
|
+
use the same launcher with Python and Node already installed. Set
|
|
59
|
+
`EVLET_OPEN_BROWSER=0` for noninteractive validation.
|
|
60
|
+
|
|
61
|
+
For an existing Milestone 1, 2, or 3 database, stop the launcher and run
|
|
62
|
+
`.venv/bin/python -m examples.playground.migrate` before restarting it. SQLite upgrades create
|
|
63
|
+
a backup and preserve existing records. When `EVLET_POSTGRES_DSN` is set, the
|
|
64
|
+
command upgrades PostgreSQL instead; take an operator-managed PostgreSQL backup
|
|
65
|
+
first, as described in the [PostgreSQL guide](docs/postgresql.md). See the [model engine guide](docs/model-engine.md)
|
|
66
|
+
for the API, migration operations, and current limits.
|
|
67
|
+
|
|
68
|
+
## Install for development
|
|
69
|
+
|
|
70
|
+
Requires Python 3.12+ with `venv`, Node **24.15+ in the 24.x line**, and npm.
|
|
71
|
+
The cloud environment was verified with Python 3.12.14 and Node 24.19.0.
|
|
72
|
+
Run from the repository root:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python3 -m venv .venv
|
|
76
|
+
.venv/bin/python -m pip install --require-hashes -r requirements-dev.lock
|
|
77
|
+
.venv/bin/python -m pip install --no-build-isolation --no-deps -e .
|
|
78
|
+
npm --prefix frontend ci
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
In a managed cloud checkout, use the existing checkout at `/workspace/evlet` and set writable
|
|
82
|
+
package caches if the default home directory is read-only:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
export PIP_CACHE_DIR=/workspace/.cache/pip
|
|
86
|
+
export npm_config_cache=/workspace/.cache/npm
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The lockfiles pin development dependencies and preserve package-integrity checks.
|
|
90
|
+
Do not regenerate them during routine setup. To intentionally update the Python
|
|
91
|
+
lock with `uv` installed, run `uv pip compile pyproject.toml --extra dev --universal
|
|
92
|
+
--generate-hashes --output-file requirements-dev.lock`, then rerun validation.
|
|
93
|
+
`uv` is only a lock-maintenance tool; ordinary setup uses pip.
|
|
94
|
+
|
|
95
|
+
## Run the Playground
|
|
96
|
+
|
|
97
|
+
From the root, start the backend in one terminal:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
.venv/bin/python -m uvicorn examples.playground.app:create_app --factory --host 127.0.0.1 --port 8000
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
In another terminal, start Vite:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm --prefix frontend run dev
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The backend listens on port 8000; Vite listens on port 5173 and proxies `/api` and
|
|
110
|
+
`/health` to it. Both bind to loopback. Use your development environment's normal
|
|
111
|
+
browser access where supported. The onboarding UI itself does not provide a web
|
|
112
|
+
preview; local HTTP requests still verify the running application.
|
|
113
|
+
|
|
114
|
+
Create the first administrator account when prompted. Use Accounts to add Editor
|
|
115
|
+
or Viewer accounts. Then create a customer, an invoice referencing it, and a payment
|
|
116
|
+
referencing that invoice. Refresh or restart the backend to verify persistence.
|
|
117
|
+
The default database is `examples/playground/.data/playground.sqlite3`, ignored
|
|
118
|
+
by Git. `EVLET_DATABASE` can select another SQLite file. Each database has its own accounts.
|
|
119
|
+
Projects demonstrate child tasks, Start/Complete actions, private attachments, and
|
|
120
|
+
a queued **Recalculate progress** action. The launcher starts its worker; with
|
|
121
|
+
manual startup, run `.venv/bin/python -m examples.playground.worker` separately.
|
|
122
|
+
|
|
123
|
+
Check the live workflow from the root:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
curl --fail http://127.0.0.1:8000/health
|
|
127
|
+
.venv/bin/python scripts/smoke.py --base-url http://127.0.0.1:5173
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The smoke check prompts for an existing Admin or Editor account. It creates a
|
|
131
|
+
uniquely named customer, invoice, payment, and Project, checks
|
|
132
|
+
updates, queries, validation, protected deletes, then removes its own records. Use a disposable database via `EVLET_DATABASE` for validation runs when you
|
|
133
|
+
do not want sample records in your working database. It also checks validation,
|
|
134
|
+
foreign-key errors, and exact decimal serialization through real HTTP requests.
|
|
135
|
+
|
|
136
|
+
## Use the Python API
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from evlet import Application, Currency, Database, Link, Model, Registry, Text
|
|
140
|
+
|
|
141
|
+
class Customer(Model):
|
|
142
|
+
name = Text(required=True)
|
|
143
|
+
|
|
144
|
+
class Invoice(Model):
|
|
145
|
+
customer = Link(Customer, required=True)
|
|
146
|
+
reference = Text(required=True, unique=True)
|
|
147
|
+
total = Currency(required=True, minimum="0.01")
|
|
148
|
+
|
|
149
|
+
database = Database(".data/example.sqlite3", Registry(Customer, Invoice))
|
|
150
|
+
database.initialize()
|
|
151
|
+
|
|
152
|
+
with database.transaction() as transaction:
|
|
153
|
+
customer = transaction.create(Customer, name="Acme")
|
|
154
|
+
invoice = transaction.create(
|
|
155
|
+
Invoice, customer=customer.id, reference="INV-001", total="100.10"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
assert database.get(Invoice, invoice.id).to_dict()["total"] == "100.10"
|
|
159
|
+
# Configure LocalAuth before serving protected records over HTTP.
|
|
160
|
+
app = Application(database)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Transactions commit on success and roll back together on failure. Use decimal
|
|
164
|
+
strings for currency. `Integer` is also available; `Text` supports length bounds,
|
|
165
|
+
required values, defaults, and uniqueness. `Link` uses actual SQLite foreign keys.
|
|
166
|
+
An application registers models explicitly, so unrelated classes are not exposed.
|
|
167
|
+
When shipping migrations, declare the complete history with
|
|
168
|
+
`Database(path, registry, migrations=migrations)` so fresh initialization records
|
|
169
|
+
its baseline. Apply later schema changes explicitly with `database.migrate(migrations)`.
|
|
170
|
+
See the [migration guide](docs/model-engine.md#explicit-migrations).
|
|
171
|
+
|
|
172
|
+
## Test and build
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
.venv/bin/python -m pytest
|
|
176
|
+
npm --prefix frontend test
|
|
177
|
+
npm --prefix frontend run build
|
|
178
|
+
.venv/bin/python -m build --no-isolation
|
|
179
|
+
python3 -m venv /tmp/evlet-wheel-check
|
|
180
|
+
/tmp/evlet-wheel-check/bin/python -m pip install --no-deps dist/evlet-0.1.0-py3-none-any.whl
|
|
181
|
+
/tmp/evlet-wheel-check/bin/python -I scripts/check_package.py
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The Python suite exercises real SQLite files, validation, rollback, constraints,
|
|
185
|
+
concurrent short writes, and full ASGI requests. The frontend suite uses real
|
|
186
|
+
PrimeVue components and substitutes only HTTP responses and missing DOM layout
|
|
187
|
+
APIs. The live smoke check covers the actual server and Vite proxy. Package checks
|
|
188
|
+
verify the installed wheel outside the source tree. GitHub Actions runs Python
|
|
189
|
+
tests on 3.12 and 3.13, distribution checks, frontend tests/build, and a live API
|
|
190
|
+
smoke check. Cloud validation does not establish that remote CI has run.
|
|
191
|
+
|
|
192
|
+
Build output is in `dist/` and `frontend/dist/`. `npm --prefix frontend run preview`
|
|
193
|
+
serves the production frontend on port 4173 with the same local backend proxy.
|
|
194
|
+
This is a development preview, not a deployment recipe.
|
|
195
|
+
|
|
196
|
+
## Current boundaries
|
|
197
|
+
|
|
198
|
+
- Create, read, update, delete, typed queries, and explicit additive/rename/index
|
|
199
|
+
migrations are supported. Destructive migrations and arbitrary field-type
|
|
200
|
+
conversions are not supported. Startup rejects schema drift.
|
|
201
|
+
- Local accounts and backend model/record/field permissions are implemented.
|
|
202
|
+
The Playground uses them; a bare `Application(database)` stays unauthenticated
|
|
203
|
+
for compatibility. Keep development servers on loopback. SSO and email recovery
|
|
204
|
+
remain outside the implemented scope.
|
|
205
|
+
- Currency has two decimal places and no currency-code conversion. The Playground
|
|
206
|
+
is a persistence example, not a ledger or payment processor.
|
|
207
|
+
- Email is a bounded text field; browser email validation is convenience only.
|
|
208
|
+
- Lists, counts, search, filters, and relationship choices use bounded server queries.
|
|
209
|
+
Text search uses SQLite ASCII case folding. Browser integer actions require safe integers.
|
|
210
|
+
- Forms, lists, and details are generated from renderer-neutral Python metadata.
|
|
211
|
+
Full PrimeVue controls and the theme currently produce a JavaScript bundle above
|
|
212
|
+
Vite's 500 kB advisory threshold. The build succeeds; bundle optimization is
|
|
213
|
+
follow-up work. No performance or scalability guarantees are claimed.
|
|
214
|
+
|
|
215
|
+
Read [architecture decisions](docs/architecture.md), the [full specification](docs/specification.md),
|
|
216
|
+
and [agent instructions](AGENTS.md). The [model engine guide](docs/model-engine.md)
|
|
217
|
+
covers the model engine. See [application generation](docs/application-generation.md)
|
|
218
|
+
for Milestone 3 metadata, defaults, generated screens, and upgrade instructions.
|
|
219
|
+
See [business capabilities](docs/business-capabilities.md) for Milestone 4 APIs,
|
|
220
|
+
account setup, permissions, actions, children, attachments, and limits.
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
## Milestone 5: tenants and deployment
|
|
224
|
+
|
|
225
|
+
The [tenant and deployment guide](docs/tenancy-deployment.md) covers separate tenant
|
|
226
|
+
SQLite files, hostname routing, DNS ownership verification, private file storage,
|
|
227
|
+
and the Caddy/systemd reference deployment. The ordinary Playground retains its
|
|
228
|
+
existing database and startup command. Tenant setup uses a separate catalog and
|
|
229
|
+
prompts for its first administrator through the local management command.
|
|
230
|
+
|
|
231
|
+
Run `python scripts/smoke_tenants.py` with the virtual environment active to check
|
|
232
|
+
two disposable tenants over HTTP. Add `--frontend` to test Vite or `--caddy` to test
|
|
233
|
+
local HTTPS after building the frontend. No deployment runs automatically.
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
## Milestone 6: jobs, live updates, and adapters
|
|
237
|
+
|
|
238
|
+
The [jobs and live-update guide](docs/jobs-realtime.md) covers transactional jobs,
|
|
239
|
+
retries, schedules, permission-filtered SSE and polling, and the optional S3/R2
|
|
240
|
+
storage and Lambda HTTP adapters. No Redis or distributed queue is required.
|
|
241
|
+
Use `EVLET_RUN_JOBS=0` to run the Playground without a worker.
|
|
242
|
+
|
|
243
|
+
The Lambda adapter supplies HTTP transport only. It does not make the SQLite
|
|
244
|
+
Playground deployable on an ephemeral filesystem. No cloud resources are created,
|
|
245
|
+
and live cloud operation has not been verified. WebSockets and broader production
|
|
246
|
+
hardening remain deferred.
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
## Milestone 7: discovery and connector interfaces
|
|
250
|
+
|
|
251
|
+
The [connector guide](docs/connector.md) covers compact discovery, model input
|
|
252
|
+
schemas, a local MCP stdio connector, private session files, and starter generation.
|
|
253
|
+
Run `python -m evlet new ./my-app` to create a small authenticated application.
|
|
254
|
+
The connector starts with read tools; `--allow-writes` explicitly enables permitted
|
|
255
|
+
writes through the existing HTTP API. Every request retains backend permission
|
|
256
|
+
checks. No external provider or additional runtime dependency is required.
|
|
257
|
+
|
|
258
|
+
Install `requirements-mcp.lock` with `--require-hashes` to include the optional
|
|
259
|
+
official-client interoperability test. CI includes it; ordinary Playground setup
|
|
260
|
+
does not need it. Remote MCP hosting and production hardening remain separate work.
|
|
261
|
+
|
|
262
|
+
## Milestone 8: performance and hardening
|
|
263
|
+
|
|
264
|
+
- [PostgreSQL setup, transaction behavior, and migration limits](docs/postgresql.md)
|
|
265
|
+
- [Reproducible workload and measured adapter comparison](docs/performance.md)
|
|
266
|
+
- [Deployment, backup, recovery, and monitoring](docs/production-operations.md)
|
|
267
|
+
|
|
268
|
+
The existing tenant catalog remains SQLite-only. PostgreSQL supports authenticated
|
|
269
|
+
single-application deployments and conservatively serializes writes per database.
|
|
270
|
+
No production scale or parallel-write capacity is implied by this release.
|