Skip to content

XyloLabs API — Comprehensive Code Review

Date: 2026-03-30 Scope: Full codebase (~36,000 LOC Rust + React/TypeScript frontend) Crate Count: 12 workspace crates + frontend SPA + infrastructure Files Reviewed: 140+


Executive Summary

The XyloLabs API is a well-architected IoT data-ingestion platform with a Rust backend (axum), multi-MCU embedded SDK, and React admin frontend. The codebase demonstrates strong engineering practices: parameterized SQL throughout, proper Argon2id password hashing, timing-oracle-protected login, cross-language codec parity testing, and comprehensive RBAC.

89 findings were identified across 6 review domains. The distribution:

Severity Count Action
CRITICAL 10 Must fix before production
HIGH 22 Should fix in next iteration
MEDIUM 33 Address during regular development
LOW 24 Optional improvements

The most urgent issues center on unsafe unwrap() calls in binary protocol parsing, secrets management in configuration files, and race conditions in auth/session handling. No fundamental architectural problems were found — the crate decomposition, trait design, and layering are sound.


Table of Contents

  1. Architecture Overview
  2. Critical Findings
  3. High Findings
  4. Medium Findings
  5. Low Findings
  6. Positive Observations
  7. Recommendations by Priority

1. Architecture Overview

Repository layout summary: - crates/xylolabs-core — models, DTOs, protocol encode/decode, downsampling - crates/xylolabs-db — PostgreSQL pool + repository layer - crates/xylolabs-server — Axum server, routes, middleware, auth - crates/xylolabs-protocol — XAP/XMBP binary codec (no_std) - crates/xylolabs-sdk — embedded SDK: client, session, transport, codecs - crates/xylolabs-hal-* — per-platform HAL crates - crates/xylolabs-storage — S3-compatible storage client - crates/xylolabs-transcode — FFmpeg pipeline + async worker - frontend/ — React SPA - deploy/nginx/ — NGINX reverse proxy configs - docker-compose*.yml — deployment/test orchestration - tests/ — integration and performance suites

Key Strengths

  • Clean workspace separation: core types are dependency-free, protocol/sdk are no_std
  • All SQL uses parameterized queries — zero injection vectors found
  • Argon2id with timing-oracle protection on login
  • Family-based refresh token rotation with automatic revocation on reuse
  • Cross-language codec parity tests (Rust ↔ C) ensure protocol compatibility
  • Proper SKIP LOCKED for concurrent job claiming
  • Role-based access control with facility-level tenant isolation

2. Critical Findings

C-01. Unchecked unwrap() in XMBP decode can panic on malformed input

File: crates/xylolabs-protocol/src/decode.rs:213 Component: Protocol

The try_into().unwrap() on fixed-size array conversion can panic if the byte slice has an unexpected length. While read_bytes validates length, the implicit safety contract is fragile.

