Logs were a separate pipeline: env_logger in debug (never exported) and opentelemetry-appender-log in release (exported without trace context, so SigNoz could not link logs to traces). - tracing-subscriber `tracing-log` feature: try_init installs LogTracer, so log:: records become tracing events: one console format, and RUST_LOG per-module directives still apply to them. - opentelemetry-appender-tracing exports events as OTel log records when OTLP_OTLS_ENDPOINT is set (any build), stamped with the enclosing span's trace_id/span_id. A per-layer filter drops the exporter's own crates. - The OTel span layer keeps only WARN/ERROR events as span events; the info/debug copies duplicated the correlated logs. - Remove env_logger init in the server, init_logs, and the unused opentelemetry-appender-log / opentelemetry-stdout deps. - tests/otel_log_correlation.rs pins correlation (native + bridged), the span-event filter, and per-module filtering of bridged records. - Constitution 1.2.0 (Principle V logging rule), CLAUDE.md, README, .env.example. Closes #147 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.0 KiB
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): ConcreteSqlite*Daoimplementations. Every query runs inside atracingspan viaotel::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 requireSync + 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()fromdatabase::test. - Handler tests MUST use
actix_web::testutilities with JWT token injection (usingClaims::valid_user()and thetest_keysecret). - 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 inREADME.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
Contextthrough call chains. Spans parent on the ambient span. - Every
tokio::spawnor cross-thread fan-out (rayon, per-pass runtimes) MUST carry its parent span:.instrument(span)orinfo_span!(parent: &span, ...). Tracing context is thread-local, and an uncarried spawn orphans its spans onto a fresh trace. RUST_LOGgates span export as well as console output (oneEnvFilter). Deployments MUST run atinfoordebug.- Logs share the tracing subscriber:
log::records are bridged intotracing, so both print in one console format. WithOTLP_OTLS_ENDPOINTset (any build), events export as OTel log records carrying the enclosing span's trace_id / span_id. Only WARN and ERROR events are also recorded on spans as span events. - 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
jsonwebtokencrate, bcrypt password hashing - Video Processing: ffmpeg/ffprobe (CLI, must be on PATH)
- Image Processing:
imagecrate for thumbnails,kamadak-exiffor EXIF extraction - Tracing:
tracing+tracing-opentelemetry(every build), OTLP export whenOTLP_OTLS_ENDPOINTis set; logs through the same subscriber viaopentelemetry-appender-tracing(log::bridged) - 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 fmtMUST pass before committing.cargo clippywarnings MUST be resolved or explicitly suppressed with a justification comment.cargo testMUST pass with all tests green before merging to master.- Database schema changes MUST use Diesel migrations
(
diesel migration generate), with hand-written SQL inup.sqlanddown.sql, followed bydiesel print-schemato regenerateschema.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.2.0 | Ratified: 2026-02-26 | Last Amended: 2026-09-29