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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Revocation MUST require a verified key proof (see the `vault-key-proof` capabili
The requirement is on the grantor-initiated revocation of a designated contact. Envelope clearing that follows from suite revocation or rotation is a consequence of those operations, is governed by *Envelope Invalidation on Key Change*, and is not separately gated here.

#### Scenario: Revoked contact cannot break glass
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** A has designated B as an emergency contact
- **WHEN** A revokes B
- **THEN** the recovery envelope MUST be deleted and any pending request cancelled
Expand Down
16 changes: 8 additions & 8 deletions openspec/specs/emergency-access/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ The system MUST allow a vault owner (grantor), while their vault is unlocked, to
A grantee MUST have an active EncryptionSuite; designating a user with no active suite MUST fail with a clear error (the recovery envelope is encrypted to the grantee's public certificate and cannot be built otherwise).

#### Scenario: Designate a contact with a wait period
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** grantor A is unlocked and user B has an active EncryptionSuite
- **WHEN** A designates B as an emergency contact with access level `view` and a 7-day wait period
- **THEN** the system MUST record the emergency-contact relationship in state `granted`
- **AND** it MUST record the access level and wait period

#### Scenario: Grantee without an EncryptionSuite is rejected
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** user B has never opened Keepiq and has no EncryptionSuite
- **WHEN** grantor A attempts to designate B as an emergency contact
- **THEN** the system MUST return an error indicating the grantee has no encryption suite
Expand All @@ -37,7 +37,7 @@ On designation, the grantor's browser MUST build the recovery envelope entirely
The system MUST allow a designated emergency contact to initiate a break-glass request against a grantor who granted them access. Initiating a request MUST move the relationship to state `requested`, record the request time, start the grantor's configured wait period, and notify the grantor. No key material MUST be released at request time.

#### Scenario: Contact requests emergency access
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** grantor A has designated B as an emergency contact with a 7-day wait period
- **WHEN** B initiates a break-glass request
- **THEN** the relationship MUST move to state `requested` with the request time recorded and the 7-day timer started
Expand All @@ -48,7 +48,7 @@ The system MUST allow a designated emergency contact to initiate a break-glass r
At any time before the wait period elapses, the grantor MUST be able to decline a pending break-glass request. Declining MUST move the relationship out of `requested` (to `declined` or back to `granted`), MUST NOT release the recovery envelope, and MUST be recordable together with an optional revocation of the contact.

#### Scenario: Grantor declines within the wait window
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** B has a break-glass request pending against A and the wait period has not elapsed
- **WHEN** A declines the request
- **THEN** the request MUST be rejected and no recovery envelope MUST be released to B
Expand All @@ -58,21 +58,21 @@ At any time before the wait period elapses, the grantor MUST be able to decline
If the wait period elapses on a `requested` relationship without the grantor declining, the system MUST transition it to `approved`. The server MUST release the recovery envelope to the grantee **only** when the relationship is `approved` and the caller is the named grantee; it MUST refuse the envelope in any other state or to any other caller. Once released, the grantee decrypts the envelope with their **own** in-session private key to recover the grantor's private key in their browser, and MAY then read (view) the grantor's secrets. The grantor MUST be notified when the grantee actually accesses the vault.

#### Scenario: Timer elapses and the grantee gains view access
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** B has a `requested` relationship against A and the 7-day wait period has elapsed with no decline
- **WHEN** the request is evaluated
- **THEN** the relationship MUST transition to `approved`
- **AND** B MUST be able to fetch the recovery envelope and decrypt it with B's own private key to read A's secrets
- **AND** A MUST be notified when B accesses the vault

#### Scenario: Envelope is refused before approval
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** a break-glass request that is still `requested` (wait period not elapsed) or has been `declined`
- **WHEN** the grantee attempts to fetch the recovery envelope
- **THEN** the server MUST refuse to release the envelope

#### Scenario: Envelope is refused to a non-grantee
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** an `approved` emergency-access relationship between grantor A and grantee B
- **WHEN** a user other than B attempts to fetch the recovery envelope
- **THEN** the server MUST refuse to release the envelope
Expand All @@ -81,7 +81,7 @@ If the wait period elapses on a `requested` relationship without the grantor dec
The grantor MUST be able to revoke an emergency contact at any time. Revocation MUST delete the recovery envelope and cancel any pending request, and a revoked contact MUST NOT be able to break glass until re-designated (which rebuilds a fresh envelope).

#### Scenario: Revoked contact cannot break glass
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). A live Playwright run of the DOM flow is deferred: the worktree is not deployed and deploying to the shared dev instance is prohibited.
@e2e exclude State-machine/authorization contract — covered by PHPUnit EmergencyAccessServiceTest (designate/request/decline/approve-by-timeout + the approved+grantee release gate with identical wrong-state/wrong-caller refusal). This waiver covers only that server-side state machine, which is not DOM-observable. The DOM flow itself is not excluded, it is uncovered: src/views/EmergencyAccessView.vue is routed at /emergency-access, has an "Emergency access" menu entry and carries data-testid hooks (emergency-access-view, emergency-access-designate, emergency-grantee-input, emergency-wait-select, emergency-master-input), and the E2E Tests (Playwright) job provisions its own throwaway Nextcloud seeded by tests/e2e/ci-seed.sh. A Playwright spec for it is open work and nothing here claims one exists.
- **GIVEN** A has designated B as an emergency contact
- **WHEN** A revokes B
- **THEN** the recovery envelope MUST be deleted and any pending request cancelled
Expand Down
Loading