Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
155 commits
Select commit Hold shift + click to select a range
d68a0c5
test(notification): require data-rights contributor composition
seonghobae Aug 12, 2026
a558ab8
feat(notification): add service-owned data-rights contributor
seonghobae Aug 12, 2026
8bd783b
test(notification): require codepoint-stable export digest
seonghobae Aug 12, 2026
7ff1067
fix(notification): make export evidence locale-independent
seonghobae Aug 12, 2026
11a720d
ci(notification): repair data-rights review findings
seonghobae Aug 12, 2026
299f0d2
ci(notification): prepare live review repair
seonghobae Aug 12, 2026
5f29bae
ci(notification): execute verified review repair
seonghobae Aug 12, 2026
53c596a
ci(notification): repair live comparator phase
seonghobae Aug 12, 2026
cb4e7d4
ci(notification): limit repair to live findings
seonghobae Aug 12, 2026
c1ac9a4
ci(notification): make marker assertion formatting-neutral
seonghobae Aug 12, 2026
a07c315
ci(notification): fix repair phase quoting
seonghobae Aug 12, 2026
4230409
ci(notification): apply formatting-neutral test contract
seonghobae Aug 12, 2026
09a7b36
ci(notification): make owner-marker assertion formatting-neutral
seonghobae Aug 12, 2026
9b1969c
ci(notification): separate focused tests from coverage gate
seonghobae Aug 12, 2026
121279c
ci(notification): preserve covered export validator
seonghobae Aug 12, 2026
1094334
ci(notification): remove unreachable comparator branch
seonghobae Aug 12, 2026
da8f4ad
ci(notification): make comparator repair shape-tolerant
seonghobae Aug 12, 2026
2a021b4
ci(notification): quote comparator bootstrap safely
seonghobae Aug 12, 2026
042dbaf
ci(notification): finalize verified data-rights repair
seonghobae Aug 12, 2026
7b11b72
chore(notification): remove temporary repair workflow
seonghobae Aug 12, 2026
fefe859
chore(notification): remove temporary finalize workflow
seonghobae Aug 12, 2026
989d802
chore(notification): remove temporary executor workflow
seonghobae Aug 12, 2026
d58628a
chore(notification): remove temporary kick workflow
seonghobae Aug 12, 2026
ee4b05b
chore(notification): remove temporary patch workflow
seonghobae Aug 12, 2026
fc8adc1
test(notification): prove data-rights PostgreSQL behavior
seonghobae Aug 12, 2026
ef4aade
test(notification): require scoped erasure authorization
seonghobae Aug 12, 2026
35e0366
fix(notification): scope erasure authorization to transaction
seonghobae Aug 12, 2026
32f8136
test(notification): remove dynamic role SQL from erasure integration
seonghobae Aug 12, 2026
8507294
test(notification): require exact sanitized failure message
seonghobae Aug 12, 2026
6e86aa9
test(notification): require function-only erasure preflight authority
seonghobae Aug 12, 2026
3d3643c
fix(notification): keep erasure receipts owner-only
seonghobae Aug 12, 2026
842b029
test(notification): require separated migration runtime authority
seonghobae Aug 12, 2026
de6a6fc
fix(notification): separate migration runtime authority
seonghobae Aug 13, 2026
8881b5b
fix(notification): transfer migration ownership explicitly
seonghobae Aug 13, 2026
0e308a8
docs(notification): declare distinct database roles
seonghobae Aug 13, 2026
9bb9037
fix(notification): wire migration database authority
seonghobae Aug 13, 2026
0c0e140
Merge branch 'main' into feat/notification-data-rights-contributor-v2
opencode-agent[bot] Aug 13, 2026
82798a1
docs(notification): document immutable outcome privilege boundary
seonghobae Aug 13, 2026
0c61081
chore(notification): expose formatter diff for CI RCA
seonghobae Aug 13, 2026
edc6d76
style(notification): satisfy migration test formatter
seonghobae Aug 13, 2026
f93c5e3
style(notification): satisfy behavior test formatter
seonghobae Aug 13, 2026
909c511
style(notification): satisfy integration test formatter
seonghobae Aug 13, 2026
80d06e6
style(notification): satisfy contributor formatter
seonghobae Aug 13, 2026
e6309ed
chore(notification): restore immutable formatter gate
seonghobae Aug 13, 2026
8c44023
Merge branch 'main' into feat/notification-data-rights-contributor-v2
opencode-agent[bot] Aug 13, 2026
8908c8f
ci(notification): repair unreachable canonical JSON branch
seonghobae Aug 14, 2026
06190db
chore(notification): remove temporary repair workflow
seonghobae Aug 15, 2026
5ba029d
test(notification): remove unreachable comparator branch
seonghobae Aug 15, 2026
351f095
Merge branch 'main' into feat/notification-data-rights-contributor-v2
opencode-agent[bot] Aug 22, 2026
b11e4c7
test(notification): require complete paginated data-rights export
seonghobae Aug 27, 2026
74d9334
fix(notification): paginate data-rights export
seonghobae Sep 1, 2026
caa340c
test(notification): reproduce erasure write race
seonghobae Sep 1, 2026
d51d57e
fix(notification): fence writes during data erasure
seonghobae Sep 1, 2026
a200ebe
test(notification): require durable erasure write fence
seonghobae Sep 1, 2026
97e9afb
test(notification): keep erasure fence runtime-private
seonghobae Sep 1, 2026
6c0a1a0
fix(notification): keep workspace erasure fence private
seonghobae Sep 1, 2026
3740242
test(notification): reject impossible export cursor instants
seonghobae Sep 1, 2026
7605d90
fix(notification): reject normalized cursor instants
seonghobae Sep 1, 2026
6261e7b
test(notification): require authenticated data-rights transport
seonghobae Sep 1, 2026
85d2af2
fix(notification): authenticate data-rights transport
seonghobae Sep 1, 2026
102b839
test(notification): require private contributor controller
seonghobae Sep 1, 2026
6abb844
fix(notification): expose authenticated contributor controller
seonghobae Sep 1, 2026
ff40fb3
test(notification): require destructive authority replay guard
seonghobae Sep 1, 2026
969ceee
test(notification): reject replayed destructive authority
seonghobae Sep 1, 2026
99f7448
fix(notification): persist destructive authority replay evidence
seonghobae Sep 1, 2026
97e1106
fix(notification): consume destructive signed authority once
seonghobae Sep 1, 2026
518ab2e
fix(notification): compose destructive replay guard
seonghobae Sep 1, 2026
3b30024
fix(notification): enforce destructive replay consumption
seonghobae Sep 1, 2026
9eb7aba
test(notification): require durable authority replay storage
seonghobae Sep 1, 2026
bfd99f2
fix(notification): add destructive replay storage
seonghobae Sep 1, 2026
31878b8
fix(notification): grant replay-store least privilege
seonghobae Sep 1, 2026
a03ddea
test(notification): allow safe erase retry after failure
seonghobae Sep 1, 2026
7d8fab1
fix(notification): release failed erase authority claims
seonghobae Sep 1, 2026
b1eed39
fix(notification): preserve authorized erase retries
seonghobae Sep 1, 2026
839786b
test(notification): model replay claim release contract
seonghobae Sep 1, 2026
4b0b3f4
test(notification): require bootstrapped contributor endpoint
seonghobae Sep 1, 2026
19b48fb
fix(notification): bootstrap private contributor endpoint
seonghobae Sep 1, 2026
23aa891
fix(notification): add deployable Nest runtime dependencies
seonghobae Sep 1, 2026
ade33fe
fix(notification): keep runtime dependencies lock-consistent
seonghobae Sep 1, 2026
fb39834
fix(notification): use lock-free private HTTP server
seonghobae Sep 1, 2026
bd4f019
test(notification): cover framework-free private listener
seonghobae Sep 1, 2026
841072f
test(notification): require deployable server entrypoint
seonghobae Sep 1, 2026
461db8d
fix(notification): add deployable server entrypoint
seonghobae Sep 1, 2026
a2c5d72
fix(notification): keep server entrypoint testable
seonghobae Sep 1, 2026
70fa86f
fix(notification): start emitted private server
seonghobae Sep 1, 2026
e739c0b
test(notification): require private loopback default
seonghobae Sep 1, 2026
6ce250a
fix(notification): default private listener to loopback
seonghobae Sep 1, 2026
e6a49c2
test(notification): require replay-store preflight authority
seonghobae Sep 1, 2026
fcf8f3b
test(notification): align preflight privilege fixtures
seonghobae Sep 1, 2026
3d7a62d
fix(notification): preflight replay-store authority
seonghobae Sep 1, 2026
28475cf
style(notification): format preflight regression
seonghobae Sep 1, 2026
a66a11a
test(notification): require data-rights secret at startup
seonghobae Sep 1, 2026
b4c4972
ci: repair PR 198 startup secret gate
seonghobae Sep 1, 2026
053fa89
test(notification): remove secret-like fixture and format regression
seonghobae Sep 1, 2026
da7dd12
ci: remove invalid one-shot PR 198 repair workflow
seonghobae Sep 1, 2026
a24c344
fix(notification): validate data-rights secret before runtime
seonghobae Sep 1, 2026
440cdd7
docs(notification): expose data-rights context secret
seonghobae Sep 1, 2026
de20717
docs: record PR 198 failed-check RCA
seonghobae Sep 1, 2026
e4c280d
test(notification): bind contributor auth to composed secret
seonghobae Sep 1, 2026
ba60f34
fix(notification): inject validated data-rights secret
seonghobae Sep 1, 2026
5cb50ba
fix(notification): preserve composed auth secret at runtime
seonghobae Sep 1, 2026
aa80ac8
test(notification): cover replay-store privilege fence
seonghobae Sep 1, 2026
f3d7f7c
test(notification): require local migrator provisioning
seonghobae Sep 1, 2026
a53aefa
fix(notification): provision local migration identity
seonghobae Sep 1, 2026
4112287
fix(notification): mount local migrator provisioning
seonghobae Sep 1, 2026
f0c8f48
test(notification): require safe local database roles
seonghobae Sep 1, 2026
f50da13
fix(notification): add idempotent local runtime provisioning
seonghobae Sep 1, 2026
a2023d9
fix(notification): provision local runtime on every Compose start
seonghobae Sep 1, 2026
78f5733
fix(notification): separate local migration and runtime credentials
seonghobae Sep 1, 2026
3013913
fix(notification): remove committed migrator credential
seonghobae Sep 1, 2026
aeddf10
test(contracts): require data-rights export continuation
seonghobae Sep 1, 2026
7728d1b
fix(contracts): expose data-rights export pagination
seonghobae Sep 1, 2026
b623a60
test(notification): require erase verification privileges
seonghobae Sep 1, 2026
6ea5ed9
fix(notification): preflight erase verification access
seonghobae Sep 1, 2026
ba6f682
test(notification): align preflight privilege fixtures
seonghobae Sep 1, 2026
a9f0e32
test(notification): keep preflight fixture type-safe
seonghobae Sep 1, 2026
c2eaf73
test(notification): require configured runtime role provisioning
seonghobae Sep 1, 2026
2bd1e59
fix(notification): provision configured runtime role
seonghobae Sep 1, 2026
26e02c7
fix(notification): pass configured runtime role
seonghobae Sep 1, 2026
e0b978d
test(notification): require schema usage before erasure
seonghobae Sep 1, 2026
a978d5c
fix(notification): require schema usage before erasure
seonghobae Sep 1, 2026
921284e
test(notification): reject runtime role admin collision
seonghobae Sep 1, 2026
cbba249
fix(notification): reject runtime admin role collision
seonghobae Sep 1, 2026
3366db7
test(notification): require null-safe schema preflight
seonghobae Sep 1, 2026
f83608a
fix(notification): make schema preflight null-safe
seonghobae Sep 1, 2026
5b3e5b6
test(notification): complete preflight privilege fixture
seonghobae Sep 1, 2026
ae61bbf
fix(ci): supply Compose test credentials
seonghobae Sep 1, 2026
adf1e42
test(notification): complete schema privilege fixtures
seonghobae Sep 1, 2026
2acfedf
test(ci): isolate one-shot Compose provisioning
seonghobae Sep 1, 2026
9c3b9d7
test(ci): reproduce one-shot compose wait failure
seonghobae Sep 1, 2026
434ff90
fix(ci): isolate one-shot compose provisioning
seonghobae Sep 1, 2026
82058da
fix(ci): run Notification provisioning as one-shot Compose task
seonghobae Sep 1, 2026
1b0ca91
test(ci): align Compose regression with synchronous provisioner
seonghobae Sep 1, 2026
9a010c6
test(notification): reject implicit migration ownership transfer
seonghobae Sep 2, 2026
efb133e
fix(notification): require stable migration ownership
seonghobae Sep 2, 2026
a22a1b2
fix(notification): keep established migration owner
seonghobae Sep 2, 2026
2a79b3a
docs(notification): define migration owner and data-rights boundary
seonghobae Sep 2, 2026
f0dc41d
docs(notification): record data-rights authority boundary
seonghobae Sep 2, 2026
2758902
docs(notification): record data-rights contributor
seonghobae Sep 2, 2026
b7bec14
docs(notification): record data-rights implementation plan
seonghobae Sep 2, 2026
9eb2a61
test(compose): pin legacy local password upgrade contract
seonghobae Sep 2, 2026
0eb0241
fix(compose): preserve legacy local postgres upgrade
seonghobae Sep 2, 2026
01340b9
docs(local): document existing postgres volume upgrade
seonghobae Sep 2, 2026
e54c875
docs(notification): record local compose credential compatibility
seonghobae Sep 2, 2026
06f90d8
test(compose): require explicit legacy password rotation
seonghobae Sep 2, 2026
e633b2f
fix(compose): rotate legacy local administrator credential
seonghobae Sep 2, 2026
91fc901
test(compose): reject password-bearing rotation arguments
seonghobae Sep 2, 2026
0163858
fix(compose): keep rotation secrets out of process arguments
seonghobae Sep 2, 2026
2bda705
test(notification): remove needless dynamic replay-role SQL
seonghobae Sep 2, 2026
71a55ed
test(compose): bind legacy rotation to effective database and TCP auth
seonghobae Sep 2, 2026
f0b00e1
fix(compose): bind legacy rotation to rendered database and TCP auth
seonghobae Sep 2, 2026
bff90cc
fix(ci): preserve current queue filters in notification contributor
seonghobae Sep 3, 2026
6055cc9
chore(notification): adopt current protected main queue filters
seonghobae Sep 3, 2026
973e7c4
fix(ci): keep Notification coverage for documentation changes
seonghobae Sep 3, 2026
387dc14
chore(notification): adopt current protected main workflow safety
seonghobae Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ REVIEW_SERVICE_PORT=4104
AI_SERVICE_PORT=4105
CALENDAR_SERVICE_PORT=4106
INTEGRATION_SERVICE_PORT=4107
DATABASE_URL=postgresql://lifeos:lifeos@postgres:5432/lifeos
AI_DATABASE_URL=postgresql://lifeos:lifeos@postgres:5432/lifeos
AI_TEST_DATABASE_URL=postgresql://lifeos:lifeos@postgres:5432/lifeos_test
POSTGRES_PASSWORD=replace-with-local-postgres-password
NOTIFICATION_RUNTIME_DATABASE_PASSWORD=replace-with-distinct-local-runtime-password
DATABASE_URL=postgresql://lifeos:replace-with-local-postgres-password@postgres:5432/lifeos
AI_DATABASE_URL=postgresql://lifeos:replace-with-local-postgres-password@postgres:5432/lifeos
AI_TEST_DATABASE_URL=postgresql://lifeos:replace-with-local-postgres-password@postgres:5432/lifeos_test
AI_DATABASE_POOL_MAX=10
AI_DATABASE_CONNECT_TIMEOUT_MS=5000
AI_DATABASE_IDLE_TIMEOUT_MS=30000
AI_MODEL_REQUEST_TIMEOUT_MS=10000
AI_PROPOSAL_MODEL=rule-based
CONTEXTUAL_ORCHESTRATOR_TOKEN=
CONTEXTUAL_ORCHESTRATOR_URL=
NOTIFICATION_DATABASE_URL=postgresql://lifeos:lifeos@postgres:5432/lifeos
NOTIFICATION_MIGRATION_DATABASE_URL=postgresql://lifeos:replace-with-local-postgres-password@postgres:5432/lifeos
NOTIFICATION_DATABASE_RUNTIME_ROLE=lifeos_notification
NOTIFICATION_DATABASE_URL=postgresql://lifeos_notification:replace-with-distinct-local-runtime-password@postgres:5432/lifeos
NOTIFICATION_DATA_RIGHTS_CONTEXT_SECRET=replace-with-distinct-at-least-32-random-bytes
NOTIFICATION_DATABASE_POOL_MAX=10
NOTIFICATION_DATABASE_CONNECT_TIMEOUT_MS=5000
NOTIFICATION_DATABASE_IDLE_TIMEOUT_MS=30000
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
compose_runtime:
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
POSTGRES_PASSWORD: ci-${{ github.run_id }}-${{ github.run_attempt }}
NOTIFICATION_RUNTIME_DATABASE_PASSWORD: notification-ci-${{ github.run_id }}-${{ github.run_attempt }}
steps:
- name: Checkout exact contributor head
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
Expand All @@ -33,14 +36,15 @@ jobs:
trap - EXIT
if [ "$status" -ne 0 ]; then
docker compose ps --all || true
docker compose logs --no-color --timestamps --tail 200 postgres nats || true
docker compose logs --no-color --timestamps --tail 200 postgres nats notification-db-provision || true
fi
docker compose down --volumes --remove-orphans || true
exit "$status"
}
trap cleanup EXIT

