{
  "version": 2,
  "site": "https://ruslanakchurin.dev",
  "author": {
    "id": "https://ruslanakchurin.dev/#person",
    "name": "Ruslan Akchurin",
    "url": "https://ruslanakchurin.dev/about"
  },
  "routes": [
    {
      "id": "https://ruslanakchurin.dev/",
      "url": "https://ruslanakchurin.dev/",
      "title": "Ruslan Akchurin — Senior Platform Engineer in Sydney",
      "description": "Senior platform engineer in Sydney. I make risky infrastructure boring through clear ownership and safer production changes.",
      "type": "home",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/index.md"
      }
    },
    {
      "id": "https://ruslanakchurin.dev/about",
      "url": "https://ruslanakchurin.dev/about",
      "title": "About Ruslan Akchurin — Senior Platform Engineer in Sydney",
      "description": "I’m a senior platform engineer in Sydney. I build cloud platforms that let teams work independently and keep ownership clear.",
      "type": "about",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/about.md"
      }
    },
    {
      "id": "https://ruslanakchurin.dev/blog",
      "url": "https://ruslanakchurin.dev/blog",
      "title": "Writing · Ruslan Akchurin",
      "description": "Infrastructure decisions that are easy to hand-wave and hard to live with later",
      "type": "blog-index",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog.md"
      },
      "seriesIds": [
        "https://ruslanakchurin.dev/blog/making-iac-boring#series",
        "https://ruslanakchurin.dev/blog/where-secrets-go#series"
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring#series",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring",
      "title": "Making IaC boring · Ruslan Akchurin",
      "description": "An IaC codebase takes shape before anyone names the architecture: the first migration, the account layout, the output path someone reaches for, the shared project that feels convenient. This series names those choices early enough for ownership, dependency direction, and tier membership to be reviewed before they become migration work.",
      "type": "series-hub",
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring.md"
      },
      "partIds": [
        "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
        "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
        "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
        "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
        "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
        "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
        "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b"
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/blog/where-secrets-go#series",
      "url": "https://ruslanakchurin.dev/blog/where-secrets-go",
      "title": "Where secrets go · Ruslan Akchurin",
      "description": "A store gives a secret a place to rest, but the value itself still has to arrive: enter under control, reach the runtime, build, deployment, and local-development surfaces, and leave evidence that live state still matches what was declared. This series names that route as one delivery transaction, reviewable before it scatters across console clicks, scripts, and build credentials nobody wants to call the authority.",
      "type": "series-hub",
      "series": "where-secrets-go",
      "seriesName": "Where secrets go",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/where-secrets-go.md"
      },
      "partIds": [
        "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
        "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
        "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor"
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
      "title": "Start with the shape",
      "description": "By the time anyone reviews an IaC codebase, its shape is already decided.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape.md"
      },
      "datePublished": "2026-05-17",
      "dateModified": "2026-06-25",
      "tags": [
        "infrastructure as code",
        "software architecture",
        "separation of concerns",
        "ownership boundaries",
        "tiering"
      ],
      "about": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        },
        {
          "name": "Separation of concerns",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q2465506"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-ownership-boundaries-490a9af3",
        "https://ruslanakchurin.dev/manifest.json#topic-software-architecture-5e9136a9",
        "https://ruslanakchurin.dev/manifest.json#topic-tiering-407bd3bb",
        "https://www.wikidata.org/wiki/Q2465506",
        "https://www.wikidata.org/wiki/Q24964334"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "title": "A library is not an architecture",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-ownership-boundaries-490a9af3",
            "https://ruslanakchurin.dev/manifest.json#topic-software-architecture-5e9136a9",
            "https://www.wikidata.org/wiki/Q2465506",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape#the-two-cuts-that-matter",
          "fragment": "the-two-cuts-that-matter",
          "heading": "The two cuts that matter",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape#contracts-make-cuts-durable",
          "fragment": "contracts-make-cuts-durable",
          "heading": "Contracts make cuts durable",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape#a-default-three-tier-cut",
          "fragment": "a-default-three-tier-cut",
          "heading": "A default three-tier cut",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape#dns-tests-the-boundary",
          "fragment": "dns-tests-the-boundary",
          "heading": "DNS tests the boundary",
          "level": 3
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring",
      "position": 1
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
      "title": "Resolve by contract",
      "description": "Cross-stack dependencies make another stack's outputs a public interface. A typed contract on a producer namespace and key is the alternative.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract.md"
      },
      "datePublished": "2026-05-19",
      "dateModified": "2026-06-25",
      "tags": [
        "infrastructure as code",
        "cross-stack dependencies",
        "typed contracts",
        "interfaces",
        "coupling"
      ],
      "about": [
        {
          "name": "Coupling",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q253448"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-cross-stack-dependencies-f8f5e215",
        "https://ruslanakchurin.dev/manifest.json#topic-interfaces-0274021b",
        "https://ruslanakchurin.dev/manifest.json#topic-typed-contracts-240d5fc9",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q253448"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "title": "A library is not an architecture",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q253448"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-typed-contracts-240d5fc9",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract#address-context-source",
          "fragment": "address-context-source",
          "heading": "Address, context, source",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract#dependency-by-namespace-and-key",
          "fragment": "dependency-by-namespace-and-key",
          "heading": "Dependency by namespace and key",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract#resolution-needs-type",
          "fragment": "resolution-needs-type",
          "heading": "Resolution needs type",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract#failure-is-part-of-the-contract",
          "fragment": "failure-is-part-of-the-contract",
          "heading": "Failure is part of the contract",
          "level": 2
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring",
      "position": 2
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
      "title": "Define tier membership",
      "description": "Environment boundaries blur when workload-specific members (service accounts, IAM bindings, DNS records) accumulate in the shared environment tier.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership.md"
      },
      "datePublished": "2026-05-24",
      "dateModified": "2026-06-25",
      "tags": [
        "infrastructure as code",
        "identity and access management",
        "google cloud",
        "multi-tenancy",
        "service accounts"
      ],
      "about": [
        {
          "name": "Identity management",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q977772"
        }
      ],
      "mentions": [
        {
          "name": "Google Cloud Platform",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q17054505"
        },
        {
          "name": "Amazon Web Services",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q456157"
        },
        {
          "name": "Domain Name System",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q8767"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-google-cloud-d9b1d781",
        "https://ruslanakchurin.dev/manifest.json#topic-identity-and-access-management-c3eb02b3",
        "https://ruslanakchurin.dev/manifest.json#topic-multi-tenancy-1277c69e",
        "https://ruslanakchurin.dev/manifest.json#topic-service-accounts-7c401348",
        "https://www.wikidata.org/wiki/Q17054505",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q456157",
        "https://www.wikidata.org/wiki/Q8767",
        "https://www.wikidata.org/wiki/Q977772"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-identity-and-access-management-c3eb02b3",
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q456157",
            "https://www.wikidata.org/wiki/Q8767"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "title": "A library is not an architecture",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q977772"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "title": "Fail before apply",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q8767"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "title": "Re-cut the system",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership#members-are-lifecycle-units",
          "fragment": "members-are-lifecycle-units",
          "heading": "Members are lifecycle units",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership#population-tests",
          "fragment": "population-tests",
          "heading": "Population tests",
          "level": 2
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring",
      "position": 3
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
      "title": "Fail before apply",
      "description": "An IaC contract holds only if the release path enforces it: a resolver checks type and context, then refuses invalid composition before apply.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply.md"
      },
      "datePublished": "2026-06-07",
      "dateModified": "2026-06-25",
      "tags": [
        "infrastructure as code",
        "pulumi",
        "release pipeline",
        "validation",
        "fail-fast"
      ],
      "about": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "mentions": [
        {
          "name": "Pulumi",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q138634162"
        },
        {
          "name": "Terraform",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q28957072"
        },
        {
          "name": "Domain Name System",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q8767"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-fail-fast-d69f49e0",
        "https://ruslanakchurin.dev/manifest.json#topic-release-pipeline-3384c7c0",
        "https://ruslanakchurin.dev/manifest.json#topic-validation-98c41dcd",
        "https://www.wikidata.org/wiki/Q138634162",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q28957072",
        "https://www.wikidata.org/wiki/Q8767"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "title": "Re-cut the system",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q138634162",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q28957072"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q28957072",
            "https://www.wikidata.org/wiki/Q8767"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "title": "A library is not an architecture",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q138634162",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply#where-the-resolver-runs",
          "fragment": "where-the-resolver-runs",
          "heading": "Where the resolver runs",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply#what-the-gate-refuses",
          "fragment": "what-the-gate-refuses",
          "heading": "What the gate refuses",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply#shared-surfaces-need-binding-rules",
          "fragment": "shared-surfaces-need-binding-rules",
          "heading": "Shared surfaces need binding rules",
          "level": 2
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring",
      "position": 4
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
      "title": "Re-cut the system",
      "description": "Refactoring a live IaC system toward a new architecture: freeze the old boundary, publish the new contract beside it, delete only after consumers move.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system.md"
      },
      "datePublished": "2026-06-24",
      "dateModified": "2026-06-25",
      "tags": [
        "infrastructure as code",
        "terraform",
        "pulumi",
        "migration",
        "refactoring"
      ],
      "about": [
        {
          "name": "Code refactoring",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q116877"
        },
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "mentions": [
        {
          "name": "Terraform",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q28957072"
        },
        {
          "name": "Pulumi",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q138634162"
        },
        {
          "name": "Google Cloud Platform",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q17054505"
        },
        {
          "name": "Cloudflare",
          "@type": "Organization",
          "sameAs": "https://www.wikidata.org/wiki/Q4778915"
        },
        {
          "name": "GitHub",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q364"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-migration-8a6cead4",
        "https://ruslanakchurin.dev/manifest.json#topic-refactoring-aa385028",
        "https://www.wikidata.org/wiki/Q116877",
        "https://www.wikidata.org/wiki/Q138634162",
        "https://www.wikidata.org/wiki/Q17054505",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q28957072",
        "https://www.wikidata.org/wiki/Q364",
        "https://www.wikidata.org/wiki/Q4778915"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "title": "Fail before apply",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q138634162",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q28957072"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q28957072"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "title": "A library is not an architecture",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q138634162",
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system#name-and-freeze-the-cut",
          "fragment": "name-and-freeze-the-cut",
          "heading": "Name and freeze the cut",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system#publish-the-contract-beside-the-old-path",
          "fragment": "publish-the-contract-beside-the-old-path",
          "heading": "Publish the contract beside the old path",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system#move-membership-by-access-profile",
          "fragment": "move-membership-by-access-profile",
          "heading": "Move membership by access profile",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system#secrets-by-prefix",
          "fragment": "secrets-by-prefix",
          "heading": "Secrets by prefix",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system#gate-then-delete",
          "fragment": "gate-then-delete",
          "heading": "Gate, then delete",
          "level": 2
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring",
      "position": 5
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
      "title": "Appendix A: The shape, runnable",
      "description": "A companion repository makes the series architecture executable: four tiers, typed cross-tier contracts, and resolver probes that refuse bad composition before any provider call.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a.md"
      },
      "datePublished": "2026-06-25",
      "dateModified": "2026-06-25",
      "tags": [
        "infrastructure as code",
        "typescript",
        "node.js",
        "reference implementation",
        "demo"
      ],
      "about": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "mentions": [
        {
          "name": "TypeScript",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q978185"
        },
        {
          "name": "Node.js",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q756100"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-demo-2a97516c",
        "https://ruslanakchurin.dev/manifest.json#topic-reference-implementation-909ce8cd",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q756100",
        "https://www.wikidata.org/wiki/Q978185"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-reference-implementation-909ce8cd",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q756100",
            "https://www.wikidata.org/wiki/Q978185"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "title": "Start with the shape",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a#run-it",
          "fragment": "run-it",
          "heading": "Run it",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a#what-a-consumer-looks-like",
          "fragment": "what-a-consumer-looks-like",
          "heading": "What a consumer looks like",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a#what-the-demo-refuses",
          "fragment": "what-the-demo-refuses",
          "heading": "What the demo refuses",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a#map-it-back-to-the-series",
          "fragment": "map-it-back-to-the-series",
          "heading": "Map it back to the series",
          "level": 2
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring"
    },
    {
      "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
      "url": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
      "title": "Appendix B: The contract, end to end",
      "description": "One self-contained file implements the cross-tier contract: a producer publishes a typed value, a consumer names producer, key, context, and type, and a resolver returns the bound value or refuses by name.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b.md"
      },
      "datePublished": "2026-07-07",
      "dateModified": "2026-07-07",
      "tags": [
        "infrastructure as code",
        "typescript",
        "typed contracts",
        "reference implementation",
        "node.js"
      ],
      "about": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "mentions": [
        {
          "name": "TypeScript",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q978185"
        },
        {
          "name": "Node.js",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q756100"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-reference-implementation-909ce8cd",
        "https://ruslanakchurin.dev/manifest.json#topic-typed-contracts-240d5fc9",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q756100",
        "https://www.wikidata.org/wiki/Q978185"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
          "title": "Appendix A: The shape, runnable",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-reference-implementation-909ce8cd",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q756100",
            "https://www.wikidata.org/wiki/Q978185"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "title": "Start with the shape",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b#the-type",
          "fragment": "the-type",
          "heading": "The type",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b#what-the-producer-publishes",
          "fragment": "what-the-producer-publishes",
          "heading": "What the producer publishes",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b#the-consumer-request",
          "fragment": "the-consumer-request",
          "heading": "The consumer request",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b#the-resolver",
          "fragment": "the-resolver",
          "heading": "The resolver",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b#resolve-or-refuse",
          "fragment": "resolve-or-refuse",
          "heading": "Resolve, or refuse",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b#map-it-back-to-the-series",
          "fragment": "map-it-back-to-the-series",
          "heading": "Map it back to the series",
          "level": 2
        }
      ],
      "series": "making-iac-boring",
      "seriesName": "Making IaC boring"
    },
    {
      "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
      "url": "https://ruslanakchurin.dev/blog/account-not-boundary",
      "title": "The account is not the boundary",
      "description": "Porting the IaC operating model from GCP to AWS, mechanism by mechanism: account vending, grant and limit planes, regional network admission, and the readiness evidence that replaces a project-shaped handoff.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/account-not-boundary.md"
      },
      "datePublished": "2026-07-16",
      "dateModified": "2026-07-16",
      "tags": [
        "infrastructure as code",
        "amazon web services",
        "aws organizations",
        "identity and access management",
        "cloud networking"
      ],
      "about": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        },
        {
          "name": "Amazon Web Services",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q456157"
        }
      ],
      "mentions": [
        {
          "name": "Google Cloud Platform",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q17054505"
        },
        {
          "name": "Terraform",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q28957072"
        },
        {
          "name": "Domain Name System",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q8767"
        },
        {
          "name": "Key management",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q1320561"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-aws-organizations-f9e8fb85",
        "https://ruslanakchurin.dev/manifest.json#topic-cloud-networking-0a9c451c",
        "https://ruslanakchurin.dev/manifest.json#topic-identity-and-access-management-c3eb02b3",
        "https://www.wikidata.org/wiki/Q1320561",
        "https://www.wikidata.org/wiki/Q17054505",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q28957072",
        "https://www.wikidata.org/wiki/Q456157",
        "https://www.wikidata.org/wiki/Q8767"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "title": "Define tier membership",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-identity-and-access-management-c3eb02b3",
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q456157",
            "https://www.wikidata.org/wiki/Q8767"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "title": "Fail before apply",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q28957072",
            "https://www.wikidata.org/wiki/Q8767"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "title": "Re-cut the system",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q28957072"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#properties-translate-products-dont",
          "fragment": "properties-translate-products-dont",
          "heading": "Properties translate; products don't",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#the-account-exists-before-the-boundary-is-ready",
          "fragment": "the-account-exists-before-the-boundary-is-ready",
          "heading": "The account exists before the boundary is ready",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#the-request-is-a-product",
          "fragment": "the-request-is-a-product",
          "heading": "The request is a product",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#choose-the-account-factory-deliberately",
          "fragment": "choose-the-account-factory-deliberately",
          "heading": "Choose the account factory deliberately",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#who-owns-which-side",
          "fragment": "who-owns-which-side",
          "heading": "Who owns which side",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#the-provisioning-sequence-stage-by-stage",
          "fragment": "the-provisioning-sequence-stage-by-stage",
          "heading": "The provisioning sequence, stage by stage",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#identity-separates-grants-from-limits",
          "fragment": "identity-separates-grants-from-limits",
          "heading": "Identity separates grants from limits",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#five-flows-one-limiting-plane",
          "fragment": "five-flows-one-limiting-plane",
          "heading": "Five flows, one limiting plane",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#bootstrap-trust-expires",
          "fragment": "bootstrap-trust-expires",
          "heading": "Bootstrap trust expires",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#networks-are-regional-before-they-are-shared",
          "fragment": "networks-are-regional-before-they-are-shared",
          "heading": "Networks are regional before they are shared",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#two-ownership-profiles",
          "fragment": "two-ownership-profiles",
          "heading": "Two ownership profiles",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#the-admission-contract-replaces-the-bag-of-ids",
          "fragment": "the-admission-contract-replaces-the-bag-of-ids",
          "heading": "The admission contract replaces the bag of IDs",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#a-layout-that-keeps-the-owners-apart",
          "fragment": "a-layout-that-keeps-the-owners-apart",
          "heading": "A layout that keeps the owners apart",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#the-platform-applies-in-order",
          "fragment": "the-platform-applies-in-order",
          "heading": "The platform applies in order",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#drift-has-classes",
          "fragment": "drift-has-classes",
          "heading": "Drift has classes",
          "level": 3
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#failure-modes-owners-containment",
          "fragment": "failure-modes-owners-containment",
          "heading": "Failure modes, owners, containment",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#ready-states-exactly-what-holds",
          "fragment": "ready-states-exactly-what-holds",
          "heading": "READY states exactly what holds",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary#rejected-defaults-and-what-would-overturn-them",
          "fragment": "rejected-defaults-and-what-would-overturn-them",
          "heading": "Rejected defaults, and what would overturn them",
          "level": 2
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/blog/green-placebo",
      "url": "https://ruslanakchurin.dev/blog/green-placebo",
      "title": "The green placebo",
      "description": "An autonomous coding agent ran a ratified security plan on a secrets manager and twice recommended the less-secure option, every fact correct. The green test suite certified a defence that did nothing. Here is what actually held the line, and why the human could not be removed from it.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/green-placebo.md"
      },
      "datePublished": "2026-07-03",
      "dateModified": "2026-07-04",
      "tags": [
        "ai coding agents",
        "software security",
        "security testing",
        "code review",
        "human oversight of ai"
      ],
      "about": [
        {
          "name": "Computer security",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q3510521"
        },
        {
          "name": "Software testing",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q188522"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-ai-coding-agents-3c41eea5",
        "https://ruslanakchurin.dev/manifest.json#topic-code-review-7abada36",
        "https://ruslanakchurin.dev/manifest.json#topic-human-oversight-of-ai-c6460ab1",
        "https://ruslanakchurin.dev/manifest.json#topic-security-testing-897d1f8d",
        "https://ruslanakchurin.dev/manifest.json#topic-software-security-6b99d9d5",
        "https://www.wikidata.org/wiki/Q188522",
        "https://www.wikidata.org/wiki/Q3510521"
      ],
      "related": [],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#what-i-was-working-on",
          "fragment": "what-i-was-working-on",
          "heading": "What I was working on",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#twice-it-argued-for-weak",
          "fragment": "twice-it-argued-for-weak",
          "heading": "Twice, it argued for weak",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#the-reasoning-was-not-cut-a-corner",
          "fragment": "the-reasoning-was-not-cut-a-corner",
          "heading": "The reasoning was not \"cut a corner\"",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#what-actually-held-the-line",
          "fragment": "what-actually-held-the-line",
          "heading": "What actually held the line",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#the-bias-is-at-every-layer",
          "fragment": "the-bias-is-at-every-layer",
          "heading": "The bias is at every layer",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#what-i-took-away",
          "fragment": "what-i-took-away",
          "heading": "What I took away",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo#why-this-matters",
          "fragment": "why-this-matters",
          "heading": "Why this matters",
          "level": 2
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
      "url": "https://ruslanakchurin.dev/blog/library-not-architecture",
      "title": "A library is not an architecture",
      "description": "The Pulumi component library I built made infrastructure easier to create before ownership, tiers, contracts, and release boundaries were explicit.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/library-not-architecture.md"
      },
      "datePublished": "2026-07-07",
      "dateModified": "2026-07-07",
      "tags": [
        "infrastructure as code",
        "software architecture",
        "ownership boundaries",
        "platform engineering",
        "pulumi"
      ],
      "about": [
        {
          "name": "Separation of concerns",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q2465506"
        },
        {
          "name": "Coupling",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q253448"
        }
      ],
      "mentions": [
        {
          "name": "Pulumi",
          "@type": "SoftwareApplication",
          "sameAs": "https://www.wikidata.org/wiki/Q138634162"
        },
        {
          "name": "Google Cloud Platform",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q17054505"
        },
        {
          "name": "Identity management",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q977772"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-ownership-boundaries-490a9af3",
        "https://ruslanakchurin.dev/manifest.json#topic-platform-engineering-63e71897",
        "https://ruslanakchurin.dev/manifest.json#topic-software-architecture-5e9136a9",
        "https://www.wikidata.org/wiki/Q138634162",
        "https://www.wikidata.org/wiki/Q17054505",
        "https://www.wikidata.org/wiki/Q2465506",
        "https://www.wikidata.org/wiki/Q24964334",
        "https://www.wikidata.org/wiki/Q253448",
        "https://www.wikidata.org/wiki/Q977772"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "title": "Start with the shape",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-ownership-boundaries-490a9af3",
            "https://ruslanakchurin.dev/manifest.json#topic-software-architecture-5e9136a9",
            "https://www.wikidata.org/wiki/Q2465506",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
          "title": "Resolve by contract",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q253448"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "title": "Re-cut the system",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q138634162",
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "title": "Define tier membership",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q17054505",
            "https://www.wikidata.org/wiki/Q24964334",
            "https://www.wikidata.org/wiki/Q977772"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#what-i-optimised-first",
          "fragment": "what-i-optimised-first",
          "heading": "What I optimised first",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#output-structure-is-not-a-contract",
          "fragment": "output-structure-is-not-a-contract",
          "heading": "Output structure is not a contract",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#the-missing-type-was-semantic",
          "fragment": "the-missing-type-was-semantic",
          "heading": "The missing type was semantic",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#iam-became-flexible-before-authority-was-explicit",
          "fragment": "iam-became-flexible-before-authority-was-explicit",
          "heading": "IAM became flexible before authority was explicit",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#the-project-component-almost-had-the-right-idea",
          "fragment": "the-project-component-almost-had-the-right-idea",
          "heading": "The project component almost had the right idea",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#the-cloud-run-plus-neg-wrapper-blurred-edge",
          "fragment": "the-cloud-run-plus-neg-wrapper-blurred-edge",
          "heading": "The Cloud Run plus NEG wrapper blurred edge",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#why-i-made-this-mistake",
          "fragment": "why-i-made-this-mistake",
          "heading": "Why I made this mistake",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#what-was-actually-violated",
          "fragment": "what-was-actually-violated",
          "heading": "What was actually violated",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#what-i-would-keep",
          "fragment": "what-i-would-keep",
          "heading": "What I would keep",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture#the-bigger-lesson",
          "fragment": "the-bigger-lesson",
          "heading": "The bigger lesson",
          "level": 2
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
      "url": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
      "title": "A secret has to arrive",
      "description": "Secret storage gives a value somewhere to rest; secret delivery records the route, authority, wiring, and proof that get it there.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive.md"
      },
      "datePublished": "2026-06-29",
      "dateModified": "2026-07-03",
      "tags": [
        "secrets management",
        "secret delivery",
        "credential lifecycle",
        "infrastructure as code",
        "secrets rotation"
      ],
      "about": [
        {
          "name": "Key management",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q1320561"
        },
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
        "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
        "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
        "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
        "https://www.wikidata.org/wiki/Q1320561",
        "https://www.wikidata.org/wiki/Q24964334"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "title": "Delivery is a transaction",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
            "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "title": "The bundle is the competitor",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
            "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive#the-smallest-useful-case",
          "fragment": "the-smallest-useful-case",
          "heading": "The smallest useful case",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive#the-store-has-a-smaller-job",
          "fragment": "the-store-has-a-smaller-job",
          "heading": "The store has a smaller job",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive#delivery-records-the-lifecycle",
          "fragment": "delivery-records-the-lifecycle",
          "heading": "Delivery records the lifecycle",
          "level": 2
        }
      ],
      "series": "where-secrets-go",
      "seriesName": "Where secrets go",
      "position": 1
    },
    {
      "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
      "url": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
      "title": "Delivery is a transaction",
      "description": "Secret delivery breaks when writes, wiring, and receipts are separate chores; the transaction is the unit that keeps partial success and proof limits visible.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction.md"
      },
      "datePublished": "2026-07-01",
      "dateModified": "2026-07-05",
      "tags": [
        "secret delivery",
        "secrets management",
        "credential lifecycle",
        "secrets rotation",
        "infrastructure as code",
        "drift detection"
      ],
      "about": [
        {
          "name": "Key management",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q1320561"
        }
      ],
      "mentions": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
        "https://ruslanakchurin.dev/manifest.json#topic-drift-detection-d0349409",
        "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
        "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
        "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
        "https://www.wikidata.org/wiki/Q1320561",
        "https://www.wikidata.org/wiki/Q24964334"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
            "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "title": "The bundle is the competitor",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
            "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction#the-slot-comes-before-the-value",
          "fragment": "the-slot-comes-before-the-value",
          "heading": "The slot comes before the value",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction#outputs-reconcile-together",
          "fragment": "outputs-reconcile-together",
          "heading": "Outputs reconcile together",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction#the-receipt-survives-the-operator",
          "fragment": "the-receipt-survives-the-operator",
          "heading": "The receipt survives the operator",
          "level": 2
        }
      ],
      "series": "where-secrets-go",
      "seriesName": "Where secrets go",
      "position": 2
    },
    {
      "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
      "url": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
      "title": "The bundle is the competitor",
      "description": "The working bundle around a secret store can deliver a value; rotation, revocation, and honest proof limits are what test whether it delivers one verifiable transaction or evidence held together by convention.",
      "type": "post",
      "alternate": {
        "markdown": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor.md"
      },
      "datePublished": "2026-07-05",
      "dateModified": "2026-07-05",
      "tags": [
        "secret delivery",
        "secrets management",
        "secrets rotation",
        "secret revocation",
        "credential lifecycle"
      ],
      "about": [
        {
          "name": "Key management",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q1320561"
        }
      ],
      "mentions": [
        {
          "name": "Infrastructure as code",
          "@type": "Thing",
          "sameAs": "https://www.wikidata.org/wiki/Q24964334"
        }
      ],
      "topicIds": [
        "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
        "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
        "https://ruslanakchurin.dev/manifest.json#topic-secret-revocation-2154e305",
        "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
        "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
        "https://www.wikidata.org/wiki/Q1320561",
        "https://www.wikidata.org/wiki/Q24964334"
      ],
      "related": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "title": "A secret has to arrive",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
            "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "title": "Delivery is a transaction",
          "sharedTopicIds": [
            "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
            "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
            "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "title": "The account is not the boundary",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q1320561",
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "title": "Appendix B: The contract, end to end",
          "sharedTopicIds": [
            "https://www.wikidata.org/wiki/Q24964334"
          ]
        }
      ],
      "sections": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor#every-piece-emits-its-own-proof",
          "fragment": "every-piece-emits-its-own-proof",
          "heading": "Every piece emits its own proof",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor#rotation-and-revocation-reveal-the-design",
          "fragment": "rotation-and-revocation-reveal-the-design",
          "heading": "Rotation and revocation reveal the design",
          "level": 2
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor#what-the-receipt-is-for",
          "fragment": "what-the-receipt-is-for",
          "heading": "What the receipt is for",
          "level": 2
        }
      ],
      "series": "where-secrets-go",
      "seriesName": "Where secrets go",
      "position": 3
    }
  ],
  "topics": [
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-ai-coding-agents-3c41eea5",
      "name": "ai coding agents",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q456157",
      "name": "Amazon Web Services",
      "sameAs": "https://www.wikidata.org/wiki/Q456157",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "mentions"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-aws-organizations-f9e8fb85",
      "name": "aws organizations",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-cloud-networking-0a9c451c",
      "name": "cloud networking",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q4778915",
      "name": "Cloudflare",
      "sameAs": "https://www.wikidata.org/wiki/Q4778915",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "mentions"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q116877",
      "name": "Code refactoring",
      "sameAs": "https://www.wikidata.org/wiki/Q116877",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "about"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-code-review-7abada36",
      "name": "code review",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q3510521",
      "name": "Computer security",
      "sameAs": "https://www.wikidata.org/wiki/Q3510521",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "about"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q253448",
      "name": "Coupling",
      "sameAs": "https://www.wikidata.org/wiki/Q253448",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "about"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
          "relationships": [
            "about",
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-credential-lifecycle-c59510f1",
      "name": "credential lifecycle",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-cross-stack-dependencies-f8f5e215",
      "name": "cross-stack dependencies",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-demo-2a97516c",
      "name": "demo",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q8767",
      "name": "Domain Name System",
      "sameAs": "https://www.wikidata.org/wiki/Q8767",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "mentions"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-drift-detection-d0349409",
      "name": "drift detection",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-fail-fast-d69f49e0",
      "name": "fail-fast",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q364",
      "name": "GitHub",
      "sameAs": "https://www.wikidata.org/wiki/Q364",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "mentions"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-google-cloud-d9b1d781",
      "name": "google cloud",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q17054505",
      "name": "Google Cloud Platform",
      "sameAs": "https://www.wikidata.org/wiki/Q17054505",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "mentions"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-human-oversight-of-ai-c6460ab1",
      "name": "human oversight of ai",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-identity-and-access-management-c3eb02b3",
      "name": "identity and access management",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q977772",
      "name": "Identity management",
      "sameAs": "https://www.wikidata.org/wiki/Q977772",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "about"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q24964334",
      "name": "Infrastructure as code",
      "sameAs": "https://www.wikidata.org/wiki/Q24964334",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "relationships": [
            "about",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "mentions",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "mentions"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-interfaces-0274021b",
      "name": "interfaces",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q1320561",
      "name": "Key management",
      "sameAs": "https://www.wikidata.org/wiki/Q1320561",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "relationships": [
            "about"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "about"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "about"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-migration-8a6cead4",
      "name": "migration",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-multi-tenancy-1277c69e",
      "name": "multi-tenancy",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q756100",
      "name": "Node.js",
      "sameAs": "https://www.wikidata.org/wiki/Q756100",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
          "relationships": [
            "mentions",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "relationships": [
            "mentions",
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-ownership-boundaries-490a9af3",
      "name": "ownership boundaries",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-platform-engineering-63e71897",
      "name": "platform engineering",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q138634162",
      "name": "Pulumi",
      "sameAs": "https://www.wikidata.org/wiki/Q138634162",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "mentions",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "mentions",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "mentions",
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-refactoring-aa385028",
      "name": "refactoring",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-reference-implementation-909ce8cd",
      "name": "reference implementation",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-release-pipeline-3384c7c0",
      "name": "release pipeline",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-secret-delivery-d519519a",
      "name": "secret delivery",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-secret-revocation-2154e305",
      "name": "secret revocation",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-secrets-management-01379de8",
      "name": "secrets management",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-secrets-rotation-807f159e",
      "name": "secrets rotation",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/01-a-secret-has-to-arrive",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/02-delivery-is-a-transaction",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/where-secrets-go/03-the-bundle-is-the-competitor",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-security-testing-897d1f8d",
      "name": "security testing",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q2465506",
      "name": "Separation of concerns",
      "sameAs": "https://www.wikidata.org/wiki/Q2465506",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "about"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "relationships": [
            "about",
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-service-accounts-7c401348",
      "name": "service accounts",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/03-membership",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-software-architecture-5e9136a9",
      "name": "software architecture",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/library-not-architecture",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-software-security-6b99d9d5",
      "name": "software security",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q188522",
      "name": "Software testing",
      "sameAs": "https://www.wikidata.org/wiki/Q188522",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/green-placebo",
          "relationships": [
            "about"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q28957072",
      "name": "Terraform",
      "sameAs": "https://www.wikidata.org/wiki/Q28957072",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/account-not-boundary",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "mentions"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/05-re-cut-the-system",
          "relationships": [
            "mentions",
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-tiering-407bd3bb",
      "name": "tiering",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/01-shape",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-typed-contracts-240d5fc9",
      "name": "typed contracts",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/02-contract",
          "relationships": [
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "relationships": [
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://www.wikidata.org/wiki/Q978185",
      "name": "TypeScript",
      "sameAs": "https://www.wikidata.org/wiki/Q978185",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-a",
          "relationships": [
            "mentions",
            "tagged"
          ]
        },
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/appendix-b",
          "relationships": [
            "mentions",
            "tagged"
          ]
        }
      ]
    },
    {
      "id": "https://ruslanakchurin.dev/manifest.json#topic-validation-98c41dcd",
      "name": "validation",
      "routes": [
        {
          "id": "https://ruslanakchurin.dev/blog/making-iac-boring/04-fail-before-apply",
          "relationships": [
            "tagged"
          ]
        }
      ]
    }
  ]
}
