Skip to content
This repository was archived by the owner on Nov 3, 2026. It is now read-only.

Sync alpha with latest upstream while preserving fork changes - #1

Merged
lalalune merged 1571 commits into
alphafrom
chore/sync-upstream-20260914
Sep 14, 2026
Merged

lalalune merged 1571 commits into
alphafrom
chore/sync-upstream-20260914

Conversation

@lalalune

Copy link
Copy Markdown
Collaborator

Sync the AlphaCompute alpha branch with upstream next at 0a2f04cf88e17dd6094f28d6ef44f3bdf8ceb0f3, integrating 1,569 upstream commits while retaining the fork's history.

Preserve the fork's DSTACKS1 independently encrypted environment entries, GHCR key-provider publication, and Yocto loader compatibility fix. Replace the obsolete no-TEE bypasses with upstream's explicit development simulator. Reconcile encryption helpers and retain upstream config verification, runtime-event locking, and LUKS checks. Resolve two test-only Clippy warnings without changing production behavior.

Validation on Linux x86_64 with Rust 1.92, final head 0937e30382e1d1745d02f1b7c6b74147f286273c:

  • cargo test --locked -p dstack-util -p dstack-attest -p nsm-qvl -p nsm-attest: 175 passed, zero failed or ignored.
  • cargo clippy --locked -p dstack-util -p dstack-attest -p nsm-qvl -p nsm-attest --all-targets -- -D warnings: passed.
  • cargo fmt --manifest-path dstack/Cargo.toml --all -- --check: passed.
  • python3 -m unittest discover -s dstack/build/shared/tests -v: six passed on Linux.
  • git diff --check upstream/next HEAD: passed; both original fork and upstream heads are ancestors.

This PR synchronizes source. Hardware enclave deployment, real provider requests, persistent state, authenticated ingress, and phone pairing remain separate acceptance work under elizaOS/eliza#31328. NSM fixture tests do not establish live enclave attestation.

kvinwang and others added 30 commits August 25, 2026 03:49
…nbook

docs: add a release-candidate testing runbook
…ollowups

fix(certbot): tighten the ACME challenge-domain rule and the paths that report failures
…ack-TEE#1135)

* docs: cover gateway clustering and the proxy modes left untested

* docs: record what an AMD SEV-SNP run actually requires

The last round stopped at an unreachable AMD KDS, so section 9 was written
from code reading. A completed run since then turned up four things that
cost time and are not visible from the source:

- `sev_snp_key_release` defaults off and rejects *after* the quote verifies,
  so a good quote still yields a reboot loop. Only SEV-SNP and NitroTPM are
  gated this way; the TDX variants are not, which is why the gate surprises.
- Flipping that flag on a bootstrapped KMS destroys it. The local key
  provider seals to RTMR3, which carries the compose hash. Onboarding a
  second KMS is the way, and the inherited identity shows up in the CA
  public key rather than the certificate.
- `GetKey`/`Attest` are not on the external listener, and the internal
  socket mounts without the `/prpc` prefix.
- KDS rate limiting returns 429 on repeat verification of the same chip,
  which reads like a verification failure.

Also adds an offline verification row to the section 7 table: capturing an
attestation and re-verifying a one-byte-flipped copy is the cheapest way to
show the crypto is live, provided the flip lands inside the signed report
rather than the CBOR framing.

* docs: list the AMD SEV-SNP configuration explicitly

The previous revision described `sev_snp_key_release` in prose, which is
enough to recognise the failure but not enough to set up a run. Adds a table
of every setting an AMD run touches, across vmm.toml and kms.toml, with the
default beside it -- including the ones to leave alone, so a reader can tell
'unset is correct' from 'not yet investigated'.

The trust-anchor pair is the non-obvious one: setting any `root_ca` path
without `insecure_allow_external_trust_anchors` refuses to start rather
than falling back, and the same `[core.attestation]` block is shared with
dstack-verifier.toml.

* docs: say what KDS caching exists, and drop a duplicated paragraph

'Provision the host' is advice that invites the obvious follow-up question --
is there a cache service to run instead? There is not, and the answer is not
visible without reading sev-snp-qvl: the only cache is a capacity-bounded
in-process moka cache with no TTL and no persistence, so every fresh process
starts cold. Worth stating plainly next to the advice, together with the fact
that `amd_kds` would accept a mirror if someone wrote one, and that the
NVIDIA collateral did get the persistent proxy AMD lacks.

