Skip to content

HiMe v1.2.0

Choose a tag to compare

@thinkwee thinkwee released this 25 Jul 14:20
· 79 commits to main since this release

HiMe v1.2.0

Since v1.1.0 (e7067cb) — 25 commits, 113 files changed.

This release is dominated by a full-project audit (#36) that swept the backend,
frontend, iOS app and deployment config. Several of the findings were real
security holes or silent data loss, so upgrading is recommended.

Security

  • Feishu webhook accepted tokenless event POSTs, allowing forged user
    messages. Added the missing encrypted-event (AES-256-CBC) support, signature
    freshness checks, event_id dedup, and a reconnect loop with link-loss
    detection.
  • Feishu card actions bypassed the inbound allowlist. The chat_id came
    from the attacker-controllable callback payload, so a crafted action could
    exfiltrate a user's health-data SQL results to an arbitrary chat.
  • WebSocket routes were unauthenticated. BearerAuthMiddleware subclasses
    Starlette's BaseHTTPMiddleware, which short-circuits every non-http ASGI
    scope, so WS handshakes never hit it. Routes now validate the bearer token
    themselves.
  • The API token was baked into the public JS bundle — .dockerignore did
    not match frontend/.env.local. The frontend now resolves the token at
    runtime, which also unbreaks the dashboard for Docker installs with auth on.
  • Port 8765 was unauthenticated in Docker: the watch service had no
    env_file, so API_AUTH_TOKEN never reached it.
  • Path traversal in create_page — the patch branch ran before page_id
    validation.
  • Personalised pages now render in a genuine opaque-origin sandbox (srcdoc +
    validated postMessage bridge), so agent-generated HTML can no longer reach
    the app origin, and no token appears in any URL.
  • The code tool now opens the health database in SQLite read-only URI mode,
    matching the guarantee the sql tool already enforced.
  • iOS stores its token in the Keychain.

Data loss and correctness

  • LLM retries were dead code. openai_provider._call() returned the
    completion coroutine without awaiting it, so the request happened outside
    retry_async — backoff, fallback degradation and context-overflow
    max_tokens reduction never ran. Same class of bug in anthropic_provider.
  • iOS background uploads deleted unsent records. Success was inferred from
    error == nil without checking HTTP status, popping the pending queue on
    401/403/400; a foreground WS flush could also double-pop against an in-flight
    HTTP task.
  • Sub-analysis loops appended assistant messages carrying tool_calls they then
    skipped, leaving orphan tool_use ids that 400'd on the next turn.
  • watch_db_reader read a naive local clock as UTC, so east of UTC the query
    window landed in the future and returned nothing.
  • Decibels, METs and the 6-minute walk test were aggregated with SUM.
  • Timezone handling fixed in page_helpers, data_store_reader, and frontend
    date parsing/formatting.
  • hime.sh stop used a bare pkill -f uvicorn|vite|multiprocessing, killing
    unrelated processes on the same machine.
  • Telegram: allowed_updates encoding, HTML truncation on tag boundaries, and
    offset/cursor persistence so restarts stop replaying messages.
  • Large tracebacks in a failed code run could blow the context window — the
    result truncator now caps the error field, not just output.
  • Reports now state honestly when data is missing for the requested period
    instead of inventing or recycling numbers from a previous report.

Fixes and improvements

  • Agent-sent charts now persist and replay on reload. They were delivered
    only as a live chat_image WS event, so they vanished if the app was offline
    and never came back. Images are copied to durable per-user storage (90-day
    reap) and baked into the persisted transcript as a markdown image line.
  • iOS: the dashboard's segmented control overflowed its page bounds at
    accessibility text sizes, getting clipped and colliding with the page dots.
  • iOS: the onboarding survey could be submitted twice — flushPendingSurvey()
    is called from four places with no single-flight guard.
  • Frontend: the memory / knowledge base page broke on tables with no rows.
  • Blocking I/O moved off the event loop, rate-limit buckets split per endpoint,
    WS disconnect detection in the streaming service, plus several resource leaks
    and retain cycles.
  • Widget unit conversions, and nginx / entrypoint / CI fixes.

Build and dependencies

  • pyproject.toml now reads its dependencies from requirements.txt.
    Dependabot's pip updater is registered on /backend only, so the two lists
    desynced on every dependency PR.
  • Bumps: react-router-dom 6.30 → 7.14, anthropic ≥ 0.97, pyyaml ≥ 6.0.3,
    qrcode ≥ 8.2, actions/checkout 4 → 7. Dropped the unused pyarrow dependency.
  • iOS version numbers are now declared once in a shared xcconfig and inherited
    by all four targets, instead of being duplicated across eight build
    configurations where they had already drifted apart.

Testing

Backend tests 348 → 441, frontend 61 → 75, ESLint 25 errors → 0.

Also

The HiMe paper is now on arXiv — see the badge in the README.