Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Checklist: cutting a framework release


> The canonical, end-to-end release flow is documented at https://tina4.com/general/releasing - the four release tracks, the version-bearing files per repo, the skills bundle, EV signing and the gates. This checklist is the maintainer's working quick-reference; when the two differ, the documentation is the source of truth.

A release is tagged, not merged — **merging never publishes; the tag does.** A tag matching
`[0-9]*.*.*` (e.g. `3.13.146`, no `v`) triggers the publish workflow from the protected
`release` environment (required reviewer `tina4stack`). Do FEWER, BIGGER releases — bundle many
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Checklist: signing the skills installer (`install-skills.ps1`)


> The release flow that frames this - where signing sits in the order and why it comes last - is documented at https://tina4.com/general/releasing. This checklist is the signing detail.

`install-skills.ps1` is served to users at a tag and must carry a VALID Code Infinity
Authenticode (EV) signature. BOTH the CI gate (`.github/workflows/ci.yml`, which validates it on
`windows-latest` with `Get-AuthenticodeSignature`) and the tina4.com runtime shim reject it
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Tina4 Python — Agent Instructions

v3.13.150. 140 cataloged features, zero dependencies. Python 3.12+.
v3.13.151. 140 cataloged features, zero dependencies. Python 3.12+.

## AI Skills

Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ https://tina4.com/python/36-releases
This file records framework-specific changes. The release notes above remain the
authority for shipped versions.

## 3.13.151 - 2026-10-15

### Security
- Cross-Origin-Opener-Policy joins the default security-header set (`TINA4_COOP`,
default `same-origin-allow-popups`). It pairs with a COEP header to unlock
`SharedArrayBuffer` and WASM threads in the browser.
- S3 storage is a zero-dependency SigV4 client (no boto3). It now fails loud on a
missing credential, retries 429/5xx and dropped connections with capped backoff,
honours `Retry-After`, and keeps the endpoint path prefix.
- MongoDB keeps bound values out of the parsed SQL text.
- CRUD attributes are de-raw'd, the 302 redirect is guarded, CodeQL core findings
are cleared, and uploaded files are created owner-only (0600).

### Performance
- The dispatch pipeline no longer blocks the asyncio event loop. Session load and
save, the WebSocket backplane publish, and realtime chat and upload I/O are
offloaded to threads, so concurrent requests that hit a blocking session backend
no longer serialise behind each other.

### Docs
- The README built-in feature count is corrected (55 -> 140).

## 3.13.150 — 2026-10-11

### Dev admin
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The full discipline lives in `.claude/skills/tina4-maintainer/SKILL.md`; this bl

# Tina4 Python

Version 3.13.150 - Lightweight Python web framework. See https://tina4.com for full documentation.
Version 3.13.151 - Lightweight Python web framework. See https://tina4.com for full documentation.

## Build & Test

