A host reporting an endpoint id another machine already holds hit the unique
index, and the raw refusal reached the global handler as a 500 -- telling a
host its beat broke the server rather than that the id is taken. It is now the
409 every other conflict here gives, and the route documents it.
Checked-then-written would be worse rather than better: two hosts reporting the
same id in the same instant both read "nobody holds it" and both write, which
is precisely what the index is for. The read would add a query and remove
nothing.
Before:
expect(res.status).toBe(409)
Received: 500
The machine table said who owns a host, which team it belongs to and when it
was last seen, and nothing about how to reach it. Anything standing in front of
a host and authenticating browsers on its behalf could therefore authorise a
request perfectly and then have nowhere to send it.
Reported, never assigned. A host holds the secret half of this identity and is
the only thing that can know the public half first, so it rides on the beat it
already sends as itself. Omitting the field leaves the stored value alone --
an agent that does not mention where it is has not moved, and an absent field
must never read as "nowhere", which would take every host shipped before this
field off the map on its next beat.
Nullable, because "has never reported one" is a real state that every host
registered before today is in. Unique, because an endpoint id belongs to one
host: two rows claiming the same one would send a request addressed to one
machine to another machine's agent, which is the one mistake here that the
authorisation in front of it cannot catch.
Comments and served API descriptions here had grown references that only make
sense to someone with our internal notes: relative paths that escape this
tree, filenames and titles of documents nobody outside can open, quoted prose
from them, and the name of a component that has no public surface — once in an
OpenAPI description, which is published output rather than source.
None of it was load-bearing. Every case restates as what the code actually
requires, and every rewrite came out shorter: "in the words the host agent
reports" for a component name, "republished as addresses are discovered" for a
quoted phrase, "a size tier sets vCPU, RAM and the output geometry" for a
sentence that had been carrying a path.
Internal reasoning is now cited exactly one way, ref(d-NNNN) in a source
comment, with the rule that the sentence must still stand if the marker is
deleted. CLAUDE.md leads with it, because the previous version of this mistake
was made by people who knew the repo was public and it still took ten
occurrences to notice, so "be careful" is not a mechanism.
Commit messages get the stricter rule and carry no references at all: a
comment can be fixed by the next commit and a published message cannot be
fixed at all. Git hooks now enforce both halves.
The check caught a real one while being written: the CLAUDE.md table spelled
out the paths it was prohibiting, which discloses them to exactly the reader
it protects against.
138 tests, 0 fail.
Second half of G1's "neslet registers against api.nestri.io and heartbeats".
Registration already worked; there was no heartbeat endpoint at all — grep for
it across apps/api and packages/core returned nothing, and neslet's own
main.rs says the same from its side.
POST /machine/heartbeat, machine credentials only. Two decisions worth stating
because neither is obvious from the diff:
**It returns the interval.** The auth middleware already touches lastSeen on
every authenticated machine request, so an endpoint that only did that would
add an endpoint and no capability. What a host cannot know on its own is how
often the control plane wants to hear from it, so the response carries the
cadence. A fleet whose interval can only change by shipping a new agent is a
fleet whose interval never changes.
**It takes no body.** neslet has a HostSummary ready to send, and week 2 owns
box state reporting. Accepting fields nothing acts on yet would mean a wire
shape we would have to keep, chosen before the thing that consumes it exists.
Online-ness is derived from lastSeen rather than stored: a host that stops
beating goes offline through the passage of time, which is the one mechanism
that cannot itself fail. Three missed beats, not one — a single missed beat is
a lost packet, and treating that as offline would make placement flap.
Also: the machine actor's teamID stops being optional. It was `...(teamId ? {}
: {})` in the middleware, a branch for a state that cannot exist now that
machine.team_id is notNull.
134 tests, 0 fail.
Migration 1 of 0048, and the first of the seven weeks — nothing about a live
feed works without these two tables, so it is not a cleanup during them.
box a VM someone owns: an id that is also its DNS label, an editable
label, an owning user, the machine it sits on, a tier and a state.
Owned by a person and placed on a team's hardware, which are two
different relationships, hence both userId and machineId.
session one run of one box by one linked Steam account, and what costs
money. Separate from box because the ticket changes after bind as
addresses are discovered — the vsock contract calls it "a stream,
not one value" — so it is a column a client polls, not a value it
is handed once.
Box states are neslet's own three and no more. `starting` and `stopping` are
the obvious additions and both are omitted because nothing would ever write
them; a failed box is `stopped` with stopClean false, which is how neslet
models it too.
The generated migration would have failed on live rows in three ways, so it
is hand-written and tested against a database seeded at the old schema:
- machine.team_id becomes notNull, and *every existing row is null* because
the old registration path passed null. Personal teams are backfilled for
machine owners first, reusing a team they already own rather than minting
a second, with the owner membership row repaired where missing.
- game_download.host_id becomes a foreign key. It held free-form strings,
so unattributable rows are deleted before the cast — the only destructive
statement here, and a considered loss: it is a progress report neslet
re-derives from disk.
- Team.createPersonal was written and documented in packages/core/CLAUDE.md
as part of the login flow and never actually called, so no user has a
team. ensurePersonal is idempotent and now runs on every login, which is
what backfills accounts the migration does not reach.
Verified on a seeded legacy database: three null-team machines backfilled, an
existing team reused rather than duplicated, a blank display name handled, and
both unattributable download rows dropped while the attributable one survived.
Also fixes two things this work ran into rather than caused:
- Database.client() built a new postgres pool on every call, and use()
called it twice per invocation — pools of ten connections held for a 30s
idle timeout. Invisible in a Worker where requests are short; the suite
crossed 100 connections and Postgres said "sorry, too many clients
already" in whichever file ran last, which reads as a flaky test rather
than a leak. Now one pool per connection string.
- download.test.ts asserted against `hst_…` host ids, which is exactly the
unattributable row the new foreign key exists to refuse.
There is no "no team" any more: PATCH /machine/:id took teamId null to mean
"mine alone" and now requires a team, because the personal team is the one to
name. Its test is updated to the new contract rather than deleted.
113 → 128 tests, 0 fail.
Description
<!-- Briefly describe the purpose and scope of your changes -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced comprehensive account management with combined user and
team info.
- Added advanced, context-aware logging utilities.
- Implemented invite code generation for teams with uniqueness
guarantees.
- Expanded example data for users, teams, subscriptions, sessions, and
games.
- **Enhancements**
- Refined user, team, member, and Steam account schemas for richer data
and validation.
- Streamlined user creation, login acknowledgment, and error handling.
- Improved API authentication and unified actor context management.
- Added persistent shared temporary volume support to API and auth
services.
- Enhanced Steam account management with create, update, and event
notifications.
- Refined team listing and serialization integrating Steam accounts as
members.
- Simplified event, context, and logging systems.
- Updated API and auth middleware for better token handling and actor
provisioning.
- **Bug Fixes**
- Fixed multiline log output to prefix each line with log level.
- **Removals**
- Removed machine and subscription management features, including
schemas and DB tables.
- Disabled machine-based authentication and removed related subject
schemas.
- Removed deprecated fields and legacy logic from member and team
management.
- Removed legacy event and error handling related to teams and members.
- **Chores**
- Reorganized and cleaned exports across utility and API modules.
- Updated database schemas for users, teams, members, and Steam
accounts.
- Improved internal code structure, imports, and error messaging.
- Moved logger patching to earlier initialization for consistent
logging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
This attempts to connect the Steam account to user account... for easier
management
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced user profiles and account views now display integrated Steam
account details and enriched team associations for a more comprehensive
experience.
- **Chores**
- Backend and database refinements have been implemented to improve
system stability, data integrity, and overall performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
<!-- Briefly describe the purpose and scope of your changes -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Upgraded API and authentication services with dynamic scaling,
enhanced load balancing, and real-time interaction endpoints.
- Introduced new commands to streamline local development and container
builds.
- Added new endpoints for retrieving Steam account information and
managing connections.
- Implemented a QR code authentication interface for Steam, enhancing
user login experiences.
- **Database Updates**
- Rolled out comprehensive schema migrations that improve data integrity
and indexing.
- Introduced new tables for managing Steam user credentials and machine
information.
- **UI Enhancements**
- Added refreshed animated assets and an improved QR code login flow for
a more engaging experience.
- Introduced new styled components for displaying friends and games.
- **Maintenance**
- Completed extensive refactoring and configuration updates to optimize
performance and development workflows.
- Updated logging configurations and improved error handling mechanisms.
- Streamlined resource definitions in the configuration files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Description
We are attempting to hookup maitred to the API
Maitred duties will be:
- [ ] Hookup to the API
- [ ] Wait for signal (from the API) to start Steam
- [ ] Stop signal to stop the gaming session, clean up Steam... and
maybe do the backup
## Summary by CodeRabbit
- **New Features**
- Introduced Docker-based deployment configurations for both the main
and relay applications.
- Added new API endpoints enabling real-time machine messaging and
enhanced IoT operations.
- Expanded database schema and actor types to support improved machine
tracking.
- **Improvements**
- Enhanced real-time communication and relay management with streamlined
room handling.
- Upgraded dependencies, logging, and error handling for greater
stability and performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Kristian Ollikainen <14197772+DatCaptainHorse@users.noreply.github.com>
## Description
- [x] Adds support for AWS SSO, which makes us (the team) able to use
SST and update the components independently
- [x] Splits the webpage into the landing page (Qwik), and Astro (the
console) in charge of playing. This allows us to pass in Environment
Variables to the console
- ~Migrates the docs from Nuxt to Nextjs, and connects them to SST. This
allows us to use Fumadocs _citation needed_ that's much more beautiful,
and supports OpenApi~
- Cloudflare pages with github integration is not working on our new CF
account. So we will have to push the pages deployment manually with
Github actions
- [x] Moves the current set up from my personal CF and AWS accounts to
dedicated Nestri accounts -
## Related Issues
<!-- List any related issues (e.g., "Closes #123", "Fixes #456") -->
## Type of Change
- [ ] Bug fix (non-breaking change)
- [x] New feature (non-breaking change)
- [ ] Breaking change (fix or feature that changes existing
functionality)
- [x] Documentation update
- [ ] Other (please describe):
## Checklist
- [x] I have updated relevant documentation
- [x] My code follows the project's coding style
- [x] My changes generate no new warnings/errors
## Notes for Reviewers
<!-- Point out areas you'd like reviewers to focus on, questions you
have, or decisions that need discussion -->
Please approve my PR 🥹
## Screenshots/Demo
<!-- If applicable, add screenshots or a GIF demo of your changes
(especially for UI changes) -->
## Additional Context
<!-- Add any other context about the pull request here -->
>Adds `maitred` in charge of handling automated game installs, updates,
and even execution.
>Not only that, we have the hosted stuff here
>- [x] AWS Task on ECS GPUs
>- [ ] Add a service to listen for game starts and stops
(docker-compose.yml)
>- [x] Add a queue for requesting a game to start
>- [x] Fix up the play/watch UI
>TODO:
>- Add a README
>- Add an SST docs
Edit:
- This adds a new landing page, updates the homepage etc etc
>I forgot what the rest of the updated stuff are 😅
This adds a simple way to incorporate a centralized authentication flow.
The idea is to have the user, API and SSH (for machine authentication)
all in one place using `openauthjs` + `SST`
We also have a database now :)
> We are using InstantDB as it allows us to authenticate a use with just
the email. Plus it is super simple simple to use _of course after the
initial fumbles trying to design the db and relationships_