@cofy-x/axern-sdk 0.4.1 → 0.5.1

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 CHANGED
@@ -101,10 +101,11 @@ Common options:
101
101
  - `client`: explicit `AxernClient` shared by the sandbox
102
102
  - `argv`, `env`, `cwd`: initial sandbox process configuration
103
103
  - `runtimeClass`: runtime selector, for example `runsc` or `runc`
104
- - `requestCpu`, `requestMemory`: scheduler resource requests such as `500m`
105
- and `512MiB`; numeric CPU values are cores and numeric memory values are bytes
106
- - `limitCpu`, `limitMemory`: runtime cgroup resource limits such as `1` and
107
- `1GiB`; numeric CPU values are cores and numeric memory values are bytes
104
+ - `requestCpu`, `requestMemory`, `requestEphemeralStorage`: scheduler resource
105
+ requests such as `500m`, `512MiB`, and `1GiB`; numeric CPU values are cores
106
+ and numeric memory/storage values are bytes
107
+ - `limitCpu`, `limitMemory`, `limitEphemeralStorage`: runtime hard limits;
108
+ numeric CPU values are cores and numeric memory/storage values are bytes
108
109
  - `readyTimeoutMs`: service replica readiness timeout
109
110
 
110
111
  Tunnel options:
@@ -33,13 +33,20 @@ export interface CreateServiceOptions {
33
33
  env?: Record<string, string>;
34
34
  cwd?: string;
35
35
  runtimeClass?: string;
36
+ extensionCapabilities?: readonly ExtensionCapability[];
36
37
  volumes?: readonly VolumeMount[];
37
38
  requestCpu?: ResourceQuantity;
38
39
  requestMemory?: ResourceQuantity;
40
+ requestEphemeralStorage?: ResourceQuantity;
39
41
  limitCpu?: ResourceQuantity;
40
42
  limitMemory?: ResourceQuantity;
43
+ limitEphemeralStorage?: ResourceQuantity;
41
44
  labels?: Record<string, string>;
42
45
  }
46
+ export interface ExtensionCapability {
47
+ name: string;
48
+ value?: string;
49
+ }
43
50
  export interface ReadRunOutputOptions {
44
51
  cursor?: string;
45
52
  follow?: boolean;
@@ -207,6 +207,9 @@ export class AxernClient {
207
207
  env: options.env ?? {},
208
208
  cwd: options.cwd ?? "",
209
209
  runtime_class: options.runtimeClass ?? "",
210
+ extension_capability_requirements: (options.extensionCapabilities ?? []).map((capability) => ({
211
+ capability: { name: capability.name, value: capability.value ?? "" },
212
+ })),
210
213
  volume_mounts: serviceVolumeMounts(options.volumes),
211
214
  resources,
212
215
  },
@@ -5,6 +5,8 @@ package axern.control.admin.v1;
5
5
  option go_package = "github.com/cofy-x/axern/sdk/go/gen/axern/control/admin/v1;adminv1";
6
6
 
7
7
  import "google/protobuf/timestamp.proto";
8
+ import "axern/control/capability/v1/capability.proto";
9
+ import "axern/control/node/v1/node_control.proto";
8
10
 
9
11
  enum AdminNodeLifecycleStatus {
10
12
  ADMIN_NODE_LIFECYCLE_STATUS_UNSPECIFIED = 0;
@@ -43,7 +45,93 @@ message RetireAdminNodeResponse {
43
45
  AdminNode node = 1;
44
46
  }
45
47
 
48
+ message GetNodeCapabilitySnapshotRequest {
49
+ string node_id = 1;
50
+ }
51
+
52
+ message GetNodeCapabilitySnapshotResponse {
53
+ axern.control.capability.v1.CapabilitySnapshot snapshot = 1;
54
+ }
55
+
56
+ message AdminCapabilityTransition {
57
+ string transition_id = 1;
58
+ string node_id = 2;
59
+ string snapshot_id = 3;
60
+ int64 snapshot_sequence = 4;
61
+ axern.control.capability.v1.CapabilityKey key = 5;
62
+ axern.control.capability.v1.CapabilityState old_state = 6;
63
+ axern.control.capability.v1.CapabilityState new_state = 7;
64
+ axern.control.capability.v1.CapabilityEvidence old_evidence = 8;
65
+ axern.control.capability.v1.CapabilityEvidence new_evidence = 9;
66
+ axern.control.capability.v1.CapabilityReasonCode old_reason_code = 10;
67
+ axern.control.capability.v1.CapabilityReasonCode new_reason_code = 11;
68
+ string reason = 12;
69
+ google.protobuf.Timestamp observed_at = 13;
70
+ google.protobuf.Timestamp reported_at = 14;
71
+ }
72
+
73
+ message ListNodeCapabilityTransitionsRequest {
74
+ string node_id = 1;
75
+ int32 limit = 2;
76
+ }
77
+
78
+ message ListNodeCapabilityTransitionsResponse {
79
+ repeated AdminCapabilityTransition transitions = 1;
80
+ }
81
+
82
+ message AdminCapabilityReconcileItem {
83
+ string allocation_id = 1;
84
+ string node_id = 2;
85
+ repeated axern.control.capability.v1.CapabilityDependency pending_dependencies = 3;
86
+ int32 attempts = 4;
87
+ google.protobuf.Timestamp next_run_at = 5;
88
+ google.protobuf.Timestamp lease_expires_at = 6;
89
+ string last_error = 7;
90
+ google.protobuf.Timestamp updated_at = 8;
91
+ }
92
+
93
+ message ListCapabilityReconcileQueueRequest {
94
+ string node_id = 1;
95
+ int32 limit = 2;
96
+ }
97
+
98
+ message ListCapabilityReconcileQueueResponse {
99
+ repeated AdminCapabilityReconcileItem items = 1;
100
+ }
101
+
102
+ message GetAllocationCapabilityDiagnosticsRequest {
103
+ string allocation_id = 1;
104
+ }
105
+
106
+ message GetAllocationCapabilityDiagnosticsResponse {
107
+ string allocation_id = 1;
108
+ string node_id = 2;
109
+ repeated axern.control.capability.v1.CapabilityDependency required_dependencies = 3;
110
+ repeated axern.control.capability.v1.CapabilityDependency admitted_dependencies = 4;
111
+ axern.control.capability.v1.CapabilityConditionSet condition_set = 5;
112
+ AdminCapabilityReconcileItem reconcile = 6;
113
+ int64 allocation_attempt = 7;
114
+ bool create_admission_recorded = 8;
115
+ string create_dependency_set_digest = 9;
116
+ google.protobuf.Timestamp create_admitted_at = 10;
117
+ AllocationMemoryAdmissionEvidence memory_admission = 11;
118
+ axern.control.node.v1.AllocationMemoryObservation latest_memory_observation = 12;
119
+ }
120
+
121
+ message AllocationMemoryAdmissionEvidence {
122
+ int64 sandbox_memory_request_bytes = 1;
123
+ int64 sandbox_memory_limit_bytes = 2;
124
+ axern.control.node.v1.NodeMemoryBudget node_memory_budget = 3;
125
+ google.protobuf.Timestamp summary_collected_at = 4;
126
+ int64 node_local_commitment_bytes = 5;
127
+ google.protobuf.Timestamp admitted_at = 6;
128
+ }
129
+
46
130
  service NodeAdmin {
47
131
  rpc ListAdminNodes(ListAdminNodesRequest) returns (ListAdminNodesResponse) {}
48
132
  rpc RetireAdminNode(RetireAdminNodeRequest) returns (RetireAdminNodeResponse) {}
133
+ rpc GetNodeCapabilitySnapshot(GetNodeCapabilitySnapshotRequest) returns (GetNodeCapabilitySnapshotResponse) {}
134
+ rpc ListNodeCapabilityTransitions(ListNodeCapabilityTransitionsRequest) returns (ListNodeCapabilityTransitionsResponse) {}
135
+ rpc ListCapabilityReconcileQueue(ListCapabilityReconcileQueueRequest) returns (ListCapabilityReconcileQueueResponse) {}
136
+ rpc GetAllocationCapabilityDiagnostics(GetAllocationCapabilityDiagnosticsRequest) returns (GetAllocationCapabilityDiagnosticsResponse) {}
49
137
  }
@@ -0,0 +1,209 @@
1
+ syntax = "proto3";
2
+
3
+ package axern.control.capability.v1;
4
+
5
+ option go_package = "github.com/cofy-x/axern/sdk/go/gen/axern/control/capability/v1;capabilityv1";
6
+
7
+ import "google/protobuf/timestamp.proto";
8
+
9
+ // PlatformCapability is the closed set of Axern-owned placement and
10
+ // enforcement claims. User and node configuration cannot manufacture these
11
+ // claims; only the owning observed-capability provider may publish them.
12
+ enum PlatformCapability {
13
+ PLATFORM_CAPABILITY_UNSPECIFIED = 0;
14
+ PLATFORM_CAPABILITY_PORT_FORWARDING = 1;
15
+ PLATFORM_CAPABILITY_NETWORK_BRIDGE = 2;
16
+ PLATFORM_CAPABILITY_NETWORK_BPFNET = 3;
17
+ PLATFORM_CAPABILITY_CGROUP_V2_MEMORY_CONTROLLER = 4;
18
+ PLATFORM_CAPABILITY_RUNC_MEMORY_HARD_LIMIT = 5;
19
+ PLATFORM_CAPABILITY_RUNSC_MEMORY_HARD_LIMIT = 6;
20
+ PLATFORM_CAPABILITY_FILESTORE_OVERLAYFS_UPPER = 7;
21
+ PLATFORM_CAPABILITY_XFS_PROJECT_QUOTA = 8;
22
+ PLATFORM_CAPABILITY_RUNC_EPHEMERAL_STORAGE_HARD_LIMIT = 9;
23
+ PLATFORM_CAPABILITY_RUNSC_EPHEMERAL_STORAGE_HARD_LIMIT = 10;
24
+ PLATFORM_CAPABILITY_ROOTFS_LOWER_EROFS = 11;
25
+ // Provider facts consumed by the catalog's derived workload capabilities.
26
+ // They are not valid workload requirements on their own.
27
+ PLATFORM_CAPABILITY_RUNC_MEMORY_ENFORCEMENT_SELF_TEST = 12;
28
+ PLATFORM_CAPABILITY_RUNSC_MEMORY_ENFORCEMENT_SELF_TEST = 13;
29
+ PLATFORM_CAPABILITY_RUNC_EPHEMERAL_ENFORCEMENT_SELF_TEST = 14;
30
+ PLATFORM_CAPABILITY_RUNSC_EPHEMERAL_ENFORCEMENT_SELF_TEST = 15;
31
+ }
32
+
33
+ message ExtensionCapability {
34
+ // A DNS-qualified name such as example.com/accelerator. Axern-owned DNS
35
+ // suffixes are reserved and rejected by API and node configuration parsing.
36
+ string name = 1;
37
+ // Values use exact-match semantics. An empty value is a valid claim.
38
+ string value = 2;
39
+ }
40
+
41
+ message CapabilityKey {
42
+ oneof kind {
43
+ PlatformCapability platform = 1;
44
+ ExtensionCapability extension = 2;
45
+ }
46
+ }
47
+
48
+ enum CapabilityState {
49
+ CAPABILITY_STATE_UNSPECIFIED = 0;
50
+ CAPABILITY_STATE_AVAILABLE = 1;
51
+ CAPABILITY_STATE_DEGRADED = 2;
52
+ CAPABILITY_STATE_UNAVAILABLE = 3;
53
+ CAPABILITY_STATE_UNKNOWN = 4;
54
+ }
55
+
56
+ enum CapabilityProvider {
57
+ CAPABILITY_PROVIDER_UNSPECIFIED = 0;
58
+ CAPABILITY_PROVIDER_CONFIG = 1;
59
+ CAPABILITY_PROVIDER_HOST_CGROUP = 2;
60
+ CAPABILITY_PROVIDER_FILESTORE = 3;
61
+ CAPABILITY_PROVIDER_RUNC_SELF_TEST = 4;
62
+ CAPABILITY_PROVIDER_RUNSC_SELF_TEST = 5;
63
+ CAPABILITY_PROVIDER_NETWORK_HEALTH = 6;
64
+ CAPABILITY_PROVIDER_DERIVED = 7;
65
+ }
66
+
67
+ enum CapabilityReasonCode {
68
+ CAPABILITY_REASON_CODE_UNSPECIFIED = 0;
69
+ CAPABILITY_REASON_CODE_AVAILABLE = 1;
70
+ CAPABILITY_REASON_CODE_DISABLED = 2;
71
+ CAPABILITY_REASON_CODE_PROBE_FAILED = 3;
72
+ CAPABILITY_REASON_CODE_PROBE_ERROR = 4;
73
+ CAPABILITY_REASON_CODE_EXPIRED = 5;
74
+ CAPABILITY_REASON_CODE_IDENTITY_CHANGED = 6;
75
+ CAPABILITY_REASON_CODE_DEPENDENCY_UNAVAILABLE = 7;
76
+ CAPABILITY_REASON_CODE_RECOVERY_PENDING = 8;
77
+ CAPABILITY_REASON_CODE_ENFORCEMENT_LOST = 9;
78
+ }
79
+
80
+ message ConfigEvidenceIdentity {
81
+ string config_digest = 1;
82
+ }
83
+
84
+ message BootEvidenceIdentity {
85
+ string boot_id = 1;
86
+ }
87
+
88
+ message MountEvidenceIdentity {
89
+ string boot_id = 1;
90
+ string mount_identity = 2;
91
+ }
92
+
93
+ message RuntimeEvidenceIdentity {
94
+ string boot_id = 1;
95
+ string runtime_name = 2;
96
+ string runtime_binary_digest = 3;
97
+ string runtime_config_digest = 4;
98
+ }
99
+
100
+ message DerivedEvidenceIdentity {
101
+ string catalog_digest = 1;
102
+ // Canonical digest of the typed evidence identities for every catalog
103
+ // dependency. This makes the derived subject change when its mount,
104
+ // runtime, boot, or config subjects change without treating a normal TTL
105
+ // refresh as a new identity.
106
+ string dependency_evidence_digest = 2;
107
+ }
108
+
109
+ // CapabilityEvidence identifies the concrete subject that was probed. Its
110
+ // identity is orthogonal to observation freshness: a mount or runtime proof
111
+ // may still require valid_until even though its subject identity is stable.
112
+ message CapabilityEvidence {
113
+ string evidence_id = 1;
114
+ oneof identity {
115
+ ConfigEvidenceIdentity config = 2;
116
+ BootEvidenceIdentity boot = 3;
117
+ MountEvidenceIdentity mount = 4;
118
+ RuntimeEvidenceIdentity runtime = 5;
119
+ DerivedEvidenceIdentity derived = 6;
120
+ }
121
+ }
122
+
123
+ // CapabilityObservationProof is the durable, non-recursive proof selected
124
+ // from a snapshot. Dependencies are represented as one bounded flat proof set
125
+ // so persistence and validation remain bounded.
126
+ message CapabilityObservationProof {
127
+ CapabilityKey key = 1;
128
+ string observation_id = 2;
129
+ CapabilityProvider provider = 3;
130
+ google.protobuf.Timestamp observed_at = 4;
131
+ google.protobuf.Timestamp valid_until = 5;
132
+ CapabilityEvidence evidence = 6;
133
+ }
134
+
135
+ message CapabilityObservation {
136
+ CapabilityKey key = 1;
137
+ CapabilityState state = 2;
138
+ CapabilityProvider provider = 3;
139
+ string observation_id = 4;
140
+ google.protobuf.Timestamp observed_at = 5;
141
+ google.protobuf.Timestamp valid_until = 6;
142
+ CapabilityEvidence evidence = 7;
143
+ repeated CapabilityObservationProof dependencies = 8;
144
+ CapabilityReasonCode reason_code = 9;
145
+ string reason = 10;
146
+ }
147
+
148
+ message CapabilitySnapshot {
149
+ string node_instance_id = 1;
150
+ int64 sequence = 2;
151
+ string snapshot_id = 3;
152
+ google.protobuf.Timestamp collected_at = 4;
153
+ repeated CapabilityObservation observations = 5;
154
+ }
155
+
156
+ message CapabilitySnapshotReference {
157
+ string node_instance_id = 1;
158
+ int64 sequence = 2;
159
+ string snapshot_id = 3;
160
+ google.protobuf.Timestamp collected_at = 4;
161
+ }
162
+
163
+ message ExtensionCapabilityRequirement {
164
+ ExtensionCapability capability = 1;
165
+ }
166
+
167
+ enum CapabilityLossPolicy {
168
+ CAPABILITY_LOSS_POLICY_UNSPECIFIED = 0;
169
+ CAPABILITY_LOSS_POLICY_ADMISSION_ONLY = 1;
170
+ CAPABILITY_LOSS_POLICY_DEGRADE = 2;
171
+ CAPABILITY_LOSS_POLICY_FAIL_STOP = 3;
172
+ }
173
+
174
+ message CapabilityDependency {
175
+ CapabilityKey key = 1;
176
+ CapabilityLossPolicy loss_policy = 2;
177
+ CapabilitySnapshotReference selected_snapshot = 3;
178
+ CapabilityObservationProof selected_observation = 4;
179
+ repeated CapabilityObservationProof dependency_observations = 5;
180
+ }
181
+
182
+ // CapabilityDependencySet is the durable representation stored with an
183
+ // allocation. A wrapper keeps JSON persistence on the canonical proto shape.
184
+ message CapabilityDependencySet {
185
+ repeated CapabilityDependency dependencies = 1;
186
+ }
187
+
188
+ enum CapabilityConditionState {
189
+ CAPABILITY_CONDITION_STATE_UNSPECIFIED = 0;
190
+ CAPABILITY_CONDITION_STATE_HEALTHY = 1;
191
+ CAPABILITY_CONDITION_STATE_DEGRADED = 2;
192
+ CAPABILITY_CONDITION_STATE_FAILED = 3;
193
+ CAPABILITY_CONDITION_STATE_UNKNOWN = 4;
194
+ }
195
+
196
+ message CapabilityCondition {
197
+ CapabilityKey key = 1;
198
+ CapabilityConditionState state = 2;
199
+ CapabilityReasonCode reason_code = 3;
200
+ string message = 4;
201
+ google.protobuf.Timestamp observed_at = 5;
202
+ CapabilityObservationProof proof = 6;
203
+ }
204
+
205
+ message CapabilityConditionSet {
206
+ int64 revision = 1;
207
+ google.protobuf.Timestamp observed_at = 2;
208
+ repeated CapabilityCondition conditions = 3;
209
+ }
@@ -5,11 +5,21 @@ package axern.control.common.v1;
5
5
  option go_package = "github.com/cofy-x/axern/sdk/go/gen/axern/control/common/v1;commonv1";
6
6
 
7
7
  import "google/protobuf/timestamp.proto";
8
+ import "axern/control/capability/v1/capability.proto";
8
9
  import "axern/control/storage/v1/storage_types.proto";
9
10
 
10
11
  message ResourceQuantity {
11
12
  int64 cpu_milli = 1;
13
+ // Total sandbox memory budget. requests.memory_bytes is the placement,
14
+ // reservation, and namespace-quota commitment. limits.memory_bytes is the
15
+ // host cgroup v2 hard boundary and includes runtime processes, guest
16
+ // accounting, kernel memory, shmem, and charged file/page cache.
12
17
  int64 memory_bytes = 2;
18
+ // Sandbox-lifetime node-local disposable storage managed by Axern. The
19
+ // current accounting scope is the runtime writable root overlay and its
20
+ // metadata/copy-up/whiteouts; persistent volumes and immutable image data
21
+ // are excluded.
22
+ int64 ephemeral_storage_bytes = 3;
13
23
  }
14
24
 
15
25
  message ResourceSpec {
@@ -41,11 +51,6 @@ message NetworkSpec {
41
51
  NetworkMode mode = 1;
42
52
  }
43
53
 
44
- message CapabilityRequirement {
45
- string name = 1;
46
- string value = 2;
47
- }
48
-
49
54
  message PlacementConstraints {
50
55
  map<string, string> node_selector = 1;
51
56
  }
@@ -110,7 +115,7 @@ message ExecutionConfig {
110
115
  ResourceSpec resources = 4;
111
116
  repeated PortSpec ports = 5;
112
117
  NetworkSpec network = 6;
113
- repeated CapabilityRequirement capability_requirements = 7;
118
+ repeated axern.control.capability.v1.ExtensionCapabilityRequirement extension_capability_requirements = 7;
114
119
  PlacementConstraints placement = 8;
115
120
  repeated SecretEnvVar secret_env = 9;
116
121
  repeated SecretFile secret_files = 10;
@@ -147,6 +152,8 @@ enum WorkloadDiagnosticCode {
147
152
  WORKLOAD_DIAGNOSTIC_CODE_VOLUME_PUBLISH_ERROR = 11;
148
153
  WORKLOAD_DIAGNOSTIC_CODE_VOLUME_RELEASE_ERROR = 12;
149
154
  WORKLOAD_DIAGNOSTIC_CODE_VOLUME_SPEC_CONFLICT = 13;
155
+ WORKLOAD_DIAGNOSTIC_CODE_CAPABILITY_ENFORCEMENT_LOST = 14;
156
+ WORKLOAD_DIAGNOSTIC_CODE_MEMORY_LIMIT_EXCEEDED = 15;
150
157
  }
151
158
 
152
159
  enum LeaseType {
@@ -5,6 +5,7 @@ package axern.control.node.v1;
5
5
  option go_package = "github.com/cofy-x/axern/sdk/go/gen/axern/control/node/v1;nodev1";
6
6
 
7
7
  import "axern/control/common/v1/common.proto";
8
+ import "axern/control/capability/v1/capability.proto";
8
9
  import "axern/control/tunnel/v1/tunnel.proto";
9
10
  import "google/protobuf/timestamp.proto";
10
11
 
@@ -21,6 +22,7 @@ enum MountType {
21
22
  MOUNT_TYPE_OCI = 2;
22
23
  MOUNT_TYPE_NYDUS = 3;
23
24
  MOUNT_TYPE_OSS = 4;
25
+ MOUNT_TYPE_EROFS = 5;
24
26
  }
25
27
 
26
28
  enum ComponentState {
@@ -54,6 +56,48 @@ message ResourcesSummary {
54
56
  int64 axnoded_committed_bytes = 4;
55
57
  int64 axnoded_used_bytes = 5;
56
58
  int64 axnoded_memory_unbounded_count = 6;
59
+ int64 axnoded_ephemeral_storage_committed_bytes = 7;
60
+ int64 axnoded_ephemeral_storage_used_bytes = 8;
61
+ int64 axnoded_ephemeral_storage_unbounded_count = 9;
62
+ }
63
+
64
+ // NodeMemoryBudget is the node-observed scheduling boundary. The system
65
+ // reserve covers node-local control processes and is never charged to a
66
+ // namespace or allocation.
67
+ enum NodeMemoryBudgetMode {
68
+ NODE_MEMORY_BUDGET_MODE_UNSPECIFIED = 0;
69
+ // Capacity and internal usage are observed from the delegated cgroup v2
70
+ // domain. Runtime-specific hard-limit capabilities remain separate facts.
71
+ NODE_MEMORY_BUDGET_MODE_CGROUP_V2 = 1;
72
+ // Local-development capacity is observed from the configured node resource
73
+ // source without claiming a cgroup hard boundary.
74
+ NODE_MEMORY_BUDGET_MODE_DISABLED_DEV = 2;
75
+ }
76
+
77
+ message NodeMemoryBudget {
78
+ // Physical capacity reported by the selected node resource source. This is
79
+ // a consistency/diagnostic fact and is never added to allocatable memory.
80
+ int64 physical_capacity_bytes = 1;
81
+ // The resource source's schedulable value before the Axern system reserve.
82
+ int64 source_allocatable_bytes = 2;
83
+ // A finite value further caps source_allocatable_bytes; zero with
84
+ // delegated_root_limit_finite=false represents kernel `max`.
85
+ int64 delegated_root_limit_bytes = 3;
86
+ bool delegated_root_limit_finite = 4;
87
+ int64 system_reserve_bytes = 5;
88
+ int64 effective_allocatable_bytes = 6;
89
+ int64 local_commitment_bytes = 7;
90
+ int64 cleanup_debt_bytes = 8;
91
+ int64 internal_current_bytes = 9;
92
+ // Identity of the capacity domain. Production uses the boot-scoped cgroup
93
+ // mount/root identity; disabled_dev uses a boot-scoped resource-source
94
+ // identity and cannot satisfy runtime hard-limit requirements.
95
+ string capacity_identity = 10;
96
+ google.protobuf.Timestamp sampled_at = 11;
97
+ int32 retiring_cgroup_count = 12;
98
+ int64 oldest_retiring_age_seconds = 13;
99
+ bool system_reserve_exhausted = 14;
100
+ NodeMemoryBudgetMode mode = 15;
57
101
  }
58
102
 
59
103
  message PoolsSummary {
@@ -121,6 +165,14 @@ message NodeStorageSummary {
121
165
  int64 inodes_available = 7;
122
166
  bool collected = 8;
123
167
  string error = 9;
168
+ int64 system_reserve_bytes = 10;
169
+ int64 reserved_bytes = 11;
170
+ int64 allocatable_bytes = 12;
171
+ int64 active_reservations = 13;
172
+ string filesystem_type = 14;
173
+ string mount_identity = 15;
174
+ int64 allocation_used_bytes = 16;
175
+ bool unlinked_backing_usage_unknown = 17;
124
176
  }
125
177
 
126
178
  message ComponentsSummary {
@@ -157,10 +209,11 @@ message NodeSummary {
157
209
  repeated LocalitySummary locality = 5;
158
210
  NodeState node_state = 6;
159
211
  map<string, string> labels = 7;
160
- repeated string capabilities = 8;
212
+ axern.control.capability.v1.CapabilitySnapshot capability_snapshot = 8;
161
213
  axern.control.common.v1.ResourceQuantity capacity = 9;
162
214
  axern.control.common.v1.ResourceQuantity allocatable = 10;
163
215
  repeated NodeStorageSummary storage = 11;
216
+ NodeMemoryBudget memory_budget = 12;
164
217
  }
165
218
 
166
219
  message RegisterNodeRequest {
@@ -192,6 +245,10 @@ message AllocationStatusObservation {
192
245
  google.protobuf.Timestamp observed_at = 7;
193
246
  bool ready = 8;
194
247
  string readiness_message = 9;
248
+ // diagnostic_code is the bounded, machine-readable terminal or degraded
249
+ // cause observed by the node. The message remains operator context and is
250
+ // never parsed to recover this code.
251
+ axern.control.common.v1.WorkloadDiagnosticCode diagnostic_code = 10;
195
252
  }
196
253
 
197
254
  message BatchReportAllocationStatusRequest {
@@ -202,6 +259,85 @@ message BatchReportAllocationStatusRequest {
202
259
 
203
260
  message BatchReportAllocationStatusResponse {}
204
261
 
262
+ // AllocationCapabilityConditionReport is deliberately independent from
263
+ // lifecycle status. Updating capability conditions cannot make an allocation
264
+ // RUNNING, READY, terminal, or alter its exit status/message.
265
+ message AllocationCapabilityConditionReport {
266
+ string allocation_id = 1;
267
+ int64 attempt = 2;
268
+ axern.control.capability.v1.CapabilityConditionSet condition_set = 3;
269
+ }
270
+
271
+ message BatchReportAllocationCapabilityConditionsRequest {
272
+ string node_id = 1;
273
+ string node_auth_token = 2;
274
+ repeated AllocationCapabilityConditionReport reports = 3;
275
+ }
276
+
277
+ message BatchReportAllocationCapabilityConditionsResponse {}
278
+
279
+ // AllocationMemoryObservation is a bounded latest-value report from the host
280
+ // cgroup. Runtime-specific diagnostics such as `runsc usage --full` are not
281
+ // included because they cannot be added to these kernel counters.
282
+ enum AllocationMemoryCleanupState {
283
+ ALLOCATION_MEMORY_CLEANUP_STATE_UNSPECIFIED = 0;
284
+ ALLOCATION_MEMORY_CLEANUP_STATE_ASSIGNED = 1;
285
+ ALLOCATION_MEMORY_CLEANUP_STATE_RETIRING = 2;
286
+ }
287
+
288
+ message AllocationMemoryObservation {
289
+ string allocation_id = 1;
290
+ int64 attempt = 2;
291
+ int64 revision = 3;
292
+ google.protobuf.Timestamp observed_at = 4;
293
+ int64 request_bytes = 5;
294
+ // Zero means the sandbox has no per-allocation hard limit. Usage and cgroup
295
+ // identity remain observable, while the control-verification fields must be
296
+ // false and swap_current_bytes is informational rather than constrained.
297
+ int64 limit_bytes = 6;
298
+ int64 current_bytes = 7;
299
+ int64 peak_bytes = 8;
300
+ int64 swap_current_bytes = 9;
301
+ int64 anon_bytes = 10;
302
+ int64 file_bytes = 11;
303
+ int64 shmem_bytes = 12;
304
+ int64 kernel_bytes = 13;
305
+ int64 dirty_bytes = 14;
306
+ int64 writeback_bytes = 15;
307
+ uint64 event_high = 16;
308
+ uint64 event_max = 17;
309
+ uint64 event_oom = 18;
310
+ uint64 event_oom_kill = 19;
311
+ uint64 event_oom_group_kill = 20;
312
+ double psi_some_avg10 = 21;
313
+ double psi_full_avg10 = 22;
314
+ uint64 psi_some_total_usec = 23;
315
+ uint64 psi_full_total_usec = 24;
316
+ string cgroup_identity = 25;
317
+ string runtime = 26;
318
+ bool parent_controls_verified = 27;
319
+ bool leaf_controls_verified = 28;
320
+ bool pid_roles_verified = 29;
321
+ AllocationMemoryCleanupState cleanup_state = 30;
322
+ // psi_available distinguishes an unsupported per-cgroup PSI interface from
323
+ // a supported interface that currently reports zero pressure. When false,
324
+ // all PSI values above must be zero.
325
+ bool psi_available = 31;
326
+ // peak_available reports whether peak_bytes came from the kernel
327
+ // memory.peak interface. Older cgroup v2 kernels may omit that optional
328
+ // file; in that case peak_bytes equals current_bytes and is only a sampled
329
+ // lower bound. Hard-limit evidence never depends on this observability bit.
330
+ bool peak_available = 32;
331
+ }
332
+
333
+ message BatchReportAllocationMemoryObservationsRequest {
334
+ string node_id = 1;
335
+ string node_auth_token = 2;
336
+ repeated AllocationMemoryObservation observations = 3;
337
+ }
338
+
339
+ message BatchReportAllocationMemoryObservationsResponse {}
340
+
205
341
  message WatchExecutionLeasesRequest {
206
342
  string node_id = 1;
207
343
  int64 after_revision = 2;
@@ -263,6 +399,9 @@ enum PlacementRejectionReason {
263
399
  PLACEMENT_REJECTION_REASON_NETWORK_UNSUPPORTED = 13;
264
400
  PLACEMENT_REJECTION_REASON_CAPABILITY_UNSUPPORTED = 14;
265
401
  PLACEMENT_REJECTION_REASON_NODE_RETIRED = 15;
402
+ PLACEMENT_REJECTION_REASON_INSUFFICIENT_EPHEMERAL_STORAGE = 16;
403
+ PLACEMENT_REJECTION_REASON_NODE_MEMORY_SYSTEM_RESERVE_EXHAUSTED = 17;
404
+ PLACEMENT_REJECTION_REASON_NODE_MEMORY_BUDGET_UNAVAILABLE = 18;
266
405
  }
267
406
 
268
407
  message PlacementRank {
@@ -296,6 +435,8 @@ service NodeControl {
296
435
  rpc RegisterNode(RegisterNodeRequest) returns (RegisterNodeResponse) {}
297
436
  rpc ReportNode(ReportNodeRequest) returns (ReportNodeResponse) {}
298
437
  rpc BatchReportAllocationStatus(BatchReportAllocationStatusRequest) returns (BatchReportAllocationStatusResponse) {}
438
+ rpc BatchReportAllocationCapabilityConditions(BatchReportAllocationCapabilityConditionsRequest) returns (BatchReportAllocationCapabilityConditionsResponse) {}
439
+ rpc BatchReportAllocationMemoryObservations(BatchReportAllocationMemoryObservationsRequest) returns (BatchReportAllocationMemoryObservationsResponse) {}
299
440
  rpc WatchExecutionLeases(WatchExecutionLeasesRequest) returns (stream WatchExecutionLeasesResponse) {}
300
441
  rpc WatchTunnelSessions(WatchTunnelSessionsRequest) returns (stream WatchTunnelSessionsResponse) {}
301
442
  rpc ReportTunnelSessionStatus(ReportTunnelSessionStatusRequest) returns (ReportTunnelSessionStatusResponse) {}
@@ -18,6 +18,9 @@ message NamespaceQuota {
18
18
  int64 version = 8;
19
19
  google.protobuf.Timestamp created_at = 9;
20
20
  google.protobuf.Timestamp updated_at = 10;
21
+ google.protobuf.Int64Value ephemeral_storage_bytes_limit = 11;
22
+ int64 reserved_ephemeral_storage_bytes = 12;
23
+ google.protobuf.Int64Value available_ephemeral_storage_bytes = 13;
21
24
  }
22
25
 
23
26
  enum NamespaceQuotaEventType {
@@ -36,6 +39,7 @@ enum NamespaceQuotaEventReason {
36
39
  NAMESPACE_QUOTA_EVENT_REASON_INSUFFICIENT_CPU = 1;
37
40
  NAMESPACE_QUOTA_EVENT_REASON_INSUFFICIENT_MEMORY = 2;
38
41
  NAMESPACE_QUOTA_EVENT_REASON_INSUFFICIENT_CPU_MEMORY = 3;
42
+ NAMESPACE_QUOTA_EVENT_REASON_INSUFFICIENT_EPHEMERAL_STORAGE = 4;
39
43
  }
40
44
 
41
45
  message NamespaceQuotaEvent {
@@ -56,6 +60,10 @@ message NamespaceQuotaEvent {
56
60
  google.protobuf.Int64Value available_memory_bytes = 15;
57
61
  string message = 16;
58
62
  google.protobuf.Timestamp created_at = 17;
63
+ int64 requested_ephemeral_storage_bytes = 18;
64
+ int64 reserved_ephemeral_storage_bytes = 19;
65
+ google.protobuf.Int64Value ephemeral_storage_bytes_limit = 20;
66
+ google.protobuf.Int64Value available_ephemeral_storage_bytes = 21;
59
67
  }
60
68
 
61
69
  message GetNamespaceQuotaRequest {
@@ -76,6 +84,7 @@ message ListNamespaceQuotasResponse {
76
84
  message NamespaceQuotaLimits {
77
85
  google.protobuf.Int64Value cpu_milli = 1;
78
86
  google.protobuf.Int64Value memory_bytes = 2;
87
+ google.protobuf.Int64Value ephemeral_storage_bytes = 3;
79
88
  }
80
89
 
81
90
  message SetNamespaceQuotaRequest {
@@ -5,6 +5,7 @@ package axern.control.run.v1;
5
5
  option go_package = "github.com/cofy-x/axern/sdk/go/gen/axern/control/run/v1;runv1";
6
6
 
7
7
  import "axern/control/common/v1/common.proto";
8
+ import "axern/control/capability/v1/capability.proto";
8
9
  import "google/protobuf/timestamp.proto";
9
10
 
10
11
  enum RunStatus {
@@ -34,9 +35,10 @@ message Run {
34
35
  bool exit_code_known = 13;
35
36
  string message = 14;
36
37
  axern.control.common.v1.WorkloadDiagnosticCode diagnostic_code = 15;
37
- // Reserved for durable output metadata once output retention is owned by
38
- // the control plane rather than inferred from a node-local allocation.
39
- reserved 16 to 18;
38
+ // Full, monotonically revised allocation capability condition projection.
39
+ // It is independent from run lifecycle status and message.
40
+ axern.control.capability.v1.CapabilityConditionSet capability_conditions = 16;
41
+ reserved 17 to 18;
40
42
  }
41
43
 
42
44
  message RunListFilter {
@@ -5,6 +5,7 @@ package axern.control.service.v1;
5
5
  option go_package = "github.com/cofy-x/axern/sdk/go/gen/axern/control/service/v1;servicev1";
6
6
 
7
7
  import "axern/control/common/v1/common.proto";
8
+ import "axern/control/capability/v1/capability.proto";
8
9
  import "google/protobuf/timestamp.proto";
9
10
 
10
11
  enum ServiceReplicaView {
@@ -35,6 +36,9 @@ message ServiceReplica {
35
36
  string readiness_message = 15;
36
37
  ServiceReplicaLifecycleRetry lifecycle_retry = 16;
37
38
  axern.control.common.v1.WorkspacePreparationFacts workspace_preparation = 17;
39
+ // Full, monotonically revised allocation capability condition projection.
40
+ // It is independent from replica lifecycle status and message.
41
+ axern.control.capability.v1.CapabilityConditionSet capability_conditions = 18;
38
42
  }
39
43
 
40
44
  enum ServiceReplicaLifecycleRetryReason {
@@ -7,7 +7,9 @@ export type ResourceQuantity = string | number;
7
7
  export interface ResourceOptions {
8
8
  requestCpu?: ResourceQuantity;
9
9
  requestMemory?: ResourceQuantity;
10
+ requestEphemeralStorage?: ResourceQuantity;
10
11
  limitCpu?: ResourceQuantity;
11
12
  limitMemory?: ResourceQuantity;
13
+ limitEphemeralStorage?: ResourceQuantity;
12
14
  }
13
15
  export declare function buildResourceSpec(options: ResourceOptions): Record<string, unknown> | undefined;
package/dist/resources.js CHANGED
@@ -5,22 +5,24 @@
5
5
  */
6
6
  import { SandboxValidationError } from "./errors/index.js";
7
7
  export function buildResourceSpec(options) {
8
- const requests = resourceQuantity("requestCpu", options.requestCpu, "requestMemory", options.requestMemory);
9
- const limits = resourceQuantity("limitCpu", options.limitCpu, "limitMemory", options.limitMemory);
8
+ const requests = resourceQuantity("requestCpu", options.requestCpu, "requestMemory", options.requestMemory, "requestEphemeralStorage", options.requestEphemeralStorage);
9
+ const limits = resourceQuantity("limitCpu", options.limitCpu, "limitMemory", options.limitMemory, "limitEphemeralStorage", options.limitEphemeralStorage);
10
10
  if (requests === undefined && limits === undefined) {
11
11
  return undefined;
12
12
  }
13
13
  return { requests, limits };
14
14
  }
15
- function resourceQuantity(cpuName, cpuValue, memoryName, memoryValue) {
15
+ function resourceQuantity(cpuName, cpuValue, memoryName, memoryValue, ephemeralStorageName, ephemeralStorageValue) {
16
16
  const cpu = parseCpuQuantity(cpuName, cpuValue);
17
17
  const memory = parseMemoryQuantity(memoryName, memoryValue);
18
- if (cpu <= 0 && memory <= 0) {
18
+ const ephemeralStorage = parseMemoryQuantity(ephemeralStorageName, ephemeralStorageValue);
19
+ if (cpu <= 0 && memory <= 0 && ephemeralStorage <= 0) {
19
20
  return undefined;
20
21
  }
21
22
  return {
22
23
  cpu_milli: cpu,
23
24
  memory_bytes: memory,
25
+ ephemeral_storage_bytes: ephemeralStorage,
24
26
  };
25
27
  }
26
28
  function parseCpuQuantity(name, value) {
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { AxernClient } from "../client/index.js";
7
+ import type { ExtensionCapability } from "../client/index.js";
7
8
  import type { SandboxProcess } from "../node/process.js";
8
9
  import type { ResourceQuantity } from "../resources.js";
9
10
  import type { ChmodOptions, CapabilityStatus, Command, ComputerUseDisplay, ComputerUseKeyboardOptions, ComputerUseMouseOptions, ComputerUseScreenshot, ComputerUseScreenshotOptions, ComputerUseStatus, CopyOptions, DownloadDirOptions, ExecOptions, ExecResult, ImageExecOptions, ImageProcessOptions, MkdirOptions, MoveOptions, NodeCallOptions, ProcessOptions, RemoveOptions, SandboxFileInfo, TunnelMetadata, TunnelOptions, TouchOptions, UploadDirOptions, VolumeMount, WriteFileOptions } from "../types.js";
@@ -17,11 +18,14 @@ export interface SandboxOptions {
17
18
  env?: Record<string, string>;
18
19
  cwd?: string;
19
20
  runtimeClass?: string;
21
+ extensionCapabilities?: readonly ExtensionCapability[];
20
22
  volumes?: readonly VolumeMount[];
21
23
  requestCpu?: ResourceQuantity;
22
24
  requestMemory?: ResourceQuantity;
25
+ requestEphemeralStorage?: ResourceQuantity;
23
26
  limitCpu?: ResourceQuantity;
24
27
  limitMemory?: ResourceQuantity;
28
+ limitEphemeralStorage?: ResourceQuantity;
25
29
  readyTimeoutMs?: number;
26
30
  labels?: Record<string, string>;
27
31
  registryCredentialId?: string;
@@ -59,11 +59,14 @@ export class Sandbox {
59
59
  env: this.options.env,
60
60
  cwd: this.options.cwd,
61
61
  runtimeClass: this.options.runtimeClass,
62
+ extensionCapabilities: this.options.extensionCapabilities,
62
63
  volumes: this.options.volumes,
63
64
  requestCpu: this.options.requestCpu,
64
65
  requestMemory: this.options.requestMemory,
66
+ requestEphemeralStorage: this.options.requestEphemeralStorage,
65
67
  limitCpu: this.options.limitCpu,
66
68
  limitMemory: this.options.limitMemory,
69
+ limitEphemeralStorage: this.options.limitEphemeralStorage,
67
70
  labels: sandboxLabels(this.options.labels),
68
71
  });
69
72
  this.serviceId = String(service.id ?? "");
package/dist/version.d.ts CHANGED
@@ -3,5 +3,5 @@
3
3
  * Copyright 2026 cofy-x
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- export declare const AXERN_VERSION = "0.4.1";
6
+ export declare const AXERN_VERSION = "0.5.1";
7
7
  export declare function platformName(): string;
package/dist/version.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Copyright 2026 cofy-x
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- export const AXERN_VERSION = "0.4.1";
6
+ export const AXERN_VERSION = "0.5.1";
7
7
  export function platformName() {
8
8
  return "axern";
9
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cofy-x/axern-sdk",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "TypeScript SDK for Axern agentic infrastructure and isolated sandboxes",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {