@company-semantics/contracts 2.5.0 → 2.7.0
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/package.json +2 -2
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +297 -1
- package/src/generated/openapi-routes.ts +9 -0
- package/src/index.ts +8 -3
- package/src/meetings/index.ts +34 -0
- package/src/meetings/schemas.ts +276 -0
- package/src/org/__tests__/org-units.test.ts +2 -2
- package/src/security/org-secrets.ts +37 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@company-semantics/contracts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"zod": "^4.4.3"
|
|
116
116
|
},
|
|
117
117
|
"devDependencies": {
|
|
118
|
-
"@types/node": "^
|
|
118
|
+
"@types/node": "^22.19.19",
|
|
119
119
|
"husky": "^9.1.7",
|
|
120
120
|
"lint-staged": "^17.0.5",
|
|
121
121
|
"markdownlint-cli2": "^0.22.1",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = 'dfd003162bb3' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = 'dfd003162bb3d11c451864139ca8ba09e494cfd652b177f8b5ee8611c0f54e2a' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -1730,6 +1730,74 @@ export interface paths {
|
|
|
1730
1730
|
patch: operations["updateOrgUnitDelegationScopes"];
|
|
1731
1731
|
trace?: never;
|
|
1732
1732
|
};
|
|
1733
|
+
"/api/orgs/{orgId}/admins": {
|
|
1734
|
+
parameters: {
|
|
1735
|
+
query?: never;
|
|
1736
|
+
header?: never;
|
|
1737
|
+
path?: never;
|
|
1738
|
+
cookie?: never;
|
|
1739
|
+
};
|
|
1740
|
+
get?: never;
|
|
1741
|
+
put?: never;
|
|
1742
|
+
/** Grant org-admin authority to a user */
|
|
1743
|
+
post: operations["grantOrgAdmin"];
|
|
1744
|
+
delete?: never;
|
|
1745
|
+
options?: never;
|
|
1746
|
+
head?: never;
|
|
1747
|
+
patch?: never;
|
|
1748
|
+
trace?: never;
|
|
1749
|
+
};
|
|
1750
|
+
"/api/orgs/{orgId}/admins/{userId}": {
|
|
1751
|
+
parameters: {
|
|
1752
|
+
query?: never;
|
|
1753
|
+
header?: never;
|
|
1754
|
+
path?: never;
|
|
1755
|
+
cookie?: never;
|
|
1756
|
+
};
|
|
1757
|
+
get?: never;
|
|
1758
|
+
put?: never;
|
|
1759
|
+
post?: never;
|
|
1760
|
+
/** Revoke org-admin authority from a user */
|
|
1761
|
+
delete: operations["revokeOrgAdmin"];
|
|
1762
|
+
options?: never;
|
|
1763
|
+
head?: never;
|
|
1764
|
+
patch?: never;
|
|
1765
|
+
trace?: never;
|
|
1766
|
+
};
|
|
1767
|
+
"/api/org-units/{unitId}/structural-leaders": {
|
|
1768
|
+
parameters: {
|
|
1769
|
+
query?: never;
|
|
1770
|
+
header?: never;
|
|
1771
|
+
path?: never;
|
|
1772
|
+
cookie?: never;
|
|
1773
|
+
};
|
|
1774
|
+
get?: never;
|
|
1775
|
+
put?: never;
|
|
1776
|
+
/** Assign a user as structural leader of an org unit */
|
|
1777
|
+
post: operations["assignOrgUnitStructuralLeader"];
|
|
1778
|
+
delete?: never;
|
|
1779
|
+
options?: never;
|
|
1780
|
+
head?: never;
|
|
1781
|
+
patch?: never;
|
|
1782
|
+
trace?: never;
|
|
1783
|
+
};
|
|
1784
|
+
"/api/org-units/{unitId}/structural-leaders/{userId}": {
|
|
1785
|
+
parameters: {
|
|
1786
|
+
query?: never;
|
|
1787
|
+
header?: never;
|
|
1788
|
+
path?: never;
|
|
1789
|
+
cookie?: never;
|
|
1790
|
+
};
|
|
1791
|
+
get?: never;
|
|
1792
|
+
put?: never;
|
|
1793
|
+
post?: never;
|
|
1794
|
+
/** Remove a user as structural leader of an org unit */
|
|
1795
|
+
delete: operations["removeOrgUnitStructuralLeader"];
|
|
1796
|
+
options?: never;
|
|
1797
|
+
head?: never;
|
|
1798
|
+
patch?: never;
|
|
1799
|
+
trace?: never;
|
|
1800
|
+
};
|
|
1733
1801
|
"/api/org-units/{unitId}/memberships": {
|
|
1734
1802
|
parameters: {
|
|
1735
1803
|
query?: never;
|
|
@@ -2194,6 +2262,23 @@ export interface paths {
|
|
|
2194
2262
|
patch?: never;
|
|
2195
2263
|
trace?: never;
|
|
2196
2264
|
};
|
|
2265
|
+
"/api/me/meetings/recordings": {
|
|
2266
|
+
parameters: {
|
|
2267
|
+
query?: never;
|
|
2268
|
+
header?: never;
|
|
2269
|
+
path?: never;
|
|
2270
|
+
cookie?: never;
|
|
2271
|
+
};
|
|
2272
|
+
get?: never;
|
|
2273
|
+
put?: never;
|
|
2274
|
+
/** Mint a transcription session grant for a new recording */
|
|
2275
|
+
post: operations["issueMeetingRecordingGrant"];
|
|
2276
|
+
delete?: never;
|
|
2277
|
+
options?: never;
|
|
2278
|
+
head?: never;
|
|
2279
|
+
patch?: never;
|
|
2280
|
+
trace?: never;
|
|
2281
|
+
};
|
|
2197
2282
|
"/api/internal-admin/impersonate/start": {
|
|
2198
2283
|
parameters: {
|
|
2199
2284
|
query?: never;
|
|
@@ -3234,7 +3319,7 @@ export interface components {
|
|
|
3234
3319
|
level: "none" | "viewer" | "commenter" | "editor";
|
|
3235
3320
|
reasons: {
|
|
3236
3321
|
/** @enum {string} */
|
|
3237
|
-
source: "org_rbac" | "sharing_policy" | "unit_baseline" | "acl_grant" | "doc_ownership";
|
|
3322
|
+
source: "org_rbac" | "sharing_policy" | "unit_baseline" | "unit_delegation" | "acl_grant" | "doc_ownership";
|
|
3238
3323
|
detail: string;
|
|
3239
3324
|
}[];
|
|
3240
3325
|
canShare: boolean;
|
|
@@ -3592,6 +3677,40 @@ export interface components {
|
|
|
3592
3677
|
note: string | null;
|
|
3593
3678
|
scopes: ("doc.edit" | "doc.acl" | "members.manage")[];
|
|
3594
3679
|
};
|
|
3680
|
+
OrgAdminGrantResponse: {
|
|
3681
|
+
/** Format: uuid */
|
|
3682
|
+
id: string;
|
|
3683
|
+
/** Format: uuid */
|
|
3684
|
+
orgId: string;
|
|
3685
|
+
/** Format: uuid */
|
|
3686
|
+
orgUnitId: string;
|
|
3687
|
+
/** Format: uuid */
|
|
3688
|
+
userId: string;
|
|
3689
|
+
/** @enum {string} */
|
|
3690
|
+
mechanism: "structural" | "rbac";
|
|
3691
|
+
/** Format: uuid */
|
|
3692
|
+
grantedByUserId: string;
|
|
3693
|
+
grantedAt: string;
|
|
3694
|
+
revokedAt: string | null;
|
|
3695
|
+
reason: string | null;
|
|
3696
|
+
};
|
|
3697
|
+
StructuralLeaderAssignResponse: {
|
|
3698
|
+
/** Format: uuid */
|
|
3699
|
+
id: string;
|
|
3700
|
+
/** Format: uuid */
|
|
3701
|
+
orgId: string;
|
|
3702
|
+
/** Format: uuid */
|
|
3703
|
+
orgUnitId: string;
|
|
3704
|
+
/** Format: uuid */
|
|
3705
|
+
userId: string;
|
|
3706
|
+
/** @enum {string} */
|
|
3707
|
+
mechanism: "structural" | "rbac";
|
|
3708
|
+
/** Format: uuid */
|
|
3709
|
+
grantedByUserId: string;
|
|
3710
|
+
grantedAt: string;
|
|
3711
|
+
revokedAt: string | null;
|
|
3712
|
+
reason: string | null;
|
|
3713
|
+
};
|
|
3595
3714
|
OrgUnitMembershipListResponse: {
|
|
3596
3715
|
/** Format: uuid */
|
|
3597
3716
|
unitId: string;
|
|
@@ -3802,6 +3921,43 @@ export interface components {
|
|
|
3802
3921
|
UpdateWorkItemTitleRequest: {
|
|
3803
3922
|
title: string;
|
|
3804
3923
|
};
|
|
3924
|
+
/** @description Provider-agnostic grant the mac shell consumes. Mac never sees provider name (INV-MTG-15). */
|
|
3925
|
+
TranscriptionSessionGrant: {
|
|
3926
|
+
/** @description ULID; unified trace key for the recording (INV-MTG-7). */
|
|
3927
|
+
sessionId: string;
|
|
3928
|
+
/** Format: uri */
|
|
3929
|
+
connectUrl: string;
|
|
3930
|
+
authHeaders: {
|
|
3931
|
+
[key: string]: string;
|
|
3932
|
+
};
|
|
3933
|
+
/** Format: date-time */
|
|
3934
|
+
expiresAt: string;
|
|
3935
|
+
audioConfig: {
|
|
3936
|
+
/** @constant */
|
|
3937
|
+
sampleRateHz: 16000;
|
|
3938
|
+
/** @constant */
|
|
3939
|
+
channels: 2;
|
|
3940
|
+
/** @constant */
|
|
3941
|
+
encoding: "linear16";
|
|
3942
|
+
/** @constant */
|
|
3943
|
+
chunkMs: 200;
|
|
3944
|
+
};
|
|
3945
|
+
capabilities: {
|
|
3946
|
+
interimResults: boolean;
|
|
3947
|
+
multichannelDiarization: boolean;
|
|
3948
|
+
reconnectionTokens: boolean;
|
|
3949
|
+
};
|
|
3950
|
+
};
|
|
3951
|
+
/** @description Request a transcription session grant for a new recording. */
|
|
3952
|
+
IssueMeetingRecordingGrantRequest: {
|
|
3953
|
+
/** @description ULID; unified trace key for the recording (INV-MTG-7). */
|
|
3954
|
+
clientRecordingId: string;
|
|
3955
|
+
/**
|
|
3956
|
+
* @description Auto-detected meeting app that owns the system audio stream.
|
|
3957
|
+
* @enum {string}
|
|
3958
|
+
*/
|
|
3959
|
+
detectedApp: "zoom" | "teams" | "meet-browser" | "slack-huddle" | "manual";
|
|
3960
|
+
};
|
|
3805
3961
|
ImpersonationSessionResponse: {
|
|
3806
3962
|
session: {
|
|
3807
3963
|
impersonationSessionId: string;
|
|
@@ -6603,6 +6759,108 @@ export interface operations {
|
|
|
6603
6759
|
};
|
|
6604
6760
|
};
|
|
6605
6761
|
};
|
|
6762
|
+
grantOrgAdmin: {
|
|
6763
|
+
parameters: {
|
|
6764
|
+
query?: never;
|
|
6765
|
+
header?: never;
|
|
6766
|
+
path: {
|
|
6767
|
+
orgId: string;
|
|
6768
|
+
};
|
|
6769
|
+
cookie?: never;
|
|
6770
|
+
};
|
|
6771
|
+
requestBody: {
|
|
6772
|
+
content: {
|
|
6773
|
+
"application/json": {
|
|
6774
|
+
/** Format: uuid */
|
|
6775
|
+
userId: string;
|
|
6776
|
+
reason?: string;
|
|
6777
|
+
};
|
|
6778
|
+
};
|
|
6779
|
+
};
|
|
6780
|
+
responses: {
|
|
6781
|
+
/** @description Created authority overlay row */
|
|
6782
|
+
201: {
|
|
6783
|
+
headers: {
|
|
6784
|
+
[name: string]: unknown;
|
|
6785
|
+
};
|
|
6786
|
+
content: {
|
|
6787
|
+
"application/json": components["schemas"]["OrgAdminGrantResponse"];
|
|
6788
|
+
};
|
|
6789
|
+
};
|
|
6790
|
+
};
|
|
6791
|
+
};
|
|
6792
|
+
revokeOrgAdmin: {
|
|
6793
|
+
parameters: {
|
|
6794
|
+
query?: never;
|
|
6795
|
+
header?: never;
|
|
6796
|
+
path: {
|
|
6797
|
+
orgId: string;
|
|
6798
|
+
userId: string;
|
|
6799
|
+
};
|
|
6800
|
+
cookie?: never;
|
|
6801
|
+
};
|
|
6802
|
+
requestBody?: never;
|
|
6803
|
+
responses: {
|
|
6804
|
+
/** @description Authority revoked */
|
|
6805
|
+
204: {
|
|
6806
|
+
headers: {
|
|
6807
|
+
[name: string]: unknown;
|
|
6808
|
+
};
|
|
6809
|
+
content?: never;
|
|
6810
|
+
};
|
|
6811
|
+
};
|
|
6812
|
+
};
|
|
6813
|
+
assignOrgUnitStructuralLeader: {
|
|
6814
|
+
parameters: {
|
|
6815
|
+
query?: never;
|
|
6816
|
+
header?: never;
|
|
6817
|
+
path: {
|
|
6818
|
+
unitId: string;
|
|
6819
|
+
};
|
|
6820
|
+
cookie?: never;
|
|
6821
|
+
};
|
|
6822
|
+
requestBody: {
|
|
6823
|
+
content: {
|
|
6824
|
+
"application/json": {
|
|
6825
|
+
/** Format: uuid */
|
|
6826
|
+
userId: string;
|
|
6827
|
+
reason?: string;
|
|
6828
|
+
};
|
|
6829
|
+
};
|
|
6830
|
+
};
|
|
6831
|
+
responses: {
|
|
6832
|
+
/** @description Created structural-leader overlay row */
|
|
6833
|
+
201: {
|
|
6834
|
+
headers: {
|
|
6835
|
+
[name: string]: unknown;
|
|
6836
|
+
};
|
|
6837
|
+
content: {
|
|
6838
|
+
"application/json": components["schemas"]["StructuralLeaderAssignResponse"];
|
|
6839
|
+
};
|
|
6840
|
+
};
|
|
6841
|
+
};
|
|
6842
|
+
};
|
|
6843
|
+
removeOrgUnitStructuralLeader: {
|
|
6844
|
+
parameters: {
|
|
6845
|
+
query?: never;
|
|
6846
|
+
header?: never;
|
|
6847
|
+
path: {
|
|
6848
|
+
unitId: string;
|
|
6849
|
+
userId: string;
|
|
6850
|
+
};
|
|
6851
|
+
cookie?: never;
|
|
6852
|
+
};
|
|
6853
|
+
requestBody?: never;
|
|
6854
|
+
responses: {
|
|
6855
|
+
/** @description Structural-leader authority revoked */
|
|
6856
|
+
204: {
|
|
6857
|
+
headers: {
|
|
6858
|
+
[name: string]: unknown;
|
|
6859
|
+
};
|
|
6860
|
+
content?: never;
|
|
6861
|
+
};
|
|
6862
|
+
};
|
|
6863
|
+
};
|
|
6606
6864
|
listOrgUnitMemberships: {
|
|
6607
6865
|
parameters: {
|
|
6608
6866
|
query?: never;
|
|
@@ -7283,6 +7541,44 @@ export interface operations {
|
|
|
7283
7541
|
};
|
|
7284
7542
|
};
|
|
7285
7543
|
};
|
|
7544
|
+
issueMeetingRecordingGrant: {
|
|
7545
|
+
parameters: {
|
|
7546
|
+
query?: never;
|
|
7547
|
+
header?: never;
|
|
7548
|
+
path?: never;
|
|
7549
|
+
cookie?: never;
|
|
7550
|
+
};
|
|
7551
|
+
requestBody: {
|
|
7552
|
+
content: {
|
|
7553
|
+
"application/json": components["schemas"]["IssueMeetingRecordingGrantRequest"];
|
|
7554
|
+
};
|
|
7555
|
+
};
|
|
7556
|
+
responses: {
|
|
7557
|
+
/** @description Provider-agnostic transcription session grant */
|
|
7558
|
+
200: {
|
|
7559
|
+
headers: {
|
|
7560
|
+
[name: string]: unknown;
|
|
7561
|
+
};
|
|
7562
|
+
content: {
|
|
7563
|
+
"application/json": components["schemas"]["TranscriptionSessionGrant"];
|
|
7564
|
+
};
|
|
7565
|
+
};
|
|
7566
|
+
/** @description Rate limit exceeded (10 issuances/user/hour) */
|
|
7567
|
+
429: {
|
|
7568
|
+
headers: {
|
|
7569
|
+
[name: string]: unknown;
|
|
7570
|
+
};
|
|
7571
|
+
content?: never;
|
|
7572
|
+
};
|
|
7573
|
+
/** @description Transcription provider unavailable */
|
|
7574
|
+
503: {
|
|
7575
|
+
headers: {
|
|
7576
|
+
[name: string]: unknown;
|
|
7577
|
+
};
|
|
7578
|
+
content?: never;
|
|
7579
|
+
};
|
|
7580
|
+
};
|
|
7581
|
+
};
|
|
7286
7582
|
startImpersonation: {
|
|
7287
7583
|
parameters: {
|
|
7288
7584
|
query?: never;
|
|
@@ -57,14 +57,19 @@ export const openApiRoutes = {
|
|
|
57
57
|
'/api/org-units/{unitId}/ancestors': ['GET'],
|
|
58
58
|
'/api/org-units/{unitId}/archive': ['POST'],
|
|
59
59
|
'/api/org-units/{unitId}/children': ['GET'],
|
|
60
|
+
'/api/org-units/{unitId}/delegations': ['POST'],
|
|
61
|
+
'/api/org-units/{unitId}/delegations/{id}': ['DELETE', 'PATCH'],
|
|
60
62
|
'/api/org-units/{unitId}/descendants': ['GET'],
|
|
61
63
|
'/api/org-units/{unitId}/memberships': ['GET', 'POST'],
|
|
62
64
|
'/api/org-units/{unitId}/memberships/{userId}': ['DELETE'],
|
|
63
65
|
'/api/org-units/{unitId}/memberships/{userId}/role': ['PUT'],
|
|
66
|
+
'/api/org-units/{unitId}/owners': ['GET'],
|
|
64
67
|
'/api/org-units/{unitId}/permissions': ['GET'],
|
|
65
68
|
'/api/org-units/{unitId}/relationships': ['GET', 'POST'],
|
|
66
69
|
'/api/org-units/{unitId}/reorder': ['POST'],
|
|
67
70
|
'/api/org-units/{unitId}/reparent': ['POST'],
|
|
71
|
+
'/api/org-units/{unitId}/structural-leaders': ['POST'],
|
|
72
|
+
'/api/org-units/{unitId}/structural-leaders/{userId}': ['DELETE'],
|
|
68
73
|
'/api/org/cancel-deletion': ['POST'],
|
|
69
74
|
'/api/org/delete': ['POST'],
|
|
70
75
|
'/api/org/deletion-eligibility': ['GET'],
|
|
@@ -75,6 +80,8 @@ export const openApiRoutes = {
|
|
|
75
80
|
'/api/org/transfer-ownership/accept': ['POST'],
|
|
76
81
|
'/api/org/transfer-ownership/preview': ['POST'],
|
|
77
82
|
'/api/org/transfer-ownership/status': ['GET'],
|
|
83
|
+
'/api/orgs/{orgId}/admins': ['POST'],
|
|
84
|
+
'/api/orgs/{orgId}/admins/{userId}': ['DELETE'],
|
|
78
85
|
'/api/orgs/{orgId}/ai-usage': ['GET'],
|
|
79
86
|
'/api/orgs/{orgId}/billing': ['GET'],
|
|
80
87
|
'/api/orgs/{orgId}/budget-config': ['GET', 'PUT'],
|
|
@@ -93,6 +100,8 @@ export const openApiRoutes = {
|
|
|
93
100
|
'/api/user/profile': ['PATCH'],
|
|
94
101
|
'/api/user/resync-slack-avatar': ['POST'],
|
|
95
102
|
'/api/users/org-chart': ['GET'],
|
|
103
|
+
'/api/users/org-chart/import': ['POST'],
|
|
104
|
+
'/api/users/org-chart/import/{operationId}/retry': ['POST'],
|
|
96
105
|
'/api/work-items/{id}': ['GET'],
|
|
97
106
|
'/api/work-items/{id}/content': ['PUT'],
|
|
98
107
|
'/api/work-items/{id}/title': ['PUT'],
|
package/src/index.ts
CHANGED
|
@@ -663,7 +663,8 @@ export { openApiRoutes, type OpenApiRoute, type OpenApiMethod } from './generate
|
|
|
663
663
|
export type { Secret } from './security/index'
|
|
664
664
|
export { wrapSecret, unwrapSecret } from './security/index'
|
|
665
665
|
|
|
666
|
-
// Org
|
|
666
|
+
// Org secrets DTO schemas (PRD-00629)
|
|
667
|
+
// @see src/security/org-secrets.ts for invariants (no value field on summary)
|
|
667
668
|
export {
|
|
668
669
|
UsageClassSchema,
|
|
669
670
|
OrgSecretsActionSchema,
|
|
@@ -672,7 +673,7 @@ export {
|
|
|
672
673
|
CreateSecretRequestSchema,
|
|
673
674
|
RotateSecretRequestSchema,
|
|
674
675
|
DisableSecretRequestSchema,
|
|
675
|
-
} from './security/
|
|
676
|
+
} from './security/index'
|
|
676
677
|
export type {
|
|
677
678
|
UsageClass,
|
|
678
679
|
OrgSecretsAction,
|
|
@@ -681,7 +682,7 @@ export type {
|
|
|
681
682
|
CreateSecretRequest,
|
|
682
683
|
RotateSecretRequest,
|
|
683
684
|
DisableSecretRequest,
|
|
684
|
-
} from './security/
|
|
685
|
+
} from './security/index'
|
|
685
686
|
|
|
686
687
|
// Analytics response metadata (shared vocabulary for OLTP/OLAP separation)
|
|
687
688
|
// @see ADR-CTRL-053 for design rationale
|
|
@@ -742,3 +743,7 @@ export type {
|
|
|
742
743
|
ControllerInput,
|
|
743
744
|
ControllerOutput,
|
|
744
745
|
} from './autotune'
|
|
746
|
+
|
|
747
|
+
// Meeting recorder vocabulary (PRD-00651)
|
|
748
|
+
// @see ./meetings/schemas.ts for invariants
|
|
749
|
+
export * from './meetings'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meeting recorder vocabulary barrel (PRD-00651).
|
|
3
|
+
*
|
|
4
|
+
* @see ./schemas.ts for the full schema definitions and invariants.
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
RecordingIdSchema,
|
|
8
|
+
RecordingSourceSchema,
|
|
9
|
+
DetectedMeetingAppSchema,
|
|
10
|
+
TranscriptionProviderSchema,
|
|
11
|
+
RecordingStatusSchema,
|
|
12
|
+
RecordingQualitySchema,
|
|
13
|
+
MeetingVisibilitySchema,
|
|
14
|
+
SourceSegmentSchema,
|
|
15
|
+
TranscriptChunkSchema,
|
|
16
|
+
RecordingEventSchema,
|
|
17
|
+
TranscriptionSessionGrantSchema,
|
|
18
|
+
MeetingMetadataProjectionSchema,
|
|
19
|
+
} from './schemas';
|
|
20
|
+
|
|
21
|
+
export type {
|
|
22
|
+
RecordingId,
|
|
23
|
+
RecordingSource,
|
|
24
|
+
DetectedMeetingApp,
|
|
25
|
+
TranscriptionProvider,
|
|
26
|
+
RecordingStatus,
|
|
27
|
+
RecordingQuality,
|
|
28
|
+
MeetingVisibility,
|
|
29
|
+
SourceSegment,
|
|
30
|
+
TranscriptChunk,
|
|
31
|
+
RecordingEvent,
|
|
32
|
+
TranscriptionSessionGrant,
|
|
33
|
+
MeetingMetadataProjection,
|
|
34
|
+
} from './schemas';
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meeting recorder shared vocabulary (PRD-00651).
|
|
3
|
+
*
|
|
4
|
+
* Zod schemas for the meeting capture / transcription pipeline. Consumed by
|
|
5
|
+
* the mac shell (capture runtime), backend (persistence + event projection),
|
|
6
|
+
* and app (UI surfaces). Lives in contracts because the boundary crosses
|
|
7
|
+
* three independent repos and breaking it would require coordinated releases
|
|
8
|
+
* (see CONTRACTS_POLICY.md promotion rule).
|
|
9
|
+
*
|
|
10
|
+
* INVARIANTS this vocabulary enforces:
|
|
11
|
+
* - RecordingId is the unified trace key (INV-MTG-7) — ULID, sortable,
|
|
12
|
+
* opaque to consumers.
|
|
13
|
+
* - The transcription grant is provider-agnostic (INV-MTG-15): the mac
|
|
14
|
+
* shell never sees a provider name; only `connectUrl` + `authHeaders` +
|
|
15
|
+
* audio config.
|
|
16
|
+
* - Visibility is meeting-scoped by default (`meeting_only`) and only
|
|
17
|
+
* widens via explicit policy decisions.
|
|
18
|
+
*
|
|
19
|
+
* Per the durable user preference `feedback_structured_metadata_not_json_strings`,
|
|
20
|
+
* descriptions live in `.meta({ description })` — structured metadata, not
|
|
21
|
+
* stringified JSON blobs on the schema.
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Recording identity
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ULID (Crockford base32, 26 chars). Used as the unified trace key spanning
|
|
31
|
+
* mac → backend → app per INV-MTG-7. Sortable by encoded timestamp prefix so
|
|
32
|
+
* recordings can be ordered without a separate `createdAt` column read.
|
|
33
|
+
*/
|
|
34
|
+
export const RecordingIdSchema = z
|
|
35
|
+
.string()
|
|
36
|
+
.regex(/^[0-9A-HJKMNP-TV-Z]{26}$/, 'ULID required')
|
|
37
|
+
.meta({ description: 'ULID; unified trace key for the recording (INV-MTG-7).' });
|
|
38
|
+
export type RecordingId = z.infer<typeof RecordingIdSchema>;
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Enums (vocabulary)
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
/** Which capture stream a chunk came from. Two channels: mic (channel 0) and system (channel 1). */
|
|
45
|
+
export const RecordingSourceSchema = z.enum(['mic', 'system']).meta({
|
|
46
|
+
description: 'Capture stream identity. Mic = channel 0, system audio = channel 1.',
|
|
47
|
+
});
|
|
48
|
+
export type RecordingSource = z.infer<typeof RecordingSourceSchema>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Which meeting app the system audio is coming from. `manual` covers the
|
|
52
|
+
* "user started a recording with no detected app" debug path. The
|
|
53
|
+
* `meet-browser` variant covers Google Meet running inside a browser tab,
|
|
54
|
+
* which the BrowserAudioDetector heuristic identifies (PRD-00655).
|
|
55
|
+
*/
|
|
56
|
+
export const DetectedMeetingAppSchema = z
|
|
57
|
+
.enum(['zoom', 'teams', 'meet-browser', 'slack-huddle', 'manual'])
|
|
58
|
+
.meta({ description: 'Auto-detected meeting app that owns the system audio stream.' });
|
|
59
|
+
export type DetectedMeetingApp = z.infer<typeof DetectedMeetingAppSchema>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Transcription provider class. The mac shell never sees this — it's a
|
|
63
|
+
* backend / app concern for routing + billing visibility (INV-MTG-15 ensures
|
|
64
|
+
* the mac side stays provider-opaque).
|
|
65
|
+
*/
|
|
66
|
+
export const TranscriptionProviderSchema = z
|
|
67
|
+
.enum(['deepgram', 'assemblyai', 'whisper-local'])
|
|
68
|
+
.meta({ description: 'Server-side selection of transcription provider; never crosses to mac.' });
|
|
69
|
+
export type TranscriptionProvider = z.infer<typeof TranscriptionProviderSchema>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Lifecycle states for a recording entity. `partial-transcript-network` is
|
|
73
|
+
* the explicit non-binary state for "audio captured but the transcript
|
|
74
|
+
* stream dropped mid-session" — keeping it distinct from `failed` lets the
|
|
75
|
+
* UI surface a remediation hint without losing the captured artifact.
|
|
76
|
+
*/
|
|
77
|
+
export const RecordingStatusSchema = z
|
|
78
|
+
.enum([
|
|
79
|
+
'draft',
|
|
80
|
+
'recording',
|
|
81
|
+
'processing',
|
|
82
|
+
'finalized',
|
|
83
|
+
'failed',
|
|
84
|
+
'partial-transcript-network',
|
|
85
|
+
'cancelled',
|
|
86
|
+
])
|
|
87
|
+
.meta({ description: 'Coarse lifecycle state for a recording entity.' });
|
|
88
|
+
export type RecordingStatus = z.infer<typeof RecordingStatusSchema>;
|
|
89
|
+
|
|
90
|
+
/** Quality signal emitted by the capture runtime. Used to surface degradation in the UI. */
|
|
91
|
+
export const RecordingQualitySchema = z.enum(['clean', 'degraded']).meta({
|
|
92
|
+
description: 'Capture-runtime quality signal. `degraded` triggers a UI hint.',
|
|
93
|
+
});
|
|
94
|
+
export type RecordingQuality = z.infer<typeof RecordingQualitySchema>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Visibility band for the finalized meeting projection. `meeting_only` is the
|
|
98
|
+
* default — captured material stays visible only to attendees of the meeting
|
|
99
|
+
* itself. `shared` opens it to anyone the owner explicitly shares with;
|
|
100
|
+
* `org` opens it to the org; `finalized_private` is the explicit "private
|
|
101
|
+
* even after finalization" lock per the participant-only constraint
|
|
102
|
+
* (`feedback_retrieval_default_off_for_noisy_sources`).
|
|
103
|
+
*/
|
|
104
|
+
export const MeetingVisibilitySchema = z
|
|
105
|
+
.enum(['meeting_only', 'shared', 'org', 'finalized_private'])
|
|
106
|
+
.meta({
|
|
107
|
+
description:
|
|
108
|
+
'Visibility band for the finalized meeting projection. Default `meeting_only`.',
|
|
109
|
+
});
|
|
110
|
+
export type MeetingVisibility = z.infer<typeof MeetingVisibilitySchema>;
|
|
111
|
+
|
|
112
|
+
// =============================================================================
|
|
113
|
+
// Source segments (the per-capture-stream view of a recording)
|
|
114
|
+
// =============================================================================
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A continuous segment captured from a single stream within a recording.
|
|
118
|
+
* One recording has at least one segment per active source. New segments
|
|
119
|
+
* are emitted when the upstream signal pauses and resumes.
|
|
120
|
+
*/
|
|
121
|
+
export const SourceSegmentSchema = z
|
|
122
|
+
.object({
|
|
123
|
+
recordingId: RecordingIdSchema,
|
|
124
|
+
source: RecordingSourceSchema,
|
|
125
|
+
sequence: z.number().int().nonnegative(),
|
|
126
|
+
startedAtMs: z.number().int().nonnegative(),
|
|
127
|
+
endedAtMs: z.number().int().nonnegative().nullable(),
|
|
128
|
+
sampleRateHz: z.literal(16000),
|
|
129
|
+
channels: z.literal(1),
|
|
130
|
+
})
|
|
131
|
+
.meta({ description: 'Per-source continuous capture segment within a recording.' });
|
|
132
|
+
export type SourceSegment = z.infer<typeof SourceSegmentSchema>;
|
|
133
|
+
|
|
134
|
+
// =============================================================================
|
|
135
|
+
// Transcript chunks (the per-window result of transcription)
|
|
136
|
+
// =============================================================================
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* A single transcript window from the provider. `final = true` means the
|
|
140
|
+
* provider has committed to this text; `false` is an interim hypothesis.
|
|
141
|
+
* The mac shell forwards both kinds — the UI is responsible for collapsing
|
|
142
|
+
* interim results on top of the latest commit.
|
|
143
|
+
*/
|
|
144
|
+
export const TranscriptChunkSchema = z
|
|
145
|
+
.object({
|
|
146
|
+
recordingId: RecordingIdSchema,
|
|
147
|
+
source: RecordingSourceSchema,
|
|
148
|
+
sequence: z.number().int().nonnegative(),
|
|
149
|
+
startedAtMs: z.number().int().nonnegative(),
|
|
150
|
+
endedAtMs: z.number().int().nonnegative(),
|
|
151
|
+
text: z.string(),
|
|
152
|
+
final: z.boolean(),
|
|
153
|
+
speakerLabel: z.string().nullable().optional(),
|
|
154
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
155
|
+
})
|
|
156
|
+
.meta({ description: 'A single transcript window (interim or final) from the provider.' });
|
|
157
|
+
export type TranscriptChunk = z.infer<typeof TranscriptChunkSchema>;
|
|
158
|
+
|
|
159
|
+
// =============================================================================
|
|
160
|
+
// Recording events (the event stream that drives backend persistence)
|
|
161
|
+
// =============================================================================
|
|
162
|
+
|
|
163
|
+
const SessionOpenedEventSchema = z.object({
|
|
164
|
+
kind: z.literal('session_opened'),
|
|
165
|
+
recordingId: RecordingIdSchema,
|
|
166
|
+
atMs: z.number().int().nonnegative(),
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const ChunkBatchEventSchema = z.object({
|
|
170
|
+
kind: z.literal('chunk_batch'),
|
|
171
|
+
recordingId: RecordingIdSchema,
|
|
172
|
+
chunks: z.array(TranscriptChunkSchema).min(1),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const QualityDegradedEventSchema = z.object({
|
|
176
|
+
kind: z.literal('quality_degraded'),
|
|
177
|
+
recordingId: RecordingIdSchema,
|
|
178
|
+
reason: z.string().min(1),
|
|
179
|
+
atMs: z.number().int().nonnegative(),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const SessionStoppedEventSchema = z.object({
|
|
183
|
+
kind: z.literal('session_stopped'),
|
|
184
|
+
recordingId: RecordingIdSchema,
|
|
185
|
+
atMs: z.number().int().nonnegative(),
|
|
186
|
+
expectedLastSequence: z.number().int().nonnegative(),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Discriminated union of events emitted by the mac shell during a recording
|
|
191
|
+
* session. The backend consumes these and projects them onto the
|
|
192
|
+
* `MeetingMetadataProjection` row.
|
|
193
|
+
*
|
|
194
|
+
* Ordering invariant: `session_opened` is always the first event for a
|
|
195
|
+
* recordingId; `session_stopped` is always the last. `chunk_batch` and
|
|
196
|
+
* `quality_degraded` may interleave.
|
|
197
|
+
*/
|
|
198
|
+
export const RecordingEventSchema = z
|
|
199
|
+
.discriminatedUnion('kind', [
|
|
200
|
+
SessionOpenedEventSchema,
|
|
201
|
+
ChunkBatchEventSchema,
|
|
202
|
+
QualityDegradedEventSchema,
|
|
203
|
+
SessionStoppedEventSchema,
|
|
204
|
+
])
|
|
205
|
+
.meta({
|
|
206
|
+
description:
|
|
207
|
+
'Event emitted by the capture runtime during a recording session (INV-MTG ordering).',
|
|
208
|
+
});
|
|
209
|
+
export type RecordingEvent = z.infer<typeof RecordingEventSchema>;
|
|
210
|
+
|
|
211
|
+
// =============================================================================
|
|
212
|
+
// Transcription session grant (provider-agnostic handshake)
|
|
213
|
+
// =============================================================================
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Server-issued grant that authorizes the mac shell to open a WebSocket to
|
|
217
|
+
* the transcription provider. Per INV-MTG-15 the mac never sees a provider
|
|
218
|
+
* name — only `connectUrl`, `authHeaders`, and the audio config it must
|
|
219
|
+
* speak. `authHeaders` carries an ephemeral token; rotation is the server's
|
|
220
|
+
* job, and the grant has a short `expiresAt`.
|
|
221
|
+
*/
|
|
222
|
+
export const TranscriptionSessionGrantSchema = z
|
|
223
|
+
.object({
|
|
224
|
+
sessionId: RecordingIdSchema,
|
|
225
|
+
connectUrl: z.string().url(),
|
|
226
|
+
authHeaders: z.record(z.string(), z.string()),
|
|
227
|
+
expiresAt: z.string().datetime(),
|
|
228
|
+
audioConfig: z.object({
|
|
229
|
+
sampleRateHz: z.literal(16000),
|
|
230
|
+
channels: z.literal(2),
|
|
231
|
+
encoding: z.literal('linear16'),
|
|
232
|
+
chunkMs: z.literal(200),
|
|
233
|
+
}),
|
|
234
|
+
capabilities: z.object({
|
|
235
|
+
interimResults: z.boolean(),
|
|
236
|
+
multichannelDiarization: z.boolean(),
|
|
237
|
+
reconnectionTokens: z.boolean(),
|
|
238
|
+
}),
|
|
239
|
+
})
|
|
240
|
+
.meta({
|
|
241
|
+
description:
|
|
242
|
+
'Provider-agnostic grant the mac shell consumes. Mac never sees provider name (INV-MTG-15).',
|
|
243
|
+
});
|
|
244
|
+
export type TranscriptionSessionGrant = z.infer<typeof TranscriptionSessionGrantSchema>;
|
|
245
|
+
|
|
246
|
+
// =============================================================================
|
|
247
|
+
// Meeting metadata projection (finalized read model)
|
|
248
|
+
// =============================================================================
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Finalized projection row for a completed recording. This is what the app
|
|
252
|
+
* reads for the meeting list / detail views. Drafts and in-flight recordings
|
|
253
|
+
* are excluded from this projection per
|
|
254
|
+
* `feedback_live_document_semantics` — indexers filter on `status` to
|
|
255
|
+
* suppress drafts from the search authority.
|
|
256
|
+
*/
|
|
257
|
+
export const MeetingMetadataProjectionSchema = z
|
|
258
|
+
.object({
|
|
259
|
+
recordingId: RecordingIdSchema,
|
|
260
|
+
ownerUserId: z.string().uuid(),
|
|
261
|
+
orgId: z.string().uuid(),
|
|
262
|
+
title: z.string().nullable(),
|
|
263
|
+
detectedApp: DetectedMeetingAppSchema,
|
|
264
|
+
visibility: MeetingVisibilitySchema,
|
|
265
|
+
status: RecordingStatusSchema,
|
|
266
|
+
quality: RecordingQualitySchema,
|
|
267
|
+
startedAt: z.string().datetime(),
|
|
268
|
+
endedAt: z.string().datetime().nullable(),
|
|
269
|
+
durationMs: z.number().int().nonnegative(),
|
|
270
|
+
participantUserIds: z.array(z.string().uuid()),
|
|
271
|
+
transcriptAvailable: z.boolean(),
|
|
272
|
+
})
|
|
273
|
+
.meta({
|
|
274
|
+
description: 'Finalized read projection for a recording (excludes drafts).',
|
|
275
|
+
});
|
|
276
|
+
export type MeetingMetadataProjection = z.infer<typeof MeetingMetadataProjectionSchema>;
|
|
@@ -98,7 +98,7 @@ describe('OrgUnitMembershipSchema', () => {
|
|
|
98
98
|
orgId: UUID_B,
|
|
99
99
|
unitId: UUID_C,
|
|
100
100
|
userId: UUID_A,
|
|
101
|
-
membershipRole: '
|
|
101
|
+
membershipRole: 'l1_unit_owner',
|
|
102
102
|
status: 'active',
|
|
103
103
|
source: 'google_groups',
|
|
104
104
|
sourceRef: 'eng@example.com',
|
|
@@ -226,7 +226,7 @@ describe('Enum exhaustiveness', () => {
|
|
|
226
226
|
describe('OrgUnitPermissionsEntrySchema', () => {
|
|
227
227
|
const entry = {
|
|
228
228
|
userId: UUID_A,
|
|
229
|
-
membershipRole: '
|
|
229
|
+
membershipRole: 'l1_unit_owner' as const,
|
|
230
230
|
inheritedFromUnitId: UUID_C,
|
|
231
231
|
inheritedFromUnitName: 'Engineering',
|
|
232
232
|
};
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Org Secrets
|
|
2
|
+
* Org Secrets DTO schemas (PRD-00629).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Wire shapes for the `org_secrets` admin surface. The decrypted credential
|
|
5
|
+
* value never crosses this boundary — `OrgSecretSummarySchema` exposes only a
|
|
6
|
+
* masked prefix derived from the plaintext (e.g. the first few characters of
|
|
7
|
+
* `sk-...`), never the value itself. Plaintext travels INTO the server through
|
|
8
|
+
* `CreateSecretRequestSchema` / `RotateSecretRequestSchema` via
|
|
9
|
+
* `SecretValueStringSchema`, which is a branded bounded string so accidental
|
|
10
|
+
* propagation through logs / template literals is obvious in code review.
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* and never re-emit it to clients.
|
|
12
|
+
* Server is expected to wrap incoming `plaintext` in `Secret<T>` (see
|
|
13
|
+
* `./secret.ts`) before storage / encryption.
|
|
12
14
|
*/
|
|
13
15
|
import { z } from 'zod';
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
+
* Usage class of an org secret. Drives which resolver layer is allowed to read
|
|
19
|
+
* the value (AI provider keys, webhook signing secrets, generic credentials).
|
|
18
20
|
*/
|
|
19
21
|
export const UsageClassSchema = z.enum(['AI_PROVIDER', 'WEBHOOK_SIGNING', 'GENERIC']);
|
|
20
22
|
export type UsageClass = z.infer<typeof UsageClassSchema>;
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* Audit-log action vocabulary for `org_secrets_audit_log`. Includes both
|
|
26
|
+
* lifecycle events (`created`, `rotated`, `disabled`, `enabled`) and access
|
|
27
|
+
* signals (`access_denied`, `explicit_export`, `unusual_access`).
|
|
25
28
|
*/
|
|
26
29
|
export const OrgSecretsActionSchema = z.enum([
|
|
27
30
|
'created',
|
|
@@ -34,30 +37,34 @@ export const OrgSecretsActionSchema = z.enum([
|
|
|
34
37
|
]);
|
|
35
38
|
export type OrgSecretsAction = z.infer<typeof OrgSecretsActionSchema>;
|
|
36
39
|
|
|
40
|
+
declare const SecretValueStringBrand: unique symbol;
|
|
41
|
+
|
|
37
42
|
/**
|
|
38
|
-
*
|
|
39
|
-
* megabytes
|
|
40
|
-
*
|
|
43
|
+
* Plaintext credential string. Bounded (min 1, max 8192 chars) so accidental
|
|
44
|
+
* logs cannot leak megabytes, and branded so propagation outside the secrets
|
|
45
|
+
* domain is visible at the type level. Server MUST wrap in `Secret<T>` before
|
|
46
|
+
* storage so the multi-surface redaction protection applies.
|
|
41
47
|
*/
|
|
42
48
|
export const SecretValueStringSchema = z
|
|
43
49
|
.string()
|
|
44
50
|
.min(1)
|
|
45
51
|
.max(8192)
|
|
46
|
-
.brand<
|
|
52
|
+
.brand<typeof SecretValueStringBrand>();
|
|
47
53
|
export type SecretValueString = z.infer<typeof SecretValueStringSchema>;
|
|
48
54
|
|
|
49
55
|
/**
|
|
50
|
-
* Admin-
|
|
51
|
-
* intentionally absent — `maskedPrefix` is the only credential-derived field
|
|
52
|
-
* exposed by this surface (INV-PRD-00629: no value in summary).
|
|
56
|
+
* Admin-facing summary of an `org_secrets` row.
|
|
53
57
|
*
|
|
54
|
-
*
|
|
58
|
+
* INVARIANT: this schema MUST NOT contain the decrypted `value`. The only
|
|
59
|
+
* credential-derived field exposed is `maskedPrefix`, a short, lossy preview.
|
|
60
|
+
* Adding a `value` field here would defeat the purpose of the whole subsystem;
|
|
61
|
+
* the secrets API never decrypts to clients.
|
|
55
62
|
*/
|
|
56
63
|
export const OrgSecretSummarySchema = z.object({
|
|
57
64
|
id: z.string().uuid(),
|
|
58
65
|
orgId: z.string().uuid().nullable(),
|
|
59
66
|
usageClass: UsageClassSchema,
|
|
60
|
-
secretName: z.string().min(1)
|
|
67
|
+
secretName: z.string().min(1),
|
|
61
68
|
maskedPrefix: z.string().min(1).max(64),
|
|
62
69
|
version: z.number().int().positive(),
|
|
63
70
|
enabled: z.boolean(),
|
|
@@ -69,8 +76,9 @@ export const OrgSecretSummarySchema = z.object({
|
|
|
69
76
|
export type OrgSecretSummary = z.infer<typeof OrgSecretSummarySchema>;
|
|
70
77
|
|
|
71
78
|
/**
|
|
72
|
-
* Request
|
|
73
|
-
* `plaintext`
|
|
79
|
+
* Request body for `POST /api/admin/secrets`. `orgId` is nullable so global
|
|
80
|
+
* (platform-wide) secrets are expressible. `plaintext` is the only field that
|
|
81
|
+
* carries credential material; everything else is metadata.
|
|
74
82
|
*/
|
|
75
83
|
export const CreateSecretRequestSchema = z.object({
|
|
76
84
|
orgId: z.string().uuid().nullable(),
|
|
@@ -82,7 +90,9 @@ export const CreateSecretRequestSchema = z.object({
|
|
|
82
90
|
export type CreateSecretRequest = z.infer<typeof CreateSecretRequestSchema>;
|
|
83
91
|
|
|
84
92
|
/**
|
|
85
|
-
* Request
|
|
93
|
+
* Request body for `POST /api/admin/secrets/:id/rotate`. Re-supplies the
|
|
94
|
+
* plaintext under a new version; the previous version is retained per the
|
|
95
|
+
* rotation policy documented in the schema migration.
|
|
86
96
|
*/
|
|
87
97
|
export const RotateSecretRequestSchema = z.object({
|
|
88
98
|
newPlaintext: SecretValueStringSchema,
|
|
@@ -91,8 +101,9 @@ export const RotateSecretRequestSchema = z.object({
|
|
|
91
101
|
export type RotateSecretRequest = z.infer<typeof RotateSecretRequestSchema>;
|
|
92
102
|
|
|
93
103
|
/**
|
|
94
|
-
* Request
|
|
95
|
-
* the audit trail
|
|
104
|
+
* Request body for `POST /api/admin/secrets/:id/disable`. A non-empty reason
|
|
105
|
+
* is required so the audit trail records WHY the secret was disabled, not just
|
|
106
|
+
* that it was.
|
|
96
107
|
*/
|
|
97
108
|
export const DisableSecretRequestSchema = z.object({
|
|
98
109
|
reason: z.string().min(1).max(512),
|