feat(ops_db): Ecto schemas for five Ops DB tables
Direct translation of DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2: - AccordBinding: governance context per artifact - GovernedArtifact: registry with JSONB content + content_schema - CustodyTransition: append-only chain of custody - DeploymentState: current deployment per (artifact, target) - VerificationResult: continuous attestation records - ProjectorCheckpoint: Chronicle projector resume state All JSONB fields use Ecto :map type (Postgres JSONB). GIN indexes (jsonb_path_ops) on accord_terms and content columns. Partitioning notes for time-series tables (DBA applies in prod). Migrations renumbered to enforce FK dependency order (accord_bindings → governed_artifacts → dependents). Dev seed data (priv/repo/seeds.exs) creates nine governed artifacts matching the JSONB content examples in the design doc §2.3 (Intune profile, DNS zone, DSC MOF, YANG config, Helm values, JEA role, Conditional Access, TLS cert, OCI image) plus custody transitions, deployment states, and verification results. Verified: mix ecto.create + mix ecto.migrate + mix run seeds all pass; 9 artifacts in governed_artifacts table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Tyler J King <tking@guildhouse.dev>
This commit is contained in:
parent
4e22729bef
commit
4d9acf96d8
13 changed files with 671 additions and 0 deletions
47
apps/guildhall_ops_db/lib/guildhall/ops_db/accord_binding.ex
Normal file
47
apps/guildhall_ops_db/lib/guildhall/ops_db/accord_binding.ex
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
defmodule Guildhall.OpsDb.AccordBinding do
|
||||
@moduledoc """
|
||||
Governance context under which an artifact is governed.
|
||||
Schema per DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.5.
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "accord_bindings" do
|
||||
field :accord_cid, :string
|
||||
field :accord_name, :string
|
||||
field :accord_terms, :map, default: %{}
|
||||
field :member_dids, {:array, :string}, default: []
|
||||
field :witness_dids, {:array, :string}, default: []
|
||||
field :tier_policy, :map, default: %{}
|
||||
field :ceremony_classes, {:array, :string}, default: []
|
||||
field :verification_interval_sec, :integer
|
||||
field :chronicle_retention_days, :integer
|
||||
field :effective_from, :utc_datetime_usec
|
||||
field :effective_until, :utc_datetime_usec
|
||||
|
||||
has_many :governed_artifacts, Guildhall.OpsDb.GovernedArtifact
|
||||
|
||||
timestamps(type: :utc_datetime_usec, inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
def changeset(binding, attrs) do
|
||||
binding
|
||||
|> cast(attrs, [
|
||||
:accord_cid,
|
||||
:accord_name,
|
||||
:accord_terms,
|
||||
:member_dids,
|
||||
:witness_dids,
|
||||
:tier_policy,
|
||||
:ceremony_classes,
|
||||
:verification_interval_sec,
|
||||
:chronicle_retention_days,
|
||||
:effective_from,
|
||||
:effective_until
|
||||
])
|
||||
|> validate_required([:accord_cid, :accord_name])
|
||||
|> unique_constraint(:accord_cid)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
defmodule Guildhall.OpsDb.CustodyTransition do
|
||||
@moduledoc """
|
||||
Append-only chain of custody. Each row is one custody event
|
||||
ordered by `sequence_number`.
|
||||
Schema per DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.2.
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@primary_key {:id, :id, autogenerate: true}
|
||||
schema "custody_transitions" do
|
||||
field :sequence_number, :integer
|
||||
field :from_state, :string
|
||||
field :to_state, :string
|
||||
field :actor_did, :string
|
||||
field :ceremony_id, :string
|
||||
field :witness_sigs, {:array, :map}, default: []
|
||||
field :governance_envelope_hash, :string
|
||||
field :chronicle_entry_id, :string
|
||||
field :content_diff, :map, default: %{}
|
||||
field :transitioned_at, :utc_datetime_usec
|
||||
|
||||
belongs_to :governed_artifact, Guildhall.OpsDb.GovernedArtifact
|
||||
end
|
||||
|
||||
def changeset(transition, attrs) do
|
||||
transition
|
||||
|> cast(attrs, [
|
||||
:sequence_number,
|
||||
:from_state,
|
||||
:to_state,
|
||||
:actor_did,
|
||||
:ceremony_id,
|
||||
:witness_sigs,
|
||||
:governance_envelope_hash,
|
||||
:chronicle_entry_id,
|
||||
:content_diff,
|
||||
:transitioned_at,
|
||||
:governed_artifact_id
|
||||
])
|
||||
|> validate_required([:sequence_number, :to_state, :transitioned_at, :governed_artifact_id])
|
||||
|> foreign_key_constraint(:governed_artifact_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
defmodule Guildhall.OpsDb.DeploymentState do
|
||||
@moduledoc """
|
||||
Current deployment per (artifact, target). Updated on each deployment,
|
||||
rewound on rollback.
|
||||
Schema per DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.3.
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "deployment_states" do
|
||||
field :target_kind, :string
|
||||
field :target_identifier, :string
|
||||
field :deployed_cid, :string
|
||||
field :desired_cid, :string
|
||||
field :target_config, :map, default: %{}
|
||||
field :last_deployed_at, :utc_datetime_usec
|
||||
field :last_reconciled_at, :utc_datetime_usec
|
||||
field :drift_status, :string
|
||||
|
||||
belongs_to :governed_artifact, Guildhall.OpsDb.GovernedArtifact
|
||||
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
def changeset(state, attrs) do
|
||||
state
|
||||
|> cast(attrs, [
|
||||
:target_kind,
|
||||
:target_identifier,
|
||||
:deployed_cid,
|
||||
:desired_cid,
|
||||
:target_config,
|
||||
:last_deployed_at,
|
||||
:last_reconciled_at,
|
||||
:drift_status,
|
||||
:governed_artifact_id
|
||||
])
|
||||
|> validate_required([:target_kind, :target_identifier, :governed_artifact_id])
|
||||
|> validate_inclusion(:drift_status, [nil, "match", "drift", "unknown"])
|
||||
|> unique_constraint([:governed_artifact_id, :target_kind, :target_identifier],
|
||||
name: :deployment_states_artifact_target_uniq
|
||||
)
|
||||
|> foreign_key_constraint(:governed_artifact_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
defmodule Guildhall.OpsDb.GovernedArtifact do
|
||||
@moduledoc """
|
||||
Registry of governed artifacts.
|
||||
Schema per DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.1.
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "governed_artifacts" do
|
||||
field :cid, :string
|
||||
field :artifact_type, :string
|
||||
field :artifact_name, :string
|
||||
field :artifact_runtime, :string
|
||||
field :content, :map, default: %{}
|
||||
field :content_schema, :map, default: %{}
|
||||
field :sbom_cid, :string
|
||||
field :tier, :string
|
||||
field :shell_class_required, :string
|
||||
field :chronicle_entry_id, :string
|
||||
field :human_authored, :boolean, default: true
|
||||
|
||||
belongs_to :accord_binding, Guildhall.OpsDb.AccordBinding
|
||||
|
||||
has_many :custody_transitions, Guildhall.OpsDb.CustodyTransition
|
||||
has_many :deployment_states, Guildhall.OpsDb.DeploymentState
|
||||
has_many :verification_results, Guildhall.OpsDb.VerificationResult
|
||||
|
||||
timestamps(type: :utc_datetime_usec, inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
def changeset(artifact, attrs) do
|
||||
artifact
|
||||
|> cast(attrs, [
|
||||
:cid,
|
||||
:artifact_type,
|
||||
:artifact_name,
|
||||
:artifact_runtime,
|
||||
:content,
|
||||
:content_schema,
|
||||
:sbom_cid,
|
||||
:tier,
|
||||
:shell_class_required,
|
||||
:chronicle_entry_id,
|
||||
:human_authored,
|
||||
:accord_binding_id
|
||||
])
|
||||
|> validate_required([:cid, :artifact_type, :artifact_name])
|
||||
|> unique_constraint(:cid)
|
||||
|> foreign_key_constraint(:accord_binding_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
defmodule Guildhall.OpsDb.ProjectorCheckpoint do
|
||||
@moduledoc """
|
||||
Chronicle projector resume state. See
|
||||
DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.5.
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@primary_key {:source, :string, autogenerate: false}
|
||||
schema "projector_checkpoint" do
|
||||
field :last_entry_id, :string
|
||||
field :last_processed_at, :utc_datetime_usec
|
||||
end
|
||||
|
||||
def changeset(checkpoint, attrs) do
|
||||
checkpoint
|
||||
|> cast(attrs, [:source, :last_entry_id, :last_processed_at])
|
||||
|> validate_required([:source])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
defmodule Guildhall.OpsDb.VerificationResult do
|
||||
@moduledoc """
|
||||
Continuous attestation records. High-volume time-series table;
|
||||
partitioned monthly in production.
|
||||
Schema per DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.4.
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "verification_results" do
|
||||
field :verified_cid, :string
|
||||
field :target_kind, :string
|
||||
field :target_identifier, :string
|
||||
field :layer, :string
|
||||
field :verdict, :string
|
||||
field :evidence, :map, default: %{}
|
||||
field :verified_at, :utc_datetime_usec
|
||||
field :verifier_did, :string
|
||||
field :verifier_infrastructure, :string
|
||||
|
||||
belongs_to :governed_artifact, Guildhall.OpsDb.GovernedArtifact
|
||||
end
|
||||
|
||||
def changeset(result, attrs) do
|
||||
result
|
||||
|> cast(attrs, [
|
||||
:verified_cid,
|
||||
:target_kind,
|
||||
:target_identifier,
|
||||
:layer,
|
||||
:verdict,
|
||||
:evidence,
|
||||
:verified_at,
|
||||
:verifier_did,
|
||||
:verifier_infrastructure,
|
||||
:governed_artifact_id
|
||||
])
|
||||
|> validate_required([:verified_cid, :verdict, :verified_at, :governed_artifact_id])
|
||||
|> validate_inclusion(:verdict, ["match", "drift", "error"])
|
||||
|> validate_inclusion(:layer, [nil, "layer1", "layer2", "layer3"])
|
||||
|> validate_inclusion(:verifier_infrastructure, [nil, "local", "external", "insurer"])
|
||||
|> foreign_key_constraint(:governed_artifact_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
defmodule Guildhall.OpsDb.Repo.Migrations.CreateAccordBindings do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:accord_bindings) do
|
||||
add :accord_cid, :string, null: false
|
||||
add :accord_name, :string, null: false
|
||||
add :accord_terms, :map, null: false, default: %{}
|
||||
add :member_dids, {:array, :string}, default: []
|
||||
add :witness_dids, {:array, :string}, default: []
|
||||
add :tier_policy, :map, default: %{}
|
||||
add :ceremony_classes, {:array, :string}, default: []
|
||||
add :verification_interval_sec, :integer
|
||||
add :chronicle_retention_days, :integer
|
||||
add :effective_from, :utc_datetime_usec
|
||||
add :effective_until, :utc_datetime_usec
|
||||
|
||||
timestamps(type: :utc_datetime_usec, inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
create unique_index(:accord_bindings, [:accord_cid])
|
||||
create index(:accord_bindings, [:accord_name])
|
||||
|
||||
execute(
|
||||
"CREATE INDEX accord_bindings_terms_gin_idx ON accord_bindings USING GIN (accord_terms jsonb_path_ops)",
|
||||
"DROP INDEX accord_bindings_terms_gin_idx"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
defmodule Guildhall.OpsDb.Repo.Migrations.CreateGovernedArtifacts do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:governed_artifacts) do
|
||||
add :cid, :string, null: false
|
||||
add :artifact_type, :string, null: false
|
||||
add :artifact_name, :string, null: false
|
||||
add :artifact_runtime, :string
|
||||
add :content, :map, null: false, default: %{}
|
||||
add :content_schema, :map, default: %{}
|
||||
add :sbom_cid, :string
|
||||
add :tier, :string
|
||||
add :shell_class_required, :string
|
||||
add :chronicle_entry_id, :string
|
||||
add :human_authored, :boolean, default: true
|
||||
add :accord_binding_id, references(:accord_bindings, on_delete: :restrict)
|
||||
|
||||
timestamps(type: :utc_datetime_usec, inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
create unique_index(:governed_artifacts, [:cid])
|
||||
create index(:governed_artifacts, [:artifact_type])
|
||||
create index(:governed_artifacts, [:accord_binding_id])
|
||||
create index(:governed_artifacts, [:tier])
|
||||
|
||||
execute(
|
||||
"CREATE INDEX governed_artifacts_content_gin_idx ON governed_artifacts USING GIN (content jsonb_path_ops)",
|
||||
"DROP INDEX governed_artifacts_content_gin_idx"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
defmodule Guildhall.OpsDb.Repo.Migrations.CreateCustodyTransitions do
|
||||
@moduledoc """
|
||||
Append-only chain of custody. Partitioning note: production deployments
|
||||
should apply RANGE partitioning on `transitioned_at` (quarterly) per
|
||||
DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.2. The DBA applies partition
|
||||
DDL after migration; Ecto does not manage partitions.
|
||||
"""
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:custody_transitions) do
|
||||
add :sequence_number, :bigint, null: false
|
||||
add :from_state, :string
|
||||
add :to_state, :string, null: false
|
||||
add :actor_did, :string
|
||||
add :ceremony_id, :string
|
||||
add :witness_sigs, {:array, :map}, default: []
|
||||
add :governance_envelope_hash, :string
|
||||
add :chronicle_entry_id, :string
|
||||
add :content_diff, :map, default: %{}
|
||||
add :transitioned_at, :utc_datetime_usec, null: false
|
||||
|
||||
add :governed_artifact_id,
|
||||
references(:governed_artifacts, on_delete: :restrict),
|
||||
null: false
|
||||
end
|
||||
|
||||
create index(:custody_transitions, [:governed_artifact_id, :sequence_number])
|
||||
create index(:custody_transitions, [:ceremony_id])
|
||||
create index(:custody_transitions, [:actor_did])
|
||||
create index(:custody_transitions, [:transitioned_at])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
defmodule Guildhall.OpsDb.Repo.Migrations.CreateDeploymentStates do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:deployment_states) do
|
||||
add :target_kind, :string, null: false
|
||||
add :target_identifier, :string, null: false
|
||||
add :deployed_cid, :string
|
||||
add :desired_cid, :string
|
||||
add :target_config, :map, default: %{}
|
||||
add :last_deployed_at, :utc_datetime_usec
|
||||
add :last_reconciled_at, :utc_datetime_usec
|
||||
add :drift_status, :string
|
||||
|
||||
add :governed_artifact_id,
|
||||
references(:governed_artifacts, on_delete: :restrict),
|
||||
null: false
|
||||
|
||||
timestamps(type: :utc_datetime_usec)
|
||||
end
|
||||
|
||||
create unique_index(
|
||||
:deployment_states,
|
||||
[:governed_artifact_id, :target_kind, :target_identifier],
|
||||
name: :deployment_states_artifact_target_uniq
|
||||
)
|
||||
|
||||
create index(:deployment_states, [:drift_status])
|
||||
create index(:deployment_states, [:target_kind])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
defmodule Guildhall.OpsDb.Repo.Migrations.CreateVerificationResults do
|
||||
@moduledoc """
|
||||
Continuous attestation records. Partitioning note: production deployments
|
||||
should apply RANGE partitioning on `verified_at` (monthly) per
|
||||
DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.2.4. Ecto does not manage partitions.
|
||||
"""
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:verification_results) do
|
||||
add :verified_cid, :string, null: false
|
||||
add :target_kind, :string
|
||||
add :target_identifier, :string
|
||||
add :layer, :string
|
||||
add :verdict, :string, null: false
|
||||
add :evidence, :map, default: %{}
|
||||
add :verified_at, :utc_datetime_usec, null: false
|
||||
add :verifier_did, :string
|
||||
add :verifier_infrastructure, :string
|
||||
|
||||
add :governed_artifact_id,
|
||||
references(:governed_artifacts, on_delete: :restrict),
|
||||
null: false
|
||||
end
|
||||
|
||||
create index(:verification_results, [:governed_artifact_id, :verified_at])
|
||||
create index(:verification_results, [:verdict, :verified_at])
|
||||
create index(:verification_results, [:verifier_infrastructure])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Guildhall.OpsDb.Repo.Migrations.CreateProjectorCheckpoint do
|
||||
@moduledoc """
|
||||
Chronicle projector resume state. See §2.5 of
|
||||
DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 — the projector persists its
|
||||
last processed entry_id so it can resume from the correct position
|
||||
after a restart.
|
||||
"""
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:projector_checkpoint, primary_key: false) do
|
||||
add :source, :string, primary_key: true
|
||||
add :last_entry_id, :string
|
||||
add :last_processed_at, :utc_datetime_usec
|
||||
end
|
||||
end
|
||||
end
|
||||
238
apps/guildhall_ops_db/priv/repo/seeds.exs
Normal file
238
apps/guildhall_ops_db/priv/repo/seeds.exs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# Dev seed data for guildhall_ops_db.
|
||||
# Matches the nine JSONB content examples in
|
||||
# DESIGN-OPS-DB-CHAIN-OF-CUSTODY-0001 §2.3.
|
||||
#
|
||||
# Run with: mix run apps/guildhall_ops_db/priv/repo/seeds.exs
|
||||
|
||||
alias Guildhall.OpsDb.{
|
||||
Repo,
|
||||
AccordBinding,
|
||||
GovernedArtifact,
|
||||
CustodyTransition,
|
||||
DeploymentState,
|
||||
VerificationResult
|
||||
}
|
||||
|
||||
now = DateTime.utc_now()
|
||||
|
||||
# ────────────────────────────────────────────────────────
|
||||
# 1. Accord binding (shared by all artifacts below)
|
||||
# ────────────────────────────────────────────────────────
|
||||
accord =
|
||||
Repo.insert!(%AccordBinding{
|
||||
accord_cid: "sha256:dev-accord-example",
|
||||
accord_name: "acme-saas-dev",
|
||||
member_dids: ["did:web:guildhouse.dev:user:tking"],
|
||||
witness_dids: ["did:web:guildhouse.dev:user:tking"],
|
||||
ceremony_classes: ["SingleApproval"],
|
||||
verification_interval_sec: 300,
|
||||
chronicle_retention_days: 90,
|
||||
effective_from: now,
|
||||
accord_terms: %{
|
||||
"name" => "Development Accord",
|
||||
"tier_policy" => %{"default" => "a"}
|
||||
}
|
||||
})
|
||||
|
||||
# ────────────────────────────────────────────────────────
|
||||
# 2. Governed artifacts — nine examples from §2.3
|
||||
# ────────────────────────────────────────────────────────
|
||||
artifacts = [
|
||||
%{
|
||||
cid: "sha256:intune-profile-example",
|
||||
artifact_type: "intune_config_profile",
|
||||
artifact_name: "Production Baseline",
|
||||
artifact_runtime: "intune",
|
||||
tier: "b",
|
||||
content: %{
|
||||
"@odata.type" => "#microsoft.graph.windows10GeneralConfiguration",
|
||||
"displayName" => "Production Baseline",
|
||||
"bitLockerEncryptDevice" => true,
|
||||
"defenderRequireRealTimeMonitoring" => true,
|
||||
"passwordMinimumLength" => 12,
|
||||
"assignments" => [%{"groupId" => "corp-devices"}]
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:dns-zone-example",
|
||||
artifact_type: "dns_zone",
|
||||
artifact_name: "guildhouse.dev zone",
|
||||
artifact_runtime: "zone",
|
||||
tier: "c",
|
||||
content: %{
|
||||
"zone_apex" => "guildhouse.dev.",
|
||||
"serial" => 2_026_041_701,
|
||||
"records" => [
|
||||
%{"name" => "@", "type" => "SOA", "rdata" => "ns1.guildhouse.dev. ..."},
|
||||
%{"name" => "@", "type" => "NS", "rdata" => "ns1.guildhouse.dev."},
|
||||
%{"name" => "auth", "type" => "A", "rdata" => "10.0.0.10"}
|
||||
]
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:dsc-bitlocker-example",
|
||||
artifact_type: "dsc_configuration",
|
||||
artifact_name: "EnsureBitLockerEnabled",
|
||||
artifact_runtime: "mof",
|
||||
tier: "b",
|
||||
content: %{
|
||||
"instances" => [
|
||||
%{
|
||||
"class" => "MSFT_BitLockerResource",
|
||||
"name" => "EnsureBitLockerEnabled",
|
||||
"properties" => %{
|
||||
"MountPoint" => "C:",
|
||||
"PrimaryProtector" => "TpmProtector",
|
||||
"AutoUnlock" => true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:yang-running-config-example",
|
||||
artifact_type: "restconf_config",
|
||||
artifact_name: "edge-router-1 interfaces",
|
||||
artifact_runtime: "json",
|
||||
tier: "c",
|
||||
content: %{
|
||||
"ietf-interfaces:interfaces" => %{
|
||||
"interface" => [
|
||||
%{
|
||||
"name" => "GigabitEthernet0/0",
|
||||
"type" => "iana-if-type:ethernetCsmacd",
|
||||
"enabled" => true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:helm-values-example",
|
||||
artifact_type: "helm_values",
|
||||
artifact_name: "substrate-operator production values",
|
||||
artifact_runtime: "values",
|
||||
tier: "b",
|
||||
content: %{
|
||||
"bound_chart_cid" => "sha256:abc...",
|
||||
"environment" => "production",
|
||||
"image" => %{"digest" => "sha256:def..."},
|
||||
"replicas" => 3
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:jea-application-role-example",
|
||||
artifact_type: "jea_role_capability",
|
||||
artifact_name: "Application-class JEA role",
|
||||
artifact_runtime: "psrc",
|
||||
tier: "c",
|
||||
content: %{
|
||||
"visible_cmdlets" => ["Get-Service", "Get-Process", "Test-DscConfiguration"],
|
||||
"visible_providers" => ["FileSystem"],
|
||||
"language_mode" => "ConstrainedLanguage",
|
||||
"session_type" => "RestrictedRemoteServer"
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:conditional-access-example",
|
||||
artifact_type: "entra_conditional_access",
|
||||
artifact_name: "Require compliant device",
|
||||
artifact_runtime: "json",
|
||||
tier: "c",
|
||||
content: %{
|
||||
"displayName" => "Require compliant device for corp resources",
|
||||
"conditions" => %{"users" => %{"includeGroups" => ["all-employees"]}},
|
||||
"grantControls" => %{"builtInControls" => ["compliantDevice"]}
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:tls-keycloak-cert-example",
|
||||
artifact_type: "tls_server_cert",
|
||||
artifact_name: "keycloak.guildhouse.dev",
|
||||
artifact_runtime: "x509",
|
||||
tier: "b",
|
||||
content: %{
|
||||
"subject" => "CN=keycloak.guildhouse.dev",
|
||||
"not_before" => "2026-04-17T00:00:00Z",
|
||||
"not_after" => "2027-04-17T00:00:00Z",
|
||||
"fingerprint_sha256" => "abc...",
|
||||
"ct_log_refs" => []
|
||||
}
|
||||
},
|
||||
%{
|
||||
cid: "sha256:oci-image-example",
|
||||
artifact_type: "oci_image",
|
||||
artifact_name: "ghcr.io/guildhouse/substrate-operator",
|
||||
artifact_runtime: "oci",
|
||||
tier: "b",
|
||||
content: %{
|
||||
"image_reference" => "ghcr.io/guildhouse/substrate-operator",
|
||||
"digest" => "sha256:a1b2c3...",
|
||||
"architectures" => ["linux/amd64", "linux/arm64"],
|
||||
"sbom_cid" => "sha256:..."
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
inserted =
|
||||
Enum.map(artifacts, fn attrs ->
|
||||
%GovernedArtifact{accord_binding_id: accord.id, human_authored: true}
|
||||
|> GovernedArtifact.changeset(attrs)
|
||||
|> Repo.insert!()
|
||||
end)
|
||||
|
||||
# ────────────────────────────────────────────────────────
|
||||
# 3. One custody transition per artifact (proposed → ceremony_open)
|
||||
# ────────────────────────────────────────────────────────
|
||||
Enum.each(inserted, fn artifact ->
|
||||
Repo.insert!(%CustodyTransition{
|
||||
governed_artifact_id: artifact.id,
|
||||
sequence_number: 1,
|
||||
from_state: "proposed",
|
||||
to_state: "ceremony_open",
|
||||
actor_did: "did:web:guildhouse.dev:user:tking",
|
||||
ceremony_id: "ceremony-dev-#{artifact.id}",
|
||||
witness_sigs: [],
|
||||
transitioned_at: now
|
||||
})
|
||||
end)
|
||||
|
||||
# ────────────────────────────────────────────────────────
|
||||
# 4. Mix of deployment_states (healthy / drifted)
|
||||
# ────────────────────────────────────────────────────────
|
||||
inserted
|
||||
|> Enum.with_index()
|
||||
|> Enum.each(fn {artifact, idx} ->
|
||||
drift = if rem(idx, 3) == 0, do: "drift", else: "match"
|
||||
|
||||
Repo.insert!(%DeploymentState{
|
||||
governed_artifact_id: artifact.id,
|
||||
target_kind: "dev-cluster",
|
||||
target_identifier: "hetzner-talos",
|
||||
deployed_cid: artifact.cid,
|
||||
desired_cid: artifact.cid,
|
||||
last_deployed_at: now,
|
||||
last_reconciled_at: now,
|
||||
drift_status: drift
|
||||
})
|
||||
end)
|
||||
|
||||
# ────────────────────────────────────────────────────────
|
||||
# 5. Recent verification results
|
||||
# ────────────────────────────────────────────────────────
|
||||
Enum.each(inserted, fn artifact ->
|
||||
Repo.insert!(%VerificationResult{
|
||||
governed_artifact_id: artifact.id,
|
||||
verified_cid: artifact.cid,
|
||||
target_kind: "dev-cluster",
|
||||
target_identifier: "hetzner-talos",
|
||||
layer: "layer1",
|
||||
verdict: "match",
|
||||
verified_at: now,
|
||||
verifier_did: "did:web:guildhouse.dev:verifier:local",
|
||||
verifier_infrastructure: "local",
|
||||
evidence: %{"method" => "seed_data"}
|
||||
})
|
||||
end)
|
||||
|
||||
IO.puts("Seeded #{length(inserted)} governed artifacts under Accord #{accord.accord_name}.")
|
||||
Loading…
Reference in a new issue