docker compose up --detach --wait --wait-timeout 90
docker compose up --detach --wait --wait-timeout 90 postgres nats
docker compose run --rm --no-deps notification-db-provision
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
docker compose exec --no-TTY postgres psql -U lifeos -d lifeos -v ON_ERROR_STOP=1 -tAc 'SELECT 1' |
grep -Fx 1
curl --fail --silent --show-error --max-time 5 \
Expand Down Expand Up @@ -103,6 +107,8 @@ jobs:
HABIT_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
NOTIFICATION_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
PRIVACY_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
POSTGRES_PASSWORD: ci-${{ github.run_id }}-${{ github.run_attempt }}
NOTIFICATION_RUNTIME_DATABASE_PASSWORD: notification-ci-${{ github.run_id }}-${{ github.run_attempt }}
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
services:
postgres:
image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ jobs:
HABIT_DATABASE_URL: ${{ secrets.HABIT_DATABASE_URL }}
AI_DATABASE_URL: ${{ secrets.AI_DATABASE_URL }}
REVIEW_DATABASE_URL: ${{ secrets.REVIEW_DATABASE_URL }}
NOTIFICATION_MIGRATION_DATABASE_URL: ${{ secrets.NOTIFICATION_MIGRATION_DATABASE_URL }}
NOTIFICATION_DATABASE_RUNTIME_ROLE: ${{ vars.NOTIFICATION_DATABASE_RUNTIME_ROLE }}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
shell: bash
run: |
set -Eeuo pipefail
Expand Down
29 changes: 29 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ flowchart LR
P --> PDB
H --> HDB
A --> ADB
NO[Notification service] --> NDB
```

### Required invariants
Expand Down Expand Up @@ -129,3 +130,31 @@ The pinned OpenCode configuration disables project-local overrides, explicitly r
8. `CHANGELOG.md` — user-visible unreleased and released changes.

A behavior or boundary change is incomplete until the relevant level is updated and executable tests prove the claim.

## 7. Notification data-rights authority boundary

Notification owns its reminder occurrences, immutable outcome history, in-app inbox messages, and the data-rights evidence needed to erase those records. A data-rights orchestrator may call the private versioned contributor contract, but it does not receive direct SQL authority over `notification_service` tables.

```mermaid
sequenceDiagram
participant O as Data-rights orchestrator
participant H as Notification private HTTP boundary
participant C as Notification contributor
participant DB as Notification PostgreSQL

