Principle V now reflects the merged tracing migration (#148): - tracing + tracing-opentelemetry spans in every build, with OTLP export gated on OTLP_OTLS_ENDPOINT; - no manual Context threading; - every spawn / fan-out carries its parent span; - RUST_LOG gates span export (run at info/debug). Layer and Tech Stack wording matches. Also corrects stale "release builds only" telemetry notes in CLAUDE.md and .env.example, updates the CLAUDE.md dependency list, and fixes the README's RUST_LOG default. It also documents OTLP_OTLS_ENDPOINT in the README, as Principle IV requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
181 lines
7.8 KiB
Markdown
181 lines
7.8 KiB
Markdown
<!--
|
|
Sync Impact Report
|
|
==================
|
|
Version change: 1.0.0 -> 1.1.0
|
|
Bump rationale: MINOR. Principle V is materially expanded: the tracing
|
|
mechanism changed (tracing + tracing-opentelemetry in every build), and
|
|
it adds two new MUST rules: no manual Context threading, and carrying
|
|
the parent span across spawns. No principle is removed or redefined in
|
|
intent.
|
|
Modified principles:
|
|
- V. Observability: mechanism + rules updated for the tracing migration
|
|
(map #135, PR #148)
|
|
Also updated:
|
|
- I. Layered Architecture: Database Layer "traced" wording
|
|
- Technology Stack & Constraints: Tracing line
|
|
Added sections: N/A
|
|
Removed sections: N/A
|
|
Templates requiring updates:
|
|
- .specify/templates/plan-template.md — ✅ no changes needed (generic Constitution Check)
|
|
- .specify/templates/spec-template.md — ✅ no changes needed
|
|
- .specify/templates/tasks-template.md — ✅ no changes needed
|
|
- .specify/templates/checklist-template.md — ✅ no changes needed
|
|
- .specify/templates/agent-file-template.md — ✅ no changes needed
|
|
Runtime guidance:
|
|
- CLAUDE.md "Tracing" — ✅ already consistent (conventions landed with #148);
|
|
OTLP env line + dependency list updated alongside this amendment
|
|
- README.md — ✅ RUST_LOG default/gating corrected; OTLP_OTLS_ENDPOINT added
|
|
- .env.example — ✅ telemetry header corrected
|
|
Follow-up TODOs: None
|
|
-->
|
|
|
|
# ImageApi Constitution
|
|
|
|
## Core Principles
|
|
|
|
### I. Layered Architecture
|
|
|
|
All features MUST follow the established layered architecture:
|
|
|
|
- **HTTP Layer** (`main.rs`, feature modules): Route handlers, request
|
|
parsing, response formatting. No direct database access.
|
|
- **Service Layer** (`files.rs`, `exif.rs`, `memories.rs`, etc.): Business
|
|
logic. No HTTP-specific types.
|
|
- **DAO Layer** (`database/` trait definitions): Trait-based data access
|
|
contracts. Every DAO MUST be defined as a trait to enable mock
|
|
implementations for testing.
|
|
- **Database Layer** (Diesel ORM, `schema.rs`): Concrete `Sqlite*Dao`
|
|
implementations. Every query runs inside a `tracing` span via
|
|
`otel::trace_db_call`.
|
|
|
|
New features MUST NOT bypass layers (e.g., HTTP handlers MUST NOT
|
|
execute raw SQL). Actix actors are permitted for long-running async
|
|
work (video processing, file watching) but MUST interact with the
|
|
DAO layer through the established trait interfaces.
|
|
|
|
### II. Path Safety (NON-NEGOTIABLE)
|
|
|
|
All user-supplied file paths MUST be validated against `BASE_PATH`
|
|
using `is_valid_full_path()` before any filesystem operation. This
|
|
prevents directory traversal attacks.
|
|
|
|
- Paths stored in the database MUST be relative to `BASE_PATH`.
|
|
- Paths passed to external tools (ffmpeg, image processing) MUST be
|
|
fully resolved absolute paths.
|
|
- Extension detection MUST use the centralized helpers in
|
|
`file_types.rs` (case-insensitive). Manual string matching on
|
|
extensions is prohibited.
|
|
|
|
### III. Trait-Based Testability
|
|
|
|
All data access MUST go through trait-based DAOs so that every
|
|
handler and service can be tested with mock implementations.
|
|
|
|
- Each DAO trait MUST be defined in `src/database/` and require
|
|
`Sync + Send`.
|
|
- Mock DAOs for testing MUST live in `src/testhelpers.rs`.
|
|
- Integration tests against real SQLite MUST use in-memory databases
|
|
via `in_memory_db_connection()` from `database::test`.
|
|
- Handler tests MUST use `actix_web::test` utilities with JWT token
|
|
injection (using `Claims::valid_user()` and the `test_key` secret).
|
|
- New DAO implementations MUST include a `#[cfg(test)]` constructor
|
|
(e.g., `from_connection`) accepting an injected connection.
|
|
|
|
### IV. Environment-Driven Configuration
|
|
|
|
Server behavior MUST be controlled through environment variables
|
|
loaded from `.env` files. Hard-coded paths, URLs, or secrets are
|
|
prohibited.
|
|
|
|
- Required variables MUST call `.expect()` with a clear message at
|
|
startup so misconfiguration fails fast.
|
|
- Optional variables MUST use `.unwrap_or_else()` with sensible
|
|
defaults and be documented in `README.md`.
|
|
- Any new environment variable MUST be added to the README
|
|
environment section before the feature is considered complete.
|
|
|
|
### V. Observability
|
|
|
|
All database operations and HTTP handlers MUST be instrumented with
|
|
`tracing` spans, exported through `tracing-opentelemetry`: DAO bodies via
|
|
`otel::trace_db_call`, and handlers / feature functions via
|
|
`#[tracing::instrument]`. The per-request root span comes from the
|
|
`tracing-actix-web` middleware, which also honors an incoming W3C
|
|
`traceparent`.
|
|
|
|
- Spans are produced in debug and release builds alike. OTLP export is
|
|
enabled by `OTLP_OTLS_ENDPOINT`, not by build profile.
|
|
- Code MUST NOT thread an OpenTelemetry `Context` through call chains.
|
|
Spans parent on the ambient span.
|
|
- Every `tokio::spawn` or cross-thread fan-out (rayon, per-pass runtimes)
|
|
MUST carry its parent span: `.instrument(span)` or
|
|
`info_span!(parent: &span, ...)`. Tracing context is thread-local, and
|
|
an uncarried spawn orphans its spans onto a fresh trace.
|
|
- `RUST_LOG` gates span export as well as console output (one `EnvFilter`).
|
|
Deployments MUST run at `info` or `debug`.
|
|
- Logs are a separate pipeline: `env_logger` in debug, OTel log export in
|
|
release.
|
|
- Prometheus metrics (`imageserver_image_total`,
|
|
`imageserver_video_total`) MUST be maintained for key counters.
|
|
- Errors MUST be logged at `error!` level with sufficient context
|
|
for debugging without reproducing the issue.
|
|
|
|
**Rationale:** span parenting that relies on hand-threaded contexts drifted
|
|
into orphaned traces (the pre-migration baseline, #137). Ambient
|
|
parenting, plus an explicit carry at each spawn boundary, keeps a request or
|
|
job in one trace by construction.
|
|
|
|
## Technology Stack & Constraints
|
|
|
|
- **Language**: Rust (stable toolchain, Cargo build system)
|
|
- **HTTP Framework**: Actix-web 4
|
|
- **ORM**: Diesel 2.2 with SQLite backend
|
|
- **Auth**: JWT (HS256) via `jsonwebtoken` crate, bcrypt password
|
|
hashing
|
|
- **Video Processing**: ffmpeg/ffprobe (CLI, must be on PATH)
|
|
- **Image Processing**: `image` crate for thumbnails, `kamadak-exif`
|
|
for EXIF extraction
|
|
- **Tracing**: `tracing` + `tracing-opentelemetry` (every build), OTLP
|
|
export when `OTLP_OTLS_ENDPOINT` is set; logs via `env_logger` (debug) /
|
|
OTel log export (release)
|
|
- **Testing**: `cargo test`, `actix_web::test`, in-memory SQLite
|
|
|
|
External dependencies (ffmpeg, Ollama) are optional runtime
|
|
requirements. The server MUST start and serve core functionality
|
|
(images, thumbnails, tags) without them. Features that depend on
|
|
optional services MUST degrade gracefully with logged warnings,
|
|
not panics.
|
|
|
|
## Development Workflow
|
|
|
|
- `cargo fmt` MUST pass before committing.
|
|
- `cargo clippy` warnings MUST be resolved or explicitly suppressed
|
|
with a justification comment.
|
|
- `cargo test` MUST pass with all tests green before merging to
|
|
master.
|
|
- Database schema changes MUST use Diesel migrations
|
|
(`diesel migration generate`), with hand-written SQL in `up.sql`
|
|
and `down.sql`, followed by `diesel print-schema` to regenerate
|
|
`schema.rs`.
|
|
- Features MUST be developed on named branches
|
|
(`###-feature-name`) and merged to master via pull request.
|
|
- File uploads MUST preserve existing files (append timestamp on
|
|
conflict, never overwrite).
|
|
|
|
## Governance
|
|
|
|
This constitution defines the non-negotiable architectural and
|
|
development standards for the ImageApi project. All code changes
|
|
MUST comply with these principles.
|
|
|
|
- **Amendments**: Any change to this constitution MUST be documented
|
|
with a version bump, rationale, and updated Sync Impact Report.
|
|
- **Versioning**: MAJOR for principle removals/redefinitions, MINOR
|
|
for new principles or material expansions, PATCH for wording
|
|
clarifications.
|
|
- **Compliance**: Pull request reviews SHOULD verify adherence to
|
|
these principles. The CLAUDE.md file provides runtime development
|
|
guidance and MUST remain consistent with this constitution.
|
|
|
|
**Version**: 1.1.0 | **Ratified**: 2026-02-26 | **Last Amended**: 2026-09-29
|