Also removes a paragraph the previous commit duplicated when it split the
section.

* docs: correct the AMD host-certificate advice, which does not work on stock QEMU

The previous revision told the reader to provision host certificates with
'snphost / sevctl' and treated the KDS dependency as removable. Checking on
the host that ran this round, it is not:

- `sev-snp-guest` in QEMU 10.2.1 has no `certs-path` property, and the
  string is absent from the binary, so there is no way to hand QEMU the
  GHCB cert blob `snphost import` produces.
- `SNP_SET_EXT_CONFIG`, the other staging route, is not in upstream
  `psp-sev.h`; it was AMD's out-of-tree fork.
- The guest half works fine -- `SNP_GET_EXT_REPORT`/`certs_address` are
  present -- so the guest asks and receives an empty table, which is why
  every verification in this round went to KDS.

Also drops `sevctl` from the recommendation: it manages the pre-SNP SEV
platform and its OCA chain, and has nothing to do with SNP endorsement
certificates. Pairing it with snphost was a guess presented as instruction.

* docs: cut the host-provisioning detour from the AMD section

Section 9.4 had grown into a walkthrough of tooling that cannot be used --
snphost subcommands, a QEMU property dump, and an account of why the earlier
recommendation was wrong. A runbook is read by someone mid-test; none of that
helps them.

What is left is the part that changes what a reader does: KDS is a single
rate-limited endpoint, its 429 looks like a verification failure, the cache
is in-process only, and the host-certificate route is a dead end on a stock
QEMU so nobody should spend an afternoon on it.

* docs: show that a KDS caching mirror works, with a reference implementation

Section 9.4 already said a mirror would drop in behind `amd_kds`. Someone
reading that still has to discover the three things that decide whether the
proxy is correct, and each was found the hard way: AMD 403s an unexpected
Host header, a cached 429 converts a rate limit into an outage, and every
KDS response carries `Cache-Control: no-cache` so a plain reverse proxy
caches nothing at all. Thirty lines of Worker are cheaper to read than that
list in prose.

Measured rather than asserted: four cold-process verifications of one real
attestation pass through the mirror where the second fails going direct, and
a KMS pointed at it released keys to an AMD guest end to end.

Also notes why the TTL should be bounded even though a VCEK is valid for
seven years -- dstack checks no CRL and no validity dates, so an unbounded
cache would freeze that gap rather than merely inherit it.
…keys

`certbot cfg` walks the serialized document and the struct's doc comments in
lockstep, by position. An `Option` that is `None` serializes to nothing, so
`cf_api_url` and `renewed_hook` are absent from the document and every comment
after the first of them describes the key above the one it belongs to:

    # Optional Cloudflare-compatible API base URL
    dns_txt_ttl = 60
    # TTL for DNS TXT challenge records in seconds
    auto_set_caa = true

Look the comment up by key name instead. A key with no doc comment keeps none
rather than borrowing its neighbour's.
The CLI keeps the ACME account key and the certificate key in a plain directory
on the host that runs it, so nothing about the issuance is attested. A deployed
gateway issues its own certificates from the configuration it already holds and
publishes the public keys for `ct_monitor` to check. Say so in a README, next
to what the subcommands do and where the workdir keeps things.

`init`'s help said it initializes the configuration file; it reads the
configuration file and creates the ACME account.
Step 4 told the operator to configure `GATEWAY_CERT`/`GATEWAY_KEY` in
`build-config.sh` and run `./certbot renew -c certbot.toml` on the host. That
is not how a deployed gateway gets a certificate, and `build-config.sh` is not
part of this guide's flow: `dstack-gateway` links the `certbot` crate and runs
it inside the CVM, answering dns-01 with the DNS credential in its admin
config and keeping the ACME account key and every certificate in the CVM's
WaveKV store. Following the old text produced a certificate on the host that
nothing reads.

Describe what step 3 already set up -- `bootstrap-cluster.sh` calling
SetCertbotConfig, CreateDnsCredential and AddZtDomain -- how to watch issuance
land in `ListZtDomains`, how to pin CAA, and what switching from staging to
production takes. Zero-trust HTTPS moves to the required part of the checklist:
without a certificate for the domain the gateway cannot serve an app over TLS.
The production setup guide walked the operator through editing `certbot.toml`,
running `./certbot set-caa && ./certbot renew` by hand, and pointing
`cert_chain`/`cert_key` at the result. A gateway running on the host links the
same `certbot` crate as one running in a CVM and issues over dns-01 from its
own process, keeping the ACME account key and every certificate in its WaveKV
store -- `cert_chain`/`cert_key` only load a certificate something else
produced.