O->>H: Signed method/path/workspace/user/request context
H->>H: Verify bounded authority and replay evidence
H->>C: Normalized contributor request
C->>DB: Tenant-scoped export/preflight/erase/verify query
DB-->>C: Bounded evidence or owner-controlled erasure receipt
C-->>H: Credential-free versioned response
H-->>O: Export page / blocker / erasure / verification evidence
```

The migration authority and Notification runtime identity are deliberately separate. The connection behind `NOTIFICATION_MIGRATION_DATABASE_URL` remains the established owner of the Notification schema and existing objects; later migrations fail closed if that ownership no longer matches. The runtime role owns no schema or erasure-control table and receives only reviewed table privileges plus the explicit erasure function/replay-store permissions needed by the contributor.

Normal Notification inserts and updates take shared workspace advisory locks. Data-rights erasure takes the corresponding exclusive transaction lock, persists a terminal workspace fence before deletion, and uses backend+transaction+workspace-scoped authorization to permit the otherwise append-only outcome deletion. A write racing the erasure therefore either completes before the exclusive lock or observes the terminal fence and fails; it cannot survive after a committed erase.

Export pagination is deterministic and bounded, but its current cursor is a live keyset position rather than a transactionally frozen snapshot. No documentation or API may claim snapshot-consistent multi-page portability until a durable export-session or equivalent versioned snapshot contract exists with concurrency tests.

The repository contains a production-composable Notification server/runtime and Compose path. The current Kubernetes production reference still deploys only the web and gateway workloads; therefore this contributor is not evidence that Notification is deployed in the production reference. A release claiming end-to-end Notification data-rights support must first add and verify the corresponding workload, secret/configuration, network-policy, migration, rollout, and recovery path.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to LifeOS are documented in this file.

### Added

- A Notification-owned `life-os.data-rights-contributor.v1` boundary for deterministic tenant export pages, destructive-erasure preflight, atomic workspace erasure, exact replay, and post-erasure verification without exporting claim or raw idempotency material.
- Durable PostgreSQL plugin-installation authority with opaque UUIDv4 installation/workspace/installer identity, exact manifest digests, normalized explicit grants, bounded conflict replay, and atomic revocation evidence in the service-owned `plugin_integration` schema.
- An authenticated calendar-connection disconnect application and optional hosted HTTP composition boundary that derives workspace and requesting-user authority only from the signed `life-os.calendar-user.v1` context and returns credential-free local revocation evidence.
- A durable PostgreSQL data-rights request ledger with workspace-scoped idempotency, immutable request and terminal receipt digests, one-way completion state, and real integration evidence that erasure receipts survive removal of the source workspace and user.
Expand All @@ -31,6 +32,7 @@ All notable changes to LifeOS are documented in this file.

### Fixed

- Notification forward migrations now reject a changed migration owner before later DDL executes instead of attempting an unsupported implicit `OWNER TO CURRENT_USER` handoff against objects owned by the established migration authority.
- The public Gateway Today endpoint now fails explicitly with bounded `today_composition_unavailable` problem details instead of returning fabricated successful composition data while authenticated Planning/Habit integration is absent; issue #163 remains open for the real composition path.
- Data-rights request-ID and idempotency collisions now resolve through stable credential-free domain conflicts instead of exposing raw PostgreSQL uniqueness errors, including ambiguous dual-collision evidence.
- The OpenCode development loop now prevents project settings from overriding its pinned offline NVIDIA model, records catalog failures accurately, parses the accepted candidate's exact Compose file outside the model account, and requires digest-pinned PostgreSQL queries plus NATS JetStream probes in pull-request CI.
Expand All @@ -43,6 +45,7 @@ All notable changes to LifeOS are documented in this file.

### Security

- Notification migration credentials remain the established schema owner while the service runtime uses a separate least-privilege PostgreSQL role; owner-only erasure tables stay unavailable to the runtime and destructive deletion is reachable only through the reviewed function/replay contract.
- Habit create/list/occurrence/completion routes now reject a bare client-selected `x-workspace-id` authority and require the short-lived signed `life-os.workspace.v1` gateway context before domain access.
- Plugin installation lookup, conflict replay, and revocation now carry authenticated workspace and installing-user authority through the PostgreSQL boundary; the durable record contains no plaintext plugin secret, token, credential, or password material.
- Calendar local disconnect never accepts client-selected ownership as authority, never reads provider secret handles, revalidates durable revocation evidence against the signed workspace+user context, and maps absent or differently owned connections to the same public not-found result.
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ docker compose up -d
pnpm dev
```

