@byollm/protocol 0.1.0-alpha.53 → 0.1.0-alpha.55
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/README.md +2 -2
- package/dist/index.d.ts +110 -2
- package/dist/index.js +203 -100
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1023,12 +1023,73 @@ function verifyRequest(input) {
|
|
|
1023
1023
|
return ok ? null : "bad-signature";
|
|
1024
1024
|
}
|
|
1025
1025
|
|
|
1026
|
-
// src/
|
|
1026
|
+
// src/roster.ts
|
|
1027
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
1027
1028
|
import { z as z8 } from "zod";
|
|
1029
|
+
var ROSTER_MAX_AGE_MS = 60 * 6e4;
|
|
1030
|
+
var ROSTER_CONTEXT = "byollm/v1/roster";
|
|
1031
|
+
var SignedRoster = z8.object({
|
|
1032
|
+
/** Whose devices this roster governs. */
|
|
1033
|
+
owner: z8.string().min(1),
|
|
1034
|
+
/**
|
|
1035
|
+
* Who may have their `team` work run on this owner's devices.
|
|
1036
|
+
*
|
|
1037
|
+
* Owner ids, sorted, so the same membership always produces the same
|
|
1038
|
+
* bytes — a document that differed by iteration order would produce a new
|
|
1039
|
+
* signature every read and no way to tell a change from a shuffle.
|
|
1040
|
+
*/
|
|
1041
|
+
members: z8.array(z8.string().min(1)),
|
|
1042
|
+
/**
|
|
1043
|
+
* When the control plane signed it — epoch ms, and the **only** honest
|
|
1044
|
+
* anchor for age.
|
|
1045
|
+
*
|
|
1046
|
+
* Not when the daemon received it. A relay that simply withholds updates
|
|
1047
|
+
* would otherwise keep a removed member served forever, which is the
|
|
1048
|
+
* attack {@link ROSTER_MAX_AGE_MS} exists to bound.
|
|
1049
|
+
*/
|
|
1050
|
+
issuedAt: z8.number().int().positive(),
|
|
1051
|
+
/** Base64url Ed25519 over {@link rosterStatement}. */
|
|
1052
|
+
signature: z8.string().min(1)
|
|
1053
|
+
}).strict();
|
|
1054
|
+
function rosterStatement(input) {
|
|
1055
|
+
return Buffer2.from(
|
|
1056
|
+
[
|
|
1057
|
+
ROSTER_CONTEXT,
|
|
1058
|
+
input.owner,
|
|
1059
|
+
String(input.issuedAt),
|
|
1060
|
+
[...input.members].sort().join("\0")
|
|
1061
|
+
].join("\n"),
|
|
1062
|
+
"utf8"
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
function signRoster(keys, input) {
|
|
1066
|
+
const members = [...input.members].sort();
|
|
1067
|
+
return {
|
|
1068
|
+
owner: input.owner,
|
|
1069
|
+
members,
|
|
1070
|
+
issuedAt: input.issuedAt,
|
|
1071
|
+
signature: signWith(keys, rosterStatement({ ...input, members }))
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
function verifyRoster(input) {
|
|
1075
|
+
const { roster, owner, now } = input;
|
|
1076
|
+
if (roster.owner !== owner) return "wrong-owner";
|
|
1077
|
+
const age = now - roster.issuedAt;
|
|
1078
|
+
if (age < 0) return "from-the-future";
|
|
1079
|
+
if (age > (input.maxAgeMs ?? ROSTER_MAX_AGE_MS)) return "stale";
|
|
1080
|
+
return verifyWith(
|
|
1081
|
+
input.controlPlanePublic,
|
|
1082
|
+
rosterStatement(roster),
|
|
1083
|
+
roster.signature
|
|
1084
|
+
) ? null : "bad-signature";
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// src/succession.ts
|
|
1088
|
+
import { z as z9 } from "zod";
|
|
1028
1089
|
var SUCCESSION_CONTEXT = "byollm/v1/site-succession";
|
|
1029
1090
|
var RETIREMENT_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1030
1091
|
var MAX_SUCCESSION_CHAIN = 64;
|
|
1031
|
-
var Succession =
|
|
1092
|
+
var Succession = z9.object({
|
|
1032
1093
|
/**
|
|
1033
1094
|
* The predecessor's public identity — K1, in full.
|
|
1034
1095
|
*
|
|
@@ -1038,7 +1099,7 @@ var Succession = z8.object({
|
|
|
1038
1099
|
*/
|
|
1039
1100
|
identity: PublicIdentity,
|
|
1040
1101
|
/** K1's signature over the statement naming K1 and its successor. */
|
|
1041
|
-
signature:
|
|
1102
|
+
signature: z9.string().min(1)
|
|
1042
1103
|
}).strict();
|
|
1043
1104
|
function successionStatement(fromKeyId, toKeyId) {
|
|
1044
1105
|
return Buffer.from(`${SUCCESSION_CONTEXT}:${fromKeyId}:${toKeyId}`);
|
|
@@ -1517,7 +1578,7 @@ function mustsVerifiedBy(kind) {
|
|
|
1517
1578
|
}
|
|
1518
1579
|
|
|
1519
1580
|
// src/wire.ts
|
|
1520
|
-
import { z as
|
|
1581
|
+
import { z as z10 } from "zod";
|
|
1521
1582
|
var PROTOCOL_VERSION = "0";
|
|
1522
1583
|
var SUPPORTED_PROTOCOL_VERSIONS = Object.freeze([
|
|
1523
1584
|
PROTOCOL_VERSION
|
|
@@ -1560,7 +1621,7 @@ var ENDPOINTS = Object.freeze([
|
|
|
1560
1621
|
"result",
|
|
1561
1622
|
"release"
|
|
1562
1623
|
]);
|
|
1563
|
-
var Capability =
|
|
1624
|
+
var Capability = z10.object({
|
|
1564
1625
|
kind: JobKind,
|
|
1565
1626
|
/**
|
|
1566
1627
|
* The owner's name for the service answering this kind — byollm_016.
|
|
@@ -1569,7 +1630,7 @@ var Capability = z9.object({
|
|
|
1569
1630
|
* that something does. Phase B lets a job select by this name; until then
|
|
1570
1631
|
* it is what a device page shows and what a default is chosen between.
|
|
1571
1632
|
*/
|
|
1572
|
-
service:
|
|
1633
|
+
service: z10.string().min(1),
|
|
1573
1634
|
/**
|
|
1574
1635
|
* Whether this row is the default for its kind.
|
|
1575
1636
|
*
|
|
@@ -1579,27 +1640,27 @@ var Capability = z9.object({
|
|
|
1579
1640
|
* alone" would have to unlearn it, and the ones that did not would be
|
|
1580
1641
|
* quietly wrong. One field now, no second shape later.
|
|
1581
1642
|
*/
|
|
1582
|
-
isDefault:
|
|
1643
|
+
isDefault: z10.boolean(),
|
|
1583
1644
|
backendId: BackendIdSchema,
|
|
1584
1645
|
backendClass: BackendClass,
|
|
1585
|
-
model:
|
|
1646
|
+
model: z10.string().min(1),
|
|
1586
1647
|
offerScope: OfferScope
|
|
1587
1648
|
}).strict();
|
|
1588
|
-
var CapabilityMatrix =
|
|
1589
|
-
var WithheldKind =
|
|
1649
|
+
var CapabilityMatrix = z10.array(Capability);
|
|
1650
|
+
var WithheldKind = z10.object({
|
|
1590
1651
|
kind: JobKind,
|
|
1591
|
-
claimants:
|
|
1592
|
-
|
|
1652
|
+
claimants: z10.array(
|
|
1653
|
+
z10.object({ service: z10.string().min(1), offer: OfferScope }).strict()
|
|
1593
1654
|
).min(2)
|
|
1594
1655
|
}).strict();
|
|
1595
|
-
var PairStartRequest =
|
|
1596
|
-
protocolVersion:
|
|
1597
|
-
action:
|
|
1598
|
-
daemon:
|
|
1599
|
-
version:
|
|
1656
|
+
var PairStartRequest = z10.object({
|
|
1657
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
1658
|
+
action: z10.literal("start"),
|
|
1659
|
+
daemon: z10.object({
|
|
1660
|
+
version: z10.string().min(1),
|
|
1600
1661
|
/** Shown in the app's runner list so a user can tell their machines apart. */
|
|
1601
|
-
label:
|
|
1602
|
-
platform:
|
|
1662
|
+
label: z10.string().min(1).max(120),
|
|
1663
|
+
platform: z10.enum(["darwin", "linux", "win32"])
|
|
1603
1664
|
}),
|
|
1604
1665
|
/**
|
|
1605
1666
|
* This machine's public keys (byollm_009 §5).
|
|
@@ -1611,29 +1672,29 @@ var PairStartRequest = z9.object({
|
|
|
1611
1672
|
device: PublicIdentity,
|
|
1612
1673
|
capabilities: CapabilityMatrix
|
|
1613
1674
|
}).strict();
|
|
1614
|
-
var PairStartResponse =
|
|
1675
|
+
var PairStartResponse = z10.object({
|
|
1615
1676
|
/** Secret the daemon polls with. Never shown to the user. */
|
|
1616
|
-
deviceCode:
|
|
1677
|
+
deviceCode: z10.string().min(20),
|
|
1617
1678
|
/** Short code the user reads and confirms in the browser. */
|
|
1618
|
-
userCode:
|
|
1679
|
+
userCode: z10.string().min(4).max(16),
|
|
1619
1680
|
/** Where the user approves. Must be on the server's own origin. */
|
|
1620
|
-
verificationUrl:
|
|
1681
|
+
verificationUrl: z10.url(),
|
|
1621
1682
|
/** Epoch ms after which the code is dead ({@link MUSTS.PAIR_CODE_EXPIRES}). */
|
|
1622
|
-
expiresAt:
|
|
1683
|
+
expiresAt: z10.number().int().positive(),
|
|
1623
1684
|
/** How often the daemon may poll. */
|
|
1624
|
-
pollIntervalMs:
|
|
1685
|
+
pollIntervalMs: z10.number().int().min(500).max(6e4)
|
|
1625
1686
|
}).strict();
|
|
1626
|
-
var PairPollRequest =
|
|
1627
|
-
protocolVersion:
|
|
1628
|
-
action:
|
|
1629
|
-
deviceCode:
|
|
1687
|
+
var PairPollRequest = z10.object({
|
|
1688
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
1689
|
+
action: z10.literal("poll"),
|
|
1690
|
+
deviceCode: z10.string().min(20)
|
|
1630
1691
|
}).strict();
|
|
1631
|
-
var PairPollResponse =
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
status:
|
|
1692
|
+
var PairPollResponse = z10.discriminatedUnion("status", [
|
|
1693
|
+
z10.object({ status: z10.literal("pending") }).strict(),
|
|
1694
|
+
z10.object({ status: z10.literal("denied") }).strict(),
|
|
1695
|
+
z10.object({ status: z10.literal("expired") }).strict(),
|
|
1696
|
+
z10.object({
|
|
1697
|
+
status: z10.literal("approved"),
|
|
1637
1698
|
// `runnerToken` is gone — cloud_008 §2.4, finding 37.
|
|
1638
1699
|
//
|
|
1639
1700
|
// It was minted here, hashed into `RunnerRecord.tokenHash`, written to
|
|
@@ -1650,11 +1711,11 @@ var PairPollResponse = z9.discriminatedUnion("status", [
|
|
|
1650
1711
|
// `REQUESTS_SIGNED_NOT_BEARER` was already the rule and was already
|
|
1651
1712
|
// enforced — every authenticated call is signed by the device's pinned
|
|
1652
1713
|
// identity key. This removes the thing the MUST is named after.
|
|
1653
|
-
runnerId:
|
|
1714
|
+
runnerId: z10.string().min(1),
|
|
1654
1715
|
/** The app's id for the approving user — this daemon's owner forever. */
|
|
1655
|
-
owner:
|
|
1716
|
+
owner: z10.string().min(1),
|
|
1656
1717
|
/** Display name for the trust UI, if the app offers one. */
|
|
1657
|
-
ownerLabel:
|
|
1718
|
+
ownerLabel: z10.string().optional(),
|
|
1658
1719
|
/**
|
|
1659
1720
|
* The sites this pairing covers, for the daemon to pin (byollm_009 §5),
|
|
1660
1721
|
* keyed by each site's identity key id — cloud_009 §5.
|
|
@@ -1673,34 +1734,55 @@ var PairPollResponse = z9.discriminatedUnion("status", [
|
|
|
1673
1734
|
* runner's lookup is a map read rather than a join across two
|
|
1674
1735
|
* namespaces.
|
|
1675
1736
|
*/
|
|
1676
|
-
sites:
|
|
1737
|
+
sites: z10.record(z10.string().min(1), PublicIdentity),
|
|
1738
|
+
/**
|
|
1739
|
+
* The control plane's roster-signing key, pinned here — Amendment G.
|
|
1740
|
+
*
|
|
1741
|
+
* **Pairing is when, and that is the whole question.** Pairing is
|
|
1742
|
+
* already the ceremony where an owner proves out of band that this
|
|
1743
|
+
* device is theirs, so a key learned here rides trust that has already
|
|
1744
|
+
* happened. The rejected alternative is trust-on-first-roster, and it
|
|
1745
|
+
* is rejected because it hands the decision back to the relay: a daemon
|
|
1746
|
+
* that learns whose signature to trust from the first roster to arrive
|
|
1747
|
+
* has its membership authority chosen by whoever controls delivery.
|
|
1748
|
+
*
|
|
1749
|
+
* Optional on the wire, and only on the wire: a direct-mode server has
|
|
1750
|
+
* no control plane and signs no rosters, and a daemon that never
|
|
1751
|
+
* receives one simply serves no `team` job through it. It is not
|
|
1752
|
+
* optional for a hub — a hub that omitted it would be asking devices to
|
|
1753
|
+
* accept rosters from nobody in particular.
|
|
1754
|
+
*
|
|
1755
|
+
* Rotation is Amendment C's, with no path where a roster teaches a
|
|
1756
|
+
* daemon a new key.
|
|
1757
|
+
*/
|
|
1758
|
+
controlPlanePublic: z10.string().min(1).optional()
|
|
1677
1759
|
}).strict()
|
|
1678
1760
|
]);
|
|
1679
|
-
var PairRequest =
|
|
1761
|
+
var PairRequest = z10.discriminatedUnion("action", [
|
|
1680
1762
|
PairStartRequest,
|
|
1681
1763
|
PairPollRequest
|
|
1682
1764
|
]);
|
|
1683
|
-
var ClaimRequest =
|
|
1684
|
-
protocolVersion:
|
|
1685
|
-
runnerId:
|
|
1765
|
+
var ClaimRequest = z10.object({
|
|
1766
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
1767
|
+
runnerId: z10.string().min(1),
|
|
1686
1768
|
/** Re-sent on every claim so a server never matches against a stale matrix. */
|
|
1687
1769
|
capabilities: CapabilityMatrix,
|
|
1688
1770
|
/** Upper bound on jobs to return; the server may return fewer. */
|
|
1689
|
-
max:
|
|
1771
|
+
max: z10.number().int().min(1).max(64)
|
|
1690
1772
|
}).strict();
|
|
1691
|
-
var ClaimResponse =
|
|
1773
|
+
var ClaimResponse = z10.object({
|
|
1692
1774
|
/**
|
|
1693
1775
|
* Stubs, not jobs. The payload arrives from `fetch`, sealed to whichever
|
|
1694
1776
|
* device claimed — see {@link JobStub} for the exhaustive metadata list.
|
|
1695
1777
|
*/
|
|
1696
|
-
jobs:
|
|
1778
|
+
jobs: z10.array(ClaimedStub),
|
|
1697
1779
|
/** Lease duration granted, so the daemon knows its renewal deadline. */
|
|
1698
|
-
leaseMs:
|
|
1780
|
+
leaseMs: z10.number().int().positive()
|
|
1699
1781
|
}).strict();
|
|
1700
|
-
var HeartbeatRequest =
|
|
1701
|
-
protocolVersion:
|
|
1702
|
-
runnerId:
|
|
1703
|
-
daemonVersion:
|
|
1782
|
+
var HeartbeatRequest = z10.object({
|
|
1783
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
1784
|
+
runnerId: z10.string().min(1),
|
|
1785
|
+
daemonVersion: z10.string().min(1),
|
|
1704
1786
|
capabilities: CapabilityMatrix,
|
|
1705
1787
|
/**
|
|
1706
1788
|
* Kinds this device is withholding, and why it can be said.
|
|
@@ -1709,20 +1791,20 @@ var HeartbeatRequest = z9.object({
|
|
|
1709
1791
|
* older daemon against a newer hub is simply a device with no withheld
|
|
1710
1792
|
* kinds rather than a parse failure.
|
|
1711
1793
|
*/
|
|
1712
|
-
withheld:
|
|
1794
|
+
withheld: z10.array(WithheldKind).default([]),
|
|
1713
1795
|
/**
|
|
1714
1796
|
* Leases this daemon believes it holds; the server renews exactly these.
|
|
1715
1797
|
*
|
|
1716
1798
|
* Lease ids rather than job ids, so a replayed heartbeat cannot renew a
|
|
1717
1799
|
* grant the runner no longer holds — see {@link Lease.id}.
|
|
1718
1800
|
*/
|
|
1719
|
-
activeLeases:
|
|
1720
|
-
|
|
1801
|
+
activeLeases: z10.array(
|
|
1802
|
+
z10.object({ jobId: z10.string().min(1), leaseId: z10.string().min(1) })
|
|
1721
1803
|
),
|
|
1722
1804
|
/** True while the owner has the daemon paused; the server stops offering work. */
|
|
1723
|
-
paused:
|
|
1805
|
+
paused: z10.boolean()
|
|
1724
1806
|
}).strict();
|
|
1725
|
-
var HeartbeatResponse =
|
|
1807
|
+
var HeartbeatResponse = z10.object({
|
|
1726
1808
|
/**
|
|
1727
1809
|
* The sites this daemon may serve, right now — cloud_008 finding 59.
|
|
1728
1810
|
*
|
|
@@ -1740,7 +1822,7 @@ var HeartbeatResponse = z9.object({
|
|
|
1740
1822
|
* rather than being told a second time — two fields for one fact is how
|
|
1741
1823
|
* they drift.
|
|
1742
1824
|
*/
|
|
1743
|
-
sites:
|
|
1825
|
+
sites: z10.record(z10.string().min(1), PublicIdentity),
|
|
1744
1826
|
/**
|
|
1745
1827
|
* How a site's current key traces back to one this daemon already holds —
|
|
1746
1828
|
* byollm_009 Amendment C.
|
|
@@ -1758,11 +1840,11 @@ var HeartbeatResponse = z9.object({
|
|
|
1758
1840
|
* history is public by construction, because a daemon that cannot read it
|
|
1759
1841
|
* cannot verify it.
|
|
1760
1842
|
*/
|
|
1761
|
-
successions:
|
|
1762
|
-
|
|
1763
|
-
|
|
1843
|
+
successions: z10.record(
|
|
1844
|
+
z10.string().min(1),
|
|
1845
|
+
z10.object({
|
|
1764
1846
|
/** Oldest last, as the projection carries it. */
|
|
1765
|
-
succeeds:
|
|
1847
|
+
succeeds: z10.array(Succession).max(MAX_SUCCESSION_CHAIN),
|
|
1766
1848
|
/**
|
|
1767
1849
|
* Until when the superseded key may still sign work — epoch ms.
|
|
1768
1850
|
*
|
|
@@ -1771,7 +1853,7 @@ var HeartbeatResponse = z9.object({
|
|
|
1771
1853
|
* window indefinitely would be a two-key site forever, decided by
|
|
1772
1854
|
* the party this design does not trust.
|
|
1773
1855
|
*/
|
|
1774
|
-
retiringUntil:
|
|
1856
|
+
retiringUntil: z10.number().int().positive().optional()
|
|
1775
1857
|
}).strict()
|
|
1776
1858
|
).optional(),
|
|
1777
1859
|
/**
|
|
@@ -1784,8 +1866,8 @@ var HeartbeatResponse = z9.object({
|
|
|
1784
1866
|
* is the unique grant and the daemon already keys its work by it; this is
|
|
1785
1867
|
* the same shape `activeLeases` sends in the other direction.
|
|
1786
1868
|
*/
|
|
1787
|
-
cancel:
|
|
1788
|
-
|
|
1869
|
+
cancel: z10.array(
|
|
1870
|
+
z10.object({ jobId: z10.string().min(1), leaseId: z10.string().min(1) }).strict()
|
|
1789
1871
|
),
|
|
1790
1872
|
// `leases` is deliberately absent — cloud_008 §1.4b, finding 16.
|
|
1791
1873
|
//
|
|
@@ -1815,11 +1897,11 @@ var HeartbeatResponse = z9.object({
|
|
|
1815
1897
|
* ambiguous across sites, and "the lease you no longer hold" is exactly
|
|
1816
1898
|
* what this field means anyway.
|
|
1817
1899
|
*/
|
|
1818
|
-
lost:
|
|
1819
|
-
|
|
1900
|
+
lost: z10.array(
|
|
1901
|
+
z10.object({ jobId: z10.string().min(1), leaseId: z10.string().min(1) }).strict()
|
|
1820
1902
|
),
|
|
1821
1903
|
/** Server clock, so a daemon with a skewed clock still honors leases. */
|
|
1822
|
-
serverTime:
|
|
1904
|
+
serverTime: z10.number().int().positive(),
|
|
1823
1905
|
/**
|
|
1824
1906
|
* Sites whose disclosure the user must read again before work moves —
|
|
1825
1907
|
* cloud_008 finding 48, named rather than counted.
|
|
@@ -1834,13 +1916,28 @@ var HeartbeatResponse = z9.object({
|
|
|
1834
1916
|
* operator stopped it" — one word with two subjects on two halves of one
|
|
1835
1917
|
* exchange is a confusion nobody untangles from a log.
|
|
1836
1918
|
*/
|
|
1837
|
-
awaitingConsent:
|
|
1919
|
+
awaitingConsent: z10.array(z10.string().min(1)),
|
|
1920
|
+
/**
|
|
1921
|
+
* Who this owner's devices may serve `team` work for — Amendment G.
|
|
1922
|
+
*
|
|
1923
|
+
* Carried by the relay and authored by nobody it can reach. The daemon
|
|
1924
|
+
* verifies it against the key pinned at pairing and admits from its own
|
|
1925
|
+
* held copy, so this field is delivery and not instruction: withholding
|
|
1926
|
+
* it narrows a device, and editing it is caught.
|
|
1927
|
+
*
|
|
1928
|
+
* Optional because a roster is a cloud-mode fact — direct mode has no
|
|
1929
|
+
* control plane to author one — and because a daemon that has never
|
|
1930
|
+
* received one must narrow rather than fail. Absent is not "admit
|
|
1931
|
+
* nobody"; {@link ROSTER_MAX_AGE_MS} is what makes absence bite, and it
|
|
1932
|
+
* bites the same way for a roster withheld as for one never sent.
|
|
1933
|
+
*/
|
|
1934
|
+
roster: SignedRoster.optional()
|
|
1838
1935
|
}).strict();
|
|
1839
|
-
var ResultDisposition =
|
|
1840
|
-
var ResultRequest =
|
|
1841
|
-
protocolVersion:
|
|
1842
|
-
runnerId:
|
|
1843
|
-
jobId:
|
|
1936
|
+
var ResultDisposition = z10.enum(["ok", "error", "canceled"]);
|
|
1937
|
+
var ResultRequest = z10.object({
|
|
1938
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
1939
|
+
runnerId: z10.string().min(1),
|
|
1940
|
+
jobId: z10.string().min(1),
|
|
1844
1941
|
/**
|
|
1845
1942
|
* The grant this result was produced under — cloud_008 §1.4a.
|
|
1846
1943
|
*
|
|
@@ -1863,7 +1960,7 @@ var ResultRequest = z9.object({
|
|
|
1863
1960
|
* learned once already, when a replayed release yanked a later grant, and
|
|
1864
1961
|
* it applies here for the same reason.
|
|
1865
1962
|
*/
|
|
1866
|
-
leaseId:
|
|
1963
|
+
leaseId: z10.string().min(1),
|
|
1867
1964
|
/**
|
|
1868
1965
|
* The outcome, sealed to the site and signed by the device.
|
|
1869
1966
|
*
|
|
@@ -1895,12 +1992,12 @@ var ResultRequest = z9.object({
|
|
|
1895
1992
|
// on it, so it is a class a routing party consumes. Nobody between the
|
|
1896
1993
|
// two ends consumes these.
|
|
1897
1994
|
}).strict();
|
|
1898
|
-
var ResultResponse =
|
|
1995
|
+
var ResultResponse = z10.object({
|
|
1899
1996
|
/**
|
|
1900
1997
|
* False when this submission wrote nothing — the daemon should discard,
|
|
1901
1998
|
* not retry ({@link MUSTS.RESULT_IDEMPOTENT}).
|
|
1902
1999
|
*/
|
|
1903
|
-
accepted:
|
|
2000
|
+
accepted: z10.boolean(),
|
|
1904
2001
|
/**
|
|
1905
2002
|
* True when this device had already recorded this job's result.
|
|
1906
2003
|
*
|
|
@@ -1914,13 +2011,13 @@ var ResultResponse = z9.object({
|
|
|
1914
2011
|
* the same refusal it would get for a job that is *not* terminal, so a job
|
|
1915
2012
|
* id cannot be used as a terminality probe.
|
|
1916
2013
|
*/
|
|
1917
|
-
duplicate:
|
|
2014
|
+
duplicate: z10.boolean().optional(),
|
|
1918
2015
|
/** The job's state after this submission. */
|
|
1919
|
-
state:
|
|
2016
|
+
state: z10.string().min(1)
|
|
1920
2017
|
}).strict();
|
|
1921
|
-
var ReleaseRequest =
|
|
1922
|
-
protocolVersion:
|
|
1923
|
-
runnerId:
|
|
2018
|
+
var ReleaseRequest = z10.object({
|
|
2019
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
2020
|
+
runnerId: z10.string().min(1),
|
|
1924
2021
|
/**
|
|
1925
2022
|
* Which leases to release — the grant, not just the job.
|
|
1926
2023
|
*
|
|
@@ -1928,8 +2025,8 @@ var ReleaseRequest = z9.object({
|
|
|
1928
2025
|
* moment it arrives, which for a replayed request is not the lease the
|
|
1929
2026
|
* daemon meant. See {@link Lease.id}.
|
|
1930
2027
|
*/
|
|
1931
|
-
leases:
|
|
1932
|
-
|
|
2028
|
+
leases: z10.array(
|
|
2029
|
+
z10.object({ jobId: z10.string().min(1), leaseId: z10.string().min(1) })
|
|
1933
2030
|
),
|
|
1934
2031
|
/**
|
|
1935
2032
|
* Why, so the app's runner list can say something true.
|
|
@@ -1940,12 +2037,12 @@ var ReleaseRequest = z9.object({
|
|
|
1940
2037
|
* stop offering that job to that runner, or the pair would spin between
|
|
1941
2038
|
* claim and release forever.
|
|
1942
2039
|
*/
|
|
1943
|
-
reason:
|
|
2040
|
+
reason: z10.enum(["shutdown", "pause", "revoked", "backend-down", "refused"])
|
|
1944
2041
|
}).strict();
|
|
1945
|
-
var ReleaseResponse =
|
|
1946
|
-
released:
|
|
2042
|
+
var ReleaseResponse = z10.object({
|
|
2043
|
+
released: z10.array(z10.string().min(1))
|
|
1947
2044
|
}).strict();
|
|
1948
|
-
var WireErrorCode =
|
|
2045
|
+
var WireErrorCode = z10.enum([
|
|
1949
2046
|
"bad-request",
|
|
1950
2047
|
"unsupported-protocol-version",
|
|
1951
2048
|
// "We do not know who you are." Exactly 401, and only that — cloud_008
|
|
@@ -1995,9 +2092,9 @@ var WireErrorCode = z9.enum([
|
|
|
1995
2092
|
"rate-limited",
|
|
1996
2093
|
"server-error"
|
|
1997
2094
|
]);
|
|
1998
|
-
var WireError =
|
|
2095
|
+
var WireError = z10.object({
|
|
1999
2096
|
error: WireErrorCode,
|
|
2000
|
-
message:
|
|
2097
|
+
message: z10.string().min(1),
|
|
2001
2098
|
/**
|
|
2002
2099
|
* What this server speaks, on `unsupported-protocol-version` — §B.4.
|
|
2003
2100
|
*
|
|
@@ -2011,10 +2108,10 @@ var WireError = z9.object({
|
|
|
2011
2108
|
* Modelled the way `clock-skew`'s two fields already are — code-specific
|
|
2012
2109
|
* extras, refused on any other code by the refinement below.
|
|
2013
2110
|
*/
|
|
2014
|
-
supported:
|
|
2015
|
-
minimum:
|
|
2111
|
+
supported: z10.array(z10.string().min(1)).optional(),
|
|
2112
|
+
minimum: z10.string().min(1).optional(),
|
|
2016
2113
|
/** Seconds; mirrors Retry-After for `rate-limited` and `server-error`. */
|
|
2017
|
-
retryAfter:
|
|
2114
|
+
retryAfter: z10.number().int().nonnegative().optional(),
|
|
2018
2115
|
/**
|
|
2019
2116
|
* The server's clock, and the window it allows. `clock-skew` only.
|
|
2020
2117
|
*
|
|
@@ -2024,8 +2121,8 @@ var WireError = z9.object({
|
|
|
2024
2121
|
* heartbeat response returns the same value, and so does every `Date`
|
|
2025
2122
|
* header.
|
|
2026
2123
|
*/
|
|
2027
|
-
serverTime:
|
|
2028
|
-
maxSkewMs:
|
|
2124
|
+
serverTime: z10.number().int().positive().optional(),
|
|
2125
|
+
maxSkewMs: z10.number().int().positive().optional()
|
|
2029
2126
|
}).strict().superRefine((error, ctx) => {
|
|
2030
2127
|
const skew = error.error === "clock-skew";
|
|
2031
2128
|
const carried = error.serverTime !== void 0 || error.maxSkewMs !== void 0;
|
|
@@ -2076,16 +2173,16 @@ var ERROR_STATUS = Object.freeze({
|
|
|
2076
2173
|
"rate-limited": 429,
|
|
2077
2174
|
"server-error": 500
|
|
2078
2175
|
});
|
|
2079
|
-
var FetchRequest =
|
|
2176
|
+
var FetchRequest = z10.object({
|
|
2080
2177
|
// `literal`, like every other request — V1-17. This one said
|
|
2081
2178
|
// `string().min(1)`, so a daemon speaking a version this server does not
|
|
2082
2179
|
// know got past the handshake on the one endpoint that hands over a
|
|
2083
2180
|
// sealed payload. The version check exists so that a mismatch is a named
|
|
2084
2181
|
// refusal rather than a schema failure three fields later; here it was
|
|
2085
2182
|
// neither.
|
|
2086
|
-
protocolVersion:
|
|
2087
|
-
runnerId:
|
|
2088
|
-
jobId:
|
|
2183
|
+
protocolVersion: z10.literal(PROTOCOL_VERSION),
|
|
2184
|
+
runnerId: z10.string().min(1),
|
|
2185
|
+
jobId: z10.string().min(1),
|
|
2089
2186
|
/**
|
|
2090
2187
|
* The grant this daemon holds.
|
|
2091
2188
|
*
|
|
@@ -2093,9 +2190,9 @@ var FetchRequest = z9.object({
|
|
|
2093
2190
|
* only the job would be answerable for whatever lease exists when it
|
|
2094
2191
|
* arrives ({@link Lease.id}).
|
|
2095
2192
|
*/
|
|
2096
|
-
leaseId:
|
|
2193
|
+
leaseId: z10.string().min(1)
|
|
2097
2194
|
}).strict();
|
|
2098
|
-
var FetchResponse =
|
|
2195
|
+
var FetchResponse = z10.object({
|
|
2099
2196
|
/**
|
|
2100
2197
|
* The work, sealed to the device that claimed it — byollm_009 §6.
|
|
2101
2198
|
*
|
|
@@ -2164,6 +2261,8 @@ export {
|
|
|
2164
2261
|
PublicIdentity,
|
|
2165
2262
|
REFUSAL_MESSAGES,
|
|
2166
2263
|
RETIREMENT_WINDOW_MS,
|
|
2264
|
+
ROSTER_CONTEXT,
|
|
2265
|
+
ROSTER_MAX_AGE_MS,
|
|
2167
2266
|
RefusalReason,
|
|
2168
2267
|
ReleaseRequest,
|
|
2169
2268
|
ReleaseResponse,
|
|
@@ -2178,6 +2277,7 @@ export {
|
|
|
2178
2277
|
SUPPORTED_PROTOCOL_VERSIONS,
|
|
2179
2278
|
SealedEnvelope,
|
|
2180
2279
|
SealedOutcome,
|
|
2280
|
+
SignedRoster,
|
|
2181
2281
|
SizeClass,
|
|
2182
2282
|
StoredKeys,
|
|
2183
2283
|
Succession,
|
|
@@ -2210,8 +2310,10 @@ export {
|
|
|
2210
2310
|
provenanceFor,
|
|
2211
2311
|
publicIdentityOf,
|
|
2212
2312
|
resolveCost,
|
|
2313
|
+
rosterStatement,
|
|
2213
2314
|
seal,
|
|
2214
2315
|
signRequest,
|
|
2316
|
+
signRoster,
|
|
2215
2317
|
signSiteRequest,
|
|
2216
2318
|
signSuccession,
|
|
2217
2319
|
signWith,
|
|
@@ -2221,6 +2323,7 @@ export {
|
|
|
2221
2323
|
verifyLink,
|
|
2222
2324
|
verifyPublicIdentity,
|
|
2223
2325
|
verifyRequest,
|
|
2326
|
+
verifyRoster,
|
|
2224
2327
|
verifySiteRequest,
|
|
2225
2328
|
verifyWith,
|
|
2226
2329
|
walkSuccession
|