@oxy.so/core 7.23.1 → 7.24.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/dist/cjs/api/accounts.js +102 -1
- package/dist/cjs/api/storeInstaller.js +1 -1
- package/dist/cjs/i18n/locales/compact/es-ES.generated.js +1 -1
- package/dist/cjs/i18n/locales/en-US.js +10 -0
- package/dist/cjs/i18n/locales/es-ES.js +10 -0
- package/dist/cjs/index.js +1 -1
- package/dist/esm/api/accounts.js +68 -1
- package/dist/esm/api/storeInstaller.js +1 -1
- package/dist/esm/i18n/locales/compact/es-ES.generated.js +1 -1
- package/dist/esm/i18n/locales/en-US.js +10 -0
- package/dist/esm/i18n/locales/es-ES.js +10 -0
- package/dist/esm/index.js +1 -1
- package/dist/types/api/accounts.d.ts +41 -1
- package/dist/types/api/storeInstaller.d.ts +1 -1
- package/dist/types/i18n/locales/en-US.d.ts +10 -0
- package/dist/types/i18n/locales/es-ES.d.ts +10 -0
- package/dist/types/server/appAssertion.d.ts +1 -1
- package/package.json +2 -2
- package/src/api/accounts.ts +135 -1
- package/src/api/storeInstaller.ts +1 -1
- package/src/i18n/locales/compact/es-ES.generated.ts +1 -1
- package/src/i18n/locales/en-US.ts +10 -0
- package/src/i18n/locales/es-ES.ts +10 -0
- package/src/index.ts +1 -1
|
@@ -679,6 +679,16 @@ const dict = {
|
|
|
679
679
|
add: 'Add your links',
|
|
680
680
|
placeholder: 'Enter URL',
|
|
681
681
|
yourLinks: 'Your Links',
|
|
682
|
+
linkTitle: 'Title',
|
|
683
|
+
moveUp: 'Move up',
|
|
684
|
+
moveDown: 'Move down',
|
|
685
|
+
errors: {
|
|
686
|
+
empty: 'Enter a link.',
|
|
687
|
+
invalid: 'Enter a valid link.',
|
|
688
|
+
insecure: 'Links must start with https://.',
|
|
689
|
+
duplicate: 'That link is already on your profile.',
|
|
690
|
+
limit: 'You can add up to {{count}} links.',
|
|
691
|
+
},
|
|
682
692
|
count: '{{count}} link added',
|
|
683
693
|
count_plural: '{{count}} links added',
|
|
684
694
|
},
|
|
@@ -381,6 +381,16 @@ const dict = {
|
|
|
381
381
|
add: 'Añade tus enlaces',
|
|
382
382
|
placeholder: 'Introduce la URL',
|
|
383
383
|
yourLinks: 'Tus enlaces',
|
|
384
|
+
linkTitle: 'Título',
|
|
385
|
+
moveUp: 'Subir',
|
|
386
|
+
moveDown: 'Bajar',
|
|
387
|
+
errors: {
|
|
388
|
+
empty: 'Introduce un enlace.',
|
|
389
|
+
invalid: 'Introduce un enlace válido.',
|
|
390
|
+
insecure: 'Los enlaces deben empezar por https://.',
|
|
391
|
+
duplicate: 'Ese enlace ya está en tu perfil.',
|
|
392
|
+
limit: 'Puedes añadir hasta {{count}} enlaces.',
|
|
393
|
+
},
|
|
384
394
|
count: '{{count}} enlace añadido',
|
|
385
395
|
count_plural: '{{count}} enlaces añadidos',
|
|
386
396
|
},
|
package/dist/esm/index.js
CHANGED
|
@@ -35,7 +35,7 @@ export { ASSET_MULTIPART_UPLOAD_MAX_BYTES, assetUrlCacheTTL, MAX_UPLOAD_BYTES }
|
|
|
35
35
|
export { DirectUploadInterruptedError } from './api/directUpload.js';
|
|
36
36
|
export { buildUserDid } from './api/identity.js';
|
|
37
37
|
export { ACCOUNT_CATEGORY_IDS, MAX_ACCOUNT_CATEGORIES, SELECTABLE_ACCOUNT_CATEGORY_IDS, isSelectableAccountCategoryId, kindAcceptsAccountCategories, } from './api/accounts.js';
|
|
38
|
-
// The store's installer (ADR
|
|
38
|
+
// The store's installer (ADR 0059): what a runtime that installs applications registers beside
|
|
39
39
|
// `oxy.store`, and what a store asks it.
|
|
40
40
|
export { getStoreInstaller, setStoreInstaller, StoreInstallerError, } from './api/storeInstaller.js';
|
|
41
41
|
export { REPUTATION_CACHE_PREFIX } from './api/reputation.js';
|
|
@@ -18,7 +18,7 @@ import type { AccountAccessGrant, OrganizationType } from '@oxy.so/contracts';
|
|
|
18
18
|
* no per-request "acting-as" header. Identity is carried by the session/token,
|
|
19
19
|
* so a switch propagates through reload and cross-domain exactly like a login.
|
|
20
20
|
*/
|
|
21
|
-
import type { AgentKeyOperation, AgentKeyOperationProof } from '@oxy.so/contracts';
|
|
21
|
+
import type { AgentKeyOperation, AgentKeyOperationProof, OrganizationIdentityKeyState, OrganizationKeyApplyRequest, OrganizationKeyChallenge, OrganizationKeyChallengeRequest, OrganizationKeyOperation } from '@oxy.so/contracts';
|
|
22
22
|
import type { User } from '../models/interfaces';
|
|
23
23
|
import type { AccountCategoryId, AccountKind, AvatarCharacter, ChildAccountKind } from '@oxy.so/contracts';
|
|
24
24
|
import type { SessionLoginResponse } from '../models/session';
|
|
@@ -384,6 +384,44 @@ export declare class AccountAgentKeysApi {
|
|
|
384
384
|
revokedMethodIds: string[];
|
|
385
385
|
}>;
|
|
386
386
|
}
|
|
387
|
+
/** A secp256k1 key pair held on this device: lowercase hex, the public key uncompressed. */
|
|
388
|
+
export interface OrganizationKeyPair {
|
|
389
|
+
privateKey: string;
|
|
390
|
+
publicKey: string;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* `oxy.accounts.identityKey` — an organization's identity key (ADR 0061): its
|
|
394
|
+
* DID `#key-1`, the key Peable derives its FairCoin receive addresses from.
|
|
395
|
+
* Only the organization's owners and admins may read or change it; the key is
|
|
396
|
+
* created and held in Commons and never signs anybody in.
|
|
397
|
+
*
|
|
398
|
+
* `get` answers `publicKey: null` for an organization that has no key yet (its
|
|
399
|
+
* DID lists no `#key-1`): that is when a client shows its owners "Set up the
|
|
400
|
+
* organization's Peable wallet". A 403 (`ORGANIZATION_KEY_FORBIDDEN`) means the
|
|
401
|
+
* signed-in person is not an owner or admin of a claimed, active organization.
|
|
402
|
+
*/
|
|
403
|
+
export declare class AccountIdentityKeyApi {
|
|
404
|
+
private readonly ctx;
|
|
405
|
+
constructor(ctx: OxyContext);
|
|
406
|
+
/** The organization's key (`null` without one) and every key it has had, oldest first. */
|
|
407
|
+
get(accountId: string): Promise<OrganizationIdentityKeyState>;
|
|
408
|
+
/** A one-use challenge for one change, with the claims every signer signs. */
|
|
409
|
+
requestChallenge(accountId: string, request: OrganizationKeyChallengeRequest): Promise<OrganizationKeyChallenge>;
|
|
410
|
+
/** Apply a change with its signatures (see {@link link} for the whole ceremony). */
|
|
411
|
+
apply(accountId: string, request: OrganizationKeyApplyRequest): Promise<OrganizationIdentityKeyState>;
|
|
412
|
+
/**
|
|
413
|
+
* Commons' whole ceremony. NATIVE-ONLY: the actor is THIS device's personal
|
|
414
|
+
* root (`KeyManager`), which must be the signed-in person's. `key` is the
|
|
415
|
+
* organization key the device generated (`create`, or `replace` with a new
|
|
416
|
+
* one); `previousKey` is the current organization key when the device holds
|
|
417
|
+
* it, so the replacement is recorded as proven by it.
|
|
418
|
+
*/
|
|
419
|
+
link(accountId: string, input: {
|
|
420
|
+
operation: OrganizationKeyOperation;
|
|
421
|
+
key: OrganizationKeyPair;
|
|
422
|
+
previousKey?: OrganizationKeyPair;
|
|
423
|
+
}): Promise<OrganizationIdentityKeyState>;
|
|
424
|
+
}
|
|
387
425
|
export interface AccountInvitation {
|
|
388
426
|
id: string;
|
|
389
427
|
accountId: string;
|
|
@@ -425,6 +463,8 @@ export declare class AccountsApi {
|
|
|
425
463
|
readonly members: AccountMembersApi;
|
|
426
464
|
readonly invitations: AccountInvitationsApi;
|
|
427
465
|
readonly agentKeys: AccountAgentKeysApi;
|
|
466
|
+
/** An organization's identity key: its DID `#key-1` and Peable wallet key (ADR 0061). */
|
|
467
|
+
readonly identityKey: AccountIdentityKeyApi;
|
|
428
468
|
constructor(ctx: OxyContext);
|
|
429
469
|
/**
|
|
430
470
|
* List the accounts the caller can access: their own personal (root)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `oxy.store` says what the store lists; an installer says what the machine can do with a
|
|
6
6
|
* listing. A runtime that installs applications registers one (OxyOS's Bloom host registers one
|
|
7
7
|
* backed by the system's install service before the application's code runs, OxyOS ADR 0030
|
|
8
|
-
* decision 9; ADR
|
|
8
|
+
* decision 9; ADR 0059 here). Where none is registered — the web, iOS, Android — there is
|
|
9
9
|
* nothing to install, and a store shows what it shows without one. An application never asks
|
|
10
10
|
* which platform it runs on: it asks whether there is an installer.
|
|
11
11
|
*
|
|
@@ -670,6 +670,16 @@ declare const dict: {
|
|
|
670
670
|
add: string;
|
|
671
671
|
placeholder: string;
|
|
672
672
|
yourLinks: string;
|
|
673
|
+
linkTitle: string;
|
|
674
|
+
moveUp: string;
|
|
675
|
+
moveDown: string;
|
|
676
|
+
errors: {
|
|
677
|
+
empty: string;
|
|
678
|
+
invalid: string;
|
|
679
|
+
insecure: string;
|
|
680
|
+
duplicate: string;
|
|
681
|
+
limit: string;
|
|
682
|
+
};
|
|
673
683
|
count: string;
|
|
674
684
|
count_plural: string;
|
|
675
685
|
};
|
|
@@ -381,6 +381,16 @@ declare const dict: {
|
|
|
381
381
|
add: string;
|
|
382
382
|
placeholder: string;
|
|
383
383
|
yourLinks: string;
|
|
384
|
+
linkTitle: string;
|
|
385
|
+
moveUp: string;
|
|
386
|
+
moveDown: string;
|
|
387
|
+
errors: {
|
|
388
|
+
empty: string;
|
|
389
|
+
invalid: string;
|
|
390
|
+
insecure: string;
|
|
391
|
+
duplicate: string;
|
|
392
|
+
limit: string;
|
|
393
|
+
};
|
|
384
394
|
count: string;
|
|
385
395
|
count_plural: string;
|
|
386
396
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/core",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.24.0",
|
|
4
4
|
"description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
"dependencies": {
|
|
182
182
|
"@noble/ciphers": "^2.4.0",
|
|
183
183
|
"@noble/hashes": "^2.4.0",
|
|
184
|
-
"@oxy.so/contracts": "^6.
|
|
184
|
+
"@oxy.so/contracts": "^6.11.0",
|
|
185
185
|
"@oxy.so/protocol": "^1.6.1",
|
|
186
186
|
"@oxy.so/telemetry": "^2.1.0",
|
|
187
187
|
"@scure/bip39": "^2.4.0",
|
package/src/api/accounts.ts
CHANGED
|
@@ -18,13 +18,26 @@ import type { AccountAccessGrant, OrganizationType } from '@oxy.so/contracts';
|
|
|
18
18
|
* no per-request "acting-as" header. Identity is carried by the session/token,
|
|
19
19
|
* so a switch propagates through reload and cross-domain exactly like a login.
|
|
20
20
|
*/
|
|
21
|
-
import type {
|
|
21
|
+
import type {
|
|
22
|
+
AgentKeyOperation,
|
|
23
|
+
AgentKeyOperationProof,
|
|
24
|
+
OrganizationIdentityKeyState,
|
|
25
|
+
OrganizationKeyApplyRequest,
|
|
26
|
+
OrganizationKeyChallenge,
|
|
27
|
+
OrganizationKeyChallengeRequest,
|
|
28
|
+
OrganizationKeyOperation,
|
|
29
|
+
} from '@oxy.so/contracts';
|
|
22
30
|
import {
|
|
23
31
|
agentKeyListResponseCheck,
|
|
24
32
|
agentKeyOperationChallengeCheck,
|
|
25
33
|
agentKeyOperationCheck,
|
|
26
34
|
agentKeyOperationResultCheck,
|
|
27
35
|
executeAgentKeyOperationCheck,
|
|
36
|
+
organizationIdentityKeyStateCheck,
|
|
37
|
+
organizationKeyApplyRequestCheck,
|
|
38
|
+
organizationKeyChallengeCheck,
|
|
39
|
+
organizationKeyChallengeRequestCheck,
|
|
40
|
+
organizationKeyProofClaims,
|
|
28
41
|
parseContract,
|
|
29
42
|
} from '@oxy.so/contracts/client';
|
|
30
43
|
import type { User } from '../models/interfaces';
|
|
@@ -457,6 +470,124 @@ export class AccountAgentKeysApi {
|
|
|
457
470
|
}
|
|
458
471
|
}
|
|
459
472
|
|
|
473
|
+
/** A secp256k1 key pair held on this device: lowercase hex, the public key uncompressed. */
|
|
474
|
+
export interface OrganizationKeyPair {
|
|
475
|
+
privateKey: string;
|
|
476
|
+
publicKey: string;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* `oxy.accounts.identityKey` — an organization's identity key (ADR 0061): its
|
|
481
|
+
* DID `#key-1`, the key Peable derives its FairCoin receive addresses from.
|
|
482
|
+
* Only the organization's owners and admins may read or change it; the key is
|
|
483
|
+
* created and held in Commons and never signs anybody in.
|
|
484
|
+
*
|
|
485
|
+
* `get` answers `publicKey: null` for an organization that has no key yet (its
|
|
486
|
+
* DID lists no `#key-1`): that is when a client shows its owners "Set up the
|
|
487
|
+
* organization's Peable wallet". A 403 (`ORGANIZATION_KEY_FORBIDDEN`) means the
|
|
488
|
+
* signed-in person is not an owner or admin of a claimed, active organization.
|
|
489
|
+
*/
|
|
490
|
+
export class AccountIdentityKeyApi {
|
|
491
|
+
constructor(private readonly ctx: OxyContext) {}
|
|
492
|
+
|
|
493
|
+
/** The organization's key (`null` without one) and every key it has had, oldest first. */
|
|
494
|
+
async get(accountId: string): Promise<OrganizationIdentityKeyState> {
|
|
495
|
+
return parseContract(
|
|
496
|
+
organizationIdentityKeyStateCheck,
|
|
497
|
+
await this.ctx.request<unknown>(
|
|
498
|
+
'GET',
|
|
499
|
+
`/accounts/${enc(accountId)}/identity-key`,
|
|
500
|
+
undefined,
|
|
501
|
+
{ cache: false },
|
|
502
|
+
),
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** A one-use challenge for one change, with the claims every signer signs. */
|
|
507
|
+
async requestChallenge(
|
|
508
|
+
accountId: string,
|
|
509
|
+
request: OrganizationKeyChallengeRequest,
|
|
510
|
+
): Promise<OrganizationKeyChallenge> {
|
|
511
|
+
return parseContract(
|
|
512
|
+
organizationKeyChallengeCheck,
|
|
513
|
+
await this.ctx.request<unknown>(
|
|
514
|
+
'POST',
|
|
515
|
+
`/accounts/${enc(accountId)}/identity-key/challenge`,
|
|
516
|
+
parseContract(organizationKeyChallengeRequestCheck, request),
|
|
517
|
+
{ cache: false },
|
|
518
|
+
),
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Apply a change with its signatures (see {@link link} for the whole ceremony). */
|
|
523
|
+
async apply(
|
|
524
|
+
accountId: string,
|
|
525
|
+
request: OrganizationKeyApplyRequest,
|
|
526
|
+
): Promise<OrganizationIdentityKeyState> {
|
|
527
|
+
return parseContract(
|
|
528
|
+
organizationIdentityKeyStateCheck,
|
|
529
|
+
await this.ctx.request<unknown>(
|
|
530
|
+
'POST',
|
|
531
|
+
`/accounts/${enc(accountId)}/identity-key`,
|
|
532
|
+
parseContract(organizationKeyApplyRequestCheck, request),
|
|
533
|
+
{ cache: false },
|
|
534
|
+
),
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Commons' whole ceremony. NATIVE-ONLY: the actor is THIS device's personal
|
|
540
|
+
* root (`KeyManager`), which must be the signed-in person's. `key` is the
|
|
541
|
+
* organization key the device generated (`create`, or `replace` with a new
|
|
542
|
+
* one); `previousKey` is the current organization key when the device holds
|
|
543
|
+
* it, so the replacement is recorded as proven by it.
|
|
544
|
+
*/
|
|
545
|
+
async link(
|
|
546
|
+
accountId: string,
|
|
547
|
+
input: {
|
|
548
|
+
operation: OrganizationKeyOperation;
|
|
549
|
+
key: OrganizationKeyPair;
|
|
550
|
+
previousKey?: OrganizationKeyPair;
|
|
551
|
+
},
|
|
552
|
+
): Promise<OrganizationIdentityKeyState> {
|
|
553
|
+
const { KeyManager, signIdentityProof } = await import('../crypto/index');
|
|
554
|
+
const [rootPrivateKey, rootPublicKey] = await Promise.all([
|
|
555
|
+
KeyManager.getPrivateKey(),
|
|
556
|
+
KeyManager.getPublicKey(),
|
|
557
|
+
]);
|
|
558
|
+
if (!rootPrivateKey || !rootPublicKey) {
|
|
559
|
+
throw new Error('No identity on this device to sign with');
|
|
560
|
+
}
|
|
561
|
+
const challenge = await this.requestChallenge(accountId, {
|
|
562
|
+
operation: input.operation,
|
|
563
|
+
publicKey: input.key.publicKey,
|
|
564
|
+
});
|
|
565
|
+
const root = rootPublicKey.trim().toLowerCase();
|
|
566
|
+
if (challenge.actorPublicKey !== root) {
|
|
567
|
+
throw new Error("This device's identity is not the signed-in account's");
|
|
568
|
+
}
|
|
569
|
+
const sign = async (signer: OrganizationKeyPair, publicKey: string) =>
|
|
570
|
+
(
|
|
571
|
+
await signIdentityProof(
|
|
572
|
+
{ privateKey: signer.privateKey, publicKey },
|
|
573
|
+
organizationKeyProofClaims(challenge, publicKey),
|
|
574
|
+
)
|
|
575
|
+
).signature;
|
|
576
|
+
const previousPublicKey = challenge.previousPublicKey;
|
|
577
|
+
return this.apply(accountId, {
|
|
578
|
+
operation: input.operation,
|
|
579
|
+
publicKey: challenge.publicKey,
|
|
580
|
+
challenge: challenge.challenge,
|
|
581
|
+
expiresAt: challenge.expiresAt,
|
|
582
|
+
actorSignature: await sign({ privateKey: rootPrivateKey, publicKey: root }, root),
|
|
583
|
+
keySignature: await sign(input.key, challenge.publicKey),
|
|
584
|
+
...(input.previousKey && previousPublicKey
|
|
585
|
+
? { previousKeySignature: await sign(input.previousKey, previousPublicKey) }
|
|
586
|
+
: {}),
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
460
591
|
export interface AccountInvitation {
|
|
461
592
|
id: string;
|
|
462
593
|
accountId: string;
|
|
@@ -540,11 +671,14 @@ export class AccountsApi {
|
|
|
540
671
|
readonly members: AccountMembersApi;
|
|
541
672
|
readonly invitations: AccountInvitationsApi;
|
|
542
673
|
readonly agentKeys: AccountAgentKeysApi;
|
|
674
|
+
/** An organization's identity key: its DID `#key-1` and Peable wallet key (ADR 0061). */
|
|
675
|
+
readonly identityKey: AccountIdentityKeyApi;
|
|
543
676
|
|
|
544
677
|
constructor(private readonly ctx: OxyContext) {
|
|
545
678
|
this.members = new AccountMembersApi(ctx);
|
|
546
679
|
this.invitations = new AccountInvitationsApi(ctx);
|
|
547
680
|
this.agentKeys = new AccountAgentKeysApi(ctx);
|
|
681
|
+
this.identityKey = new AccountIdentityKeyApi(ctx);
|
|
548
682
|
}
|
|
549
683
|
|
|
550
684
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `oxy.store` says what the store lists; an installer says what the machine can do with a
|
|
6
6
|
* listing. A runtime that installs applications registers one (OxyOS's Bloom host registers one
|
|
7
7
|
* backed by the system's install service before the application's code runs, OxyOS ADR 0030
|
|
8
|
-
* decision 9; ADR
|
|
8
|
+
* decision 9; ADR 0059 here). Where none is registered — the web, iOS, Android — there is
|
|
9
9
|
* nothing to install, and a store shows what it shows without one. An application never asks
|
|
10
10
|
* which platform it runs on: it asks whether there is an installer.
|
|
11
11
|
*
|