Expand Down Expand Up @@ -943,7 +943,7 @@ uv run tina4python test # Discovers @tests in src/**/*.py
- SSE/Streaming via `response.stream()` — Server-Sent Events support for real-time data push. Pass an async generator; framework handles chunked transfer encoding, `text/event-stream` content type, and connection keep-alive
- MCP server (`tina4_python.mcp`): built-in dev tools auto-start when MCP is a capability of the deployment. Developer API: `McpServer`, `@mcp_tool`, `@mcp_resource`. JSON-RPC 2.0 over SSE. **Security is a two-layer gate (v3.13.40):** `is_enabled()` is a pure capability gate (explicit `TINA4_MCP` wins, else `TINA4_DEBUG`; host-independent), and `is_request_allowed(remote_ip, has_valid_token)` authorises each request on the RAW socket peer (`request.remote_ip`, never X-Forwarded-For): loopback always; a remote caller needs `TINA4_MCP_REMOTE=true` AND a token matching `TINA4_MCP_TOKEN` (never `TINA4_API_KEY`, ADR-0078; sent as Authorization Bearer / X-MCP-Token; no configured token means remote is always denied). Every `/__dev` request (reads too), the MCP endpoints and the `/__dev_reload` socket also require a loopback Host (`localhost`, `127.0.0.1`, `[::1]` or `TINA4_HOST`) and refuse `Sec-Fetch-Site: same-site`/`cross-site` (ADR-0078). `/health` omits `version` outside debug. All MCP surfaces (REST shim, JSON-RPC, SSE) 404 a disallowed caller. `database_query` is SELECT/WITH-only and rejects stacked statements; the file tools are sandboxed to the project root. `is_localhost()` is informational only, not the gate. **`TINA4_DEV_ALLOWED_PEERS`** (opt-in, default none) is a comma-separated IP/CIDR allow-list of RAW socket peers (v4/v6, never a forwarded header) admitted to `/__dev` - the documented way to reach the dashboard from a Docker dev box whose requests arrive from the container-network gateway, e.g. `172.16.0.0/12` (#279). `/__dev/toolbar.css` and `/__dev/toolbar.js` are static and always load; a viewer the gate would refuse gets no toolbar injected
- Tests: 6,088 passing, 0 failures, **0 skipped** — measured 2026-09-17 on the lab (Ubuntu 24.04.4 LTS x86_64, Python 3.13.13, live services, `TINA4_REQUIRE_SERVICES=1`, 604s, run as root). **Firebird is NOT excluded.** The lab provisions a live Firebird 5 (`firebirdsql/firebird:5` on :3050, `TINA4_TEST_FIREBIRD_URL`) and those tests run. What IS deliberate is Firebird's absence from the `TINA4_REQUIRE_SERVICES` keyword gate in `tests/conftest.py`: GitHub CI does not provision Firebird, so a Firebird skip has to stay green *there*. Those are two different things and this line used to conflate them into "excluded by design", which read as "the Firebird tests do not run" — they do. **Nothing skips any more.** Reaching 0 skips now also needs the graph engines (Neo4j, Memgraph, ArangoDB, Ultipa, wired via `TINA4_TEST_NEO4J_URL`/`_MEMGRAPH_URL`/`_ARANGO_URL`/`_ULTIPA_URL` + the `graph` extra) and the lab Keycloak OIDC realm (`TINA4_REQUIRE_OIDC=1`), both added after the August baseline; without them `test_graph.py` (Feature 139) and the real OIDC contract test skip. The last skip was `tests/test_session_backend_failure.py` `[needs:no-dac-override]`: the suite runs as root, root holds `CAP_DAC_OVERRIDE`, so a write went straight through a 0400 file and no real `EACCES` was reachable — and as root the second `save()` returned `True`, so that skip was hiding a FAILING assertion, not merely an unrunnable one. The test now stops being root for the length of the failing write (`os.seteuid` to `nobody`; the saved uid stays 0, so a `finally` always restores it) and the kernel raises the genuine errno-13 the test asserts. Two parts of that are load-bearing: the log directory is handed to the same uid, because dropping the uid otherwise denies `Log.error` as well and the EACCES under test goes unrecorded (`_LogWriter.write` swallows `OSError` unless `TINA4_LOG_STRICT`); and the fixture root is its own 0755 `mkdtemp` rather than `tmp_path`, whose 0700 root-owned parents make every denial a directory traversal, which would pass for the wrong reason. Re-measure with `.venv/bin/python -m pytest tests/ -q` and quote the summary line, never the exit code
- Version: 3.13.150
- Version: 3.13.151

## Links

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tina4-python"
version = "3.13.150"
version = "3.13.151"
description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
authors = [
{name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
Expand Down
2 changes: 1 addition & 1 deletion tina4_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _resolve_version() -> str:
#
# test_version_constant.py now asserts this literal equals the pyproject
# version, so the release bump cannot leave it behind again.
return "3.13.150"
return "3.13.151"


__version__ = _resolve_version()
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading