// Demo runs a minimal two-agent ECT workflow: Agent A creates a root ECT, // "sends" it to Agent B; Agent B verifies, appends to ledger, then creates // a child ECT; verification runs with DAG validation against the ledger. package main import ( "crypto/ecdsa" "fmt" "log" "time" "github.com/nennemann/ect-refimpl/go-lang/ect" ) func main() { ledger := ect.NewMemoryLedger() now := time.Now() // Agent A: spec reviewer keyA, err := ect.GenerateKey() if err != nil { log.Fatal(err) } agentA := "spiffe://example.com/agent/spec-reviewer" agentB := "spiffe://example.com/agent/implementer" kidA := "agent-a-key" // 1) Agent A creates root ECT (task id = jti per spec) payloadA := &ect.Payload{ Iss: agentA, Aud: []string{agentB}, Iat: now.Unix(), Exp: now.Add(10 * time.Minute).Unix(), Jti: "550e8400-e29b-41d4-a716-446655440001", Wid: "wf-demo-001", ExecAct: "review_requirements_spec", Par: []string{}, Pol: "spec_review_policy_v2", PolDecision: ect.PolDecisionApproved, } ectA, err := ect.Create(payloadA, keyA, ect.CreateOptions{KeyID: kidA}) if err != nil { log.Fatal(err) } fmt.Println("Agent A created root ECT (jti=550e8400-..., review_requirements_spec)") // 2) Agent B verifies (no store for DAG on first ECT) resolveKey := func(kid string) (*ecdsa.PublicKey, error) { if kid == kidA { return &keyA.PublicKey, nil } return nil, nil } opts := ect.VerifyOptions{ VerifierID: agentB, ResolveKey: resolveKey, Store: ledger, Now: now, IATMaxAge: 15 * time.Minute, IATMaxFuture: 30 * time.Second, } parsed, err := ect.Verify(ectA, opts) if err != nil { log.Fatal(err) } _, err = ledger.Append(ectA, parsed.Payload) if err != nil { log.Fatal(err) } fmt.Println("Agent B verified root ECT and appended to ledger") // 3) Agent B creates child ECT (par contains parent jti values per spec) keyB, _ := ect.GenerateKey() kidB := "agent-b-key" payloadB := &ect.Payload{ Iss: agentB, Aud: []string{"spiffe://example.com/system/ledger"}, Iat: now.Unix() + 1, Exp: now.Add(10 * time.Minute).Unix(), Jti: "550e8400-e29b-41d4-a716-446655440002", Wid: "wf-demo-001", ExecAct: "implement_module", Par: []string{"550e8400-e29b-41d4-a716-446655440001"}, Pol: "coding_standards_v3", PolDecision: ect.PolDecisionApproved, } ectB, err := ect.Create(payloadB, keyB, ect.CreateOptions{KeyID: kidB}) if err != nil { log.Fatal(err) } fmt.Println("Agent B created child ECT (jti=550e8400-...002, implement_module, par=[parent jti])") // 4) Verify child ECT with DAG (ledger has task-001) resolverB := ect.KeyResolver(func(kid string) (*ecdsa.PublicKey, error) { if kid == kidB { return &keyB.PublicKey, nil } if kid == kidA { return &keyA.PublicKey, nil } return nil, nil }) optsB := ect.VerifyOptions{ VerifierID: "spiffe://example.com/system/ledger", ResolveKey: resolverB, Store: ledger, Now: now.Add(2 * time.Second), IATMaxAge: 15 * time.Minute, IATMaxFuture: 30 * time.Second, } parsedB, err := ect.Verify(ectB, optsB) if err != nil { log.Fatal(err) } _, err = ledger.Append(ectB, parsedB.Payload) if err != nil { log.Fatal(err) } fmt.Println("Verified child ECT with DAG validation and appended to ledger") fmt.Printf("Ledger entries: %s (%s), %s (%s)\n", parsed.Payload.Jti, parsed.Payload.ExecAct, parsedB.Payload.Jti, parsedB.Payload.ExecAct) }