Skip to content
Spinbox is experimental: APIs change and data may be lost. Status lists what is built and what proves it.

Rules and their gates

View Markdown

The rules every entry point must keep, from spin’s docs/security/README.md at 8eee32b9d. Code cites a rule’s number at the line that keeps it. Gate is what fails a build when the rule is broken; none marks a rule kept only by each place remembering it. What is still open is on Known gaps.

#RuleGate
I1Every procedure and route behind auth names its scope class.TestEveryProcedureHasAScope
I2Every id taken from a request is authorized for the caller before anything is read or written with it.TestEveryHandlerResolvesTheWorkspaceItActsOn, TestEveryProcedureUsesTheMechanismItNames
I3“Not yours” is answered as “not found”, and ownership is checked before state.the same
I4Membership and role are asked at the time of the action, not only when the resource was made.TestOnlyTheListedProceduresSkipTheTeam, TestAnOwnerRemovedFromTheTeamCannotUseItsWorkspace, TestAPortOfATeamsWorkspaceIsReachedOnlyByWhoIsInTheTeam, TestAStreamIntoATeamsWorkspaceEndsWithItsViewersMembership - the gate reads handlers that resolve a workspace, and a port’s decision is not one, so a new way in that decides for itself is held by a test of its own and not by the gate
I5A cookie-authenticated request that changes state or opens a socket names the dashboard’s origin (or none) - or the browser terminal’s, for the calls it makes alone.TestAPageOnAnotherSiteCannotSpendTheVisitorsCookie, TestEveryHTTPRouteIsMountedBehindAuth, TestOnlyTheTerminalIsTheTerminalsOrigin, TestEveryTerminalRouteIsOneServed
I6A credential is minted from crypto/rand, stored only hashed or sealed with its resource as AAD, compared in constant time, and revoked with what issued it. A guest holds none to the control plane: its host carries its supervisor’s stream under the host’s own (I8), and revoking the host cuts every stream it carries.TestAGuestsStreamIsCarriedByItsHostAndCutWithIt; otherwise none
I7Bytes a guest controls are bounded (size, count, time) and made valid (UTF-8, paths) where they cross into the host.the CHECKs; TestWhatAGuestWritesIsBoundedBeforeItIsARow
I8A host is trusted only for what is placed on it, and says only what a host may say - its guests’ supervisors included, whose streams it carries under its own token.runnerMessageBinding and its two tests; TestAHostCarriesOnlyTheSupervisorsOfWorkspacesPlacedOnIt, TestWhatReachesTheControlPlaneIsTheSupervisorsCall; TestAHostsMintedCredentialWritesOnlyItsOwnSubtree, against the store; TestAHostsCredentialsLastAQuarterOfAnHour, TestAKeyForgottenIsAskedForAgainAndRefused, TestARevokedHostsVolumesArePendingRekey, TestARekeyedVolumeOpensWithItsOwnKeyAlone, TestAHeadWrittenByAHostThatLeftIsPassedOverAndSaid, the DST’s a-share-opens-only-its-snapshot and a-rekeyed-volume-depends-on-no-other-key; TestAMovedVolumeIsRestoredFromItsHoldersHistory, TestAMoveGrantsTheVolumeOverItsHistory, the DST’s a-moved-volume-restores-its-new-holders-history, the storage lane’s TestAMoveFencesTheHostTheVolumeLeft
I9Object-store bytes are checked against the database-anchored digest before use; a shared key is read at its published version.TestALayerWrittenOverIsStillReadAsPublished
I10An effect outside the database happens before the row that names it is forgotten.none
I11Every third-party action is pinned by commit.task lint:workflows
I12A URL a non-operator can set is refused when it resolves into the node’s networks.TestAConnectionToThisNodeIsRefusedAsItIsMade, TestARedirectIsHeldToTheRule, TestAProxiedRequestGoesToTheAddressItWasCheckedAt, TestAnIssuersEndpointsAreHeldAsTheIssuerIs
I13A component that speaks for another principal proves it is that component.TestOnlyTheProxyAsksForABrowser, TestAProxyIsGivenItsTokenForWhatItProves, TestAProxysProofIsGivenATokenOnce, TestAnOperatorGivesTheProxyItsTokenFromTheCommandLine, TestAMachineJoinsByWhoItIsOnlyAsANewHostOfANamedRole, TestAHostIsTheBuildItJoinedAs, TestAControlPlaneRetiresTheHostsOfBuildsItNoLongerServes, TestARetiredHostIsRevokedOnceItsWorkspacesAreSuspended, TestARetiredHostThatKeepsItsWorkspacesIsRevokedAtItsDeadline, TestARetiringHostIsGivenOnlyWhatItsDrainTakes, TestAHostOfARetiredBuildRunsNoWorkspace
I14A machine’s QEMU reaches only what it is handed, and holds only what it is bounded to - a core included.TestAProductionInstallationPlacesOnlyOnIsolatedHosts, TestEveryIsolationIsAskedOnce, TestAHostSaysItKeepsWhatItCanGiveAMachine, TestSMTTurnedOnGivesTheMachinesCoresOfTheirOwn, TestAHostThatNoLongerKeepsItsMachinesApartIsDrained; the VM lane’s TestAMachinesQEMUReachesNothingOnTheHostOrAnotherMachine, TestAGuestAnswersOnlyTheRunner, TestAMachineIsBornWithinItsBounds; TestAMachineWithAUserOfItsOwnIsLaunchedApart, TestARunnersMachineMergesNoPagesAcrossMachines
I15A program that parses a tenant’s bytes on the host opens only the files of its operation.TestOnlyARootRunnerOnANewLandlockSaysItHoldsAProgramToItsFiles, TestAProductionInstallationPlacesOnlyOnIsolatedHosts, TestQemuImgDoesItsWorkWithOnlyItsFiles, TestAProgramReadsOnlyWhatItIsGiven, TestAProgramWritesOnlyWhatItIsGiven, TestAProgramMakesNoSocket; the VM lane’s TestARootRunnersProgramHoldsNothingOfRoots; the storage fakes refuse a file a run was not given, as the kernel does
I16A store on the control plane’s machine is reached by the installation’s administrators only through the control plane, and by its processes only for what each is.TestOnlyTheControlPlaneSaysWhoTheAdministratorIs, TestGrafanaShowsTheStoreToTheAdministratorItIsTold, TestNothingWithoutTheStoresKeyTakesItsMetrics, TestTheStoresRunWhileTheirControlPlaneDoes (each account to each port, against the kernel), TestTheStoresGuardNamesEveryPortAndOnlyAccountsTheImageHas; the dashboard lane’s Monitoring access and Admin Metrics
I17Only the keys of the people a workspace’s access names - its owner and the guests they invited - log in to it, each with the installation’s certificate over it; the control plane holds no key that logs in and is never an SSH client of a guest (ADR-0030).the sshd lanes’ TestSSHDLetsInOnlyACertificateOverAKeyTheHostAdmits, TestThePasskeyTerminalLogsInToTheClaimedGuest; TestAPreparedRootLetsInOnlyByItsHostsWord, TestALoginIsWhatTheHostSaysOfTheKey, TestALoginIsWhatTheNewestAccessSaysOfTheKey, TestAWordAdmitsTheKeysOfThePeopleItNames, TestTheNewestKeptWordIsTheHighestItsOwnerSigns, TestAMachineIsGivenItsOwnWorkspacesNewestAccess, TestTheControlPlaneKeepsOnlyTheOwnersNewerAccess, TestAGuestIsLetInByTheAccessThatNamesThem, TestATunnelCertifiesOnlyAKeyOfSomebodyTheAccessNames, TestOnlyTheListedProceduresAdmitAGuest, TestAKeyringStartedAnewIsAnotherChain; the VM lane’s TestOnlyThePeopleTheAccessNamesLogInToAWorkspace, TestAGuestTakenOutStaysOutOfAWorkspaceRestoredToBeforeIt, TestTheOwnerLogsInToACloneOfTheirOwnSnapshot; TestAGuestIsConnectedToOnlyByAClaimThatHolds, TestASessionIsRefusedBeforeTheRelayIsDialled
I19A workspace’s identity is signed by the host that runs it, for the machine that asked, named only as its owner’s access says - its team, owner, image and parent, fixed by the first - held to the owner’s keyring and the team’s roster, which people sign, and given only for an audience the access names; the control plane holds no key that signs one (ADR-0030).TestATokenIsTheAskingMachinesWorkspace, TestATokenIsForAnAudienceItsOwnerNamed, TestAWordGivesTheIdentityForTheAudiencesItNames, TestARunIsHeldToTheFirstKeyItPresents, TestAMachineIsNamedAsItsAccessSays, TestAnOwnerWhoseKeyringStartedAnewNamesNoMachine, TestABookSaysWhoIsAMemberByTheKeyringTheRosterNames, TestAKeyringIsAChainItsOwnKeysSigned, TestARosterIsAChainItsOwnersSigned, TestOnlyATeamOfOneStartsAnew, TestASignatureIsKeptOnlyAsProposedByWhoMaySignIt, TestAKeyringStartsAnewWhenItsPersonAsks, TestATeamOfMoreThanOneIsSignedAgainByAnotherOwner, TestAHostOpensTheKeyThroughAnAttestedDecrypt, TestAHostWritesOnlyItsOwnIdentityRecords; the VM lane’s TestAWorkspacesIdentityIsTheHostsSignatureOfWhatItsOwnerSigned
I18What a workspace applies of its owner’s settings, what of it leaves it, and which egress root it trusts are its access’s: settings only as approved by their digest, the journal and the console only with consent, the root only as named by its digest (ADR-0030).TestOnlySettingsTheOwnerApprovedAreApplied, TestTheJournalIsSentOnlyWithTheOwnersConsent, TestTheEgressRootIsTrustedOnlyByTheOwnersWord, TestAWordNamesOnlyTheEgressRootThisMachinePinned, TestTheConsoleLeavesTheHostOnlyAsTheNewestAccessSays, TestAConsoleLeavesTheHostOnlyWithItsOwnersConsent
I20No commit holds a credential, in any file.task lint:secrets
I21A team’s secret kept as a reference is read only as the team whose secret it is, so a team’s admin cannot name another team’s secret, or the installation’s.TestASecretIsReadInASessionTaggedWithItsTeam, TestASecretSetAsAReferenceIsReadAsTheTeamBeforeItIsKept, TestASecretKeptAsAReferenceIsReadAsTheTeamThatKeepsIt; spin-terraform-aws’s posture run a_teams_secret_is_read_only_as_the_team
I22A header the egress proxy sets for a workspace goes on no request its rule’s scope - methods, a path prefix by whole segments - leaves out, and a host that cannot hold a scope is not given one.TestAnInjectionIsSetOnlyOnTheRequestsItsScopeNames, TestARequestOutsideAnInjectionsScopeIsRefused, TestAScopedInjectionGoesOnlyToAHostThatHoldsIt, TestAnInjectionCrossesTheWireWhole

Generated by task reference from spin/docs/security/README.md at 8eee32b9d.

Documents Spinbox v20261019.02. This page as Markdown, for a model or a terminal.