package hostfloor import ( "context" "crypto/ed25519" "errors" "github.com/aws/smithy-go" "github.com/carlosframework/platform/internal/release" "net" "os" ) func Offline(err error) bool { if errors.Is(err, context.DeadlineExceeded) { return true } var network net.Error if errors.As(err, &network) { return true } var api smithy.APIError if errors.As(err, &api) { switch api.ErrorCode() { case "SlowDown", "ServiceUnavailable", "InternalError": return true } } return false } func CheckRemote(ctx context.Context, s release.Store, pub ed25519.PublicKey, bucket string, local Verified) error { remote, err := Read(ctx, s, pub, bucket) if err != nil { if Offline(err) { return nil } return err } if remote.Digest != local.Digest { return ErrFloor } return nil } // CheckRemotePolicy is guard-exec's comparison of the local policy with the // published floor (D1). An armed policy is the prospective floor, persisted // before publication, so a store holding neither floor object is the // expected state and the local policy, already enforced, stands (Opus 8b // review I-1). Every other answer refuses unless it matches: a half-written // pair, a malformed or unauthorized object, a different digest, or no // remote floor at all once the policy is enforced. An unreachable store is // offline, as in CheckRemote. func CheckRemotePolicy(ctx context.Context, s release.Store, pub ed25519.PublicKey, bucket string, p Policy, local Verified) error { raw, _, rawErr := s.Get(ctx, Key) if rawErr != nil { if Offline(rawErr) { return nil } if p.Mode == "armed" && errors.Is(rawErr, os.ErrNotExist) { _, _, authErr := s.Get(ctx, AuthKey) if errors.Is(authErr, os.ErrNotExist) { return nil } } // A later offline read cannot erase the first definite refusal. return rawErr } auth, _, authErr := s.Get(ctx, AuthKey) if authErr != nil { if Offline(authErr) { return nil } return authErr } remote, err := Verify(pub, bucket, raw, auth) if err != nil { return err } if remote.Digest != local.Digest { return ErrFloor } return nil }