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
16 changes: 8 additions & 8 deletions notes/encryption.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Encryption implementation notes

Overview
## Overview

- ActivityPub supports public-key distribution via actor profiles.
- Leverage ActivityPub public keys where possible to avoid re-inventing
discovery and distribution.
- Goal: enable CaseActors and other actors to send and receive encrypted
messages while preserving semantic routing and handler behavior.

Principles
## Principles

- Prefer standard mechanisms (Actor.publicKey) for discovery and storage.
- Perform decryption before semantic extraction/dispatch so semantics can be
Expand All @@ -18,7 +18,7 @@ Principles
- Default to encrypting outgoing messages when the recipient advertises a
public key.

Incoming messages — where to decrypt
## Incoming messages — where to decrypt

- Decryption should occur upstream of the dispatcher/handler layer. Reasons:
- Semantic extraction depends on object types and fields that may be
Expand All @@ -33,7 +33,7 @@ Incoming messages — where to decrypt
- Ensure authorization and integrity checks (signatures, sender identity)
are performed as part of decryption/validation.

Outgoing messages — strategies and trade-offs
## Outgoing messages — strategies and trade-offs

- Per-recipient messages (recommended):
- For each intended recipient, encrypt a separate message to that
Expand All @@ -50,7 +50,7 @@ Outgoing messages — strategies and trade-offs
- Recommendation: use per-recipient encryption by default; evaluate
multi-recipient schemes only if the message volume or latency requires it.

Public key discovery and rotation
## Public key discovery and rotation

- Read public keys from the recipient actor's profile (ActivityPub
`publicKey` property). Validate the key format and its association with the
Expand All @@ -61,7 +61,7 @@ Public key discovery and rotation
- Fall back to sending an unencrypted message with a warning only if
encryption is explicitly required and no valid key exists.

Implementation guidance
## Implementation guidance

- Store private keys securely (OS key store, HSM, or encrypted configuration).
- Log encryption/decryption actions at INFO or DEBUG levels without exposing
Expand All @@ -74,7 +74,7 @@ Implementation guidance
the referenced activity's ID string (not an inline object) to ensure
rehydration and validation succeed after transport.

Open questions (to decide)
## Open questions (to decide)

- Do we accept the extra network cost of per-recipient messages to simplify
crypto, or do we invest in multi-recipient schemes?
Expand All @@ -83,7 +83,7 @@ Open questions (to decide)
- How should public-key metadata (creation, expiry, usage rules) be surfaced
in actor profiles for automated decision-making?

References
## References

- ActivityPub actor publicKey: <https://docs.joinmastodon.org/spec/activitypub/#publicKey>
- ActivityPub security considerations: <https://docs.joinmastodon.org/spec/security/>
2 changes: 1 addition & 1 deletion vultron/api/v2/routers/datalayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def reset_datalayer(
if init:
from vultron.as_vocab.examples._base import initialize_examples

initialize_examples()
initialize_examples(datalayer=datalayer)
Comment on lines 203 to +205
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The init=True branch behavior was changed here (examples now initialized into the injected datalayer). There doesn't appear to be coverage for DELETE /datalayer/reset/?init=true, and this bug would only show up when the dependency injects a non-singleton DataLayer instance. Please add a regression test that overrides the FastAPI dependency to return a separate DataLayer instance (not created via get_datalayer()), calls the reset endpoint with init=true, and asserts the example objects were created in that injected instance (and not in the global singleton).

Copilot uses AI. Check for mistakes.

return {
"status": "datalayer reset successfully",
Expand Down
5 changes: 3 additions & 2 deletions vultron/as_vocab/examples/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import random
from uuid import uuid4

from vultron.api.v2.datalayer.abc import DataLayer
from vultron.as_vocab.base.base import as_Base
from vultron.as_vocab.base.objects.actors import as_Organization, as_Person
from vultron.as_vocab.objects.vulnerability_case import VulnerabilityCase
Expand Down Expand Up @@ -103,11 +104,11 @@ def gen_report() -> VulnerabilityReport:
return _REPORT


def initialize_examples() -> None:
def initialize_examples(datalayer: DataLayer | None = None) -> None:
from vultron.api.v2.datalayer.db_record import Record
from vultron.api.v2.datalayer.tinydb_backend import get_datalayer

dl = get_datalayer()
dl = datalayer if datalayer is not None else get_datalayer()
for obj in [_FINDER, _VENDOR, _COORDINATOR, _REPORT]:
record = Record.from_obj(obj)
dl.create(record)
Expand Down