DEV Community

Cover image for I Tried to Build Persistent Compute on an Xbox SSD. The Failed Tests Changed the Architecture.
DaC
DaC

Posted on

I Tried to Build Persistent Compute on an Xbox SSD. The Failed Tests Changed the Architecture.

The Xbox wrote 8 GiB of persistent data.

I still couldn't call the experiment successful.

The bytes were there, but the operation failed because a completely different part of the system had timed out. In another test, the worker correctly refused to allocate more storage even though its configuration technically allowed a much larger workload.

Eventually, the experiment passed. But getting there required much more than writing files to an SSD.

I had to investigate how Xbox processes share resources, build a separate coordination component, introduce a recoverable page store, survive real application restarts, and learn which failures were bugs and which were legitimate platform boundaries.

This is one of the experiments behind XCP that I hadn't explored in the previous articles.

The five-part XCP series ended with the platform becoming open source. This bonus chapter goes back into the original Xbox research to explain how I approached persistent computation inside the Developer Mode sandbox.

The problem wasn't just memory capacity

In the earlier experiments, XCP measured an application memory budget of 5 GiB in one Xbox Series X Developer Mode configuration.

That was useful. It gave us an actual resource boundary for the worker rather than a number inferred from the console's hardware specifications.

But it also raised another question.

What happens when the work I want to execute is larger than the memory I can keep resident?

I wasn't trying to bypass the sandbox or convince the Xbox that its SSD was additional physical RAM.

I wanted to build something different: an execution system that could keep data and completed work in persistent storage, bring bounded pieces into memory, process them, and continue from a valid checkpoint after an interruption.

Before building the storage layer, though, I had another hypothesis to investigate.

Could a second process help?

First, I investigated the Xbox process model

XCP already had a foreground worker capable of running CPU and GPU workloads.

I wanted to understand whether another process inside the same UWP package could perform useful background coordination without consuming the worker's entire resource budget.

The candidate was an out-of-process App Service.

On desktop Windows, creating another process might seem straightforward. On Xbox Developer Mode, I wasn't going to assume that the desktop behavior applied unchanged.

So I started measuring.

The first live probes established that the worker and an auxiliary App Service could run with distinct process and activation identities. They exchanged messages, maintained independent heartbeat activity and retained touched, verified memory during overlapping execution windows.

That was already useful.

But it did not prove that we had discovered a second independent 5 GiB memory budget.

To investigate properly, I built an eleven-phase matrix covering CPU execution, verified GPU execution, simultaneous activity, progressive memory pressure, bidirectional storage visibility, suspension, termination, restart, redeployment and cleanup.

And this is where the testing started getting complicated.

The first failures weren't even storage failures

Worker .150 reached the Xbox but exposed incomplete measurement data. It could report IPC latency summaries, yet the acceptance contract required the underlying distribution, including median, p95 and bidirectional throughput.

There was no legitimate way to reconstruct missing measurements afterward.

So that run remained diagnostic.

Worker .151 produced another interesting failure: 79 of 81 preflight checks passed.

The two failures came from heartbeat validation.

The foreground heartbeat ran at 25 ms intervals. The auxiliary broker used a 100 ms timer. During an approximately 935 ms observation window, the broker produced nine ticks.

The test incorrectly expected 32 because it had reused the foreground count.

The broker wasn't failing to run. Our expectation was wrong.

That candidate stayed failed, and the next version corrected the measurement contract.

Worker .152 passed the preflight, but memory-pressure testing exposed another gap. The Xbox refused a bounded allocation, yet the error didn't carry enough structured information to establish the responsible component, observed headroom, applied reserve and cleanup outcome.

A text error saying that memory allocation failed wasn't sufficient to classify the platform behavior.

Worker .153 added the missing observations.

The completed matrix passed all eleven phases, retaining fourteen actual attempts in its evidence history.

The separate classification step selected Case B: a distinct but resource-limited useful process.

That result changed the design.

I didn't have evidence for a second large compute process. But I had enough evidence for a small coordination service.

That was what I built.

The second process became a coordinator

Instead of creating another general-purpose worker, I gave the auxiliary process a narrower responsibility.

It could participate in lifecycle observation, heartbeat management and storage reservations.

The main worker still controlled execution admission, semantic verification, recovery authorization and final result publication.

This separation mattered because the broker could disappear and restart independently.

A reservation could not simply be assumed valid because some old record said it existed.

The worker had to validate the reservation identity, generation and storage plan before accepting new work.

Worker .154 tested this arrangement on the physical Xbox.

55 of 55 checks passed, with zero violations.

The useful result wasn't more RAM.

It was an operational coordination mechanism on which persistent computation could be built.

Next, I needed storage that could survive failure

Writing a file is easy compared with deciding whether that file represents completed computation.

Imagine a workload producing hundreds of output pages.

The application writes a page, updates some metadata, and then disappears.

After restarting, which pages can be trusted?

Did a page finish writing? Does its recorded hash match the bytes? Was its journal entry committed? Can the computation resume from that point without silently skipping or duplicating work?

For XCP, I built the storage system around content-addressed data and an append-only journal.