Configure the gateway instead: the proxy fields, the admin API the ACME
settings are stored through, and a `data_dir` that survives restarts so a
restart does not ask the CA for a fresh certificate. Then SetCertbotConfig,
CreateDnsCredential and AddZtDomain, with what to watch in ListZtDomains, what
SetCaa pins, and what switching from staging to production takes.

Also: `core.admin.auth_token` is the current key name (`admin_token` is the
accepted older one), URL Format becomes its own section rather than sitting
inside a configuration step, and the alerting table listed
`dstack_gateway_kv_persist_failures_total` twice.
Bumps [@fastify/static](https://github.com/fastify/fastify-static) to 10.1.3 and updates ancestor dependency [@fastify/swagger-ui](https://github.com/fastify/fastify-swagger-ui). These dependencies need to be updated together.


Updates `@fastify/static` from 6.12.0 to 10.1.3
- [Release notes](https://github.com/fastify/fastify-static/releases)
- [Commits](fastify/fastify-static@v6.12.0...v10.1.3)

Updates `@fastify/swagger-ui` from 2.1.0 to 6.1.1
- [Release notes](https://github.com/fastify/fastify-swagger-ui/releases)
- [Commits](fastify/fastify-swagger-ui@v2.1.0...v6.1.1)

---
updated-dependencies:
- dependency-name: "@fastify/static"
  dependency-version: 10.1.3
  dependency-type: indirect
- dependency-name: "@fastify/swagger-ui"
  dependency-version: 6.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
…nfig

fix(certbot): reissue when the configured domains change
…_yarn/dstack/kms/auth-eth/multi-406b073587

build(deps): bump @fastify/static and @fastify/swagger-ui in /dstack/kms/auth-eth
Rotation, CAA reconciliation, and first-use registration all read or
re-pin the same account, so they now take the same KV lock. Two
consequences.

Registration under the lock cannot be reached from inside a region that
already holds it: set_caa_all held the in-process caa_lock across its
per-domain loop, and a fresh cluster's first SetCaa registers from inside
that loop -- which, with a non-reentrant tokio Mutex and no timeout, hung
the task and left the lock held for the life of the process. set_caa_all
now registers the account before it takes the lock, where the steady
state costs one KV read.

Dropping caa_lock for the KV lock also widens what is ordered: CAA
reconciliation was serialized within a process only, so one node's SetCaa
could interleave with another's rotation over the same zone. Reconciling
rewrites a zone's issuer records in place -- guard, sweep, write, unguard
-- and two runs over one zone delete each other's records, which can
leave the ";" guards behind and block issuance until a later run
succeeds.
Nothing under the ACME lock may take it again. `set_caa` now loads the
stored account instead of registering one, so the locked region cannot
reach `acquire_acme_lock` at all -- the caller registers up front, and
the property holds by construction rather than by argument.

The E2E suite never called SetCaa or RotateAcmeCredentials, so the two
admin operations that take the cluster-wide lock ran nowhere outside
single-process unit tests. Both nodes now reconcile CAA, then one rotates
the shared account and the other issues with it, which is also what
proves the switch reached the cluster rather than one process. The zone
is read back from the mock provider after each step: exactly one `issue`
and one `issuewild` pinned to the same account, no ";" guard left behind
-- the state a reconciliation that was interleaved, or that died halfway,
does not produce.

Admin RPCs are bounded by `--max-time` so an RPC that blocks forever
fails the suite where the cause is obvious, instead of hanging it until
the job timeout.
The suite was one indivisible run: KMS and Gateway 0.5.8 rolled to
current, with two apps. A change confined to one area had to pay for all
of it, and one area it could never reach at all -- a fresh cluster's
first ACME account registration, because 0.5.8 registers the shared
account long before the current binary starts.

A phase is now the unit a run can be limited to (`./run-e2e.sh --phase
certbot`, or DSTACK_E2E_PHASE). Each is self-contained: it deploys what
it needs and asserts on it. The `certbot` phase brings up a current KMS
and two current Gateway nodes, then reconciles CAA on a cluster holding
no account -- registering one from inside the region that holds the
cluster-wide ACME lock -- issues against it, requires the second node to
adopt that account rather than register its own, and rotates.

Only the upgrade phase boots the v0.5.11 compatibility image, so no other
phase requires it to be present, and none widens the authorization
allowlist with an OS digest it will never launch. `run-upgrade-e2e.sh`
stays as a wrapper.
Two things a current-only cluster hits that an upgraded one never does.

`max_dns_wait: 0` is rejected by current code at credential creation --
an issuance that never waits for propagation cannot succeed against a
real provider -- and only 0.5.8 ever accepted it. The upgrade phase
configures the cluster while the nodes still run 0.5.8, so the value
survives into current code as stored state and the validation is never
reached. One second is as good as none against a Pebble configured to
validate unconditionally.

Adding a ZT domain starts an issuance, and on a fresh cluster that
issuance registers the shared ACME account -- so an operator's SetCaa,
issued right after, races it for the cluster-wide lock and is refused.
That refusal is deliberate and says to retry after the holder finishes,
which is seconds for a registration. So retry, the way this suite
already retries the per-domain certificate lock. Any other error still
fails immediately.
…egister-race

fix(gateway): register the shared ACME account under the rotation lock
dns-01 rewrites the zone on every order, so certbot holds a DNS provider
credential for the life of the deployment. dns-persist-01
(draft-ietf-acme-dns-persist-01) proves control with a `_validation-persist`
TXT record published once, out of band: the record names the CA and the ACME
account, nothing about it changes between orders, and certbot only ever reads
DNS.

`ValidationMethod` replaces the `Dns01Client` field on `AcmeClient`, so the
dns-01-only state -- the provider client and the TXT TTL -- lives in the one
variant that has any use for it, and dns-persist-01 cannot be constructed
holding a credential it would never call. `check_dns` widens from an exact
match on the key authorization to matching whatever the challenge expects,
and the cleanup pass skips records certbot did not create: a persistent
record is the operator's and outlives every order.

The self-check stays advisory for both methods. Our resolver is not the CA's,
and under dns-persist-01 our expectation can be stricter than the CA's -- the
challenge's `issuer-domain-names` are not exposed by instant-acme -- so a
record we cannot see is named in a warning and the order proceeds.

CAA content now names the challenge in use, because a record pinned to
`validationmethods=dns-01` refuses every dns-persist-01 order. The dns-01
string is unchanged byte for byte, pinned by a test, so records published by
earlier releases keep matching.

`required_dns_records` renders the whole one-time setup -- validation record
plus CAA -- from an account URI rather than a live client, so a caller holding
only stored credentials can render it without a round trip to the CA. The
record grammar is an RFC 8659 issue-value, and the parser mirrors what CAs
run down to the parts that reject rather than ignore (trailing semicolon,
repeated tag, whitespace in a value), so certbot never renders a record the
CA would refuse or accepts one it would.

The gateway keeps its dns-01 behaviour; the call sites move to the new
constructor unchanged.
Under dns-persist-01 certbot cannot write the records it needs, so the
records are the setup. `certbot dns-records` prints them as zone-file lines
for the configured domains, ready to paste into any provider, once
`certbot init` has registered the account they name.

`challenge` and `issuer_domain_name` join certbot.toml, and `cf_api_token`
becomes optional -- a token left configured alongside dns-persist-01 is
warned about rather than silently ignored. `auto_set_caa` is refused outright
with dns-persist-01: it promises certbot keeps CAA in sync, and without write
access nothing can keep that promise.
A gateway CVM running dns-01 holds a Cloudflare token with write access to
the operator's whole zone. Attestation covers what the CVM runs, not what
becomes of a secret it holds, so that token is the widest credential in the
deployment and it exists only to write one TXT record per order.
dns-persist-01 removes it: control comes from a `_validation-persist` record
the operator publishes once, and the CVM never gets DNS write access at all.

`ZtDomainConfig.challenge` picks the method per domain and defaults to
dns-01, so records written before the field existed decode as the method
those deployments were using -- pinned by a test over both the named and the
legacy positional msgpack encodings. Such a domain needs no DNS credential,
and `validation_for` never looks one up for it.

`GetZtDomain` and `ListZtDomains` return the records to publish in
`required_dns_records`, rendered from the stored account URI with no ACME
round trip so the listing endpoints stay cheap; it comes back empty rather
than failing when no account exists yet.

Two operations cannot be self-service for such a domain, and say so rather
than failing silently:

- `SetCaa` skips it and logs the records instead. There is nothing to
  reconcile without write access, and one such domain must not make the RPC
  unusable for the dns-01 domains beside it; the summary reports how many
  were left to the operator.
- `RotateAcmeCredentials` moves the cluster to a new account while every
  `_validation-persist` record still names the old one, so orders for those
  domains fail until the operator republishes. The response now carries the
  new records in `required_dns_records`, rendered after the switch so they
  name the account the cluster actually moved to, and `domains_updated`
  counts only the domains whose CAA was re-pinned.
kvinwang and others added 29 commits September 13, 2026 23:23
The mkosi backend still consumes kernel, OVMF and ZFS patches, the
initramfs init script, systemd units, the AWS hardening audit and version
references from os/yocto/. Saying only "deprecated, will be removed"
invites deleting that tree or skipping edits to files that are live inputs
of the default image. State the dependency wherever the deprecation is.

Also correct statements that no longer hold with mkosi as the default:
the source-boundary description (mkosi mounts the whole repository as an
ephemeral build source), the AWS runbook's "os/build.sh produces the
archive" (a cached native mkosi build skips tarballs), the -o DIR hint
that make os-image cannot take, the missing root requirement for native
builds, and a leftover "experimental" comment in versions.env.
…_yarn/dstack/kms/auth-eth-bun/hono-4.13.5

build(deps): bump hono from 4.12.34 to 4.13.5 in /dstack/kms/auth-eth-bun
…_yarn/dstack/kms/auth-mock/hono-4.13.5

build(deps): bump hono from 4.12.34 to 4.13.5 in /dstack/kms/auth-mock
…_yarn/dstack/kms/auth-simple/hono-4.13.5

build(deps): bump hono from 4.12.34 to 4.13.5 in /dstack/kms/auth-simple
…_yarn/dstack/kms/auth-mock/vitest-4.1.11

build(deps-dev): bump vitest from 4.1.0 to 4.1.11 in /dstack/kms/auth-mock
…pack

feat(guest-agent)!: always encode v1 attestations as msgpack
…-os-backend

docs(os): follow-up review fixes for the mkosi default switch
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.26.0 to 7.29.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.7/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 7.29.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
The builder images point APT at snapshot.debian.org. When the snapshot
service is flaky (e.g. 503 No healthy backends), apt-get update only
warns about indexes it failed to fetch and exits 0. If the security
index is the one that failed, apt silently installs older versions from
the main suite and the build later fails in verify-pinned-packages.sh
with a misleading "regenerate pinned packages" diff.

Pass -o APT::Update::Error-Mode=any so a failed index fetch aborts the
build right away with the real cause.
…_yarn/dstack/kms/auth-eth-bun/vitest-4.1.11

build(deps-dev): bump vitest from 3.2.6 to 4.1.11 in /dstack/kms/auth-eth-bun
Bumps  and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together.

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v3.1.2...v3.1.5)

Updates `minimatch` from 5.1.6 to 5.1.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v3.1.2...v3.1.5)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 5.1.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
…_yarn/dstack/kms/auth-eth/babel/core-7.29.7

build(deps-dev): bump @babel/core from 7.26.0 to 7.29.7 in /dstack/kms/auth-eth
…r-mode

build: fail apt-get update on any fetch error in builder images
A bridge NIC used to have two owners: netd built the TAP when libvirt
filtering was on or the NIC wanted more than one queue pair, and QEMU's
setuid qemu-bridge-helper built it otherwise. Each path answered the
data-plane questions differently -- which netdev QEMU gets, whether vhost
is really on, whether queues had to be clamped for want of netd -- and the
VMM had to probe netd and record which path a NIC took so teardown could
undo it.

Every bridge NIC now gets a netd-built TAP and `-netdev tap,ifname=`,
with vhost and queues applied uniformly:

- drop the bridge-helper paths, `cvm.qemu_bridge_helper`, the netd
  reachability probe, and the no-netd queue clamp
- drop the `netd_interface` runtime marker; `remove` no longer carries
  `filtered` and clears any binding best-effort
- one-shot `run` refuses bridge and macvtap outside `--dry-run`, since it
  does not manage netd interface lifecycle
- `is_unreachable` now finds the marker (`chain()` never did), and only
  ENOENT/ECONNREFUSED count as an absent netd
- setup-bridge.sh no longer configures qemu-bridge-helper or bridge.conf
The yocto docker-moby recipe installed a docker.service drop-in that set
CPUAffinity=0. dockerd validates --cpus against sched_getaffinity(2), so
the pin capped every container at one CPU and made
'docker run --cpus 2' fail with 'Range of CPUs is from 0.01 to 1.00'.

Drop the override so containers can use the CPU limits users ask for.
The mkosi images never carried this pin, so both OS backends now behave
the same.

Fixes Dstack-TEE#410
fix(os): stop pinning dockerd to CPU 0 in yocto images
Split out of the vhost/multiqueue change (Dstack-TEE#1145); carries the port mapping
parts of Dstack-TEE#1154.

`port_map` is implemented as QEMU `hostfwd=` entries, and QEMU attached
every one to the first user-mode NIC. With several NICs that choice was
made silently, and a VM with no user-mode NIC dropped its mappings with
no warning.

- `PortMapping.nic_index` in the RPC, `--port ...@<nic>` in vmm-cli.py and
  dstack-cli, and a NIC column in the UI port editor
- an unpinned mapping still goes to the first user-mode NIC
- a pin to a NIC the VM does not have, or to a backend that cannot publish
  a host port (bridge, macvtap, custom), is refused at deployment
- a mapping stranded by a later topology change is named in the launch log
…tiqueue

feat(vmm): run virtio-net on vhost-net with configurable queue pairs
…_yarn/dstack/kms/auth-eth/multi-acd8535d99

build(deps): bump minimatch in /dstack/kms/auth-eth
Docker Build Check exported each builder stage to the Actions cache with
mode=max. The source is cloned at DSTACK_REV inside the build, so the cargo
layer misses on every commit, yet the export still uploaded the toolchain and
target directory: about 150s of the ~9.5 minute image step in each of the three
jobs, and the bulk of the repository's Actions cache usage. The layers it could
reuse, the pinned-package install and rustup, take about fifteen seconds.

SDK tests cached only the `sdk/rust` workspace, while `sdk/run-tests.sh` first
builds the release simulator from `dstack/`. That build ran cold on every run
and took 4m36s of a 6m39s job. Add `dstack` to the rust-cache workspaces.
…m pushes only

The `docker-container` builder that setup-buildx creates by default exports
every `--load` as a tarball and imports it into the daemon. For the
`*-builder` targets, which carry the toolchain and the cargo target directory,
that took 2-3 minutes per job once the gha layer cache no longer paid the
compression cost up front. The daemon's own builder puts the image in the store
directly, and the second build of the builder target reuses the first.

The SDK cargo cache is about 1 GB. Saving it from every pull request ref fills
the 10 GB repository quota and evicts the shared entries, so save it only from
pushes and let pull requests restore from the base branch.
…cache

ci: speed up Docker Build Check and SDK tests
…ing-nic

feat(vmm): allow port mappings to specify a NIC index
…tion

netd accepted a single JSON message per connection, tagged with an
`operation` field and decoded into one `Request` enum, and answered with a
flat response struct whose fields each operation filled in differently.

Define the protocol in `netd_rpc.proto` instead: `PrepareBridge`,
`PrepareMacvtap`, `RemoveInterface` and `CheckInterface` each have their own
request and response messages. netd serves the generated `NetdServer` with
Rocket and ra-rpc on the same Unix socket (bound itself or handed over by
systemd), and the VMM calls it through the generated `NetdClient`. The
hand-written framing, size limits, liveness-probe handling and the tagged
request/response types go away.

Operations still run one at a time: a FIFO async mutex keeps them in arrival
order, which the VMM's rollback after a timed-out Prepare relies on, and the
flock still serializes against other netd processes.

`PrpcClient` now includes the server's `{"error": ...}` message when a call
fails, so netd's reason for refusing a request reaches the VMM log instead of
only a status code.

This is a wire-protocol break: the VMM and netd must come from the same
release, and `dstack-netd` has to be restarted together with the VMM.
refactor(vmm): serve netd as a pRPC service with one method per operation
… support

Integrate upstream 0a2f04c. Retain DSTACKS1 per-secret envelopes, GHCR publication, and the Yocto loader fix. Supersede the old no-TEE bypass with upstream development simulator support and fail-closed production paths.
@lalalune
lalalune merged commit c12e96a into alpha Sep 14, 2026
5 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants