package quartermaster import ( "encoding/json" "time" ) // NetworkMutationArtifact is recorded when Kedge (via Bascule SDK dispatch) // mutates physical infrastructure (underlay mode). type NetworkMutationArtifact struct { MutationID string `json:"mutation_id"` SATHash []byte `json:"sat_hash"` SessionID string `json:"session_id"` // Links to SessionTransitArtifact Device string `json:"device"` // e.g., "fortigate.transit.local" DeviceType string `json:"device_type"` // e.g., "fortios" Operation string `json:"operation"` // e.g., "vlan_create", "zone_policy_set" YANGSourceHash []byte `json:"yang_source_hash"` // Hash of YANG instance data ConfigBeforeHash []byte `json:"config_before_hash"` // Device config before mutation ConfigAfterHash []byte `json:"config_after_hash"` // Device config after mutation SDKMethod string `json:"sdk_method"` // e.g., "fortiosapi.set('firewall','policy',...)" Timestamp time.Time `json:"timestamp"` } // ArtifactID returns the unique identifier for this artifact. func (a *NetworkMutationArtifact) ArtifactID() string { return a.MutationID } // RegistryType returns the registry type discriminator. func (a *NetworkMutationArtifact) RegistryType() string { return "network-mutation" } // CanonicalBytes returns the RFC 8785 (JCS) canonical JSON serialization // for deterministic hashing and merkle anchoring. func (a *NetworkMutationArtifact) CanonicalBytes() ([]byte, error) { canonical := map[string]any{ "mutation_id": a.MutationID, "sat_hash": a.SATHash, "session_id": a.SessionID, "device": a.Device, "device_type": a.DeviceType, "operation": a.Operation, "yang_source_hash": a.YANGSourceHash, "config_before_hash": a.ConfigBeforeHash, "config_after_hash": a.ConfigAfterHash, "sdk_method": a.SDKMethod, "timestamp": a.Timestamp.UTC().Format(time.RFC3339Nano), } // TODO: Use a proper JCS (RFC 8785) library for canonical serialization. return json.Marshal(canonical) }