Repository navigation
A remedy is data until the door that prints it renders it - #334
Merged
Merged
Conversation
R1: every root-only refusal names the caller's own command, every word as typed and shell-quoted, with sudo in front (RootRequired); host sync and host update refuse a non-root caller before reading host.yaml. R2: core, harness, the api package and the daemons name commands as a Remedy (words, and commands each a verb and its arguments) carried through Remedied failures, Result.Failure.remedy(), Refusal.fix, stored sync errors and main's session refusal. The CLI's doors render it with OperatorCommand.here(); the API serialises it beside a verb-only action. OperatorCommand moves to sail-infra; elsewhere() and unknownReader() go. R3: SailPaths.workstationPublicKeyPath() resolves the one workstation key file from the box owner's home; host config set ssh-public-key writes it under PairingTrust.
…the host's refusal
…ocal event lane, a run's room post
…a dropped malformed event
…ry wrapped failure keeps its reason and remedy apart ProjectEditCommand.validate wrapped SailYaml's refusal as a sentence, so a deleted key's 'sail project skills add' reached a sudo reader plain. It now throws Remedied.argument with the cause's reason and remedy kept apart, through Remedied.reasonOf/remedyOf/joined, which ProjectReader.Unreadable uses in place of its own copy. PtyHostYield joins per-session refusals as a Remedy, and PagedSyncSession quotes a broken channel's first failure as one. The changelog names the pty refusal frame, the stream envelopes and the local lane's event refusal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spec:
sail-remedy-rendered-at-the-door(takes over the gaps PR #325 specced).R1: a root-only refusal names the command as typed
commands/RootRequiredbuilds every root-only refusal from the caller's own words: picocli'sexpandedArgs()after the verb path found in theSailtree, each word throughStrings.shellWord,sudo sailin front.Main.refusaluses the samererun.host keys syncandhost ssh-identity.JoinCommandchecks writability before it prompts.host syncandhost updaterefuse a non-root caller before they readhost.yaml. On a fresh box (scenario 3) the refusal now names root, not "not initialized".shellWordleaves@,,and+bare, so the re-run readssudo sail init --main sail@m.RootRequiredTestcovers scenarios 1–4 with exact sentences. It also runs every root-only verb with every option as a non-root caller, and asserts the re-run's words equal the typed words and parse to the same options.R2: a remedy is data until the door renders it
common.Remedyholds words and commands; a command is{runs: SAIL | CLIENT | PROGRAM, verb, args}.common.Remedied(reason + remedy) is carried by ISE/IAE/IOException flavours,SyncTransportException,ApiExceptionandSailApiClient.Refused. A rawgetMessage()is the plain reading.Result.Failure.remedy()replaces the String action.Refusal.fixandDispatchDecision.Refused.fixare Remedies.last_erroras encoded data; plain rows from before read back as words.Refusesends its remedy beside the plainreasonolder nodes read.Main.report,CliCommand.run/describe/linesandsync statusrender withOperatorCommand.here(). The API body keepsaction, with each command named by its verb only, and addsremedy: [{text}|{run,verb,args}]when it names a command.SailApiClientturns the body back into a Remedy.Consumer<Remedy>chosen at the door:sail syncrenders, sail-api printsRemedy.plain. Daemon log lines, webhook notices and agent prompts print the plain reading.OperatorCommand.elsewhere(),unknownReader(), and theSUDOconstants inSyncDatabaseandTokenAuth. The operator parameters onFdeStore,NodeRound,SyncSession/PagedSyncSession(whoseSystem.errline is now a notice), the migrations,SyncDatabase, everyapiclass,OperationsFactory,SyncOperations,MainHostKey,ConflictOperations,HostAccess,CliOperator,ConnectEnvironment,WorkstationIdentity,ContainerStateGuard,CurrentProject, the pty host classes, the watcher and server start.OperatorCommandmoved tosail-infra, so core and harness cannot compile against it.OperatorHintsTest.codeThatCannotSeeItsReaderNeverRendersACommandscans core, harness,api/and the daemons' classes. The rendered-command scan now parsesRemedy.sail(verb, args…)too.here()'s operator, as the spec allows:LocalIdentity,HandleChange,DaemonSecretInstaller,ProjectProvisionerand others. Hints for another box's operator in CLI code useOperatorCommand.SUDO.saildirectly.R3: one file is the box's workstation key
SailPaths.workstationPublicKeyPath()=workstationPublicKeyPath(identityHome()). It is the one resolver behind${SSH_PUBLIC_KEY},registered(),fde pair,host config set ssh-public-keyand connect's warning.host config set ssh-public-keywrites underPairingTrust(installWorkstationKey).Scenarios
RootRequiredTestRemedyOverHttpTest(real HTTP: the body as data with nosailsentence; rendered forSUDO, the gateway'sUNPRIVILEGEDandPLAIN)SyncCommandTest(a stored host-key failure read through sudo and by root);SyncDiagnosticTestSailPathsTest.theBoxsWorkstationKeyIsTheOneFileInItsOwnersHome,FdeCommandTest(pairing path ==${SSH_PUBLIC_KEY}path)HostConfigSetCommandTest.aKeyChangeWaitsWhileAnApplyOrAPairingHoldsTheTrustMainRebuiltRoundTest(version refusal),FdeCommandTest(scoped grant), each throughSUDOthenPLAINNotes
--git-tokenincluded, back to the same terminal, because the spec asks for the whole argv.Refusecarries a remedy.NativeFleetITnow expects a scoped node's refusal to readsail fde grant nia beta. The fleet's boxes run as root logged in directly, the reader scenario 7 describes.RootRequiredTestrun onmainfails 5 of 6; the sixth only checks that the verb table is complete. The full-Pintegrationverify is green locally, and all five CI checks pass.Takeover (after review iteration 6)
ProjectEditCommand.validatewrapped a definition's refusal as"not valid YAML: " + getMessage(), so a deleted key'ssail project skills addreached asudoreader plain. It now throwsRemedied.argumentwith the cause's reason and remedy kept apart.RemediedgainedreasonOf,remedyOfandjoined, andProjectReader.Unreadable's hand-rolled copy of the same logic uses them.ProjectEditCommandTestasserts the sentence throughCliCommand.linesforSUDOandUNPRIVILEGED; red before the fix.PtyHostYield.endjoined per-session refusals bygetMessage()andPagedSyncSessionquoted a broken channel's first failure bygetMessage(); both now carry the failure as a Remedy (text unchanged, existing tests prove it). The review's suggestion to replaceRootRequired's tree walk with picocli's qualified name was tried and reverted: a command built outside theSailtree (asInitCommandTestandProjectApplyCommandTestbuild theirs) has no path, so the walk stays. A stacked javadoc inSyncCommandremoved. CHANGELOG names the pty refusal frame, the stream envelopes and the local lane'sPOST /v1/eventsenvelope.actionstays each command's verb and arguments (what a reader who types nothing reads), as the spec's scenario 10 asks; thesync_degradedevent'sdetailand the run-log stream's foreign-run body keep their plain text, as narration for readers the daemon cannot see. Scenario 11 is proven at the resolver (SailPathsTest,FdeCommandTest); the end-to-end check on a fresh VM joins the field check list.ApiException.ofdropping the remedy → 5 red; the sync wire dropping its remedy array → 3 red; the pty wire dropping its remedy tail → 5 red.