Each committed page had an identity derived from its content. The journal recorded ordered transitions, with records linked through hashes.

A page was not considered committed just because its file existed.

The system had to verify the relevant content and metadata before including it in the committed sequence.

Recovery worked from the longest valid committed prefix. Anything beyond that boundary could not silently become trusted state.

That approach allowed the runtime to distinguish a completed checkpoint from bytes left behind by an interrupted operation.

Then I tried breaking it

Worker .155 exercised the page store and recovery system on Xbox.

The tests included truncated journal entries, corrupted hashes, missing pages, incomplete root manifests, conflicting page identities, exhausted quotas, stale reservations and repeated cleanup.

The live campaign also tested recovery after worker restart, auxiliary restart and package redeployment.

One deployment experiment uncovered an unexpected problem.

An in-place installation using the same package version left the application running, but Xbox Network Isolation silently dropped the inbound worker connection.

The correction was not to pretend the storage operation had succeeded.

We used a clean uninstall with application data preserved, reinstalled the exact package, restored connectivity and verified that the committed storage root had survived.

The complete .155 campaign eventually passed 248 of 248 checks.

At this point I had a storage substrate capable of preserving committed data through the tested failure and lifecycle scenarios.

But I still hadn't demonstrated useful persistent computation.

That required the next experiment.

Running computation through persistent storage

For the first workload, I reused an existing XCP test called Provable Worlds.

It produces a deterministic field of 1024 × 1024 unsigned 32-bit values.

The complete result is only 4 MiB.

That may sound small for an SSD experiment, but this test wasn't intended to prove beyond-RAM scale yet.

I wanted to establish that computation could be divided into independently checked units, committed in order, interrupted and resumed correctly.

The field was divided into 256 tiles of 16 KiB each.

Each tile had a known global position in the output. The signed CPU Capsule could compute its bounded portion, and XCP compared that result against the CPU reference.

The runtime used bounded output buffers rather than requiring the complete result to stay resident.

The important condition was that the durable cursor advanced only after a page had been committed successfully.

The first uninterrupted runs passed. Restart failed.

Worker .156 completed two uninterrupted executions.

Then I tested the restart path.

Both real-restart resume attempts failed.

The problem was the reservation lifecycle.

After the foreground interruption, the system attempted to continue using the old coordinator lease. But that lease belonged to the previous activation.

This contradicted what the earlier process-topology experiments had already shown.

Persistent data could survive the interruption.

A live process reservation could not be treated the same way.

So I separated those two concepts.

The committed journal retained its original storage identity and generation. A resumed worker had to obtain a fresh live reservation, with a newer generation, and reauthorize the persisted work before continuing.

Worker .157 implemented that correction.

The test deliberately committed the first 128 tiles, computed the next tile without committing it, and interrupted execution.

After the real restart, the worker verified the committed prefix, skipped those first 128 tiles and computed the remaining 128.

It reproduced the expected final field hash.

The uninterrupted tests, restart/resume path, cleanup and same-package regression checks passed.

XCP admitted that specific bounded persistent-compute capability.

One distinction is worth keeping: the tiled execution used the signed CPU Capsule, not a newly implemented GPU-tiled engine. The existing GPU path was checked separately. Nor did this test demonstrate performance gains from asynchronous I/O overlap.

It demonstrated correct bounded computation and recovery.

Now I wanted to investigate the storage scale.

Eight gigabytes, and another failed experiment

Conceptual illustration of XCP persistent storage research on Xbox Series X, showing an 8 GiB SSD experiment, verified data pages, recovery tests and a successful result.

For the scale test, I moved to a different page geometry.

Instead of the 16 KiB computation tiles, the storage characterization used 4 MiB pages.

The target was 8 GiB: exactly 2,048 pages.

I also defined a progression through 256 MiB, 512 MiB, 1 GiB, 2 GiB, 4 GiB and finally 8 GiB.

The data was generated deterministically, with page-specific content, so the test could verify distinct pages without treating a single repeated blob as proof of physical growth.

And there was a safety condition: the operation had to preserve 16 GiB of reported available storage as a reserve.

Then came worker .158.

The worker materialized the full 8 GiB.

But the operation failed.

After more than 163 seconds of storage work, the coordinator attempted to release its reservation using an IPC deadline that had been established at the beginning of the operation.

That deadline was only 30 seconds.

The data had been written, but the protocol could no longer complete the operation correctly.

This was not an accepted result.

I corrected the lease-release timeout handling, added an explicit cleanup path for the failed run and tested again.

Worker .159 successfully committed all 2,048 pages.

The measured seed operation took approximately 164 seconds.

That is an end-to-end operation time, including page generation, hashing, storage and journal work. It isn't a raw SSD throughput benchmark.

But .159 was not the final success either.

The 64 GiB attempt was refused

The broader storage policy allowed much larger declared reservations, so I also tested what happened when the system attempted to continue beyond the 8 GiB seed.

The remaining planned growth was 56 GiB.

The platform-reported filesystem space was nowhere near sufficient for that growth plus the required 16 GiB safety reserve.

The worker returned:

STORAGE_SCALE_PLATFORM_HEADROOM_REFUSED

And crucially, it refused before writing additional data.

