simulo-interfaces 0.18.0__tar.gz → 0.20.0__tar.gz
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.
- {simulo_interfaces-0.18.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.20.0}/PKG-INFO +1 -1
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/pyproject.toml +1 -1
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/__init__.py +8 -1
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/asset_catalog.py +1 -1
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/enums.py +42 -2
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/manifest.py +3 -3
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/outputs.py +3 -2
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/runs.py +18 -2
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/submit.py +68 -8
- simulo_interfaces-0.20.0/src/simulo/interfaces/platform/systems.py +106 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/SOURCES.txt +1 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/MANIFEST.in +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/PYPI.md +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/setup.cfg +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/__init__.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/__init__.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/asset.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/entity.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/light.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/materials.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/physics.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/pose.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/prop.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/record.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/robot.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/scene.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/sensor.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/terrain.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/visual.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/world.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/exceptions.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/ids.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/_payload.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/app.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/asset.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/debug.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/domain.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/job_events.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/policy_bundle.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/runtime.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/volume.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/py.typed +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/__init__.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/components.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/env.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/player.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/policy.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/scenario.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/task.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/trainer.py +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
- {simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo-interfaces"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.20.0"
|
|
8
8
|
description = "Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/__init__.py
RENAMED
|
@@ -70,7 +70,7 @@ from simulo.interfaces.platform.asset_catalog import (
|
|
|
70
70
|
from simulo.interfaces.platform.callbacks import JobCallbackProtocol
|
|
71
71
|
from simulo.interfaces.platform.debug import DebugSessionProtocol, ReplaySessionProtocol, VisualizationSessionProtocol
|
|
72
72
|
from simulo.interfaces.platform.domain import Checkpoint, Job, Package, Project, Resource, Tag, TrainedModel
|
|
73
|
-
from simulo.interfaces.platform.enums import OutputKind
|
|
73
|
+
from simulo.interfaces.platform.enums import OutputKind, SystemType
|
|
74
74
|
from simulo.interfaces.platform.job_events import (
|
|
75
75
|
MAX_PUBLIC_ASSET_FINDING_COUNT,
|
|
76
76
|
MAX_PUBLIC_JOB_EVENT_ATTEMPT,
|
|
@@ -228,6 +228,7 @@ from simulo.interfaces.platform.submit import (
|
|
|
228
228
|
SEED_JOB_REF_GRAMMAR,
|
|
229
229
|
SEED_SOURCE_CONFLICT_CODE,
|
|
230
230
|
SEED_SOURCE_FIELDS,
|
|
231
|
+
WORKER_BOOTSTRAP_ROUTE,
|
|
231
232
|
WORKER_CLAIM_ROUTE,
|
|
232
233
|
WORKER_JOB_COMPLETE_ROUTE_TEMPLATE,
|
|
233
234
|
WORKER_JOB_HEARTBEAT_ROUTE_TEMPLATE,
|
|
@@ -240,11 +241,16 @@ from simulo.interfaces.platform.submit import (
|
|
|
240
241
|
canonical_pypi_name,
|
|
241
242
|
is_platform_pinned,
|
|
242
243
|
)
|
|
244
|
+
from simulo.interfaces.platform.systems import SYSTEM_SPECS, SystemSpec
|
|
243
245
|
from simulo.interfaces.platform.volume import VolumeProtocol
|
|
244
246
|
|
|
245
247
|
__all__ = [
|
|
246
248
|
# enums
|
|
247
249
|
"OutputKind",
|
|
250
|
+
"SystemType",
|
|
251
|
+
# systems (GPU system-tier catalog, issue #1362)
|
|
252
|
+
"SystemSpec",
|
|
253
|
+
"SYSTEM_SPECS",
|
|
248
254
|
# closed public job-event contract (#656)
|
|
249
255
|
"PUBLIC_JOB_EVENT_SCHEMA_VERSION",
|
|
250
256
|
"WORKER_JOB_EVENTS_ROUTE_TEMPLATE",
|
|
@@ -346,6 +352,7 @@ __all__ = [
|
|
|
346
352
|
"JOB_MODELS_ROUTE_TEMPLATE",
|
|
347
353
|
"JOB_MODEL_ROUTE_TEMPLATE",
|
|
348
354
|
"JOB_MODEL_DOWNLOAD_ROUTE_TEMPLATE",
|
|
355
|
+
"WORKER_BOOTSTRAP_ROUTE",
|
|
349
356
|
"WORKER_CLAIM_ROUTE",
|
|
350
357
|
"WORKER_JOB_LOGS_ROUTE_TEMPLATE",
|
|
351
358
|
"WORKER_JOB_HEARTBEAT_ROUTE_TEMPLATE",
|
|
@@ -493,7 +493,7 @@ ASSET_VERSION_REPORT_ROUTE_TEMPLATE = "/v1/asset-versions/{version_id}/report"
|
|
|
493
493
|
ASSET_VERSION_DOWNLOAD_ROUTE_TEMPLATE = "/v1/asset-versions/{version_id}/download"
|
|
494
494
|
|
|
495
495
|
# --------------------------------------------------------------------------
|
|
496
|
-
# Worker routes (bearer =
|
|
496
|
+
# Worker routes (bearer = per-worker identity JWT; + lease header, see submit.py)
|
|
497
497
|
# --------------------------------------------------------------------------
|
|
498
498
|
#
|
|
499
499
|
# The claim response (submit.WORKER_CLAIM_ROUTE) additionally carries an
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/enums.py
RENAMED
|
@@ -11,8 +11,9 @@ class JobStatus(StrEnum):
|
|
|
11
11
|
"""Lifecycle state of a submitted job.
|
|
12
12
|
|
|
13
13
|
A job moves ``QUEUED`` → ``RUNNING`` → one terminal state
|
|
14
|
-
(``COMPLETED`` / ``FAILED`` / ``CANCELLED``). Terminal
|
|
15
|
-
again — logs and results are final once a job
|
|
14
|
+
(``COMPLETED`` / ``FAILED`` / ``CANCELLED`` / ``TIMED_OUT``). Terminal
|
|
15
|
+
states never change again — logs and results are final once a job
|
|
16
|
+
reaches one.
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
19
|
QUEUED = "queued"
|
|
@@ -30,6 +31,23 @@ class JobStatus(StrEnum):
|
|
|
30
31
|
CANCELLED = "cancelled"
|
|
31
32
|
"""Stopped on request (``simulo cancel``) before completing."""
|
|
32
33
|
|
|
34
|
+
TIMED_OUT = "timed_out"
|
|
35
|
+
"""Stopped for exceeding its organization's configured runtime limit —
|
|
36
|
+
distinct from :attr:`FAILED`: the job did not crash, it ran past its
|
|
37
|
+
organization's configured runtime limit and was stopped by the platform
|
|
38
|
+
(the supervisor's own kill, or a backstop reaper after the deadline plus
|
|
39
|
+
a grace period). Any checkpoint the run had produced by then is salvaged
|
|
40
|
+
the same way a cancelled run's is.
|
|
41
|
+
|
|
42
|
+
Appended, never inserted: this member was added after the first five and
|
|
43
|
+
keeps its place at the end of the wire enum per this module's own
|
|
44
|
+
append-only rule. A client should treat this value as
|
|
45
|
+
terminal-and-distinct wherever it reaches one directly (e.g. a
|
|
46
|
+
worker-authored `complete` body); the control plane's own public read
|
|
47
|
+
routes may still project it as `FAILED` for compatibility with clients
|
|
48
|
+
published before this member existed — see that rollout's own record for
|
|
49
|
+
when the projection lifts."""
|
|
50
|
+
|
|
33
51
|
|
|
34
52
|
class ResumePolicy(StrEnum):
|
|
35
53
|
"""How a job resumes from a discovered checkpoint."""
|
|
@@ -44,6 +62,28 @@ class ResumePolicy(StrEnum):
|
|
|
44
62
|
"""Resume only on explicit request. Reserved: currently behaves like ``NEVER``."""
|
|
45
63
|
|
|
46
64
|
|
|
65
|
+
class SystemType(StrEnum):
|
|
66
|
+
"""An abstract GPU system tier.
|
|
67
|
+
|
|
68
|
+
Each tier's hardware profile and fleet-provisioning state lives in
|
|
69
|
+
:data:`simulo.interfaces.platform.systems.SYSTEM_SPECS`, keyed by these
|
|
70
|
+
members. The AWS instance type behind a tier is never part of this name —
|
|
71
|
+
see :class:`simulo.interfaces.platform.systems.SystemSpec`'s own docstring.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
TIER_1 = "tier1"
|
|
75
|
+
"""Entry tier — always available."""
|
|
76
|
+
|
|
77
|
+
TIER_2 = "tier2"
|
|
78
|
+
"""Mid tier — always available."""
|
|
79
|
+
|
|
80
|
+
TIER_3 = "tier3"
|
|
81
|
+
"""High tier — not yet available on the deployable fleet."""
|
|
82
|
+
|
|
83
|
+
TIER_4 = "tier4"
|
|
84
|
+
"""Top tier — not yet available on the deployable fleet."""
|
|
85
|
+
|
|
86
|
+
|
|
47
87
|
class ResourceKind(StrEnum):
|
|
48
88
|
"""Kind of a registry-resolved, read-only resource mount."""
|
|
49
89
|
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/manifest.py
RENAMED
|
@@ -164,9 +164,9 @@ class Manifest:
|
|
|
164
164
|
#: this endpoint must not break a newer client.
|
|
165
165
|
META_ROUTE = "/v1/meta"
|
|
166
166
|
|
|
167
|
-
#: ``POST`` — the worker's runtime-manifest self-report (bearer =
|
|
168
|
-
#:
|
|
169
|
-
#: see :mod:`simulo.interfaces.platform.submit`). The worker introspects its
|
|
167
|
+
#: ``POST`` — the worker's runtime-manifest self-report (bearer = the
|
|
168
|
+
#: per-worker identity JWT, same trust zone as every ``/internal/worker/*``
|
|
169
|
+
#: route — see :mod:`simulo.interfaces.platform.submit`). The worker introspects its
|
|
170
170
|
#: own interpreter (``importlib.metadata.distributions()``) on startup and
|
|
171
171
|
#: POSTs a :class:`RuntimeManifest` — the reporter *is* the runtime.
|
|
172
172
|
#: **Best-effort by contract:** a failed report must never stop a worker from
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/outputs.py
RENAMED
|
@@ -196,8 +196,9 @@ JOB_OUTPUTS_ROUTE_TEMPLATE = "/v1/jobs/{job_id}/outputs"
|
|
|
196
196
|
JOB_OUTPUT_DOWNLOAD_ROUTE_TEMPLATE = "/v1/jobs/{job_id}/outputs/{output_id}/download"
|
|
197
197
|
|
|
198
198
|
# --------------------------------------------------------------------------
|
|
199
|
-
# Worker-facing routes (bearer =
|
|
200
|
-
#
|
|
199
|
+
# Worker-facing routes (bearer = the per-worker identity JWT
|
|
200
|
+
# `WORKER_BOOTSTRAP_ROUTE` mints, verified server-side per call; same auth
|
|
201
|
+
# surface as the recordings/models worker routes in submit.py)
|
|
201
202
|
# --------------------------------------------------------------------------
|
|
202
203
|
|
|
203
204
|
#: ``POST`` — obtain a presigned upload URL for a new general output (a
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/runs.py
RENAMED
|
@@ -302,7 +302,16 @@ LOG_ARCHIVE_READ_FAILED_CODE = "log_archive_read_failed"
|
|
|
302
302
|
|
|
303
303
|
#: Statuses after which a job's log stream and record no longer change. A log
|
|
304
304
|
#: follower drains the remaining bytes and stops once the status is terminal.
|
|
305
|
-
|
|
305
|
+
#:
|
|
306
|
+
#: ``TIMED_OUT`` joined this set in `M55-org-runtime-policy` (issue #1537).
|
|
307
|
+
#: The control plane's public read routes do not emit it yet (they project a
|
|
308
|
+
#: `timed_out` job as `FAILED` with `status_reason="timeout"` until a
|
|
309
|
+
#: tolerant-client floor is recorded — see :class:`~simulo.interfaces.
|
|
310
|
+
#: platform.enums.JobStatus`'s own docstring), so this set already carrying
|
|
311
|
+
#: it is what makes a client that widens itself ahead of that projection
|
|
312
|
+
#: lift (per this contract's own unknown-status-is-terminal guidance) treat
|
|
313
|
+
#: the value correctly the moment it does appear on the wire.
|
|
314
|
+
TERMINAL_JOB_STATUSES = frozenset({JobStatus.COMPLETED, JobStatus.FAILED, JobStatus.CANCELLED, JobStatus.TIMED_OUT})
|
|
306
315
|
|
|
307
316
|
|
|
308
317
|
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
@@ -334,7 +343,14 @@ class JobRecord:
|
|
|
334
343
|
"""When the job was created (ISO 8601 UTC, e.g. ``2026-07-03T12:00:00Z``)."""
|
|
335
344
|
|
|
336
345
|
args: Mapping[str, Any] = field(default_factory=dict)
|
|
337
|
-
"""The keyword arguments the job body was invoked with
|
|
346
|
+
"""The keyword arguments the job body was invoked with, AS SERVED ON
|
|
347
|
+
THIS RECORD — not necessarily what the job actually ran with. The
|
|
348
|
+
control plane redacts any credential-shaped key or value (replacing it
|
|
349
|
+
with the literal string ``"<redacted>"``) when building this public
|
|
350
|
+
record; the job itself executed with the real, unredacted values. A
|
|
351
|
+
client reading this field to display a job's history sees the redacted
|
|
352
|
+
view; nothing about a redacted entry here implies the run itself saw
|
|
353
|
+
``"<redacted>"``."""
|
|
338
354
|
|
|
339
355
|
started_at: Optional[str] = None
|
|
340
356
|
"""When execution began (ISO 8601 UTC). ``None`` while queued."""
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/submit.py
RENAMED
|
@@ -25,10 +25,16 @@ architecture section for the full picture):
|
|
|
25
25
|
* **Client-facing `/v1/...`** — bearer = the user's Cognito JWT;
|
|
26
26
|
``require_active_org`` is enforced server-side. These are the routes a
|
|
27
27
|
human's ``simulo`` CLI/SDK calls.
|
|
28
|
-
* **Worker-facing `/internal/worker/...`** — bearer = a
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
* **Worker-facing `/internal/worker/...`** — bearer = a short-lived,
|
|
29
|
+
per-worker identity JWT (see :data:`WORKER_BOOTSTRAP_ROUTE` below). The worker obtains it from an
|
|
30
|
+
IAM-signed bootstrap call, unique per EC2 instance identity; the server
|
|
31
|
+
verifies it (audience, issuer, expiry, and — live, on every call — the
|
|
32
|
+
issuing worker row's revocation status) rather than comparing against a
|
|
33
|
+
single shared secret. Empty server-side signing-secret config disables
|
|
34
|
+
every ``/internal/worker/*`` route fail-closed — there is no "auth
|
|
35
|
+
disabled" state for these routes. (Before this cutover the bearer was a
|
|
36
|
+
single static ``WORKER_TOKEN`` shared by the whole fleet; that scheme is
|
|
37
|
+
retired.)
|
|
32
38
|
* **Presigned URLs** (`download_url` / `upload_url` fields below) carry their
|
|
33
39
|
own auth (S3 SigV4 query params, or a control-plane-served one-time token
|
|
34
40
|
when running on the local storage backend) — a caller must send the
|
|
@@ -159,8 +165,28 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
|
|
|
159
165
|
|
|
160
166
|
## Worker-facing (`/internal/worker/*`)
|
|
161
167
|
|
|
162
|
-
* :data:`
|
|
163
|
-
|
|
168
|
+
* :data:`WORKER_BOOTSTRAP_ROUTE` (``POST``) — request
|
|
169
|
+
``{"signature_b64": "..."}``, the caller's own IAM-signed EC2 instance
|
|
170
|
+
identity document exactly as ``dynamic/instance-identity/rsa2048``
|
|
171
|
+
returns it (base64, no armor). Carries NO shared secret of its own — the
|
|
172
|
+
signed document IS the credential. Response ``200``::
|
|
173
|
+
|
|
174
|
+
{"worker_id": "wkr_...", "tier": "tier1", "pinned_organization_id": null,
|
|
175
|
+
"status": "active", "token": "<jwt>", "token_expires_at": "2026-07-03T13:00:00Z"}
|
|
176
|
+
|
|
177
|
+
Idempotent per EC2 instance identity: a repeat call from an instance that
|
|
178
|
+
already holds a worker identity returns that SAME identity (still pinned,
|
|
179
|
+
if pinned) with a freshly-issued ``token`` — the recommended renewal
|
|
180
|
+
cadence is half of the deployment's configured token TTL. ``token`` is
|
|
181
|
+
the bearer every OTHER ``/internal/worker/*`` route now requires (below);
|
|
182
|
+
nothing on the wire ever carries the ``WORKER_TOKEN`` shared bearer this
|
|
183
|
+
route's own token retired.
|
|
184
|
+
|
|
185
|
+
* :data:`WORKER_CLAIM_ROUTE` (``POST``) — request body carries no worker
|
|
186
|
+
identity field: the claiming worker is whoever the ``Authorization``
|
|
187
|
+
bearer (above) verifies to, never a caller-asserted value — a
|
|
188
|
+
pre-cutover claim body's ``{"worker_id": "..."}`` was trusted outright,
|
|
189
|
+
the actual gap this contract change closed. Response ``200``::
|
|
164
190
|
|
|
165
191
|
{"job": <JobRecord>,
|
|
166
192
|
"package": {"download_url": "...", "tar_sha256": "sha256:...", "size_bytes": 123456},
|
|
@@ -495,10 +521,19 @@ MAX_SEED_REF_CHARS = 80
|
|
|
495
521
|
SEED_SOURCE_CONFLICT_CODE = "seed_source_conflict"
|
|
496
522
|
|
|
497
523
|
# --------------------------------------------------------------------------
|
|
498
|
-
# Worker-facing routes (bearer =
|
|
499
|
-
# empty server-side config disables every
|
|
524
|
+
# Worker-facing routes (bearer = a per-worker identity JWT this bootstrap
|
|
525
|
+
# route mints; empty server-side signing-secret config disables every
|
|
526
|
+
# OTHER route below, fail-closed — see the module docstring's trust zones)
|
|
500
527
|
# --------------------------------------------------------------------------
|
|
501
528
|
|
|
529
|
+
#: ``POST`` — bind (or re-bind) a worker identity to the calling EC2 instance
|
|
530
|
+
#: via an IAM-signed instance identity document. Body
|
|
531
|
+
#: ``{"signature_b64": "..."}``. Carries NO shared secret of its own — the
|
|
532
|
+
#: signed document IS this route's authentication, which is the whole point
|
|
533
|
+
#: of it existing: it is the ONE route that MINTS the token every OTHER
|
|
534
|
+
#: ``/internal/worker/*`` route now requires, so it cannot itself require
|
|
535
|
+
#: that token without a chicken-and-egg cycle.
|
|
536
|
+
WORKER_BOOTSTRAP_ROUTE = "/internal/worker/bootstrap"
|
|
502
537
|
#: ``POST`` — atomically claim the next queued job (``FOR UPDATE SKIP LOCKED``).
|
|
503
538
|
WORKER_CLAIM_ROUTE = "/internal/worker/claim"
|
|
504
539
|
#: ``POST`` — append ordinary-job raw log bytes at a byte offset
|
|
@@ -850,6 +885,31 @@ RESERVED_RUNTIME_ENV_KEYS = frozenset(
|
|
|
850
885
|
"SIMULO_VIEWSTREAM_ENABLED",
|
|
851
886
|
"SIMULO_RUNTIME_FACTS_PATH",
|
|
852
887
|
"MAX_JOB_TIMEOUT_S",
|
|
888
|
+
# M40-job-sandbox (issue #527): the container launcher sets this to
|
|
889
|
+
# advertise the job container's publicly reachable stream endpoint
|
|
890
|
+
# (`--/app/livestream/publicEndpointAddress=<instance IP>`) to Kit —
|
|
891
|
+
# a bridge-networked job container needs this because its own
|
|
892
|
+
# address is NAT'd, unlike the pre-M40 host-networking deployment
|
|
893
|
+
# where Kit's default self-detected address was already correct.
|
|
894
|
+
# `simulo/backend/isaaclab/stream_adapter.py`'s `get_launch_args`
|
|
895
|
+
# merges this env var in LAST, so an unreserved key would let a
|
|
896
|
+
# manifest's own `runtime_env` override the launcher's own address
|
|
897
|
+
# and redirect where the job's live-view media is advertised
|
|
898
|
+
# (ADR-0016's threat table: "Job redirects the stream endpoint via a
|
|
899
|
+
# job-controllable env key"). Reserving it here is the
|
|
900
|
+
# denylist-enforcement half of the fix; `worker._child_env` is the
|
|
901
|
+
# other half, unconditionally scrubbing any ambient value before the
|
|
902
|
+
# launcher re-sets it from the resolved instance address.
|
|
903
|
+
"SIMULO_KIT_EXTRA_ARGS",
|
|
904
|
+
# M40-job-sandbox (issue #527): the same "worker mount wins" seam as
|
|
905
|
+
# SIMULO_CHECKPOINT_DIR / SIMULO_ANOMALY_DEBUG_DIR — the worker
|
|
906
|
+
# pre-resolves and bind-mounts the validation run-dir output
|
|
907
|
+
# contract (verdict/report/facts/runtime-findings) at this path
|
|
908
|
+
# before an asset-validation job's container starts, because that
|
|
909
|
+
# container's own `~` is ephemeral tmpfs the worker could never
|
|
910
|
+
# read back. A manifest-borne override would point the worker's own
|
|
911
|
+
# later read at a job-chosen path instead.
|
|
912
|
+
"SIMULO_VALIDATION_OUTPUT_DIR",
|
|
853
913
|
}
|
|
854
914
|
)
|
|
855
915
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""GPU system-tier catalog — the closed hardware contract behind `SystemType`.
|
|
2
|
+
|
|
3
|
+
`SYSTEM_SPECS` is the single published source of truth for what each tier
|
|
4
|
+
means: the marketing GPU name, its VRAM, the host's vCPU/memory, whether the
|
|
5
|
+
tier is provisionable on the deployable fleet today, and (internal-only) the
|
|
6
|
+
AWS instance type behind it. Nothing here schedules a job against real
|
|
7
|
+
hardware — selection stays record-only until a later milestone adds live
|
|
8
|
+
enforcement.
|
|
9
|
+
|
|
10
|
+
Stdlib-only (`dataclasses` plus the sibling `enums` module): this file must
|
|
11
|
+
import nothing else, so the package's zero-runtime-dependency guarantee
|
|
12
|
+
(`test_no_heavy_deps.py`) holds.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
from simulo.interfaces.platform.enums import SystemType
|
|
18
|
+
|
|
19
|
+
# Shared dataclass options: immutable value objects (frozen), memory-lean
|
|
20
|
+
# (slots), and keyword-only construction (kw_only) — see `platform/domain.py`'s
|
|
21
|
+
# identical `_DC` for the full rationale.
|
|
22
|
+
_DC = {"frozen": True, "slots": True, "kw_only": True}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(**_DC)
|
|
26
|
+
class SystemSpec:
|
|
27
|
+
"""One GPU tier's hardware profile and fleet-provisioning state.
|
|
28
|
+
|
|
29
|
+
`gpu_model` (e.g. `"T4"`) is the user-facing marketing name — the only
|
|
30
|
+
hardware identity a CLI table, `--json` output, or documentation page may
|
|
31
|
+
ever show. `instance_type` is the AWS EC2 instance type behind the tier,
|
|
32
|
+
kept here ONLY for an offline fleet crosscheck against the deployable
|
|
33
|
+
CloudFormation allowlist and the control plane's own bookkeeping — it
|
|
34
|
+
must never reach a user-facing surface. Hiding it is deliberate: a future
|
|
35
|
+
author copying an AWS SKU into an example is exactly how `gpu="L4"`
|
|
36
|
+
(actually Tier 3, not provisionable) ended up in 40 tracked files before
|
|
37
|
+
this catalog existed.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
system: SystemType
|
|
41
|
+
gpu_model: str
|
|
42
|
+
vram_gb: int
|
|
43
|
+
vcpu: int
|
|
44
|
+
memory_gb: int
|
|
45
|
+
max_parallel_envs: int | None
|
|
46
|
+
available: bool
|
|
47
|
+
instance_type: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# vcpu/memory_gb: AWS's published instance-type specifications
|
|
51
|
+
# (https://docs.aws.amazon.com/ec2/latest/instancetypes/ac.html, confirmed at
|
|
52
|
+
# M20-system-catalog implementation time, 2026-09-07) for
|
|
53
|
+
# g4dn.xlarge/g5.xlarge/g6.xlarge/g6e.xlarge. vram_gb: each GPU model's
|
|
54
|
+
# published VRAM capacity (NVIDIA product specifications) — deliberately NOT
|
|
55
|
+
# the ~22/44 GiB AWS's own instance-type page reports as *usable* GPU memory
|
|
56
|
+
# on g5/g6/g6e after ECC/hypervisor reservation, since `vram_gb` documents the
|
|
57
|
+
# hardware users are told they are getting (`gpu_model`), not a fleet
|
|
58
|
+
# provisioning detail.
|
|
59
|
+
SYSTEM_SPECS: dict[SystemType, SystemSpec] = {
|
|
60
|
+
SystemType.TIER_1: SystemSpec(
|
|
61
|
+
system=SystemType.TIER_1,
|
|
62
|
+
gpu_model="T4",
|
|
63
|
+
vram_gb=16,
|
|
64
|
+
vcpu=4,
|
|
65
|
+
memory_gb=16,
|
|
66
|
+
# Measured ceiling for g4dn.xlarge's default workload (`.claude/skills/isaaclab/SKILL.md`,
|
|
67
|
+
# `.claude/skills/aws-infra/SKILL.md`, `cloud-infra/gpu-worker/gpu-worker.yaml`): 16
|
|
68
|
+
# parallel environments; a 4096-environment run exhausted GPU memory and made the
|
|
69
|
+
# instance unrecoverable. `aws-infra/SKILL.md` is explicit that this is
|
|
70
|
+
# workload/instance evidence for the template's default workload, not a universal
|
|
71
|
+
# constant — re-benchmark before relying on it for a different workload shape.
|
|
72
|
+
max_parallel_envs=16,
|
|
73
|
+
available=True,
|
|
74
|
+
instance_type="g4dn.xlarge",
|
|
75
|
+
),
|
|
76
|
+
SystemType.TIER_2: SystemSpec(
|
|
77
|
+
system=SystemType.TIER_2,
|
|
78
|
+
gpu_model="A10G",
|
|
79
|
+
vram_gb=24,
|
|
80
|
+
vcpu=4,
|
|
81
|
+
memory_gb=16,
|
|
82
|
+
max_parallel_envs=None,
|
|
83
|
+
available=True,
|
|
84
|
+
instance_type="g5.xlarge",
|
|
85
|
+
),
|
|
86
|
+
SystemType.TIER_3: SystemSpec(
|
|
87
|
+
system=SystemType.TIER_3,
|
|
88
|
+
gpu_model="L4",
|
|
89
|
+
vram_gb=24,
|
|
90
|
+
vcpu=4,
|
|
91
|
+
memory_gb=16,
|
|
92
|
+
max_parallel_envs=None,
|
|
93
|
+
available=False,
|
|
94
|
+
instance_type="g6.xlarge",
|
|
95
|
+
),
|
|
96
|
+
SystemType.TIER_4: SystemSpec(
|
|
97
|
+
system=SystemType.TIER_4,
|
|
98
|
+
gpu_model="L40S",
|
|
99
|
+
vram_gb=48,
|
|
100
|
+
vcpu=4,
|
|
101
|
+
memory_gb=32,
|
|
102
|
+
max_parallel_envs=None,
|
|
103
|
+
available=False,
|
|
104
|
+
instance_type="g6e.xlarge",
|
|
105
|
+
),
|
|
106
|
+
}
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/SOURCES.txt
RENAMED
|
@@ -38,6 +38,7 @@ src/simulo/interfaces/platform/policy_bundle.py
|
|
|
38
38
|
src/simulo/interfaces/platform/runs.py
|
|
39
39
|
src/simulo/interfaces/platform/runtime.py
|
|
40
40
|
src/simulo/interfaces/platform/submit.py
|
|
41
|
+
src/simulo/interfaces/platform/systems.py
|
|
41
42
|
src/simulo/interfaces/platform/volume.py
|
|
42
43
|
src/simulo/interfaces/runtime/__init__.py
|
|
43
44
|
src/simulo/interfaces/runtime/anomaly.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/__init__.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/actuator.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/asset.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/entity.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/light.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/materials.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/physics.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/pose.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/primitives.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/prop.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/record.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/robot.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/scene.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/sensor.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/terrain.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/visual.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/world.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/_payload.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/asset.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/callbacks.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/debug.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/domain.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/job_events.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/runtime.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/volume.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/__init__.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/anomaly.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/components.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/player.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/policy.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/scenario.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/tensors.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/trainer.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/requires.txt
RENAMED
|
File without changes
|
{simulo_interfaces-0.18.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/top_level.txt
RENAMED
|
File without changes
|