Health stores sensitive personal health and wellbeing information.
Security and privacy are therefore architectural requirements, not optional features.
This document defines mandatory security rules for:
- backend implementation
- OCS API
- database access
- frontend behavior
- logging
- notifications
- external clients
- AI-assisted development
Health follows a simple rule:
A user may access only their own Health data.
This rule applies to:
- entries
- notes
- goals
- reminders
- configuration
- summaries
- statistics
There is no Health-specific mechanism for accessing another user's personal Health data.
Health provides application-level privacy.
Through the Health application and Health API:
- users only access their own data
- administrators cannot browse other users' Health data
- managers cannot browse employee Health data
- other applications do not automatically receive Health data
- there is no organization-wide health dashboard
However, Health does not implement additional application-level encryption in the MVP.
Therefore:
A Nextcloud server administrator or database administrator with direct infrastructure or database access may technically be able to access stored Health data.
Health must not claim that data is cryptographically hidden from the Nextcloud server operator.
Health uses only official Nextcloud authentication mechanisms.
Health must not implement:
- passwords
- custom login tokens
- custom sessions
- custom OAuth-like mechanisms
External clients use authentication mechanisms supported by Nextcloud.
Health must never store:
- Nextcloud passwords
- app passwords
- authentication tokens
- OIDC tokens
in Health application tables.
All Health data APIs use OCSController.
Normal Health users require:
#[NoAdminRequired]because Health is intended for regular Nextcloud users.
Health API endpoints must not use:
#[PublicPage]Health API endpoints must not use:
#[NoTwoFactorRequired]Health API endpoints must not use:
#[NoCSRFRequired]unless a future, explicitly reviewed use case proves this absolutely necessary.
Health API endpoints must not use:
#[CORS]in the MVP.
Do not disable Nextcloud CSRF protection for Health data endpoints.
The Nextcloud web frontend uses the standard Nextcloud request mechanisms.
External OCS clients must use the appropriate OCS request headers and authentication.
Do not weaken CSRF protection merely to make an external client easier to implement.
Health deliberately does not allow arbitrary websites to access Health API data directly through browser JavaScript.
Mobile applications, desktop clients and command-line tools do not require unrestricted browser CORS.
Do not enable wildcard origins such as:
Access-Control-Allow-Origin: *
Do not enable credentialed cross-origin browser access without a dedicated security review.
The controller used only to render the main Health application page may follow official Nextcloud template-controller conventions.
A template-only route may use security attributes appropriate for rendering the application shell.
This exception applies only to the HTML application entry point.
It must never be used as justification for weakening security on Health data APIs.
The authenticated Nextcloud session is the authoritative source of user identity.
Clients must never select the owner of a Health resource.
Never accept authorization based on request fields such as:
{
"userId": "alice"
}A client-supplied user ID must never grant access to data.
Every query involving personal Health data must include the authenticated user's ID.
Unsafe:
findById($entryId)
Preferred:
findForUserById($userId, $entryId)
Unsafe:
findEntries()
Preferred:
findEntriesForUser($userId, ...)
Unsafe:
deleteById($entryId)
Preferred:
deleteForUser($userId, $entryId)
Ownership must be enforced in backend persistence/service logic.
It must never depend only on frontend behavior.
Numeric resource IDs are identifiers, not authorization credentials.
Knowing:
entryId = 4711
must not allow another user to access Entry 4711.
This applies to:
- GET
- PUT
- DELETE
operations.
The same principle applies to:
- goals
- reminders
- configuration resources
If User B requests a resource belonging to User A, the response must not expose:
- the resource content
- its metric
- whether the resource exists for User A
- timestamps
- notes
- ownership information
Errors should reveal only what the requesting user is allowed to know.
A Nextcloud administrator may use Health for their own personal journal.
Administrator status does not grant access to Health data belonging to other users through Health.
There must be no API endpoint such as:
GET /admin/users/{userId}/health
There must be no administrator Health-data browser.
All input is validated server-side.
The frontend may validate values for usability, but frontend validation is not trusted for security or correctness.
Examples:
stress = 4
is valid.
stress = 999
is invalid.
hydration = large_glass
is valid.
hydration = arbitrary_client_value
is invalid.
All enums, ranges, timestamps, IDs and lengths must be validated.
Batch entry creation must be bounded.
Clients must not be allowed to submit arbitrarily large batches.
The maximum batch size is 20 atomic entries and must be enforced by the API.
Each individual entry in a batch must be validated.
A failed validation causes the complete transactional batch to fail.
Use official Nextcloud database APIs.
Use:
- entities
- mappers
- QueryBuilder
- parameterized queries
Do not concatenate untrusted values into SQL.
Do not construct SQL fragments directly from:
- metric keys
- cursor values
- sort parameters
- request parameters
without explicit validation.
Client-controlled values used to influence queries must be mapped to known server-side values.
Unsafe concept:
ORDER BY $requestValuePreferred concept:
requested "recordedAt"
→ predefined recorded_at column
Unknown sort or filter identifiers must be rejected.
Pagination cursors are opaque client values.
Clients must not control arbitrary SQL through a cursor.
Cursor contents must be:
- validated
- safely decoded
- bounded
- treated as untrusted input
Invalid cursors return a safe client error.
A cursor must never contain sensitive Health values.
Journal notes are plain text in the MVP.
Journal notes have a maximum length of 1000 characters.
Notes do not support arbitrary HTML.
Example:
Project deadline today
must be treated as text.
Do not render note content through unsafe HTML mechanisms.
Do not use unescaped user content with:
v-html
innerHTML
unless a future feature introduces a reviewed sanitization mechanism.
All user-generated content must be rendered safely.
This includes:
- notes
- future custom labels
- imported content
- API-provided strings
Prefer Vue's normal escaped text rendering.
Do not manually build HTML using user-controlled strings.
The MVP must not load Health-related content from external services.
Do not load:
- analytics scripts
- advertising scripts
- remote fonts
- remote JavaScript
- remote tracking pixels
- third-party wellness content
from external origins.
Frontend dependencies must be bundled with the application.
Chart.js must be bundled locally and must not be loaded from a CDN.
Health does not send personal Health usage or measurements to third parties.
Do not implement external telemetry containing:
- metric values
- journal entries
- notes
- stress levels
- mood
- sleep
- weight
- hydration
- movement
- goal completion
Any future telemetry feature requires explicit product and privacy review.
Health uses the official Nextcloud PSR-3 logging infrastructure.
Logger instances should normally be provided through dependency injection.
Logs are for operational and technical information.
Logs are not a secondary Health-data store.
Never log:
- metric values
- journal notes
- sleep values
- weight values
- mood values
- stress values
- hydration events
- movement values
- check-in contents
- check-out contents
- API request bodies
- API response bodies containing Health data
Bad:
User steffen recorded stress level 5
Bad:
POST /entries payload: {...}
Better:
Failed to create Health entry
where necessary accompanied only by safe operational identifiers.
Never log:
- passwords
- app passwords
- authorization headers
- cookies
- bearer tokens
- OIDC tokens
- session tokens
Do not add these values to exception context.
Where operationally necessary, logs may contain non-health technical information such as:
- internal resource ID
- controller operation
- exception class
- processing duration
User IDs should only be logged when genuinely needed for troubleshooting.
Avoid combining an identifiable user with personal health information in logs.
Exceptions may be logged through the standard Nextcloud logger.
Do not attach complete:
- Entry objects
- request objects
- request bodies
- Health response payloads
to logging context.
Exception messages returned to API clients must not expose internal implementation details.
API errors must not expose:
- SQL
- database table names
- filesystem paths
- stack traces
- PHP class internals
- secrets
- another user's resource information
Development environments may expose additional debugging information through Nextcloud configuration, but production API responses remain safe.
Health reminders use official Nextcloud Notifications.
Notifications must be privacy-preserving.
A Health notification may say:
Time for your Health check-in
or:
You scheduled a Health reminder
A Health notification must not say:
Your stress level was 5 yesterday
or:
You only drank 3 glasses today
Goal reminder notifications may identify the associated metric or topic using its localized label and associated icon. This deliberate exception makes reminders useful, for example by showing a Water reminder with the Water icon.
Goal reminder notifications must not include measurement values, goal target values, current progress or completion, remaining amounts, percentages, journal text or notes, detailed history, or medical interpretation. Health values must not be included in notification titles or messages.
This is important because notifications may be visible outside the Health application.
Background jobs responsible for reminders should process only the information required to deliver the reminder.
They should not unnecessarily load journal history or statistics.
Reminder jobs must not produce Health-data logs.
Statistics are calculated only for the authenticated user.
There is no cross-user statistics service.
Do not implement a generic service accepting arbitrary:
userId
for statistics from public API calls.
The authenticated context supplies the user identity.
Nextcloud Capabilities may expose:
- Health installation status
- API versions
- supported modules
- supported features
Capabilities must not expose:
- enabled modules of another user
- entries
- goals
- reminders
- journal activity
- whether an individual user uses Health
Capabilities describe application functionality, not personal Health state.
If Health later provides an export feature, export requests must:
- require authentication
- export only the requesting user's data
- use a documented format
- avoid temporary public URLs
- avoid storing exports in web-accessible temporary directories
Export security must be reviewed before implementation.
Export is not required for the first implementation milestone.
Users may delete individual entries through the documented API.
Deleting a Health resource must always verify ownership.
A future "Delete all my Health data" feature should remove all personal Health records belonging to the authenticated user.
Health must not silently delete historical entries merely because a module is disabled.
Health should not intentionally retain orphaned personal data after the corresponding Nextcloud account has been permanently removed.
Account lifecycle handling must use an official Nextcloud user lifecycle mechanism compatible with the minimum supported Nextcloud version.
Do not implement database cleanup by reaching into private Nextcloud internals.
Health data is private by default.
The MVP does not support:
- public sharing
- user-to-user sharing
- manager sharing
- doctor sharing
- link sharing
- group sharing
Do not reuse Nextcloud file-sharing concepts automatically for Health records.
Sharing Health data is a future product decision and requires a dedicated security model.
The Health data API has no anonymous public endpoints.
Do not use:
#[PublicPage]for Health API data.
Public pages may only be introduced through a dedicated future security review.
The MVP does not implement application-specific encryption.
Do not create:
- custom encryption algorithms
- custom password-derived encryption
- home-grown key storage
- reversible obfuscation presented as encryption
If application-specific encryption is introduced later, it requires a separate architecture and threat-model review.
Security-sensitive functionality should use official Nextcloud APIs where available.
Do not introduce external packages for functionality already safely provided by Nextcloud.
New dependencies must have a clear purpose.
The MVP intentionally keeps external frontend dependencies minimal.
General UI:
@nextcloud/vue
Charts:
Chart.js
Do not add another UI or charting framework without an architectural decision.
API tests must explicitly verify user isolation.
At least two users are required:
health-user-a
health-user-b
Required scenarios include:
- User A creates an entry.
- User A can read it.
- User B cannot read it.
- User B cannot update it.
- User B cannot delete it.
- User A can update it.
- User A can delete it.
Equivalent ownership tests are required for:
- goals
- reminders
- user configuration where applicable
API tests must verify that unauthenticated access to personal Health endpoints fails.
Tests must not disable authentication merely to simplify test setup.
API tests must cover invalid values including:
- unknown metric
- value outside allowed range
- invalid option
- invalid context
- invalid timestamp
- malformed cursor
- excessive page size
- excessive batch size
- oversized note
Security-related requirements must be represented correctly in generated API documentation.
Do not accidentally document authenticated endpoints as public.
Public API documentation must not contain real Health examples copied from development databases.
Use synthetic example values only.
Development environments must use synthetic Health information.
Do not copy production Health databases into local AI-assisted development environments.
Do not provide real employee or patient Health information to coding agents.
AI coding agents must treat security requirements as hard constraints.
Agents must not:
- weaken authentication
- add
#[PublicPage] - add
#[NoCSRFRequired]to data APIs - add
#[CORS] - expose other users' data
- remove ownership checks
- log Health values
- log API payloads
- disable security tests
- invent custom authentication
- invent custom cryptography
to make implementation easier.
If a feature appears to require weakening one of these controls, implementation must stop and the architecture must be reviewed first.
A Health API feature is complete only when:
- authentication is enforced
- regular users can access their own functionality
- user ownership is enforced server-side
- cross-user access is tested
- input is validated server-side
- no sensitive Health data is logged
- CSRF protections remain intact
- unrestricted CORS is not enabled
- API errors do not leak internal information
- OpenAPI documentation correctly represents the endpoint
- security-critical API tests pass
When implementation convenience conflicts with security:
Security wins.
When feature richness conflicts with privacy:
Privacy wins.
When an AI coding agent is uncertain whether a Nextcloud security mechanism can safely be bypassed:
Do not bypass it.
Daily notes are sensitive Health data. Reads and writes are scoped to the authenticated user and date; note contents are never logged. The API and frontend handle only plain text, never stored or rendered HTML. Native Unified Search is opt-in per user (searchDailyNotes defaults to false); when it is disabled the provider must not query notes, and when enabled every query remains scoped to the requesting user.
Goals, revisions, derived progress, and reminder state are personal Health data. Every goal lookup and source query is scoped to the authenticated owner; no client user ID is accepted, and reminder state has no public API. The background job evaluates each stored goal using that goal's owner and configuration only. Notifications are sent through the authenticated owner's Nextcloud notifications account and may identify the associated metric or topic using its localized label and icon. They must not include measurement values, goal target values, current progress or completion, remaining amounts, percentages, notes, journal text, dates, or detailed Health history, and they link only to the authenticated Goals route. No Health request, response, progress result, or reminder evaluation is logged.
Only the PWA shell, manifest, service worker, and referenced static assets are public. Health OCS endpoints remain authenticated, owner-scoped, CSRF-protected, and without unrestricted CORS. Login Flow v2 creates a revocable app password; the normal account password is never stored. The app password and non-value metric configuration are held in the PWA's origin-scoped IndexedDB account record. Cache Storage contains no authenticated Health API response.
Pending writes are stored in an origin-scoped IndexedDB outbox with only the metric, required value/unit, timestamp or local date, state, and random operation ID. Values are never placed in URLs, logs, analytics, manifests, notifications, or Cache Storage. Confirmed operations are removed. Authentication and transient failures retain them for explicit retry. Disconnect warns that pending writes will be removed, attempts app-password revocation, and clears both account and outbox data. Entry and measurement operation IDs are owner-scoped and idempotent; daily-value PUTs are naturally idempotent.
The PWA technical diagnostics dialog is generated only from a fixed allow-list: app/build version, server origin, online state, authentication state without credentials, synchronization state, last successful synchronization time, generic outbox count, and service-worker state. It never reads or serializes Health values, metric configuration values, goals, Journal content, app passwords, tokens, authorization headers, cookies, or encryption material. Connection notices likewise contain only generic technical state and count, never pending entry details.