// Panics if read_bytes returns wrong size (defensive programming failure)
Ok(SampleValue::F64(f64::from_be_bytes(bytes.try_into().unwrap()))

Fix: Replace with explicit error propagation or expect() with a safety justification:

Ok(SampleValue::F64(f64::from_be_bytes(
    bytes.try_into().expect("read_bytes guarantees 8 bytes for F64")
)))


C-02. Integer overflow in JSON content-length decode

File: crates/xylolabs-protocol/src/decode.rs:462 Component: Protocol

The val variable is clamped to MAX_CONTENT_LENGTH but subsequent buf[pos + val] can still overflow if pos + val > buf.len(). The overflow handling truncates the value but doesn't fix the buffer access.

Fix: Guard against buffer bounds explicitly:

let remaining = buf.len().saturating_sub(pos);
let copy_len = val.min(remaining);


C-03. SQL injection pattern in test cleanup

File: crates/xylolabs-server/tests/common/mod.rs:260 Component: Server (Tests)

Dynamic SQL via format!("DELETE FROM {table}"). Currently safe because table names are hardcoded, but the pattern is dangerous if extended.

for table in tables {
    let query = format!("DELETE FROM {table}");
    sqlx::query(&query).execute(pool).await.ok();
}

Fix: Use an allowlist of known table names or sqlx::query! macro.


C-04. HeaderValue::from_static() may panic at runtime

File: crates/xylolabs-server/src/routes/metadata_query.rs:240 Component: Server

from_static requires &'static str but is called where a dynamic value could be passed in future refactors.

Fix: Use HeaderValue::from_str() for non-static strings.


C-05–C-08. Secrets exposed in .env.example and docker-compose.test.yml

Files: .env.example:15-16,25-26,47-48 and docker-compose.test.yml:7-8,20-21 Component: Infrastructure

  • Weak example secrets that could be copy-pasted to production
  • Default admin credentials are guessable
  • Test environment secrets hardcoded in version control

Fix: Replace actual secrets with <generate-strong-secret-here> placeholders. Use Docker secrets or CI/CD environment variables for test credentials.


C-09. Auth token refresh race condition

File: frontend/src/api/client.ts:54-56, 90-94 Component: Frontend

The setAccessToken interceptor checks isLoggingOut but the Zustand state snapshot may be stale between the check and the refresh call, creating a window where concurrent refreshes can occur.

Fix: Capture the logout flag atomically before calling tryRefreshToken. Use a mutex or refresh-in-progress flag.


C-10. FormatConfig::content_type() method does not exist

File: crates/xylolabs-transcode/src/pipeline.rs:129 Component: Transcode

Code calls format.content_type() but the method doesn't exist on FormatConfig. This will fail to compile.

Fix: Either add a content_type() method to FormatConfig or call it on the inner format field:

let content_type = format.format.content_type();


3. High Findings

H-01. Unsafe from_raw_parts in SDK client

File: crates/xylolabs-sdk/src/client.rs:71, 178 Component: SDK

Uses unsafe { core::slice::from_raw_parts(samples.as_ptr() as *const u8, byte_len) } to reinterpret i16 samples as bytes. While i16 has no padding, the byte length calculation can overflow u16 on large buffers.

Fix: Add bounds check: let byte_len = (samples.len() * 2).min(u16::MAX as usize) as u16; or use bytemuck::cast_slice.


H-02. Refresh token family not validated against user

File: crates/xylolabs-server/src/routes/auth.rs:194-200 Component: Server

The refresh() endpoint validates user_id and family_id match the stored record, but doesn't verify the family_id belongs to the user. A leaked token could mint tokens for another user's family.

Fix: Store and validate user_id per refresh token family, rejecting cross-family reuse.


H-03. TOCTOU race in WebSocket session validation

File: crates/xylolabs-server/src/routes/ingest.rs:464-474 Component: Server

Time gap between session ownership check (DB) and session registration (in-memory manager). A malicious client could register a session between these checks.

Fix: Validate inside the registration transaction atomically.


H-04. Rate limiter race condition

File: crates/xylolabs-server/src/router.rs:63-69 Component: Server

The check_and_increment method has a TOCTOU between reading and writing the count. Two concurrent requests can both pass the limit check.

let count = self.attempts.get(&ip).unwrap_or(0);
if count >= AUTH_RATE_LIMIT { return false; }
self.attempts.insert(ip, count + 1);

Fix: Use HashMap::entry().and_modify() for atomic read-modify-write.


H-05. update_password lacks authorization context

File: crates/xylolabs-db/src/repo/user.rs:119-126 Component: Database

The function updates any user's password given just a UUID. No facility_id or old-password verification at the repository layer.

Fix: Add facility_id parameter for tenant isolation, or enforce old-password verification at the service layer.


H-06. Unsafe std::env::set_var in async context

File: crates/xylolabs-server/src/config.rs:326-341 Component: Server

std::env::set_var is not thread-safe and can cause UB in multi-threaded async contexts. The unsafe blocks are unnecessary (the functions are safe in Rust).

Fix: Remove unsafe wrappers. Use a config struct passed via State instead of mutating global environment.


H-07. Open redirect vulnerability in login page

File: frontend/src/pages/LoginPage.tsx:13-17 Component: Frontend

safeRedirect only blocks // prefixes but doesn't validate window.location.origin, allowing redirects to external domains.

Fix: Use new URL(redirect) and verify origin === window.location.origin.


H-08. Significant code duplication between HAL modem drivers

Files: crates/xylolabs-hal-rp/src/modem.rs:278-413 and crates/xylolabs-hal-stm32/src/modem.rs:238-351 Component: HAL

~175 lines of identical AT command helpers (format_tcp_open_cmd, parse_csq_rssi, format_u16) duplicated across RP and STM32 crates.

Fix: Extract to a shared xylolabs-hal-common crate or feature-gated module in xylolabs-core.


H-09–H-12. Additional HIGH findings (summary)

ID File Component Issue
H-09 sdk/src/transport.rs:389 SDK Array indexing without bounds check on HTTP response parsing
H-10 sdk/src/client.rs:763 SDK elapsed_ms can overflow near u64::MAX
H-11 sdk/src/session.rs:494 SDK Session ID validation allows short/test strings — should enforce UUID-like format
H-12 sdk/src/codec/lc3.rs:103 (XAP encoder) SDK sample_rate * frame_duration can overflow u32 — widen to u64

4. Medium Findings

Security & Auth

ID File Issue
M-01 server/src/routes/tags.rs:59 Tag color validation allows arbitrary-length hex strings; should enforce ^#[0-9a-fA-F]{6}$
M-02 frontend/src/api/client.ts:85 No explicit CSRF header for state-changing operations
M-03 server/src/ingest/manager.rs:526 Session flush retry has no backoff or max retry count

Data Integrity

ID File Issue
M-04 core/src/protocol.rs:140 encode_batch flag/device_id consistency not enforced — device_id: Some(x) with flags: 0 silently skips encoding
M-05 core/src/protocol.rs:273 encode_value uses unwrap_or_default() for JSON serialization — silent data corruption if serialization fails
M-06 server/src/routes/metadata_query.rs:401 CSV export doesn't fully escape header columns

Performance

ID File Issue
M-07 core/src/downsample.rs:86 downsample_samples takes ownership but creates a new Vec — confusing; take &[MetadataSample] instead
M-08 server/src/routes/ingest.rs:230 Excessive clone operations in ingest hot path
M-09 storage/src/client.rs:94 Entire file buffered in memory before upload — rejects files >64MB with warning but still buffers
M-10 transcode/src/worker.rs:94 No backoff for repeated DB errors in job claiming loop

Configuration & Validation

ID File Issue
M-11 sdk/src/config.rs:40 Missing validation for audio_batch_ms, max_retries, retry_max_ms, http_timeout_ms
M-12 hal-rp/src/decimator.rs:160 Hardcoded frame sizes (960/320) — panics if hardware config changes
M-13 transcode/src/ffmpeg.rs:129 Codec/container allowlists hardcoded — should be configurable

Frontend

ID File Issue
M-14 frontend/src/i18n/index.ts:564 Corrupted Korean translation string
M-15 frontend/src/App.tsx No ErrorBoundary component wrapping Routes
M-16 Multiple pages Using browser confirm() instead of existing ConfirmDialog component
M-17 Multiple pages Missing loading/disabled states on delete buttons during mutation
M-18 frontend/src/stores/localeStore.ts:46 localStorage preferences have no expiration or migration

Code Quality

ID File Issue
M-19 server/src/routes/devices.rs:112 Collapsible if statements (Clippy warnings)
M-20 server/src/routes/facilities.rs:89 Multiple collapsible if statements (Clippy warnings)
M-21 sdk/src/session.rs:639 Stream name silently truncated at 31 bytes — should return error
M-22 sdk/src/ring_buffer.rs:27 Overly strict Acquire ordering in available()Relaxed sufficient for SPSC reads
M-23 hal-rp/src/decimator.rs:19 Filter coefficients lack generation script reference
M-24 transcode/src/worker.rs:221 expect() on signal handler installation can panic on startup

5. Low Findings

Rust Backend

ID File Issue
L-01 core/src/protocol.rs:14 Magic number 10 for header size — define as const MIN_XMBP_HEADER_SIZE
L-02 core/src/protocol.rs:330 Production unwrap() lacks context messages — use expect("why this is safe")
L-03 db/src/repo/*.rs SELECT * used throughout (27 occurrences) — fragile to schema changes
L-04 db/ Zero unit tests for repository functions — bulk ops, tenant isolation untested
L-05 protocol/src/decode.rs:104 Redundant duplicate data.len() < 10 check
L-06 sdk/src/transport.rs:380 Inefficient string building via many small copy_from_slice calls
L-07 sdk/src/codec/lc3.rs:126 (XAP encoder) Magic value 32768.0f32 for Q15 scaling not documented
L-08 sdk/src/client.rs:195 SdkError::InvalidState doesn't distinguish session states
L-09 sdk/src/session.rs:408 reconnect_attempts uses wrapping add — should use saturating_add
L-10 hal-rp/modem.rs:372 vs hal-stm32/modem.rs:313 Inconsistent naming: parse_u8_from_ascii vs parse_u8_ascii

Frontend

ID File Issue
L-11 Multiple pages Hardcoded fontSize: '16px' duplicated — extract to CSS class
L-12 UploadsPage.tsx:34, TranscodePage.tsx:40 Magic number per_page: 20 — define as constant
L-13 api/uploads.ts:119 and api/transcode.ts:92 Duplicate mapPage function — extract to shared utility
L-14 pages/SettingsPage.tsx:183 animate-[fadeIn_0.15s_ease] assumes Tailwind animation configured

Infrastructure

ID File Issue
L-15 deploy/nginx/*.conf:86 WebSocket timeout 3600s (1 hour) is excessive — reduce to 600s
L-16 docker-compose.yml:3 Using latest tag — pin to specific semantic version
L-17 docker-compose.test.yml:11 3s timeout may fail on slow CI runners

6. Positive Observations

Security

  • Zero SQL injection vectors — all 27+ repository functions use parameterized $N binding
  • Argon2id password hashing with timing-oracle protection (dummy hash for missing users)
  • Family-based refresh token rotation with automatic revocation on reuse
  • API key hashing (SHA256) before storage; plaintext shown only once on creation
  • Comprehensive security headers: HSTS, X-Frame-Options, X-Content-Type-Options, CSP
  • Decompression bomb protection: 64 MiB limit in decode_chunk_compressed
  • Allocation DoS protection: sample_count * 8 > remaining check in protocol decode
  • CORS origin allowlist (rejects wildcard outside dev/test)

Architecture

  • Clean workspace decomposition with no_std protocol/SDK crates
  • Platform trait abstraction allows same SDK to run on STM32/ESP/nRF/RP2350
  • Feature-flagged codec implementations (ADPCM, XAP)
  • SKIP LOCKED for concurrent worker job claiming
  • Broadcast channels for SSE live streaming
  • spawn_blocking for CPU-intensive compression

Testing

  • Cross-language parity tests (Rust ↔ C SDK) ensure byte-identical encoding
  • Codec stress tests validate correctness under high throughput
  • Integration tests cover auth, devices, facilities, health, config, ingest, and metadata query
  • Ring buffer SPSC correctness validated with concurrent tests

Data Layer

  • Sensible connection pool timeouts (acquire_timeout, idle_timeout, max_lifetime)
  • LIMIT caps on all list operations (500–10,000 by entity)
  • unnest() with array binding for bulk inserts
  • Partial indexes for efficient querying
  • Tenant isolation enforced at repository layer with facility_id on all delete/revoke operations

7. Recommendations by Priority

Immediate (Block Production)

  1. Fix all unwrap() calls in protocol decode/encode with explicit error handling or expect() with justification
  2. Remove actual secrets from .env.example — use placeholders
  3. Fix FormatConfig::content_type() in transcode pipeline (compile error)
  4. Add origin validation to login redirect
  5. Fix auth token refresh race — add refresh-in-progress mutex

Next Sprint

  1. Harden rate limiter with atomic entry().and_modify()
  2. Add facility_id to update_password for tenant isolation
  3. Remove std::env::set_var from config — use State-passed config struct
  4. Strengthen refresh token family validation against cross-family attacks
  5. Add #[must_use] to all bool-returning HAL methods
  6. Gate incomplete nRF/ESP HAL stubs with feature flags or compile_error!

Ongoing

  1. Extract shared modem AT code from RP and STM32 HALs
  2. Add repository unit tests (bulk ops, tenant isolation, tag transactions)
  3. Replace SELECT * with explicit column lists in hot-path queries
  4. Add ErrorBoundary to React app
  5. Replace browser confirm() with existing ConfirmDialog component
  6. Make transcode allowlists configurable via environment or database
  7. Implement storage streaming upload for files >16MB

Review Metrics

Component Files CRITICAL HIGH MEDIUM LOW Total
xylolabs-core 27 1 2 3 2 8
xylolabs-db 23 0 1 1 3 5
xylolabs-server 35 2 5 8 2 17
protocol + SDK 16 2 8 7 6 23
HAL + transcode + storage 28 1 3 6 4 14
Frontend + infra 45 4 3 8 7 22
Total 174 10 22 33 24 89

This review was generated through automated static analysis and manual code inspection. All findings should be verified against the current codebase state before acting on them.