The existing workspace remained unchanged. Cleanup subsequently removed the 8 GiB seed and a second cleanup confirmed that the namespace was already absent.

This result did not establish that the Xbox SSD had a 64 GiB limit, or that the entire physical SSD was visible to the application.

The application had observed a much smaller storage filesystem, approximately 32.2 billion bytes of reported capacity in that test.

The configured storage quota was not the same as actual platform capacity.

The refusal was correct.

Instead of trying to force a larger allocation, I froze the demonstrated 8 GiB size as the target for the final characterization.

The result: worker 0.1.160.0

Worker .160 was built and signed as a new exact package and tested on the physical Xbox.

This time, the process completed the full storage lifecycle.

It committed 2,048 distinct 4 MiB pages, totaling 8 GiB, then released the reservation.

I returned to the Xbox Home screen and resumed the worker without power-cycling the console.

The resumed worker obtained a fresh reservation generation, recovered the persisted namespace, reread and rehashed the complete 8 GiB, and committed the ordered root without allocating another storage rung.

A separate verification operation then read the complete data again under another fresh lease and reproduced the same root identity.

Finally, cleanup removed the 2,048 unreferenced blobs and all 8 GiB of their content.

A second cleanup found nothing left to remove.

The complete .160 lifecycle passed its checks.

That was the accepted storage-scale result.

Not 64 GiB. Not the console's full SSD. Not virtual RAM.

Eight GiB of bounded, recoverable, content-verified persistent storage, measured through the actual Xbox worker lifecycle.

What this proves, and what it doesn't

The completed experiments establish two related capabilities.

First, XCP demonstrated a persistent-compute workflow in which deterministic tiled execution could survive a real worker interruption, verify previous committed work and resume without recomputing the valid prefix.

Second, it demonstrated an 8 GiB persistent storage namespace with recovery, full-root verification and cleanup on the physical Xbox.

These were separate tests.

The 4 MiB computation experiment did not prove that an arbitrary dataset larger than RAM could be processed end to end.

The 8 GiB storage experiment did not prove an 8 GiB GPU or CPU workload, transparent paging, unrestricted SSD access or a performance-optimized streaming engine.

In fact, the recorded streaming runtime exposed no measured prefetch/compute overlap advantage. The bounded buffers, ordered commits and recovery path were the capabilities being established.

Combining those pieces into larger real workloads is further engineering work, not something I can claim was already completed.

But the distinction between demonstrated components and future integration doesn't make the result less interesting.

It tells me which parts of the problem are actually solved.

Why the failed tests mattered more than the number

Looking back, there wasn't one dramatic failure.

There were several small, independent ones.

The auxiliary process existed, but didn't prove an independent memory budget.

The heartbeat worked, but the test expected the wrong number of ticks.

Memory pressure produced a refusal, but not enough structured evidence to classify it.

The storage layer survived, but an installation sequence disrupted the worker connection.

Computation succeeded uninterrupted, but couldn't resume under the old lease.

Eight gigabytes were written, but the IPC timeout invalidated the operation.

And a larger storage request was refused because the available space could not satisfy the reserve.

Each result forced a different correction.

Some changed the implementation. Some changed the measurement contract. One changed the resource-topology decision. Another changed the scale we were prepared to claim.

I think this is one of the most interesting aspects of XCP.

It didn't become more capable by assuming the Xbox restrictions were irrelevant.

It became more capable by discovering which capabilities could be combined reliably inside those restrictions.

The result was not a magic way to make SSD behave like RAM.

It was a storage and recovery architecture that allows computation to have a durable history independent of the process that produced it.

The code is now open source

The original Xbox experiments were performed in the historical XCP research environment.

The public repository contains reconstructed reference implementations and the source needed to inspect the design, including:

The distinction between the original historical results and the newly reconstructed development packages remains important.

The public source lets developers inspect and rebuild the relevant components. The historical hardware results belong to their original package versions and evidence records.

Newly reconstructed packages do not automatically inherit those physical-Xbox results. They require their own device validation.

That boundary is explained in the public reconstruction audit.

Beyond the SSD experiment

XCP started as an attempt to make an Xbox Series X a programmable execution target.

The project eventually expanded into a virtual machine, CPU/GPU execution, agent-driven development, a desktop studio and tools for adapting and building software.

The SSD experiments explored another necessary part of that picture.

A useful execution target needs more than the ability to run a program once.

It needs a way to preserve work, recover from interruption and continue under a new execution context without pretending that the old one still exists.

The most valuable result of this experiment wasn't the eight-gigabyte figure by itself.

It was the realization that the lifetime of a computation does not have to be identical to the lifetime of the process executing it.

On this particular Xbox, we demonstrated some concrete parts of that idea.

And as with the rest of XCP, the next meaningful step is to take those capabilities into a more demanding workload and find out where they stop being sufficient.


XCP is open source

GitHub: https://github.com/Daniele-Cangi/xcp-xbox

XCP Studio: https://xcpstudio.com/

Previous articles: XcpStudio — Five-Part Series

Bonus Chapter 01 — From memory limits to recoverable computation on a real Xbox Series X.

Top comments (0)