package domain import "time" type SCUMDataCapability string const ( SCUMDataCapabilitySchemaProbe SCUMDataCapability = "schema-probe" SCUMDataCapabilityPlayerRead SCUMDataCapability = "players.read" SCUMDataCapabilityPlayerDetailRead SCUMDataCapability = "player-details.read" SCUMDataCapabilitySquadRead SCUMDataCapability = "squads.read" SCUMDataCapabilitySquadMemberRead SCUMDataCapability = "squad-members.read" SCUMDataCapabilityVehicleRead SCUMDataCapability = "vehicles.read" SCUMDataCapabilityFlagRead SCUMDataCapability = "flags.read" SCUMDataCapabilityPositionRead SCUMDataCapability = "positions.read" SCUMDataCapabilityProfileXMLWrite SCUMDataCapability = "profile-xml.write" SCUMDataCapabilityEconomyCommand SCUMDataCapability = "economy-command.write" SCUMDataCapabilityGiftCommand SCUMDataCapability = "gift-command.write" ) type SCUMCapabilityEvidenceStatus string const ( SCUMSchemaProbeStatusSucceeded SCUMCapabilityEvidenceStatus = "succeeded" SCUMCapabilityEvidenceMissing SCUMCapabilityEvidenceStatus = "missing" SCUMCapabilityEvidenceCompatible SCUMCapabilityEvidenceStatus = "compatible" SCUMCapabilityEvidenceIncompatible SCUMCapabilityEvidenceStatus = "incompatible" SCUMCapabilityEvidenceFailed SCUMCapabilityEvidenceStatus = "failed" ) type SCUMCapabilityGateState string const ( SCUMCapabilityGateEnabled SCUMCapabilityGateState = "enabled" SCUMCapabilityGateDisabled SCUMCapabilityGateState = "disabled" ) type SCUMSafeErrorCode string const ( SCUMSafeErrorNone SCUMSafeErrorCode = "none" SCUMSafeErrorProbeExecutorAbsent SCUMSafeErrorCode = "probe_executor_absent" SCUMSafeErrorProbeMissing SCUMSafeErrorCode = "probe_missing" SCUMSafeErrorProbeFailed SCUMSafeErrorCode = "probe_failed" SCUMSafeErrorSchemaIncompatible SCUMSafeErrorCode = "schema_incompatible" SCUMSafeErrorBindingMismatch SCUMSafeErrorCode = "binding_mismatch" SCUMSafeErrorAdapterMismatch SCUMSafeErrorCode = "adapter_mismatch" SCUMSafeErrorFingerprintMismatch SCUMSafeErrorCode = "fingerprint_mismatch" SCUMSafeErrorDigestMismatch SCUMSafeErrorCode = "digest_mismatch" SCUMSafeErrorEvidenceExpired SCUMSafeErrorCode = "evidence_expired" SCUMSafeErrorInvalidProbePayload SCUMSafeErrorCode = "invalid_probe_payload" SCUMSafeErrorInvalidRequest SCUMSafeErrorCode = "invalid_request" SCUMSafeErrorTargetUnavailable SCUMSafeErrorCode = "target_unavailable" SCUMSafeErrorSourceUnavailable SCUMSafeErrorCode = "source_unavailable" SCUMSafeErrorSQLiteOpenFailed SCUMSafeErrorCode = "sqlite_open_failed" SCUMSafeErrorSQLiteReadFailed SCUMSafeErrorCode = "sqlite_read_failed" SCUMSafeErrorDatabaseBusy SCUMSafeErrorCode = "database_busy" SCUMSafeErrorTimeout SCUMSafeErrorCode = "timeout" SCUMSafeErrorCancelled SCUMSafeErrorCode = "cancelled" SCUMSafeErrorSourceChanged SCUMSafeErrorCode = "source_changed" SCUMSafeErrorResultLimitExceeded SCUMSafeErrorCode = "result_limit_exceeded" ) type SCUMSafeError struct { Code SCUMSafeErrorCode Message string Retryable bool } type SCUMBindingIdentity struct { ServerInstanceID string RunBindingID string RunEndpointID string PluginID string PluginVersion string AdapterVersion string GameVersion string DatabaseIdentity string } type SCUMSchemaProbeBounds struct { MaxObjects int MaxColumnsPerObject int MaxIndexesPerObject int MaxForeignKeys int MaxCardinalityReads int MaxSampleRows int TimeoutMS int MaxResultBytes int } func DefaultSCUMSchemaProbeBounds() SCUMSchemaProbeBounds { return SCUMSchemaProbeBounds{MaxObjects: 256, MaxColumnsPerObject: 128, MaxIndexesPerObject: 64, MaxForeignKeys: 64, MaxCardinalityReads: 64, MaxSampleRows: 3, TimeoutMS: 5000, MaxResultBytes: 512 * 1024} } type SCUMSchemaProbeRequest struct { RequestID string JobID string Binding SCUMBindingIdentity Bounds SCUMSchemaProbeBounds RequestedAt time.Time } type SCUMSchemaProbeDeclaration struct { Capability string TargetKey string Bounds SCUMSchemaProbeBounds } type SCUMLiveDataCapabilityGateDeclaration struct { Capability SCUMDataCapability Gate SCUMCapabilityGateState AdapterVersion string RequiredSchemaFingerprint string RequiredAssetDigests []string EvidenceStatus SCUMCapabilityEvidenceStatus SafeReason string } type SCUMLiveDataManifest struct { SchemaVersion string Probe SCUMSchemaProbeDeclaration CapabilityGates []SCUMLiveDataCapabilityGateDeclaration } type SCUMSchemaColumnEvidence struct { NameFingerprint string DeclaredType string Nullable *bool PrimaryKey bool Ordinal int } type SCUMSchemaIndexEvidence struct { NameFingerprint string Unique bool ColumnHashes []string } type SCUMSchemaForeignKeyEvidence struct { FromColumnHash string ToObjectHash string ToColumnHash string } type SCUMSchemaObjectEvidence struct { ObjectHash string Kind string NameFingerprint string DeclaredColumns []SCUMSchemaColumnEvidence Indexes []SCUMSchemaIndexEvidence ForeignKeys []SCUMSchemaForeignKeyEvidence ApproximateRows *int64 SampleFingerprints []string } type SCUMSchemaProbeResult struct { RequestID string JobID string Binding SCUMBindingIdentity Status SCUMCapabilityEvidenceStatus SourceFingerprint string SchemaFingerprint string ObservedAt time.Time ResultDigest string Objects []SCUMSchemaObjectEvidence SafeError SCUMSafeError Limits SCUMSchemaProbeBounds } type SCUMCapabilityRequirement struct { Capability SCUMDataCapability AdapterVersion string SchemaFingerprint string AssetDigests []string } type SCUMCapabilityEvidence struct { Capability SCUMDataCapability Status SCUMCapabilityEvidenceStatus Binding SCUMBindingIdentity AdapterVersion string SchemaFingerprint string ProbeResultDigest string AssetDigests []string ObservedAt time.Time ExpiresAt time.Time SafeError SCUMSafeError } type SCUMCapabilityGate struct { Capability SCUMDataCapability State SCUMCapabilityGateState Enabled bool ReasonCode SCUMSafeErrorCode Reason string Evidence SCUMCapabilityEvidence } func EvaluateSCUMCapabilityGate(requirement SCUMCapabilityRequirement, evidence SCUMCapabilityEvidence, active SCUMBindingIdentity, probeExecutorAvailable bool, now time.Time) SCUMCapabilityGate { gate := SCUMCapabilityGate{Capability: requirement.Capability, State: SCUMCapabilityGateDisabled, ReasonCode: SCUMSafeErrorProbeMissing, Reason: "current-service evidence is required before this SCUM capability can run"} if !probeExecutorAvailable { gate.ReasonCode = SCUMSafeErrorProbeExecutorAbsent gate.Reason = "bound Run does not expose the generic SQLite schema-probe executor" return gate } if evidence.Status == SCUMCapabilityEvidenceMissing || evidence.Capability == "" { return gate } gate.Evidence = CopySCUMCapabilityEvidence(evidence) if evidence.Status == SCUMCapabilityEvidenceFailed { gate.ReasonCode = SCUMSafeErrorProbeFailed gate.Reason = safeReason(evidence.SafeError.Message, "last schema probe failed") return gate } if evidence.Status == SCUMCapabilityEvidenceIncompatible { gate.ReasonCode = SCUMSafeErrorSchemaIncompatible gate.Reason = safeReason(evidence.SafeError.Message, "current schema is incompatible with the plugin adapter") return gate } if evidence.Capability != requirement.Capability { gate.ReasonCode = SCUMSafeErrorSchemaIncompatible gate.Reason = "capability evidence does not match the requested SCUM capability" return gate } if !sameSCUMBinding(evidence.Binding, active) { gate.ReasonCode = SCUMSafeErrorBindingMismatch gate.Reason = "evidence belongs to a different server, Run binding, plugin, adapter, game, or database identity" return gate } if evidence.AdapterVersion != requirement.AdapterVersion { gate.ReasonCode = SCUMSafeErrorAdapterMismatch gate.Reason = "evidence adapter version does not match the plugin requirement" return gate } if evidence.SchemaFingerprint == "" || evidence.SchemaFingerprint != requirement.SchemaFingerprint { gate.ReasonCode = SCUMSafeErrorFingerprintMismatch gate.Reason = "schema fingerprint does not match the plugin requirement" return gate } if !containsAllStrings(evidence.AssetDigests, requirement.AssetDigests) { gate.ReasonCode = SCUMSafeErrorDigestMismatch gate.Reason = "packaged asset digest does not match the compatible evidence" return gate } if !evidence.ExpiresAt.IsZero() && !now.IsZero() && !now.Before(evidence.ExpiresAt) { gate.ReasonCode = SCUMSafeErrorEvidenceExpired gate.Reason = "current-service evidence has expired and must be probed again" return gate } gate.State = SCUMCapabilityGateEnabled gate.Enabled = true gate.ReasonCode = SCUMSafeErrorNone gate.Reason = "current-service evidence matches the versioned plugin adapter" return gate } func CopySCUMCapabilityEvidence(value SCUMCapabilityEvidence) SCUMCapabilityEvidence { value.AssetDigests = append([]string(nil), value.AssetDigests...) return value } func CopySCUMSchemaProbeResult(value SCUMSchemaProbeResult) SCUMSchemaProbeResult { value.Objects = append([]SCUMSchemaObjectEvidence(nil), value.Objects...) for index := range value.Objects { value.Objects[index].DeclaredColumns = append([]SCUMSchemaColumnEvidence(nil), value.Objects[index].DeclaredColumns...) value.Objects[index].Indexes = append([]SCUMSchemaIndexEvidence(nil), value.Objects[index].Indexes...) value.Objects[index].ForeignKeys = append([]SCUMSchemaForeignKeyEvidence(nil), value.Objects[index].ForeignKeys...) value.Objects[index].SampleFingerprints = append([]string(nil), value.Objects[index].SampleFingerprints...) for idx := range value.Objects[index].Indexes { value.Objects[index].Indexes[idx].ColumnHashes = append([]string(nil), value.Objects[index].Indexes[idx].ColumnHashes...) } } return value } func CopySCUMSchemaProbeRequestPtr(value *SCUMSchemaProbeRequest) *SCUMSchemaProbeRequest { if value == nil { return nil } copy := *value return © } func CopySCUMSchemaProbeResultPtr(value *SCUMSchemaProbeResult) *SCUMSchemaProbeResult { if value == nil { return nil } copy := CopySCUMSchemaProbeResult(*value) return © } func CopySCUMLiveDataManifest(value SCUMLiveDataManifest) SCUMLiveDataManifest { value.CapabilityGates = append([]SCUMLiveDataCapabilityGateDeclaration(nil), value.CapabilityGates...) for index := range value.CapabilityGates { value.CapabilityGates[index].RequiredAssetDigests = append([]string(nil), value.CapabilityGates[index].RequiredAssetDigests...) } return value } func sameSCUMBinding(a SCUMBindingIdentity, b SCUMBindingIdentity) bool { return a.ServerInstanceID == b.ServerInstanceID && a.RunBindingID == b.RunBindingID && a.RunEndpointID == b.RunEndpointID && a.PluginID == b.PluginID && a.PluginVersion == b.PluginVersion && a.AdapterVersion == b.AdapterVersion && a.GameVersion == b.GameVersion && a.DatabaseIdentity == b.DatabaseIdentity } func containsAllStrings(values []string, required []string) bool { set := map[string]struct{}{} for _, value := range values { set[value] = struct{}{} } for _, value := range required { if _, ok := set[value]; !ok { return false } } return true } func safeReason(value string, fallback string) string { if value == "" { return fallback } return value }