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
18 changes: 18 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ on:
jobs:
verify:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: civicaccess
POSTGRES_USER: civicaccess
POSTGRES_DB: civicaccess_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U civicaccess -d civicaccess_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
Expand All @@ -16,5 +30,9 @@ jobs:
run: python -m pip install https://github.com/CivicSuite/civiccore/releases/download/v1.2.0/civiccore-1.2.0-py3-none-any.whl
- name: Install package
run: python -m pip install -e ".[dev]"
- name: Assert CivicCore release version
run: python -c "import civiccore; assert civiccore.__version__ == '1.2.0', civiccore.__version__"
- name: Run release gate
env:
CIVICACCESS_POSTGRES_TEST_URL: postgresql+psycopg2://civicaccess:civicaccess@localhost:5432/civicaccess_test
run: bash scripts/verify-release.sh
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ The format follows Keep a Changelog, and this project follows Semantic Versionin

## [Unreleased]

## [0.4.0] - 2026-06-28

City-core hardening: closes probe gaps #2 (authz), #3 (audit), and #4 (backup/restore), and makes the shared CivicCore PostgreSQL the default review store.

### Added

- Added a trusted-write guard: persistent writes (`POST /api/v1/civicaccess/review`, `POST /api/v1/civicaccess/reviews/{id}/records-export`) now require the `CIVICACCESS_TRUSTED_WRITE_TOKEN` server secret, sent as the `X-CivicAccess-Write-Token` header. Missing/invalid token returns 403; unconfigured guard fails closed with 503. (Probe gap #2.)
- Added `POST /api/v1/civicaccess/analyze`: a stateless, public, no-persistence accessibility check. The public `/civicaccess` surface now uses it, so public users can no longer write city records.
- Added a persisted `audit_events` table and module audit events on writes/exports: `review.create` is committed atomically in the same transaction as the review record; `review.records_export` emits a standalone audit row on export. (Probe gap #3.)
- Added durability proofs: a Postgres reconnect round-trip for the default store (review + audit survive a fresh engine) and a SQLite dev-fallback backup/restore round-trip. (Probe gap #4.)
- Added a mandatory PostgreSQL release gate: `verify-release.sh` and CI require `CIVICACCESS_POSTGRES_TEST_URL` so PostgreSQL persistence coverage cannot be skipped, plus `tests/test_postgres_persistence.py`.

### Changed

- Defaulted the review store to the shared CivicCore PostgreSQL: the module reads the supervisor's `DATABASE_URL` (asyncpg) and derives a sync psycopg2 URL. `CIVICACCESS_REVIEW_DB_URL` still overrides; SQLite is now an explicit dev fallback rather than the default.
- Moved `psycopg2-binary` from dev/optional dependencies to a runtime dependency so the PostgreSQL default works out of the box.
- Renamed the schema migration id to `civicaccess-windows-local-state-v1` to match the CivicCore Windows Local module convention.
- Staff `/civicaccess/staff` surface sends an operator-supplied write token (entered in the UI, kept in the browser session) on save and export. The server secret is never embedded in served HTML.

## [0.3.0] - 2026-06-25

### Added
Expand Down
40 changes: 40 additions & 0 deletions PROBE-PROGRESS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# CivicAccess City-Core Probe Progress

Tracks the city-core readiness probe gaps for CivicAccess. The probe demoted CivicAccess from
city-core (`excluded_from_city_core_needs_work_probe`) until these gaps are closed with evidence.

Phase A (this release, **v0.4.0**) closes the module-repo gaps #1–#4. Gaps #5–#6 are integration/QA
gaps owned by later phases of the CivicAccess → city-core plan
(`CivicSuite/civicsuite` → `docs/roadmap/civicaccess-citycore-integration/`).

| Gap | Description | Status | Evidence |
|-----|-------------|--------|----------|
| #1 | Clean install with the published CivicCore v1.2.0 wheel pin | **Closed** (v0.3.0) | `pyproject.toml` pins `civiccore` to the v1.2.0 release wheel + SHA256; `tests/test_runtime_foundation.py::test_pyproject_uses_published_civiccore_release_wheel` (and asserts the bad `civiccore==1.1.0`/`1.0.0` pins are absent). CI installs the wheel and asserts `civiccore.__version__ == "1.2.0"`. |
| #2 | Staff/public authz boundary on persistent writes | **Closed** (v0.4.0) | Trusted-write guard `_authorize_persistent_write` (`civicaccess/main.py`) on `POST /api/v1/civicaccess/review` and `POST /api/v1/civicaccess/reviews/{id}/records-export` — requires `CIVICACCESS_TRUSTED_WRITE_TOKEN` via `X-CivicAccess-Write-Token`; 403 on missing/invalid, 503 fail-closed when unconfigured. Public surface uses the new stateless `POST /api/v1/civicaccess/analyze` (no persistence, no token). Tests: `tests/test_citycore_hardening.py::test_review_write_rejects_missing_and_wrong_token`, `::test_records_export_write_requires_token`, `::test_write_guard_not_configured_returns_503`, `::test_analyze_is_open_and_never_persists`. |
| #3 | Module audit logging on writes/exports | **Closed** (v0.4.0) | `audit_events` table (`civicaccess/access_review.py`) + `record_audit_event`; `review.create` is written in the same transaction as the review, `review.records_export` on export. Tests: `tests/test_citycore_hardening.py::test_audit_event_persisted_on_review_create`, `::test_audit_event_persisted_on_records_export`; Postgres-side in `tests/test_postgres_persistence.py`. |
| #4 | Backup/restore proof (not declaration) | **Closed** (v0.4.0) | Default store (Postgres) durability: `tests/test_postgres_persistence.py::test_postgres_review_and_audit_survive_reconnect` writes a review + audit, disposes the engine (simulated process restart), reconnects with a fresh engine, and asserts both reload — the property the supervisor's wholesale `Data/postgres` backup relies on. Dev fallback (SQLite): `tests/test_citycore_hardening.py::test_backup_restore_roundtrip_preserves_records_and_audit` backs up the live Data file, loses it, restores, and asserts records + audit survive. The supervisor's end-to-end backup/restore on a clean VM is exercised in Phase D. |
| #5 | Installer / desktop registry record (6-module city-core) | **Deferred → Phases B–C** | The `civicaccess` record in `CivicSuite/civicsuite` `installer/modules.json` still carries the stale `civiccore_requirement: "1.1.0"` and lacks the full contract fields. Authoring the runtime-valid record (Phase B) and flipping the city-core profile to 6 modules (Phase C) are out of scope for the module repo. |
| #6 | Clean-VM browser QA + full accessibility acceptance | **Deferred → Phase D** | Exercised on a clean VM (Windows Sandbox) against the installer-built stack with a full accessibility + export-correctness acceptance pass. |

## Phase A persistence model (v0.4.0)

- **Default store:** the shared CivicCore PostgreSQL. The module reads the supervisor-injected
`DATABASE_URL` (`postgresql+asyncpg://…:15432/…`) and derives a sync psycopg2 URL via
`_sync_database_url` (`civicaccess/main.py`).
- **Override:** `CIVICACCESS_REVIEW_DB_URL` (a dev SQLite path or a pre-built Postgres URL).
- **Fallback:** SQLite under `CIVICACCESS_DATA_DIR` only when neither is set (explicit dev use).
- **Release gate:** `CIVICACCESS_POSTGRES_TEST_URL` is required by `scripts/verify-release.sh` and CI
(a `postgres:16` service), so PostgreSQL persistence coverage cannot be silently skipped.

## Notes / deliberate decisions

- `POST /api/v1/civicaccess/export` (the generic export-checklist builder) is **not** token-guarded:
it is stateless advisory compute with no persisted data, in the same class as the form/plain-language/
workflow planning routes. The records-grade export that reads persisted data
(`/reviews/{id}/records-export`) **is** guarded. This is the correct reading of "every
persistence-write route".
- The write token is **never embedded in served HTML**. The staff surface provides a field where the
operator pastes the token; it is kept in the browser session (`sessionStorage`) and sent as
`X-CivicAccess-Write-Token` on save/export only. The token gates the API for all callers and fails
closed (503) when `CIVICACCESS_TRUSTED_WRITE_TOKEN` is unconfigured. The token comparison is
constant-time (`hmac.compare_digest`).
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

CivicAccess is the CivicSuite module for accessibility, plain-language, multilingual, and ADA Title II review-support workflows.

Current state: **v0.3.0 standalone readiness candidate**. This repo contains a FastAPI package aligned to the published CivicCore v1.2.0 release wheel, health/root endpoints, readiness gates, WCAG-aligned review support, local database-backed review records, accessible form planning, accessible publishing workflow checks, plain-language rewrites, multilingual draft variants, ADA Title II review-support packages, tagged-PDF expectations, records-ready export checklists, an API-backed public review UI at `/civicaccess`, and a staff review/export workspace at `/civicaccess/staff`. The previous `v1.0.0` release was published in error and remains historical evidence only.
Current state: **v0.4.0 standalone readiness candidate**. This repo contains a FastAPI package aligned to the published CivicCore v1.2.0 release wheel, health/root endpoints, readiness gates, WCAG-aligned review support, database-backed review records that default to the shared CivicCore PostgreSQL, accessible form planning, accessible publishing workflow checks, plain-language rewrites, multilingual draft variants, ADA Title II review-support packages, tagged-PDF expectations, records-ready export checklists, a stateless public accessibility checker at `/civicaccess`, and a staff review/export workspace at `/civicaccess/staff`. As of v0.4.0, persistent writes (saving reviews and records exports) require a trusted-write token, every write/export emits a persisted audit event, and the public surface analyzes without persisting. The previous `v1.0.0` release was published in error and remains historical evidence only.

CivicAccess does **not** provide legal advice, certified ADA compliance, official translation certification, live LLM calls, or final publication approval. City staff, ADA coordinators, translators, and qualified reviewers remain responsible for publication decisions.

Expand Down
2 changes: 1 addition & 1 deletion README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CivicAccess

CivicAccess is the CivicSuite module for accessibility, plain-language, multilingual, and ADA Title II review-support workflows.

Current state: v0.3.0 corrective demotion state. This repo contains a deterministic scaffold with a FastAPI package aligned to the published CivicCore v1.2.0 release wheel, health/root endpoints, readiness gates, WCAG-aligned review support, optional database-backed review records via CIVICACCESS_REVIEW_DB_URL, accessible form planning, accessible publishing workflow checks, plain-language rewrites, multilingual draft variants, ADA Title II review-support packages, tagged-PDF expectations, records-ready export checklists, and an API-backed public review UI at /civicaccess. The previous v1.0.0 release was published in error and is superseded by this honest sub-1.0.0 label.
Current state: v0.4.0 standalone readiness candidate. This repo contains a deterministic FastAPI package aligned to the published CivicCore v1.2.0 release wheel, health/root endpoints, readiness gates, WCAG-aligned review support, database-backed review records that default to the shared CivicCore PostgreSQL (with a SQLite dev fallback), accessible form planning, accessible publishing workflow checks, plain-language rewrites, multilingual draft variants, ADA Title II review-support packages, tagged-PDF expectations, records-ready export checklists, a stateless public accessibility checker at /civicaccess, and a trusted-write-token-guarded staff persistence/export surface with persisted audit events. The previous v1.0.0 release was published in error and is superseded by this honest sub-1.0.0 label.

CivicAccess does not provide legal advice, certified ADA compliance, official translation certification, live LLM calls, or final publication approval. City staff, ADA coordinators, translators, and qualified reviewers remain responsible for publication decisions.

Expand Down
4 changes: 3 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Security

CivicAccess version: `0.3.0`.
CivicAccess version: `0.4.0`.

CivicAccess is self-hosted municipal software. It provides advisory accessibility, plain-language, multilingual draft, and ADA Title II review-support workflows; it does not make legal, certification, translation, or publication decisions.

Persistent writes (saving reviews and records exports) require the `CIVICACCESS_TRUSTED_WRITE_TOKEN` server secret, sent as the `X-CivicAccess-Write-Token` header and compared in constant time. The token is **never embedded in served HTML**: the staff surface provides a field where the operator pastes it, and it is kept only in the browser session. The public surface (`/civicaccess`) analyzes content statelessly and never persists. When the write token is not configured, persistence-backed writes fail closed (HTTP 503) rather than accepting unauthenticated writes.

Report vulnerabilities privately through the CivicSuite project maintainers. Do not include secrets, resident data, or protected municipal records in public issues.
2 changes: 1 addition & 1 deletion USER-MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

CivicAccess helps cities make public information easier to read, reach, translate, review, and preserve. It supports accessibility review, accessible forms, public publishing workflows, plain-language rewrites, multilingual draft variants, ADA Title II review support, tagged-PDF expectations, and records-ready export checklists.

Current state: `0.3.0` standalone readiness candidate. CivicAccess includes deterministic checks, local database-backed review records, readiness gates, an API-backed public review UI at `/civicaccess`, a staff review/export workspace at `/civicaccess/staff`, and CivicCore v1.2.0 release-wheel alignment. The previous `v1.0.0` release was published in error and remains historical evidence only. CivicAccess does not provide legal advice, certified ADA compliance, official translation certification, live LLM calls, or final publication approval.
Current state: `0.4.0` standalone readiness candidate. CivicAccess includes deterministic checks, database-backed review records that default to the shared CivicCore PostgreSQL (with a SQLite dev fallback), readiness gates, a stateless public accessibility checker at `/civicaccess`, a staff review/export workspace at `/civicaccess/staff`, trusted-write-token-guarded persistence with persisted audit events, and CivicCore v1.2.0 release-wheel alignment. The previous `v1.0.0` release was published in error and remains historical evidence only. CivicAccess does not provide legal advice, certified ADA compliance, official translation certification, live LLM calls, or final publication approval.

## For IT And Technical Staff

Expand Down
2 changes: 1 addition & 1 deletion USER-MANUAL.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CivicAccess User Manual

CivicAccess helps cities make public information easier to read, reach, translate, review, and preserve. It supports accessibility review, accessible forms, public publishing workflows, plain-language rewrites, multilingual draft variants, ADA Title II review support, tagged-PDF expectations, and records-ready export checklists.

Current state: 0.3.0 corrective demotion state. CivicAccess includes deterministic checks, optional database-backed review records, readiness gates, an API-backed public review UI at /civicaccess, and CivicCore v1.2.0 release-wheel alignment. The previous v1.0.0 release was published in error and is superseded by this honest sub-1.0.0 label. It does not provide legal advice, certified ADA compliance, official translation certification, live LLM calls, or final publication approval.
Current state: 0.4.0 standalone readiness candidate. CivicAccess includes deterministic checks, database-backed review records that default to the shared CivicCore PostgreSQL (with a SQLite dev fallback), readiness gates, a stateless public accessibility checker at /civicaccess, a staff review/export workspace at /civicaccess/staff, trusted-write-token-guarded persistence with persisted audit events, and CivicCore v1.2.0 release-wheel alignment. The previous v1.0.0 release was published in error and is superseded by this honest sub-1.0.0 label. It does not provide legal advice, certified ADA compliance, official translation certification, live LLM calls, or final publication approval.

Runtime routes:

Expand Down
2 changes: 1 addition & 1 deletion civicaccess/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""civicaccess package."""

__version__ = "0.3.0"
__version__ = "0.4.0"
Loading