feat(postbox): add governed content protection profiles

This commit is contained in:
2026-08-20 03:42:58 +02:00
parent 15d93aaa25
commit 174ee97719
16 changed files with 3917 additions and 383 deletions
+145 -1
View File
@@ -41,11 +41,84 @@ export type PostboxDirectoryItem = {
template_revision_id?: string | null;
holder_count: number;
vacant: boolean;
encryption_profile: PostboxProtectionProfileId;
key_epoch: number;
encryption_vault_id?: string | null;
protection_policy: PostboxProtectionPolicy;
access?: PostboxAccessDecision | null;
resource_revision: number;
etag: string;
};
export type PostboxProtectionProfileId =
| "plaintext_v1"
| "server_envelope_v1"
| "external_e2ee_v1";
export type PostboxProtectionPolicy = {
new_incumbent_history: "all_retained" | "since_assignment" | "bounded_days";
history_days?: number | null;
ordinary_rotation: "rewrap" | "reencrypt";
compromise_rotation: "rewrap" | "reencrypt";
recovery_authority: "disabled" | "user_consent" | "institutional_key_holders" | "dual_control";
recovery_quorum: number;
handover_authority: "user_consent" | "institutional_key_holders" | "dual_control";
handover_quorum: number;
emergency_access: "disabled" | "dual_control";
emergency_quorum: number;
export_authority: "user_consent" | "institutional_key_holders" | "dual_control";
export_quorum: number;
destruction_authority: "institutional_key_holders" | "dual_control";
destruction_quorum: number;
external_recipient_assurance: "disabled" | "email_otp" | "strong_identity";
vacancy_escalation_content_access: "metadata_only";
};
export type PostboxProtectionProfile = {
id: PostboxProtectionProfileId;
label: string;
description: string;
server_can_decrypt: boolean;
requires_encryption_module: boolean;
requires_external_client: boolean;
available: boolean;
standard: boolean;
};
export type PostboxProtectionTransition = {
id: string;
postbox_id: string;
source_profile: string;
target_profile: string;
source_vault_id?: string | null;
target_vault_id?: string | null;
history_mode: string;
authority_mode: string;
required_quorum: number;
evidence_refs: string[];
reason: string;
state: string;
message_count: number;
completed_count: number;
failed_count: number;
requested_by?: string | null;
activated_at?: string | null;
completed_at?: string | null;
resource_revision: number;
etag: string;
configuration_snapshot: Record<string, unknown>;
items: Array<{
id: string;
message_id: string;
source_profile: string;
target_profile: string;
state: string;
source_digest?: string | null;
target_digest?: string | null;
error_code?: string | null;
}>;
};
export type PostboxParticipant = {
kind: string;
reference_type: string;
@@ -93,7 +166,7 @@ export type PostboxMessage = {
producer_resource_id?: string | null;
in_reply_to_message_id?: string | null;
replaces_message_id?: string | null;
encryption_profile: string;
encryption_profile: PostboxProtectionProfileId;
key_epoch: number;
ciphertext_ref?: string | null;
signed_manifest_ref?: string | null;
@@ -213,6 +286,8 @@ export type PostboxTemplateRevision = {
allow_vacant_delivery: boolean;
portal_visible: boolean;
encryption_profile: string;
encryption_vault_id?: string | null;
protection_policy: PostboxProtectionPolicy;
history_policy: Record<string, unknown>;
routing_policy: PostboxRoutingPolicy;
retention_policy: Record<string, unknown>;
@@ -248,6 +323,9 @@ export type PostboxTemplateRevisionPayload = Pick<
| "classification"
| "allow_vacant_delivery"
| "portal_visible"
| "encryption_profile"
| "encryption_vault_id"
| "protection_policy"
| "routing_policy"
>;
@@ -290,12 +368,18 @@ export type PostboxExactCreatePayload = {
address_key?: string | null;
classification: string;
portal_visible: boolean;
encryption_profile: PostboxProtectionProfileId;
encryption_vault_id?: string | null;
protection_policy: PostboxProtectionPolicy;
};
export type PostboxMessageAuthoringPayload = {
idempotency_key: string;
subject: string;
body_text?: string | null;
ciphertext_ref?: string | null;
signed_manifest_ref?: string | null;
wrapped_keys?: PostboxMessage["wrapped_keys"];
classification: string;
participants: PostboxParticipant[];
attachments: PostboxAttachment[];
@@ -469,6 +553,66 @@ export async function listAdminPostboxes(settings: ApiSettings): Promise<Postbox
return response.postboxes;
}
export async function listPostboxProtectionProfiles(
settings: ApiSettings
): Promise<{ standard_profile: PostboxProtectionProfileId; profiles: PostboxProtectionProfile[] }> {
return apiFetch(settings, "/api/v1/postbox/admin/protection-profiles");
}
export async function listPostboxProtectionTransitions(
settings: ApiSettings,
postboxId: string
): Promise<PostboxProtectionTransition[]> {
const response = await apiFetch<{ transitions: PostboxProtectionTransition[] }>(
settings,
`/api/v1/postbox/admin/postboxes/${encodeURIComponent(postboxId)}/protection-transitions`
);
return response.transitions;
}
export function createPostboxProtectionTransition(
settings: ApiSettings,
postbox: PostboxDirectoryItem,
payload: {
idempotency_key: string;
target_profile: PostboxProtectionProfileId;
target_vault_id?: string | null;
history_mode: "future_only" | "migrate_history";
authority_mode: "user_consent" | "institutional_key_holders" | "dual_control";
required_quorum: number;
user_consent_refs: string[];
institutional_authorization_refs: string[];
reason: string;
acknowledge_irreversibility: boolean;
}
): Promise<PostboxProtectionTransition> {
return apiPostJson(
settings,
`/api/v1/postbox/admin/postboxes/${encodeURIComponent(postbox.id)}/protection-transitions`,
{ ...payload, base_revision: postbox.resource_revision },
{ headers: { "If-Match": postbox.etag } }
);
}
export function updatePostboxProtectionPolicy(
settings: ApiSettings,
postbox: PostboxDirectoryItem,
protectionPolicy: PostboxProtectionPolicy
): Promise<PostboxDirectoryItem> {
return apiFetch(
settings,
`/api/v1/postbox/admin/postboxes/${encodeURIComponent(postbox.id)}/protection-policy`,
{
method: "PUT",
headers: { "If-Match": postbox.etag },
body: JSON.stringify({
base_revision: postbox.resource_revision,
protection_policy: protectionPolicy
})
}
);
}
export async function listPostboxOrganizationTargets(
settings: ApiSettings
): Promise<PostboxOrganizationTargets> {