`POSTGRES_PASSWORD` and `NOTIFICATION_RUNTIME_DATABASE_PASSWORD` are required local credentials. Keep them distinct and replace the example placeholders before Compose startup. New local volumes never fall back to the historical public `lifeos` administrator password.

Existing PostgreSQL volumes created before explicit local credential provisioning may still store the historical `lifeos` administrator password. Do not delete those volumes to upgrade and do not restore the old Compose fallback. Supply the current stored password only through `LEGACY_POSTGRES_PASSWORD`, set a new `POSTGRES_PASSWORD`, keep a distinct `NOTIFICATION_RUNTIME_DATABASE_PASSWORD`, and run the bounded rotation path once:

```bash
LEGACY_POSTGRES_PASSWORD='<current legacy password>' \
POSTGRES_PASSWORD='<new local administrator password>' \
NOTIFICATION_RUNTIME_DATABASE_PASSWORD='<distinct runtime password>' \
infra/postgres/provision/upgrade-legacy-local.sh
```

The upgrade script starts the existing volume without changing its stored role, authenticates with the operator-supplied legacy credential, rotates the `lifeos` administrator inside PostgreSQL, verifies the new credential, and then provisions the least-privilege Notification runtime role. After it succeeds, persist the new values in your untracked `.env`; `LEGACY_POSTGRES_PASSWORD` is no longer needed.

Default endpoints:

- Web: `http://localhost:3000`
Expand Down
Loading