Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[![CI](https://img.shields.io/badge/CI-GitHub%20Actions-2088FF?style=flat-square&logo=githubactions&logoColor=white)](#)
[![Java](https://img.shields.io/badge/Java-25-orange?style=flat-square&logo=openjdk)](https://openjdk.org)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-4.0.1-brightgreen?style=flat-square&logo=springboot)](https://spring.io/projects/spring-boot)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-4.1.1-brightgreen?style=flat-square&logo=springboot)](https://spring.io/projects/spring-boot)
[![Architecture](https://img.shields.io/badge/Architecture-Hexagonal%20%7C%20DDD-blueviolet?style=flat-square)](https://alistair.cockburn.us/hexagonal-architecture/)
[![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](LICENSE)

Expand Down Expand Up @@ -64,9 +64,9 @@ It is not a payment-card switch or a full core-banking platform. The scope is in

| Service | Responsibility | Persistence | Current State |
|---|---|---|---|
| `wallet-service` | Account balances, double-entry ledger, holds/captures | Dedicated Oracle database via **jOOQ** (append-only, no ORM overhead) | In development |
| `loan-service` | Loan lifecycle, amortization schedules, repayments | Dedicated Oracle database via JPA/Hibernate | In development |
| `collateral-service` | Collateral locking, price valuation, LTV monitoring, liquidation | Dedicated Oracle database via JPA/Hibernate | In development |
| `wallet-service` | Account balances, double-entry ledger, holds/captures | Dedicated PostgreSQL database with jOOQ Open Source configured; SQL adapter pending | In development |
| `loan-service` | Loan lifecycle, amortization schedules, repayments | Dedicated PostgreSQL schema; JPA repository adapter pending | In development |
| `collateral-service` | Collateral locking, price valuation, LTV monitoring, liquidation | Dedicated PostgreSQL schema; JPA repository adapter pending | In development |
| `api-gateway` | Single entry point; REST from clients, gRPC to internal services | — | Planned |

> `wallet-service` deliberately uses jOOQ instead of JPA: a ledger is append-only, and jOOQ gives explicit control over `INSERT`-only SQL with no risk of an accidental `UPDATE` slipping in through dirty-checking. `loan-service` and `collateral-service` manage mutable, CRUD-shaped state, where JPA is a better fit.
Expand Down Expand Up @@ -109,7 +109,7 @@ Use JDK **25**, Docker/Compose, Python 3 and OpenSSL. The Maven wrapper is inclu
./mvnw -B -ntp clean verify
```

The full build still exposes existing domain/static-analysis defects described in the implementation guide. Do not bypass those checks to claim a passing build.
The committed Identity integration passed full Maven verification with 91 tests and zero failures, errors or skips, including the existing domain regressions and real PostgreSQL tests. Formatting and static-analysis checks also passed. See [the publication repair log](docs/tasks/identity-publication-repair.md) for the exact source revision and evidence.

For the isolated, persistent TLS PostgreSQL/Vault rehearsal, follow [the implementation guide](docs/guides/postgresql-vault-migration.md). It gives the exact preparation, initialization, migration and test commands. Generated credentials, certificates and unseal material live under ignored `.local/payguard` with protected permissions. `.env.example` contains only public settings. Never copy secrets into an image or publish that directory.

Expand Down
22 changes: 13 additions & 9 deletions docs/guides/postgresql-vault-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

## Scope and evidence boundary

This guide records the migration implementation originally developed in `amg-postgresql-vault` and published separately on `amg-postgres-vault`. The checkout already contained substantial owner changes, including the replacement of the retired OTP service by Identity. Those changes were preserved. No commit, push, PR, existing-volume deletion or live-data cutover was performed.
This guide records the migration originally developed in `amg-postgresql-vault`, published in PR #35, and integrated with the owner's Identity replacement on `amg-complete-identity-service`. No existing-volume deletion or live-data cutover was performed.

Available business services are Identity, Wallet, Loan and Collateral. Gateway and Proto have no owned database. Identity contains four package modules in one deployment, so it keeps one owned database. Source inspection found actual Identity JPA challenge/audit adapters, but only repository ports in Loan/Collateral and no Wallet schema, SQL adapter or jOOQ code-generation setup. Configuring their PostgreSQL/Vault infrastructure does not establish persisted financial workflows that do not yet exist.

In the complete implementation checkout, normal runtime/build/service tests select PostgreSQL. Oracle migrations remain historical input, unchanged, in vendor-separated legacy locations. No running Oracle source or approved historical UUID-to-prefixed-ID mapping was supplied; live-data migration remains untested and blocked on those inputs. Absence of a running database was never treated as evidence that data could be discarded.
Normal runtime/build/service tests on the integration branch select PostgreSQL. Oracle migrations remain historical input, unchanged, in vendor-separated legacy locations. No running Oracle source or approved historical UUID-to-prefixed-ID mapping was supplied; live-data migration remains untested and blocked on those inputs. Absence of a running database was never treated as evidence that data could be discarded.

## Publication scope and dependencies

The migration-only branch intentionally excludes the owner's separate Identity domain work in PR #33 (`amg-identity-otp-domain`), unrelated local edits, retirement of the old OTP module, and the root module switch. Those changes are not attributed to this migration commit. Some pre-existing Identity application/port/bootstrap files also remain outside this branch because they were not changed during the migration.
Migration-only PR #35 deliberately excluded the owner's Identity domain PR #33, OTP retirement, root module switch and unchanged application foundations. PRs #33 and #35 were subsequently merged. The integration branch preserves the owner's retirement commit, registers Identity in the reactor and includes its required application, bootstrap, adapter and test files. Five unused future identifier classes and unrelated local files remain outside the publication.

The test/image/readiness results below were executed against the complete local implementation checkout, including those owner foundations. They are not claims that this migration-only commit builds independently from current main. Apply/merge the separately owned Identity integration and retire the old module before treating the reproduction commands as a clean-branch result. The PR records this dependency and the existing PMD findings explicitly; do not resolve them by committing someone else's pending work or disabling checks.
The historical migration rehearsal results below came from the earlier full local checkout. The current integration was separately verified from a clean committed worktree: full Maven verification on `fd29d35` passed 91 tests and every configured quality gate. The first clean run reproduced two domain regressions; bounded production fixes restored stable identity hashing and rejection before challenge creation, with the existing test assertions retained. See [the publication repair log](../tasks/identity-publication-repair.md) for current publication and CI status.

## Versions and decisions

Expand Down Expand Up @@ -120,6 +120,8 @@ python3 scripts/local/verify_applications.py

Skipping tests in the first command builds helper classpaths; it does not claim verification. Run the suites below. Wallet has no schema migration command because no business schema was supplied.

The parent Maven Flyway plugin reads connection settings directly from `FLYWAY_URL`, `FLYWAY_USER`, `FLYWAY_PASSWORD` and `FLYWAY_INIT_SQL` in the invoking process. They are not Maven property references, so ordinary IDE imports and builds do not need migration credentials. An explicitly managed Maven migration must receive a verified TLS URL, a separately issued migration login and `FLYWAY_INIT_SQL` selecting the service's durable owner, for example `SET ROLE identity_owner`. That variable overrides the plugin's default SQL, which refuses database operations when owner-role selection is omitted. The supported local commands above use `migrate.py` and `SchemaMigrator`, which retain their internal `PAYGUARD_MIGRATION_*` variables, derive the owner from the validated service name, apply the post-migration grants and revoke the operator token. A raw Maven invocation does not perform that surrounding workflow. [Flyway Maven environment configuration](https://documentation.red-gate.com/fd/maven-goal-277579365.html).

Preparation creates mode-0600 files under a mode-0700 ignored directory: synthetic random bootstrap passwords, CA/private keys and per-server TLS certificates. Both application-to-database and Vault-to-database URLs use `sslmode=verify-full` with the local CA. The CA/certificates expire after 30 days; this is explicit local rehearsal material, not production certificate lifecycle. TLS remains enabled on persistent Vault/Raft. Testcontainers tests use isolated disposable test passwords/plain local JDBC; they are not the non-disposable design.

`PostgreSqlTls` validates the configured single-host JDBC URL before any connection. It requires an absolute CA path and exactly one `sslmode=verify-full`; duplicate/encoded overrides, URL passwords, userinfo and custom SSL factories/hostname verifiers are refused. Multi-host failover is not implemented by this local topology. Owner default EXECUTE is revoked globally for future functions because a per-schema revoke cannot remove PostgreSQL's global PUBLIC default grant.
Expand Down Expand Up @@ -224,7 +226,7 @@ Before PostgreSQL accepts writes, rollback can redirect to the frozen Oracle sou

| Repository-relative path | Before → resulting behavior | Reason / evidence |
| --- | --- | --- |
| `pom.xml` | Oracle dependency management removed; separate PostgreSQL Flyway plugin configured | Managed compatible versions; Java/Spring unchanged; helper library path exercised, alternate Maven plugin path not yet exercised |
| `pom.xml` | Oracle dependency management removed; PostgreSQL Flyway plugin reads native `FLYWAY_*` variables at execution time, with required owner-role selection | Managed compatible versions; Java/Spring unchanged; parent validation passed; isolated PostgreSQL 17.11 Maven migration applied Loan V1, verified five tables/two sequences owned by `loan_owner`, and rejected omitted owner initialization. The Vault-managed helper remains the complete local migration workflow |
| `payguard-{identity,loan,collateral,wallet}-service/pom.xml` | Oracle JDBC/Flyway/test containers → PostgreSQL and actuator | Actual service persistence approach retained; compilation and real database suites |
| `payguard-{identity,loan,collateral,wallet}-service/src/main/resources/application.yaml` | Oracle/env runtime passwords → PostgreSQL/Vault pool, explicit readiness | No pepper/password fallback; runtime Flyway disabled; separate migration path |
| `payguard-identity-service/src/main/resources/db/migration/postgresql/V1__identity_schema.sql` | New vendor history in final prefixed-ID shape | UUID audit/string identity/timestamp/check constraints |
Expand Down Expand Up @@ -268,9 +270,11 @@ Do not run clean builds concurrently with the Java Vault runner: it reads shared

Baseline full verify failed existing shared import formatting before tests. Baseline Identity diagnostic run executed 53 tests with 4 failures, 25 errors and 8 skips. Required nullable lifecycle fixes made persisted flows testable. An earlier verification executed 76 tests with two domain-contract failures; the current checkout contains owner changes to aggregate identity hashing and pre-creation validation, and those regressions now pass. They are recorded as historical findings, not unresolved defects.

Observed evidence as of this checkpoint:
Current committed integration evidence: `./mvnw --batch-mode --no-transfer-progress verify` on `fd29d35` exited 0 in a clean worktree with **91 tests, zero failures/errors/skips** (Identity 74, other modules 17). Spotless, Checkstyle, PMD, CPD and SpotBugs passed. This includes the 14 PostgreSQL Identity infrastructure cases and existing concurrency/domain/application tests. Native Maven Flyway configuration was also verified on disposable PostgreSQL 17.11: Loan V1 installed, all five tables/two sequences were owned by `loan_owner`, and missing owner initialization was rejected. The Vault-managed helper remains the complete local migration workflow.

Historical migration rehearsal evidence from the earlier full working checkout:

- Current normal `./mvnw -B -ntp verify`: **95 tests passed, zero failures/errors/skips**. Shared 4, Proto 1, Gateway 2, Wallet 1, Loan 5, Collateral 4, Identity 78. Exit code **1** because Identity PMD reports six existing `AvoidFieldNameMatchingMethodName` findings; no checks were disabled. Log `/tmp/payguard-final-current-verify.log`.
- Earlier normal `./mvnw -B -ntp verify`: **95 tests passed, zero failures/errors/skips**. Shared 4, Proto 1, Gateway 2, Wallet 1, Loan 5, Collateral 4, Identity 78. Exit code **1** because Identity PMD reported six existing `AvoidFieldNameMatchingMethodName` findings; no checks were disabled. Log `/tmp/payguard-final-current-verify.log`.
- All modules before Identity passed normal Spotless/Checkstyle/PMD/CPD/SpotBugs. Independent Identity SpotBugs passed with zero bug/error instances; its six typed-ID PMD naming findings remain separate work. Package build with tests skipped succeeded for all four service JARs; it is not counted as test evidence.
- Identity real PostgreSQL suite: 14 passing tests, including competing verification, failed attempts, first issuance/replacement, final boundaries, restored states, ownership, case and rollback. Application contracts: 11 passing tests. Loan/Collateral tests exercise exact schema amounts/UUID/time/null/constraints; absent persisted financial flows remain unproved.
- Synthetic export conversion: 3 Python tests passed, zero skipped.
Expand All @@ -289,7 +293,7 @@ Surefire reports are under each module's `target/surefire-reports`; the Vault ru

## Remaining Oracle references and limits

The migration-only publication also retains the original tracked `payguard-otp-service` and root module selection, because their retirement/switch was owner work excluded from this commit. They still reference Oracle until that separate integration is applied. The list below describes the complete implementation checkout after that integration, not a claim that the migration-only branch has already removed those owner-controlled paths.
PR #35 retained the old OTP module because its retirement was separate owner work. The integration branch removes that module and selects Identity. The remaining Oracle references are historical migrations, documentation and unrelated price-oracle terminology:

- `payguard-{identity,loan,collateral}-service/src/main/resources/db/legacy/oracle/*.sql`: exact historical vendor migrations and the legacy populated-data guard.
- `OtpInfrastructureIntegrationTest.archivedOracleMigrationRetainsPopulatedDataGuard`: reads the legacy guard as text, without an Oracle engine. `PostgresqlMigrationRehearsalTest` mentions the unavailable Oracle source in Javadoc; it executes PostgreSQL only.
Expand All @@ -299,6 +303,6 @@ The migration-only publication also retains the original tracked `payguard-otp-s

Full remaining-reference search is `rg -n -i 'oracle|ojdbc|varchar2|sys_guid|gvenzl|pl/sql' --glob '!**/target/**' --glob '!.local/**'`. Treat matched market-oracle names and URLs to Java documentation separately from JDBC/database references. All original Oracle script bytes are retained; no clean/truncation command is added.

Implemented: available PostgreSQL dependency/config/schema paths, Identity database concurrency, service-scoped Vault client/pool/secrets, persistent local TLS/SCRAM/Raft/audit provisioning, separate migrations and reviewable rehearsal tooling. Locally tested scope is listed above and in the work log. Remaining quality gate: six existing typed-ID PMD naming findings. Production-dependent: certificate/SecretID/operator supervision, HA/auto-unseal, encrypted volume/backup hosting, custody, audit retention, capacity and disaster recovery. Not implemented: missing financial repositories, authenticator enrollment/recovery, external auth/Keycloak, durable event/delivery recovery or a new IAM system.
Implemented: available PostgreSQL dependency/config/schema paths, complete Identity module registration, database concurrency, service-scoped Vault client/pool/secrets, persistent local TLS/SCRAM/Raft/audit provisioning, separate migrations and reviewable rehearsal tooling. Current committed Maven verification passes; five unpublished future ID classes remain local owner work. Production-dependent: certificate/SecretID/operator supervision, HA/auto-unseal, encrypted volume/backup hosting, custody, audit retention, capacity and disaster recovery. Not implemented: missing financial repositories, authenticator enrollment/recovery, external auth/Keycloak, durable event/delivery recovery or a new IAM system.

Review [separate GitHub Issue drafts](../issues/postgresql-vault-follow-up.md) for acceptance criteria. Security references describe the PayGuard design, not invented internal company deployments: [OWASP secrets management](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html), [cryptographic storage](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html), [Vault AppRole](https://developer.hashicorp.com/vault/docs/auth/approle), [KV v2](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2), [Transit](https://developer.hashicorp.com/vault/docs/secrets/transit), [Stripe customer API-key guidance](https://docs.stripe.com/keys-best-practices).
Loading
Loading