@mriekki/project-forge 4.26.1 → 4.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/authentication/distribution.json +90 -0
- package/assets/components.json +17 -3
- package/assets/core/distribution.json +12 -0
- package/assets/deployment/distribution.json +292 -68
- package/assets/generated/ASSEMBLY.json +150 -4
- package/assets/generated/authentication/docs/AUTH_RECOVERY_RETENTION.md +101 -0
- package/assets/generated/authentication/docs/HOUSEHOLD_INFRASTRUCTURE_ADOPTION.md +55 -0
- package/assets/generated/authentication/docs/HOUSEHOLD_REGISTRY.md +246 -0
- package/assets/generated/authentication/docs/PROJECT_AUTH_REFERENCE.md +878 -0
- package/assets/generated/authentication/samples/ProjectForgeAuth.Api/Program.cs +40 -0
- package/assets/generated/authentication/samples/ProjectForgeAuth.Api/ProjectForgeAuth.Api.csproj +13 -0
- package/assets/generated/authentication/samples/ProjectForgeAuth.Api/wwwroot/auth.js +65 -0
- package/assets/generated/authentication/samples/ProjectForgeAuth.Api/wwwroot/index.html +44 -0
- package/assets/generated/authentication/src/ProjectForge.Auth/AdmissionPolicy.cs +167 -0
- package/assets/generated/authentication/src/ProjectForge.Auth/AuthenticationDiagnostics.cs +159 -0
- package/assets/generated/authentication/src/ProjectForge.Auth/ProjectForge.Auth.csproj +9 -0
- package/assets/generated/authentication/src/ProjectForge.Auth/PwaExistingProjectCompatibility.cs +64 -0
- package/assets/generated/authentication/src/ProjectForge.Auth.Runtime/ProjectAuthStore.cs +471 -0
- package/assets/generated/authentication/src/ProjectForge.Auth.Runtime/ProjectForge.Auth.Runtime.csproj +15 -0
- package/assets/generated/authentication/src/ProjectForge.Auth.Runtime/ProjectForgeAuthRuntimeExtensions.cs +472 -0
- package/assets/generated/authentication/src/ProjectForge.Auth.Runtime/ResendProjectInvitationDelivery.cs +105 -0
- package/assets/generated/authentication/src/ProjectForge.Transport/ProjectForge.Transport.csproj +11 -0
- package/assets/generated/authentication/src/ProjectForge.Transport/StrictPublicOriginTransport.cs +95 -0
- package/assets/generated/authentication/tools/household_connect.py +145 -0
- package/assets/generated/authentication/tools/project_auth_recovery.py +716 -0
- package/assets/generated/core/core/process/ENGINEERING_BACKLOG.md +150 -0
- package/assets/generated/core/core/skills/manage-backlog/SKILL.md +115 -0
- package/assets/generated/core/core/skills/review-user-feedback/SKILL.md +50 -0
- package/assets/generated/deployment/docs/RECOVERY_READINESS.md +103 -0
- package/assets/generated/deployment/src/project_forge_deployment/auth_recovery_capture.py +164 -0
- package/assets/generated/deployment/src/project_forge_deployment/auth_recovery_guard.py +225 -0
- package/assets/generated/deployment/src/project_forge_deployment/auth_recovery_lifecycle.py +455 -0
- package/assets/generated/deployment/src/project_forge_deployment/auth_recovery_retention.py +220 -0
- package/assets/generated/deployment/src/project_forge_deployment/auth_recovery_schedule.py +489 -0
- package/package.json +1 -1
- package/src/cli.mjs +32 -3
- package/src/engine.mjs +87 -5
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@mriekki/project-forge",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.27.1",
|
|
5
5
|
"components": {
|
|
6
6
|
"core": {
|
|
7
|
-
"revision": "
|
|
7
|
+
"revision": "eac51d50a9e3c8931159ee8dedba79e63ba7867b",
|
|
8
8
|
"version": "4.3.1",
|
|
9
9
|
"distribution": "greenfield-workspace",
|
|
10
10
|
"files": [
|
|
@@ -368,6 +368,21 @@
|
|
|
368
368
|
"destination": ".github/skills/ux-review/SKILL.md",
|
|
369
369
|
"sha256": "2d0031a7041b8174ef056cf35015d5a9faadbc960313b3d1117bf55b8cb44486"
|
|
370
370
|
},
|
|
371
|
+
{
|
|
372
|
+
"source": "core/skills/review-user-feedback/SKILL.md",
|
|
373
|
+
"destination": ".github/skills/review-user-feedback/SKILL.md",
|
|
374
|
+
"sha256": "80f6087b222b76d93b92e5cbacf33ac1af1a6e797bf27b796b7d70b34280d5e0"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"source": "core/process/ENGINEERING_BACKLOG.md",
|
|
378
|
+
"destination": "docs/process/ENGINEERING_BACKLOG.md",
|
|
379
|
+
"sha256": "49f3f6d4f48ffed74c8a22e075a8efe1c04c6f40d1c3b978184e4c29a6e00bfa"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"source": "core/skills/manage-backlog/SKILL.md",
|
|
383
|
+
"destination": ".github/skills/manage-backlog/SKILL.md",
|
|
384
|
+
"sha256": "23561dfe3d5010ea9bfbb41ae7657734c40477d43303b56f4a05ac8fd8ad6ef8"
|
|
385
|
+
},
|
|
371
386
|
{
|
|
372
387
|
"source": "core/instructions/project-forge-web-guidance.instructions.md",
|
|
373
388
|
"destination": ".github/instructions/project-forge-web-guidance.instructions.md",
|
|
@@ -386,7 +401,7 @@
|
|
|
386
401
|
]
|
|
387
402
|
},
|
|
388
403
|
"deployment": {
|
|
389
|
-
"revision": "
|
|
404
|
+
"revision": "0f80ad06eedeeeb84fcf70cd367873b23d70af25",
|
|
390
405
|
"distribution": "greenfield-project",
|
|
391
406
|
"files": [
|
|
392
407
|
{
|
|
@@ -599,6 +614,31 @@
|
|
|
599
614
|
"destination": "tools/project-forge-deployment/project_forge_deployment/proxy_peer.py",
|
|
600
615
|
"sha256": "ae6c8cb26b83ed07765fa4740ce5c68f52cafd2209b4f055887a4457cfb41c03"
|
|
601
616
|
},
|
|
617
|
+
{
|
|
618
|
+
"source": "src/project_forge_deployment/auth_recovery_guard.py",
|
|
619
|
+
"destination": "tools/project-forge-deployment/project_forge_deployment/auth_recovery_guard.py",
|
|
620
|
+
"sha256": "8ca97923b994f07655b04062556e2dd755c2127ff134ecbfe64c64f20f0e5ac8"
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
"source": "src/project_forge_deployment/auth_recovery_capture.py",
|
|
624
|
+
"destination": "tools/project-forge-deployment/project_forge_deployment/auth_recovery_capture.py",
|
|
625
|
+
"sha256": "de8e2db1d66b07d69afe11aa331264cc180cd9b3ecf734c175cd1fdff5764d4c"
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"source": "src/project_forge_deployment/auth_recovery_lifecycle.py",
|
|
629
|
+
"destination": "tools/project-forge-deployment/project_forge_deployment/auth_recovery_lifecycle.py",
|
|
630
|
+
"sha256": "ade089a5b1e9ee98ca06e81f6629b07f3786e65a106f3bdd0c3639cfccd038a8"
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"source": "src/project_forge_deployment/auth_recovery_schedule.py",
|
|
634
|
+
"destination": "tools/project-forge-deployment/project_forge_deployment/auth_recovery_schedule.py",
|
|
635
|
+
"sha256": "10e7e9b8d2f1b24c9abb1f10aebbd9a89677896ee4d51d33d12f8a0f174bed23"
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
"source": "src/project_forge_deployment/auth_recovery_retention.py",
|
|
639
|
+
"destination": "tools/project-forge-deployment/project_forge_deployment/auth_recovery_retention.py",
|
|
640
|
+
"sha256": "953a5e4ef790a61b6ba8742352a89b97ace346b116033e03831c9a19dd7e6288"
|
|
641
|
+
},
|
|
602
642
|
{
|
|
603
643
|
"source": "src/project_forge_deployment/recovery_scope.py",
|
|
604
644
|
"destination": "tools/project-forge-deployment/project_forge_deployment/recovery_scope.py",
|
|
@@ -712,7 +752,7 @@
|
|
|
712
752
|
{
|
|
713
753
|
"source": "docs/RECOVERY_READINESS.md",
|
|
714
754
|
"destination": "docs/project-forge/RECOVERY_READINESS.md",
|
|
715
|
-
"sha256": "
|
|
755
|
+
"sha256": "61d4b26d5f126acbc544925489acb3adc8d6c7ea5d2d02b4b71bf6ccb3d3171b"
|
|
716
756
|
},
|
|
717
757
|
{
|
|
718
758
|
"source": "docs/images/developer-environment-journey.svg",
|
|
@@ -730,6 +770,112 @@
|
|
|
730
770
|
"sha256": "357896c40ff2c7a93bf77811af61d2559103d2e386a5fd887c7041d016114b10"
|
|
731
771
|
}
|
|
732
772
|
]
|
|
773
|
+
},
|
|
774
|
+
"authentication": {
|
|
775
|
+
"revision": "6db1c1e8a0d3cb0a20aa3415ca0e228f8849df46",
|
|
776
|
+
"distribution": "greenfield-project",
|
|
777
|
+
"files": [
|
|
778
|
+
{
|
|
779
|
+
"source": "src/ProjectForge.Auth/AdmissionPolicy.cs",
|
|
780
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth/AdmissionPolicy.cs",
|
|
781
|
+
"sha256": "74b76dd227ff3dd5545e14ff69283ef02dd06017165bf35189bf99e041103f22"
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
"source": "src/ProjectForge.Auth/AuthenticationDiagnostics.cs",
|
|
785
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth/AuthenticationDiagnostics.cs",
|
|
786
|
+
"sha256": "b1c0d112e963bc3122b56c1a5459c2d3e6575d391433b85f6e02d0bc4fbb58a2"
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
"source": "src/ProjectForge.Auth/ProjectForge.Auth.csproj",
|
|
790
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth/ProjectForge.Auth.csproj",
|
|
791
|
+
"sha256": "25d111608dc0c1dd47702cd0501f7de2e20d67552ff7e1c3779722985c27641e"
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
"source": "src/ProjectForge.Auth/PwaExistingProjectCompatibility.cs",
|
|
795
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth/PwaExistingProjectCompatibility.cs",
|
|
796
|
+
"sha256": "5ab5b6b4f017b8eed3d1d412966420eba07ed102b4fb41ecffd012ed77812d2c"
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
"source": "src/ProjectForge.Auth.Runtime/ProjectAuthStore.cs",
|
|
800
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth.Runtime/ProjectAuthStore.cs",
|
|
801
|
+
"sha256": "e8255f660c6834803effc5659ca8c7d8517f34c195d371c811b77dee3ecd9c23"
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
"source": "src/ProjectForge.Auth.Runtime/ProjectForge.Auth.Runtime.csproj",
|
|
805
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth.Runtime/ProjectForge.Auth.Runtime.csproj",
|
|
806
|
+
"sha256": "ae704a37487250b5198cc5c61bfcb600fe03b877d6b818c9700b8768d14365bc"
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
"source": "src/ProjectForge.Auth.Runtime/ProjectForgeAuthRuntimeExtensions.cs",
|
|
810
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth.Runtime/ProjectForgeAuthRuntimeExtensions.cs",
|
|
811
|
+
"sha256": "73a73a3aca995030c3e2db90b8670e2998df348266bff196b3686751ce0325f9"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
"source": "tools/household_connect.py",
|
|
815
|
+
"destination": "tools/project-forge-auth-kit/household_connect.py",
|
|
816
|
+
"sha256": "f3517f483fc027f9db3cfb3256000bf9738509cbeb17711fa3fbf93dd91df7be"
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
"source": "docs/HOUSEHOLD_REGISTRY.md",
|
|
820
|
+
"destination": "tools/project-forge-auth-kit/docs/HOUSEHOLD_REGISTRY.md",
|
|
821
|
+
"sha256": "17a466927402398f5b69a958325aaaaf5ea953193b8d3c60d5d5bf201dca8009"
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
"source": "docs/HOUSEHOLD_INFRASTRUCTURE_ADOPTION.md",
|
|
825
|
+
"destination": "tools/project-forge-auth-kit/docs/HOUSEHOLD_INFRASTRUCTURE_ADOPTION.md",
|
|
826
|
+
"sha256": "5cbbc6843d4a02b8f50153b6cc9cbaad9df6629748d8a541524af31bc9b07bdf"
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"source": "src/ProjectForge.Transport/ProjectForge.Transport.csproj",
|
|
830
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Transport/ProjectForge.Transport.csproj",
|
|
831
|
+
"sha256": "213e625c19e95b2a5ef315f01e2ca63fa56d3f5d1634dce920d493aeec1a4240"
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
"source": "src/ProjectForge.Transport/StrictPublicOriginTransport.cs",
|
|
835
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Transport/StrictPublicOriginTransport.cs",
|
|
836
|
+
"sha256": "3c91970ad8f7f4feb0db8eda2289109a7748d439d71752a13a1c8e394b8070fd"
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
"source": "samples/ProjectForgeAuth.Api/Program.cs",
|
|
840
|
+
"destination": "tools/project-forge-auth-kit/samples/ProjectForgeAuth.Api/Program.cs",
|
|
841
|
+
"sha256": "baf0471b649864aeb7bdad76a74fe91279cc0a6b708b4487f62b2b940f6334cf"
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
"source": "samples/ProjectForgeAuth.Api/ProjectForgeAuth.Api.csproj",
|
|
845
|
+
"destination": "tools/project-forge-auth-kit/samples/ProjectForgeAuth.Api/ProjectForgeAuth.Api.csproj",
|
|
846
|
+
"sha256": "5f77a342dbfd3f062705f77f42285d41ee7ded2122533045d54de8d66cacb93b"
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
"source": "samples/ProjectForgeAuth.Api/wwwroot/index.html",
|
|
850
|
+
"destination": "tools/project-forge-auth-kit/samples/ProjectForgeAuth.Api/wwwroot/index.html",
|
|
851
|
+
"sha256": "97f28fd0a4269ecbff7cc2ac94d12fdcad236b0f5bd07c538e7d241f4796dfb0"
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
"source": "samples/ProjectForgeAuth.Api/wwwroot/auth.js",
|
|
855
|
+
"destination": "tools/project-forge-auth-kit/samples/ProjectForgeAuth.Api/wwwroot/auth.js",
|
|
856
|
+
"sha256": "cfb0d94c2c4b9bb2093dc9e322c226a8e4eead0a8cef31f194ddbac5d3420823"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
"source": "src/ProjectForge.Auth.Runtime/ResendProjectInvitationDelivery.cs",
|
|
860
|
+
"destination": "tools/project-forge-auth-kit/src/ProjectForge.Auth.Runtime/ResendProjectInvitationDelivery.cs",
|
|
861
|
+
"sha256": "ff6a62f99eb984388c265aa72f47f3b223a3594371f8f041208f5f4e4887f017"
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
"source": "docs/PROJECT_AUTH_REFERENCE.md",
|
|
865
|
+
"destination": "tools/project-forge-auth-kit/docs/PROJECT_AUTH_REFERENCE.md",
|
|
866
|
+
"sha256": "1bbca014b897f3748e8af9039ba39cf14e8c445cf4748b2596b5ee733876a90b"
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
"source": "tools/project_auth_recovery.py",
|
|
870
|
+
"destination": "tools/project-forge-auth-kit/project_auth_recovery.py",
|
|
871
|
+
"sha256": "4f7864e33a1bc8bbb577a3459e06ed2fcf7e65a8349513c17510917680e68c9e"
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
"source": "docs/AUTH_RECOVERY_RETENTION.md",
|
|
875
|
+
"destination": "tools/project-forge-auth-kit/docs/AUTH_RECOVERY_RETENTION.md",
|
|
876
|
+
"sha256": "bc75171c05fdc3f454c67dcc3e2b7e226946ac32b5d02de62c9fc3b0b3bef0cf"
|
|
877
|
+
}
|
|
878
|
+
]
|
|
733
879
|
}
|
|
734
880
|
}
|
|
735
881
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Auth Kit recovery-set inventory and retention contract
|
|
2
|
+
|
|
3
|
+
This is an internal low-level Project recovery building block, not an
|
|
4
|
+
operator-ready Production backup command and **not** application recovery
|
|
5
|
+
qualification. The canonical generated Auth Kit helper is
|
|
6
|
+
`tools/project-forge-auth-kit/project_auth_recovery.py`.
|
|
7
|
+
|
|
8
|
+
## Inert backup set
|
|
9
|
+
|
|
10
|
+
The owner must first independently quiesce its *exact* application instance,
|
|
11
|
+
prove a stable stopped Docker container/image and stable private storage, and
|
|
12
|
+
checkpoint any SQLite WAL. The `capture_quiesced_set(source, destination,
|
|
13
|
+
declaration, runtime_guard)` helper cannot stop the app or prove that the
|
|
14
|
+
provided runtime guard is trustworthy.
|
|
15
|
+
|
|
16
|
+
Use a new private backup-set parent (0700 on POSIX), outside the live data
|
|
17
|
+
tree. Each set name must have form `YYYYMMDDTHHMMSSZ-<8..32 lowercase hex>`
|
|
18
|
+
with a valid UTC calendar timestamp and collision-resistant suffix. The set
|
|
19
|
+
contains exactly `auth.sqlite`, `keys/key-*.xml`, and `receipt.json`.
|
|
20
|
+
Capture publishes exclusively; an existing destination, even an empty one
|
|
21
|
+
created after the initial check, is never replaced. A failed capture cleans
|
|
22
|
+
only its newly created private staging directory.
|
|
23
|
+
|
|
24
|
+
The receipt contains only Project identifier, recovery policy, protector
|
|
25
|
+
generation and file digests. It is **not signed** and cannot establish
|
|
26
|
+
provenance against a privileged attacker capable of rewriting both data
|
|
27
|
+
and receipt. Provider credentials and application secrets are not stored
|
|
28
|
+
in it.
|
|
29
|
+
|
|
30
|
+
## Read-only inventory and retention
|
|
31
|
+
|
|
32
|
+
`plan_verified_retention(sets_root, declaration, keep_last=7)` inspects
|
|
33
|
+
every set before recommending **candidate names only**. It requires owner
|
|
34
|
+
private inventory storage, exact canonical names, valid receipts, complete
|
|
35
|
+
SQLite/key material, SHA-256 integrity, compatible Auth Kit schema and the
|
|
36
|
+
same declared Project, generation and authentication policy. Any
|
|
37
|
+
interrupted staging directory, symlink, missing/corrupt member, unknown
|
|
38
|
+
directory, or unexpected Project identity blocks the entire plan.
|
|
39
|
+
|
|
40
|
+
The function preserves at least two newest verified sets and at most
|
|
41
|
+
1,000 tracked sets; the **plan never deletes anything**. No background job,
|
|
42
|
+
notification, or email receipt is installed by this function.
|
|
43
|
+
|
|
44
|
+
## Explicit disposable-only deletion primitive (not a scheduled operation)
|
|
45
|
+
|
|
46
|
+
`apply_reviewed_disposable_retention(sets_root, lock_directory, declaration,
|
|
47
|
+
reviewed_plan, keep_last=7, environment="disposable",
|
|
48
|
+
authorize_disposable_delete=True)` is a Linux-only, low-level primitive.
|
|
49
|
+
It rejects every non-disposable environment, implicit authorization, unsafe
|
|
50
|
+
private directories, symlinks, corrupt sets, and inventory changes since the
|
|
51
|
+
complete **read-only plan** was reviewed. It preserves at least the latest two
|
|
52
|
+
verified recovery sets, bounds deletions to 32 per invocation, uses a private
|
|
53
|
+
nonblocking retention lock, and anchors unlink/rmdir operations to no-follow
|
|
54
|
+
directory descriptors instead of invoking recursive path deletion. After
|
|
55
|
+
each batch the operator must obtain and review a new read-only plan.
|
|
56
|
+
|
|
57
|
+
**This is not automatically called by capture or setup**, and is not a
|
|
58
|
+
Production retention CLI. The caller is responsible for proving the exact
|
|
59
|
+
generated Project's ownership, separation from live data, completed backup,
|
|
60
|
+
and the intended disposable environment. A directory deletion spanning
|
|
61
|
+
multiple files is **not atomic**; on a filesystem failure, a partially
|
|
62
|
+
pruned old candidate may need manual inspection/reconciliation before
|
|
63
|
+
any further retention or backup inventory planning. Do not treat a
|
|
64
|
+
partial failure as retention success.
|
|
65
|
+
|
|
66
|
+
Deployment Kit must still add and qualify a separately reviewed operator
|
|
67
|
+
driver coordinating the backup/recovery lifecycle lock, durable status and
|
|
68
|
+
receipts, persistent scheduling, recovery-point verification, escalation and
|
|
69
|
+
notification before automatic retention is permissible.
|
|
70
|
+
|
|
71
|
+
## Restore is isolated and not readiness
|
|
72
|
+
|
|
73
|
+
`materialize_disposable_restore` creates only a new separate directory
|
|
74
|
+
outside backup and live paths and rechecks the hashes. No live data
|
|
75
|
+
overwrite or application restart occurs. A separate disposable application
|
|
76
|
+
must demonstrate actual key-ring protector viability, session-preservation
|
|
77
|
+
or invalidation policy, provider configuration recovery and health before
|
|
78
|
+
`applicationRecoveryReady` can ever be true. Artifact inspection always
|
|
79
|
+
reports it as **false**.
|
|
80
|
+
|
|
81
|
+
## Running application health is not recovery readiness
|
|
82
|
+
|
|
83
|
+
The managed .NET Auth Kit maps `GET /health` **behind the exact trusted
|
|
84
|
+
Caddy peer/public-origin middleware**, not on an unprotected direct socket.
|
|
85
|
+
It performs a read-only check that the current per-Project Auth SQLite file
|
|
86
|
+
already exists with supported schema and expected account/session tables.
|
|
87
|
+
Missing, uninitialized or corrupt SQLite returns HTTP 503 without creating
|
|
88
|
+
a replacement identity database. An ordinary running instance with a
|
|
89
|
+
readable store returns 200. The endpoint exposes no account details,
|
|
90
|
+
filesystem paths, providers or session tokens.
|
|
91
|
+
|
|
92
|
+
This is an **application liveness signal only**, not a recovery completion
|
|
93
|
+
gate: even 200 does not prove that the restored DataProtection keyring
|
|
94
|
+
can decrypt historical state, that a prior session still authorizes,
|
|
95
|
+
that real Google/Resend work, that the backup set was preserved, that a
|
|
96
|
+
scheduled job/alert reached its target, or that normal routed traffic
|
|
97
|
+
is safe. All those remain independently proven acceptance requirements.
|
|
98
|
+
|
|
99
|
+
This material is not permission to change any PWA Production service, use
|
|
100
|
+
a household Provider credential, release the Authentication component to
|
|
101
|
+
npm, or label a generated Project ready for Production.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# One-time Household Infrastructure Adoption — PWA Pilot
|
|
2
|
+
|
|
3
|
+
**Status (2026-11-14):** Household Registry and Project Auth runtime source PRs are merged. The PWA-specific Auth Kit cutover was independently accepted in [PWA issue #43](https://github.com/mriekki/family-meal-planner-mark-pwa/issues/43), using a *fresh app-owned Auth Kit account/session database and persistent keyring* because the PWA was an unused shell; its prior records and credentials were preserved separately for rollback. **This does not qualify shared household provider resources, general generated-Project recovery or a published Authentication-enabled Project Forge package.** Auth Kit [release issue #5](https://github.com/mriekki/project-forge-auth-kit/issues/5) is the current release gate, and [recovery issue #9](https://github.com/mriekki/project-forge-auth-kit/issues/9) tracks generated-Project readiness. No provider/Production changes are authorized by this document.
|
|
4
|
+
|
|
5
|
+
This is the agreed one-time path for reusing relevant Family Meal Planner PWA infrastructure to bootstrap shared Project Forge household defaults. Do not repeat Google, Cloudflare, and Resend setup from scratch merely because a reusable household profile is introduced.
|
|
6
|
+
|
|
7
|
+
The Authentication Kit owns this provider-resource assessment proposal. Existing-repository adoption, New Project discovery, deployment mechanics, and the PWA's own work-item approvals remain owned by their established Project Forge components.
|
|
8
|
+
|
|
9
|
+
## Separate infrastructure and application responsibilities
|
|
10
|
+
|
|
11
|
+
**A — Existing household infrastructure (first):** Inventory the existing PWA Google Cloud configuration, Cloudflare account/domain/Tunnel/Caddy network, Resend sending domain, and Ubuntu/Docker environment. Determine shared administrator-owned assets versus application-specific resources. Use independent **read-only provider verification** to establish current resource existence and ownership. Reuse appropriate shared assets, but allocate separate OAuth clients/redirects, narrowly scoped Resend keys, and exact app-owned ingress/routes for future Projects. PWA's credentials, hostnames, database, production route and runtime stay as-is.
|
|
12
|
+
|
|
13
|
+
**B — PWA-specific application adoption (accepted):** This migration has already occurred under PWA's separate review and Production approval. The accepted exception uses a newly seeded, isolated Auth Kit account/session database and persistent DataProtection keyring; historical PWA identities/passwords/sessions were *not* automatically translated because no active Product users or substantive Production user data needed migration. Historical PWA data and implementation were preserved for an explicit rollback. A future consumer with established users **must not inherit** this fresh-account exception: preserve or explicitly reconcile identities, sessions, passwords and app-owned authorization through that Project's own reviewed migration. Never auto-link by email or treat Household Registry membership as Project login.
|
|
14
|
+
|
|
15
|
+
## How to run the implemented assessment
|
|
16
|
+
|
|
17
|
+
From the private Authentication Kit checkout, run:
|
|
18
|
+
|
|
19
|
+
python3 tools/household_adoption.py assess \
|
|
20
|
+
--checkout /path/to/fmp \
|
|
21
|
+
--expected-commit <exact-40-hex-pwa-head> \
|
|
22
|
+
--output /private/existing/directory/pwa-household-assessment.json
|
|
23
|
+
|
|
24
|
+
The parent output directory must already exist outside the PWA checkout. On Windows, use paths and continuation syntax appropriate for PowerShell. The output is a new JSON report: an existing report is NEVER overwritten. POSIX writes use owner-only mode; Windows permissions depend on the destination directory.
|
|
25
|
+
|
|
26
|
+
The report is bound to the exact local Git HEAD commit, with hashes for a **fixed allowlist of committed source files**: Project Forge baseline, deployment topology, OIDC implementation, account schema/service/endpoints, Resend adapter, production provider boundaries, and exact origin transport. It never reads uncommitted files, database content, .env, provider credential material, private SSH data, local settings or live provider accounts. No provider API calls or Production actions occur.
|
|
27
|
+
|
|
28
|
+
The report includes **source-evidence-found** versus **needs-source-review** markers, and all provider candidates remain **provider-inventory-unverified**. Source evidence cannot establish whether live Google, Cloudflare or Resend resources are currently provisioned, reusable or owned by the household. It contains a deterministic assessment SHA-256 for future reviewed plans and no secrets or personal account records.
|
|
29
|
+
|
|
30
|
+
The source assessor implements **assess** only and never applies changes. Separate
|
|
31
|
+
draft Registry tools now implement the GET-only provider ID verifier and
|
|
32
|
+
name-based discovery, the digest-locked adoption-plan generator, and an
|
|
33
|
+
explicitly reviewed host-local Registry resource import/rollback.
|
|
34
|
+
See `HOUSEHOLD_REGISTRY.md` for the exact operator procedure.
|
|
35
|
+
These are not a live provider migration or automatic PWA identity cutover.
|
|
36
|
+
|
|
37
|
+
## Required review stages before any migration writes
|
|
38
|
+
|
|
39
|
+
1. Assess the **delivered** PWA commit using the read-only tool, not an older chat summary or uncommitted worktree.
|
|
40
|
+
2. Independently inventory non-secret Google Cloud, Cloudflare, and Resend resources using
|
|
41
|
+
the trusted local read-only verification **commands**. Cloudflare allows read-scoped
|
|
42
|
+
API tokens, whereas Resend may require a Full access key to perform its GET
|
|
43
|
+
domain metadata reads; this verifier issues GET requests only. Never load or
|
|
44
|
+
transmit such keys from ChatGPT or hosted CI. Confirm ownership, existing
|
|
45
|
+
sharing, restrictions and per-Project scope.
|
|
46
|
+
3. Produce a reviewed household profile plan that classifies each asset as preserve, reuse existing shared resource, create separately scoped Project resource, or defer.
|
|
47
|
+
4. Separately approve any actual provider or host write with an idempotent, least-privileged executor, durable receipt and rollback/stop controls. No automatic PWA route or credential replacement.
|
|
48
|
+
5. Treat the accepted PWA Auth Kit cutover as a Project-specific completed event documented in PWA issue #43. For a different established Project, separately approve identity/session migration, disposable-data rehearsal, rollback, authorization and app-specific feature equivalence; do not assume the PWA fresh-account exception applies.
|
|
49
|
+
6. Only enable an opt-in household capability in Bootstrap's existing New Project lifecycle after full generated-Project integration proof.
|
|
50
|
+
|
|
51
|
+
## Current limitations and safety
|
|
52
|
+
|
|
53
|
+
Merged Auth Kit runtime includes .NET 10 Google OIDC code flow, per-Project SQLite account/invitation/session persistence, reference browser, exact proxy ingress checks, and a Bootstrap-managed optional generated API. Reusable, disposable Auth Kit recovery tests now cover real SQLite/DataProtection artifacts and isolated application HTTP startup; Deployment Kit includes exact Docker stopped-container capture, disposable stop/capture/resume and a durable interrupted-run journal. These are **not** real provider inventory, per-Project provider setup, durable Production scheduling/alert delivery, real full-application recovery readiness or npm release acceptance. The PWA-specific Production cutover is accepted separately; nothing here authorizes changing it.
|
|
54
|
+
|
|
55
|
+
Do not commit resulting reports to public repositories. They exclude credentials but still contain useful operational metadata. Secrets stay in protected local/provider input mechanisms, not Git, log output, shell argv, Chat, or plain JSON. PWA Production, FB-001/FB-002 requirements, provider resources and live data are all explicitly outside this change.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Project Forge Household Registry
|
|
2
|
+
|
|
3
|
+
Status: **tested Registry and adoption engine under review, not deployed or published as a supported capability**.
|
|
4
|
+
|
|
5
|
+
This is a .NET 10 / SQLite service that persists shared household membership,
|
|
6
|
+
registered Projects and non-secret infrastructure references. It can be
|
|
7
|
+
accessed from a browser on Windows, WSL/Linux or macOS by a family Developer
|
|
8
|
+
such as Kyle using his own Google identity.
|
|
9
|
+
|
|
10
|
+
## Ownership and authority
|
|
11
|
+
|
|
12
|
+
The Registry stores household identity, approved Google issuer+subject
|
|
13
|
+
membership, admin/developer/member roles, one-time invitations, registered
|
|
14
|
+
Projects, shared resource references, explicit Project-resource bindings
|
|
15
|
+
and audit entries. It does **not** store provider tokens, Project passwords,
|
|
16
|
+
application user accounts, PWA sessions, Google OAuth client secrets,
|
|
17
|
+
Resend keys, SSH credentials or application data.
|
|
18
|
+
|
|
19
|
+
A household Developer is not automatically an administrator or user of PWA
|
|
20
|
+
or any other Project. Independent applications continue to operate if the
|
|
21
|
+
Registry is temporarily offline.
|
|
22
|
+
|
|
23
|
+
## First-time setup (trusted host; not a public endpoint)
|
|
24
|
+
|
|
25
|
+
Provision a private SQLite data directory owned by the non-root service UID.
|
|
26
|
+
For deployment, provide these settings from protected local configuration:
|
|
27
|
+
|
|
28
|
+
PF_HOUSEHOLD_DB=/data/household.sqlite
|
|
29
|
+
PF_HOUSEHOLD_ID=my-household
|
|
30
|
+
PF_HOUSEHOLD_NAME=My Household
|
|
31
|
+
PF_INITIAL_GOOGLE_SUBJECT=<exact-preverified-google-subject>
|
|
32
|
+
|
|
33
|
+
In a trusted host administration session execute exactly once:
|
|
34
|
+
|
|
35
|
+
dotnet ProjectForge.HouseholdRegistry.dll admin-init
|
|
36
|
+
|
|
37
|
+
The exact initial subject must be independently verified; email matches or
|
|
38
|
+
first-visitor behavior never grant administration. The operation refuses an
|
|
39
|
+
already-initialized registry.
|
|
40
|
+
|
|
41
|
+
The runtime requires:
|
|
42
|
+
|
|
43
|
+
PF_HOUSEHOLD_DB=/data/household.sqlite
|
|
44
|
+
PF_GOOGLE_CLIENT_ID=<per-registry-oauth-client-id>
|
|
45
|
+
PF_GOOGLE_CLIENT_SECRET_FILE=/run/secrets/google_oidc_client_secret
|
|
46
|
+
PF_REGISTRY_KEY_DIR=/data/keys
|
|
47
|
+
PF_REGISTRY_PUBLIC_ORIGIN=https://registry.example.com
|
|
48
|
+
PF_TRUSTED_CADDY_IP=<exact-reviewed-managed-caddy-ip>
|
|
49
|
+
|
|
50
|
+
Google callback: https://registry.example.com/signin-google
|
|
51
|
+
|
|
52
|
+
Do not put credentials in a tracked compose file, Git, Chat, CLI arguments,
|
|
53
|
+
shell history, or a public household profile. The service Dockerfile runs
|
|
54
|
+
as non-root on internal port 8080. The Deployment Kit must own Caddy/Tunnel
|
|
55
|
+
routing, exact public origin, allowed header forwarding and peer convergence;
|
|
56
|
+
do not publish the container directly to LAN/public TCP or trust Docker
|
|
57
|
+
network subnets. Use the Kit's read-only platform peer inspection before
|
|
58
|
+
reviewing the exact Caddy transport setting. The application rejects a
|
|
59
|
+
non-HTTPS or unexpected public host.
|
|
60
|
+
|
|
61
|
+
The `deploy/household-registry.compose.yml` reference is an isolated
|
|
62
|
+
no-host-port, non-root service with a separately mounted Google OAuth secret
|
|
63
|
+
file, durable SQLite volume and persistent private DataProtection key ring.
|
|
64
|
+
Set `PF_CADDY_NETWORK`, `PF_REVIEWED_CADDY_IP`,
|
|
65
|
+
`PF_HOUSEHOLD_DATA_DIR`, `PF_REGISTRY_GOOGLE_CLIENT_ID`,
|
|
66
|
+
`PF_GOOGLE_CLIENT_SECRET_FILE`, and `PF_REGISTRY_PUBLIC_ORIGIN`
|
|
67
|
+
through trusted host setup. The exact Caddy network/IP values must come
|
|
68
|
+
from the existing Deployment Kit 4.26.1 read-only proxy-peer observation.
|
|
69
|
+
Do not enable or rewrite the shared Caddy route from this Compose template,
|
|
70
|
+
and do not put OAuth secrets into provider inventory JSON.
|
|
71
|
+
|
|
72
|
+
On POSIX the key ring directory is restricted to owner access. Protect the
|
|
73
|
+
mounted persistent volume, backups and host secret file. A Production
|
|
74
|
+
deployment must validate process UID/GID and backup/restore before cutover.
|
|
75
|
+
|
|
76
|
+
The .NET runtime uses a Google OIDC code flow with PKCE and server cookie;
|
|
77
|
+
the browser sends an antiforgery header on all mutations. Every Registry API
|
|
78
|
+
call rereads member status so disabling a Developer immediately blocks future
|
|
79
|
+
Registry API access. Provider references always start unverified unless
|
|
80
|
+
installed through a separately approved adoption plan.
|
|
81
|
+
|
|
82
|
+
## One-time existing household adoption from PWA
|
|
83
|
+
|
|
84
|
+
First create the exact-commit read-only PWA source assessment, as documented
|
|
85
|
+
in HOUSEHOLD_INFRASTRUCTURE_ADOPTION.md:
|
|
86
|
+
|
|
87
|
+
python3 tools/household_adoption.py assess --checkout /path/to/fmp \
|
|
88
|
+
--expected-commit <exact-40-hex-pwa-commit> \
|
|
89
|
+
--output /private/evidence/pwa.json
|
|
90
|
+
|
|
91
|
+
Verify existing shared provider resources with read-only scoped credentials,
|
|
92
|
+
in a trusted local terminal. The Cloudflare and Resend credentials are read
|
|
93
|
+
from PF_CF_READ_TOKEN and PF_RESEND_READ_TOKEN environment variables; Google
|
|
94
|
+
Cloud access uses the current trusted gcloud auth identity.
|
|
95
|
+
|
|
96
|
+
python3 tools/household_provider_verify.py verify \
|
|
97
|
+
--google-project <google-project-id> \
|
|
98
|
+
--cf-account <account-id> --cf-zone <zone-id> \
|
|
99
|
+
--cf-tunnel <tunnel-id> --resend-domain <domain-id> \
|
|
100
|
+
--output /private/evidence/providers.json
|
|
101
|
+
|
|
102
|
+
**Preferred when existing provider names are known but IDs are not:** locate
|
|
103
|
+
the exact existing Cloudflare zone/tunnel and Resend sender domain automatically
|
|
104
|
+
through their read-only listing endpoints, then independently verify all
|
|
105
|
+
discovered IDs using the original detailed provider API checks:
|
|
106
|
+
|
|
107
|
+
python3 tools/household_provider_discover.py discover \
|
|
108
|
+
--google-project <existing-google-cloud-project-id> \
|
|
109
|
+
--cf-zone-name <exact-cloudflare-zone-domain> \
|
|
110
|
+
--cf-tunnel-name <exact-production-tunnel-name> \
|
|
111
|
+
--resend-domain-name <exact-verified-sender-domain> \
|
|
112
|
+
--pwa-hostname <exact-existing-pwa-public-hostname> \
|
|
113
|
+
--pwa-origin-service <exact-existing-pwa-unix-origin-service> \
|
|
114
|
+
--output /private/evidence/providers.json
|
|
115
|
+
|
|
116
|
+
The name-discovery command uses the same protected environment variables and
|
|
117
|
+
`gcloud auth` context as the ID-based `verify` command. It fails closed if a
|
|
118
|
+
list is incomplete or paginated, resource names are ambiguous or missing, or
|
|
119
|
+
any detail response disagrees with the expected resource. Its only persistent
|
|
120
|
+
output is the same private, exclusive-created, non-secret provider observation
|
|
121
|
+
JSON. When both optional `--pwa-hostname` and `--pwa-origin-service`
|
|
122
|
+
are provided, it also performs a **GET** for the remotely managed tunnel's
|
|
123
|
+
ingress configuration, requires exactly one matching existing PWA hostname
|
|
124
|
+
without a path restriction, checks the exact reviewed Unix socket service and
|
|
125
|
+
the owning Cloudflare account, and records only that matched route. A
|
|
126
|
+
missing/changed/ambiguous route fails closed. A locally managed tunnel or a
|
|
127
|
+
Cloudflare account without read permission for the configuration endpoint
|
|
128
|
+
cannot satisfy this optional proof and requires a separately reviewed
|
|
129
|
+
on-host ingress check; do not silently treat a tunnel record as routing
|
|
130
|
+
evidence. The verifier never rewrites the existing PWA route.
|
|
131
|
+
|
|
132
|
+
The route proof does not check the Caddy virtual-host table, Google OAuth
|
|
133
|
+
client redirects, or live application availability; those still have
|
|
134
|
+
independent acceptance requirements. It does not change tunnel routing or
|
|
135
|
+
attest Google OAuth client callbacks.
|
|
136
|
+
|
|
137
|
+
**Optional no-API-key-in-the-shell Resend path:** the official Resend CLI
|
|
138
|
+
supports an interactive, locally approved browser OAuth login. After separately
|
|
139
|
+
installing the official `resend` CLI, the operator may authorize an isolated
|
|
140
|
+
local profile (do **not** run this in CI):
|
|
141
|
+
|
|
142
|
+
resend --profile household-review login --web
|
|
143
|
+
|
|
144
|
+
Then append `--resend-cli --resend-profile household-review` to the
|
|
145
|
+
`household_provider_discover.py discover` command above. In that mode, the
|
|
146
|
+
script runs only `resend domains list` and `resend domains get <id>` with
|
|
147
|
+
JSON output, captures/sanitizes CLI output, and does not require
|
|
148
|
+
`PF_RESEND_READ_TOKEN`, pass a token argument or read the CLI credential file.
|
|
149
|
+
The CLI's locally stored OAuth permission may still be broad; only the
|
|
150
|
+
invoked commands are read-only. Resend's browser authorization callback must
|
|
151
|
+
reach the same local machine as the CLI (check WSL/Windows localhost routing
|
|
152
|
+
if needed). No OAuth login is launched or granted by the verification script.
|
|
153
|
+
Cloudflare still needs its separately scoped Zone/Tunnel Read token. For Cloudflare, use a token scoped to **Zone Read** and
|
|
154
|
+
**Cloudflare Tunnel/Connectors Read** on the intended account. **Resend
|
|
155
|
+
currently offers Full access or Sending access keys, not a read-only API-key
|
|
156
|
+
permission**; `GET /domains` requires an authorized key that can retrieve
|
|
157
|
+
domain metadata. The variable name `PF_RESEND_READ_TOKEN` describes the
|
|
158
|
+
**read-only behavior of this verifier**, not the Resend key's privileges.
|
|
159
|
+
If the operator chooses to provide a Full access key, use it **only in the
|
|
160
|
+
trusted local terminal**, never in hosted CI or a tracked file, and remove
|
|
161
|
+
its environment variable after the read. An existing send-only key cannot
|
|
162
|
+
be used for this domain inventory. No new key should be created without
|
|
163
|
+
explicit operator review. **Never paste tokens, key files or Google
|
|
164
|
+
authorization codes into a chat or commit them into Git.**
|
|
165
|
+
|
|
166
|
+
These commands make only HTTPS GET requests; no provider resource or
|
|
167
|
+
credential is modified. The output contains independently confirmed non-secret
|
|
168
|
+
references, status and observation time. Evidence must be less than 24 hours
|
|
169
|
+
old to create a new plan. Dashboard screenshots are useful inventory clues
|
|
170
|
+
but are **not** equivalent to this API-backed evidence.
|
|
171
|
+
|
|
172
|
+
Create the exact reviewed import plan:
|
|
173
|
+
|
|
174
|
+
python3 tools/household_adoption_plan.py plan \
|
|
175
|
+
--assessment /private/evidence/pwa.json \
|
|
176
|
+
--provider-evidence /private/evidence/providers.json \
|
|
177
|
+
--household-id my-household \
|
|
178
|
+
--registry-origin https://registry.example.com \
|
|
179
|
+
--pwa-hostname <exact-existing-pwa-public-hostname> \
|
|
180
|
+
--pwa-origin-service <exact-existing-pwa-unix-origin-service> \
|
|
181
|
+
--output /private/evidence/adoption-plan.json
|
|
182
|
+
|
|
183
|
+
Supplying the exact PWA hostname and Unix origin to the plan command
|
|
184
|
+
requires the upstream provider evidence to contain a matching, independently
|
|
185
|
+
read Cloudflare tunnel route. A missing or changed route cannot be used to
|
|
186
|
+
approve the household resource-reference import. This binds the preexisting
|
|
187
|
+
PWA ingress preservation review into the exact provider-evidence digest;
|
|
188
|
+
it does not test the downstream Caddy virtual-host mapping. The two route
|
|
189
|
+
parameters must be supplied together. If the tunnel is locally managed and
|
|
190
|
+
the configuration-read API is unavailable, stop and obtain a separately
|
|
191
|
+
reviewed local ingress proof rather than claiming the remote tunnel
|
|
192
|
+
configuration was verified.
|
|
193
|
+
|
|
194
|
+
Review the full plan and its printed file SHA-256. After administrator
|
|
195
|
+
approval, copy the reviewed plan securely to the Registry host, ensure
|
|
196
|
+
PF_INITIAL_GOOGLE_SUBJECT identifies the approved household admin, and
|
|
197
|
+
set PF_APPROVED_ADOPTION_SHA to that exact reviewed file checksum and
|
|
198
|
+
PF_HOUSEHOLD_ADOPTION_PLAN to the local private file. Then run, from the
|
|
199
|
+
trusted host context only:
|
|
200
|
+
|
|
201
|
+
dotnet ProjectForge.HouseholdRegistry.dll admin-import
|
|
202
|
+
|
|
203
|
+
This is the **actual one-time household resource reference migration**.
|
|
204
|
+
It creates five reviewed Google/Cloudflare/Resend reference records in one
|
|
205
|
+
SQLite transaction with an audit receipt, preserves existing matching
|
|
206
|
+
resources, and treats an identical exact plan replay as no-op. It does not
|
|
207
|
+
create new OAuth clients, Resend keys or DNS routes and does not modify PWA
|
|
208
|
+
or its Production data. A different plan must receive a fresh reviewed
|
|
209
|
+
checksum. An operator-reviewed resource reference is not a cryptographic
|
|
210
|
+
attestation of continuing provider availability.
|
|
211
|
+
|
|
212
|
+
## Invite another Developer
|
|
213
|
+
|
|
214
|
+
The initial administrator signs in to the Registry browser with Google,
|
|
215
|
+
creates a one-time 48-hour invitation for the Developer's verified Google
|
|
216
|
+
email and shares the resulting token privately (not in a URL, Git issue or
|
|
217
|
+
terminal logs). Kyle signs into the same Registry origin using his Google
|
|
218
|
+
account and redeems that token. He can then register Projects and view
|
|
219
|
+
approved household references. He still needs separately approved provider
|
|
220
|
+
credentials and Project-specific application authorization.
|
|
221
|
+
|
|
222
|
+
The Registry address itself is a non-secret **explicit locator** shared with
|
|
223
|
+
the Developer. New Project Forge setup must verify that exact HTTPS origin
|
|
224
|
+
and perform authorized discovery; it must never scan household networks or
|
|
225
|
+
copy the Registry database.
|
|
226
|
+
|
|
227
|
+
## Backups and safe recovery
|
|
228
|
+
|
|
229
|
+
Use a trusted SQLite consistent backup or stop the Registry before copying
|
|
230
|
+
its protected volume. Protect the directory and backups with owner-only
|
|
231
|
+
permissions; perform an integrity check and disposable restore rehearsal
|
|
232
|
+
before Production adoption. A schema version newer than the service
|
|
233
|
+
understands fails closed. Backup and restoration must not overwrite PWA's
|
|
234
|
+
independent SQLite database or application-managed migration journals.
|
|
235
|
+
|
|
236
|
+
## What is still required for production and next kit
|
|
237
|
+
|
|
238
|
+
Registry deployment into the actual household, real provider credentials and
|
|
239
|
+
resource verification, controlled PWA authentication adapter/migration,
|
|
240
|
+
existing-account and password compatibility proof, full Google callback and
|
|
241
|
+
hostile Caddy/Tunnel tests, cross-platform New Project setup integration and
|
|
242
|
+
package distribution/installed consumer qualification.
|
|
243
|
+
|
|
244
|
+
These are real release blockers, not solved by the Registry's passing domain
|
|
245
|
+
tests. A successful Registry deployment is also not permission to rewrite
|
|
246
|
+
PWA Production, reset user sessions or install a new provider adapter.
|