{
    "version": "https://jsonfeed.org/version/1",
    "title": "Saved for Later",
    "feed_url": "https://savedforlater.dev/json",
    "items": [
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47806725\">Comments</a>",
            "url": "https://www.anthropic.com/news/claude-design-anthropic-labs",
            "title": "Claude Design",
            "date_modified": "2026-04-17T15:04:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47796818\">Comments</a>",
            "url": "https://stagereview.app/",
            "title": "Show HN: Stage – Putting humans back in control of code review",
            "date_modified": "2026-04-16T17:36:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47796469\">Comments</a>",
            "url": "https://openai.com/index/codex-for-almost-everything/",
            "title": "Codex for Almost Everything",
            "date_modified": "2026-04-16T17:12:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47796469\">Comments</a>",
            "url": "https://openai.com/index/codex-for-almost-everything/",
            "title": "Codex for almost everything",
            "date_modified": "2026-04-16T17:12:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47730194\">Comments</a>",
            "url": "https://cirruslabs.org/",
            "title": "Cirrus Labs to join OpenAI",
            "date_modified": "2026-04-11T13:01:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47712656\">Comments</a>",
            "url": "https://blog.gitbutler.com/series-a",
            "title": "We've raised $17M to build what comes after Git",
            "date_modified": "2026-04-10T01:52:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47704285\">Comments</a>",
            "url": "https://www.yanist.com/clean-code-in-the-age-of-coding-agents/",
            "title": "Clean code in the age of coding agents",
            "date_modified": "2026-04-09T14:33:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47699181\">Comments</a>",
            "url": "https://astral.sh/blog/open-source-security-at-astral",
            "title": "Open Source Security at Astral",
            "date_modified": "2026-04-09T04:11:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47699181\">Comments</a>",
            "url": "https://astral.sh/blog/open-source-security-at-astral",
            "title": "Open source security at Astral",
            "date_modified": "2026-04-09T04:11:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47687464\">Comments</a>",
            "url": "https://lucumr.pocoo.org/2026/4/8/mario-and-earendil/",
            "title": "Mario and Earendil",
            "date_modified": "2026-04-08T09:13:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47680404\">Comments</a>",
            "url": "https://www.allthingsdistributed.com/2026/04/s3-files-and-the-changing-face-of-s3.html",
            "title": "S3 Files",
            "date_modified": "2026-04-07T19:44:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47629382\">Comments</a>",
            "url": "https://www.skyvern.com/blog/getting-claude-to-qa-its-own-work/",
            "title": "Getting Claude to QA its own work",
            "date_modified": "2026-04-03T17:18:38.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing managed daemon support for <a href=\"https://aws.amazon.com/ecs/managed-instances/\">Amazon Elastic Container Service (Amazon ECS) Managed Instances</a>. This new capability extends the managed instances experience we <a href=\"https://aws.amazon.com/blogs/aws/announcing-amazon-ecs-managed-instances-for-containerized-applications/\">introduced in September 2025</a>, by giving platform engineers independent control over software agents such as monitoring, logging, and tracing tools, without requiring coordination with application development teams, while also improving reliability by ensuring every instance consistently runs required daemons and enabling comprehensive host-level monitoring.</p> \n<p>When running containerized workloads at scale, platform engineers manage a wide range of responsibilities, from scaling and patching infrastructure to keeping applications running reliably and maintaining the operational agents that support those applications. Until now, many of these concerns were tightly coupled. Updating a monitoring agent meant coordinating with application teams, modifying task definitions, and redeploying entire applications, a significant operational burden when you’re managing hundreds or thousands of services.</p> \n<p><span><strong>Decoupled lifecycle management for daemons<br> </strong></span>Amazon ECS now introduces a dedicated managed daemons construct that enables platform teams to centrally manage operational tooling. This separation of concerns allows platform engineers to independently deploy and update monitoring, logging, and tracing agents to infrastructure, while enforcing consistent use of required tools across all instances, without requiring application teams to redeploy their services. Daemons are guaranteed to start before application tasks and drain last, ensuring that logging, tracing, and monitoring are always available when your application needs them.</p> \n<p>Platform engineers can deploy managed daemons across multiple capacity providers, or target specific capacity providers, giving them flexibility in how they roll out agents across their infrastructure. Resource management is also centralized, allowing teams to define daemon CPU and memory parameters separately from application configurations with no need to rebuild AMIs or update task definitions, while optimizing resource utilization since each instance runs exactly one daemon copy shared across multiple application tasks.</p> \n<p><span><strong>Let’s try it out<br> </strong></span>To take ECS Managed Daemons for a spin, I decided to start with the <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html\">Amazon CloudWatch Agent</a> as my first managed daemon. I had previously set up an Amazon ECS cluster with a Managed Instance capacity provider using the <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/getting-started-managed-instances.html\">documentation</a>.</p> \n<p>From the Amazon Elastic Container Service console, I noticed a new <strong>Daemon task definitions</strong> option in the navigation pane, where I can define my managed daemons.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2026/03/27/daemons-1-1024x528.png\" alt=\"Managed daemons console\" width=\"1024\" height=\"528\"></p> \n<p>I chose <strong>Create new daemon task definition</strong> to get started. For this example, I configured the CloudWatch Agent with 1 vCPU and 0.5 GB of memory. In the <strong>Daemon task definition family field</strong>, I entered a name I’d recognize later.</p> \n<p>For the <strong>Task execution role</strong>, I selected <strong>ecsTaskExecutionRole</strong> from the dropdown. Under the <strong>Container</strong> section, I gave my container a descriptive name and pasted in the image URI: <code>public.ecr.aws/cloudwatch-agent/cloudwatch-agent:latest</code> along with a few additional details.</p> \n<p>After reviewing everything, I chose <strong>Create</strong>.</p> \n<p>Once my daemon task definition was created, I navigated to the <strong>Clusters</strong> page, selected my previously created cluster and found the new <strong>Daemons</strong> tab.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2026/03/27/daemons-2-1024x553.png\" alt=\"Managed daemons 2\" width=\"1024\" height=\"553\"></p> \n<p>Here I can simply click the <strong>Create daemon</strong> button and complete the form to configure my daemon.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2026/03/27/daemons-3-1024x575.png\" alt=\"Managed daemons 3\" width=\"1024\" height=\"575\"></p> \n<p>Under <strong>Daemon configuration</strong>, I selected my newly created daemon task definition family and then assigned my daemon a name. For <strong>Environment configuration</strong>, I selected the ECS Managed Instances capacity provider I had set up earlier. After confirming my settings, I chose <strong>Create</strong>.</p> \n<p>Now ECS automatically ensures the daemon task launches first on every provisioned ECS managed instance in my selected capacity provider. To see this in action, I deployed a sample <a href=\"https://nginx.org/\">nginx</a> web service as a test workload. Once my workload was deployed, I could see in the console that ECS Managed Daemons had automatically deployed the CloudWatch Agent daemon alongside my application, with no manual intervention required.</p> \n<p>When I later updated my daemon, ECS handled the rolling deployment automatically by provisioning new instances with the updated daemon, starting the daemon first, then migrating application tasks to the new instances before terminating the old ones. This “start before stop” approach ensures continuous daemon coverage: your logging, monitoring, and tracing agents remain operational throughout the update with no gaps in data collection. The drain percentage I configured controlled the pace of this replacement, giving me complete control over addon updates without any application downtime.</p> \n<p><span><strong>How it works<br> </strong></span>The managed daemon experience introduces a new daemon task definition that is separate from task definitions, with its own parameters and validation scheme. A new <code>daemon_bridge</code> network mode enables daemons to communicate with application tasks while remaining isolated from application networking configurations.</p> \n<p>Managed daemons support advanced host-level access capabilities that are essential for operational tooling. Platform engineers can configure daemon tasks as privileged containers, add additional Linux capabilities, and mount paths from the underlying host filesystem. These capabilities are particularly valuable for monitoring and security agents that require deep visibility into host-level metrics, processes, and system calls.</p> \n<p>When a daemon is deployed, ECS launches exactly one daemon process per container instance before placing application tasks. This guarantees that operational tooling is in place before your application starts receiving traffic. ECS also supports rolling deployments with automatic rollbacks, so you can update agents with confidence.</p> \n<p><span><strong>Now available<br> </strong></span>Managed daemon support for Amazon ECS Managed Instances is available today in all <a href=\"https://aws.amazon.com/about-aws/global-infrastructure/regions_az/\">AWS Regions</a>. To get started, visit the Amazon ECS console or review the <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/managed-daemons.html\">Amazon ECS documentation</a>. You can also explore the new managed daemons Application Programming Interface (APIs) by visiting <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/managed-daemons.html\">this website</a>.</p> \n<p>There is no additional cost to use managed daemons. You pay only for the standard compute resources consumed by your daemon tasks.</p>",
            "url": "https://aws.amazon.com/blogs/aws/announcing-managed-daemon-support-for-amazon-ecs-managed-instances/",
            "title": "Announcing managed daemon support for Amazon ECS Managed Instances",
            "date_modified": "2026-04-01T23:31:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47546262\">Comments</a>",
            "url": "https://jeapostrophe.github.io/tech/jc-workflow/",
            "title": "Don't Wait for Claude",
            "date_modified": "2026-03-27T18:13:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47540441\">Comments</a>",
            "url": "https://lubeno.dev/blog/reinventing-the-pull-request",
            "title": "Reinventing the Pull Request",
            "date_modified": "2026-03-27T09:04:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47501875\">Comments</a>",
            "url": "https://david.alvarezrosa.com/posts/optimizing-a-lock-free-ring-buffer/",
            "title": "Optimizing a lock-free ring buffer",
            "date_modified": "2026-03-24T12:52:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47436592\">Comments</a>",
            "url": "https://github.com/antithesishq/bombadil",
            "title": "Bombadil: Property-based testing for web UIs by Antithesis",
            "date_modified": "2026-03-19T08:53:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47436592\">Comments</a>",
            "url": "https://github.com/antithesishq/bombadil",
            "title": "Bombadil: Property-based testing for web UIs",
            "date_modified": "2026-03-19T08:53:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47431288\">Comments</a>",
            "url": "https://twitter.com/toddsaunders/status/2034243420147859716",
            "title": "An industrial piping contractor on Claude Code [video]",
            "date_modified": "2026-03-18T20:50:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/2tspe2/beam_metrics_clickhouse\">Comments</a></p>",
            "url": "https://andrealeopardi.com/posts/beam-metrics-in-clickhouse/",
            "title": "BEAM Metrics in ClickHouse",
            "date_modified": "2026-03-18T15:44:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47414356\">Comments</a>",
            "url": "https://trigger.dev/blog/how-trql-works",
            "title": "We give every user SQL access to a shared ClickHouse cluster",
            "date_modified": "2026-03-17T15:50:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47408205\">Comments</a>",
            "url": "https://apenwarr.ca/log/20260316",
            "title": "Every layer of review makes you 10x slower",
            "date_modified": "2026-03-17T03:20:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47401734\">Comments</a>",
            "url": "https://arxiv.org/abs/2511.04427",
            "title": "Speed at the cost of quality: Study of use of Cursor AI in open source projects",
            "date_modified": "2026-03-16T17:07:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47393805\">Comments</a>",
            "url": "https://towlion.github.io",
            "title": "An experiment to use GitHub Actions as a control plane for a PaaS",
            "date_modified": "2026-03-16T00:51:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47368135\">Comments</a>",
            "url": "https://digg.com/",
            "title": "Digg is gone again",
            "date_modified": "2026-03-13T18:52:17.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing a new feature of <a href=\"https://aws.amazon.com/s3/?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">Amazon Simple Storage Service (Amazon S3)</a> you can use to create general purpose buckets in your own account regional namespace simplifying bucket creation and management as your data storage needs grow in size and scope. You can create general purpose bucket names across multiple AWS Regions with assurance that your desired bucket names will always be available for you to use.</p> \n<p>With this feature, you can predictably name and create general purpose buckets in your own account regional namespace by appending your account’s unique suﬃx in your requested bucket name. For example, I can create the bucket <code>mybucket-123456789012-us-east-1-an</code> in my account regional namespace. <code>mybucket</code> is the bucket name prefix that I specified, then I add my account regional suffix to the requested bucket name: <code>-123456789012-us-east-1-an</code>. If another account tries to create buckets using my account’s suffix, their requests will be automatically rejected.</p> \n<p>Your security teams can use <a href=\"https://aws.amazon.com/iam/?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">AWS Identity and Access Management (AWS IAM)</a> policies and <a href=\"https://aws.amazon.com/organizations/?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">AWS Organizations</a> service control policies to enforce that your employees only create buckets in their account regional namespace using the new <code>s3:x-amz-bucket-namespace</code> condition key, helping teams adopt the account regional namespace across your organization.</p> \n<p><strong><u>Create your S3 bucket with account regional namespace in action</u></strong><br> To get started, choose <strong>Create bucket</strong> in the <a href=\"https://console.aws.amazon.com/s3?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">Amazon S3 console</a>. To create your bucket in your account regional namespace, choose <strong>Account regional namespace</strong>. If you choose this option, you can create your bucket with any name that is unique to your account and region.</p> \n<p>This configuration supports all of the same features as general purpose buckets in the global namespace. The only difference is that only your account can use bucket names with your account’s suffix. The bucket name prefix and the account regional suffix combined must be between 3 and 63 characters long.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2026/02/12/2026-s3-bucket-account-regional-namespace.png\" width=\"2098\" height=\"2381\"></p> \n<p>Using the <a href=\"https://aws.amazon.com/cli/?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">AWS Command Line Interface (AWS CLI)</a>, you can create a bucket with account regional namespace by specifying the <code>x-amz-bucket-namespace:account-regional</code> request header and providing a compatible bucket name.</p> \n<pre><code>$ aws s3api create-bucket --bucket mybucket-123456789012-us-east-1-an \\\n   --bucket-namespace account-regional \\\n   --region us-east-1</code></pre> \n<p>You can use the <a href=\"https://aws.amazon.com/sdk-for-python/\">AWS SDK for Python (Boto3)</a> to create a bucket with account regional namespace using <code>CreateBucket</code> API request.</p> \n<pre><code>import boto3\n\nclass AccountRegionalBucketCreator:\n    \"\"\"Creates S3 buckets using account-regional namespace feature.\"\"\"\n    \n    ACCOUNT_REGIONAL_SUFFIX = \"-an\"\n    \n    def __init__(self, s3_client, sts_client):\n        self.s3_client = s3_client\n        self.sts_client = sts_client\n    \n    def create_account_regional_bucket(self, prefix):\n        \"\"\"\n        Creates an account-regional S3 bucket with the specified prefix.\n        Resolves caller AWS account ID using the STS GetCallerIdentity API.\n        Format: ---an\n        \"\"\"\n        account_id = self.sts_client.get_caller_identity()['Account']\n        region = self.s3_client.meta.region_name\n        bucket_name = self._generate_account_regional_bucket_name(\n            prefix, account_id, region\n        )\n        \n        params = {\n            \"Bucket\": bucket_name,\n            \"BucketNamespace\": \"account-regional\"\n        }\n        if region != \"us-east-1\":\n            params[\"CreateBucketConfiguration\"] = {\n                \"LocationConstraint\": region\n            }\n        \n        return self.s3_client.create_bucket(**params)\n    \n    def _generate_account_regional_bucket_name(self, prefix, account_id, region):\n        return f\"{prefix}-{account_id}-{region}{self.ACCOUNT_REGIONAL_SUFFIX}\"\n\n\nif __name__ == '__main__':\n    s3_client = boto3.client('s3')\n    sts_client = boto3.client('sts')\n    \n    creator = AccountRegionalBucketCreator(s3_client, sts_client)\n    response = creator.create_account_regional_bucket('test-python-sdk')\n    \n    print(f\"Bucket created: {response}\")</code></pre> \n<p>You can update your infrastructure as code (IaC) tools, such as <a href=\"https://aws.amazon.com/cloudformation/?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">AWS CloudFormation</a>, to simplify creating buckets in your account regional namespace. AWS CloudFormation offers the pseudo parameters, <code>AWS::AccountId</code> and <code>AWS::Region</code>, making it easy to build <a href=\"https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-s3-bucket.html?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">CloudFormation templates</a> that create account regional namespace buckets.</p> \n<p>The following example demonstrates how you can update your existing CloudFormation templates to start creating buckets in your account regional namespace:</p> \n<pre><code>BucketName: !Sub \"amzn-s3-demo-bucket-${AWS::AccountId}-${AWS::Region}-an\"\nBucketNamespace: \"account-regional\"</code></pre> \n<p>Alternatively, you can also use the <code>BucketNamePrefix</code> property to update your CloudFormation template. By using the <code>BucketNamePrefix</code>, you can provide only the customer defined portion of the bucket name and then it automatically adds the account regional namespace suffix based on the requesting AWS account and Region specified.</p> \n<pre><code>BucketNamePrefix: 'amzn-s3-demo-bucket'\nBucketNamespace: \"account-regional\"\n</code></pre> \n<p>Using these options, you can build a custom CloudFormation template to easily create general purpose buckets in your account regional namespace.</p> \n<p><strong>Things to know</strong><br> You can’t rename your existing global buckets to bucket names with account regional namespace, but you can create new general purpose buckets in your account regional namespace. Also, the account regional namespace is only supported for general purpose buckets. S3 table buckets and vector buckets already exist in an account-level namespace and S3 directory buckets exist in a zonal namespace.</p> \n<p>To learn more, visit&nbsp;<a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">Namespaces for general purpose buckets</a> in the Amazon S3 User Guide.</p> \n<p><strong><u>Now available</u></strong><br> Creating general purpose buckets in your account regional namespace in Amazon S3 is now available in 37 AWS Regions including the AWS China and AWS GovCloud (US) Regions. You can create general purpose buckets in your account regional namespace at no additional cost.</p> \n<p>Give it a try in the <a href=\"https://console.aws.amazon.com/s3?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">Amazon S3 console</a> today and send feedback to <a href=\"https://repost.aws/tags/TADSTjraA0Q4-a1dxk6eUYaw/amazon-simple-storage-service?trk=d8ec3b19-0f37-4f8c-8c12-189f913e205c&amp;sc_channel=el\">AWS re:Post for Amazon S3</a> or through your usual AWS Support contacts.</p> \n<p>— <a href=\"https://linkedin.com/in/channy/\">Channy</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-account-regional-namespaces-for-amazon-s3-general-purpose-buckets/",
            "title": "Introducing account regional namespaces for Amazon S3 general purpose buckets",
            "date_modified": "2026-03-12T21:18:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47352578\">Comments</a>",
            "url": "https://www.mendral.com/blog/ci-at-scale",
            "title": "What CI looks like at a 100-person team (PostHog)",
            "date_modified": "2026-03-12T15:50:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47346511\">Comments</a>",
            "url": "https://www.proxylity.com/articles/wireguard-is-two-things.html",
            "title": "WireGuard Is Two Things",
            "date_modified": "2026-03-12T04:38:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47327351\">Comments</a>",
            "url": "https://modulus.so",
            "title": "Show HN: Modulus – Cross-repository knowledge orchestration for coding agents",
            "date_modified": "2026-03-10T18:52:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47301085\">Comments</a>",
            "url": "https://agent-safehouse.dev/",
            "title": "Agent Safehouse – macOS-native sandboxing for local agents",
            "date_modified": "2026-03-08T20:30:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47297167\">Comments</a>",
            "url": "https://github.com/gritzko/librdx/tree/master/be",
            "title": "Beagle, a source code management system that stores AST trees",
            "date_modified": "2026-03-08T13:28:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47193064\">Comments</a>",
            "url": "https://mksg.lu/blog/context-mode",
            "title": "MCP server that reduces Claude Code context consumption by 98%",
            "date_modified": "2026-02-28T10:01:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47184049\">Comments</a>",
            "url": "https://www.shayon.dev/post/2026/52/lets-discuss-sandbox-isolation/",
            "title": "Let's discuss sandbox isolation",
            "date_modified": "2026-02-27T18:49:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47138429\">Comments</a>",
            "url": "https://book.mixu.net/distsys/single-page.html",
            "title": "Distributed Systems for Fun and Profit",
            "date_modified": "2026-02-24T15:32:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47098252\">Comments</a>",
            "url": "https://trunkbaseddevelopment.com/",
            "title": "Trunk Based Development",
            "date_modified": "2026-02-21T07:07:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47067395\">Comments</a>",
            "url": "https://georgeguimaraes.com/your-agent-orchestrator-is-just-a-bad-clone-of-elixir/",
            "title": "Your Agent Framework Is Just a Bad Clone of Elixir",
            "date_modified": "2026-02-18T22:37:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47046124\">Comments</a>",
            "url": "https://haskellforall.com/2026/02/browse-code-by-meaning",
            "title": "Browse Code by Meaning",
            "date_modified": "2026-02-17T11:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47042396\">Comments</a>",
            "url": "https://www.bbc.com/news/articles/cx2gn239exlo",
            "title": "Dark web agent spotted bedroom wall clue to rescue girl from abuse",
            "date_modified": "2026-02-17T01:01:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47037781\">Comments</a>",
            "url": "https://planetscale.com/blog/zero-downtime-migrations-at-petabyte-scale",
            "title": "Zero downtime migrations at Petabyte scale",
            "date_modified": "2026-02-16T17:35:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47022238\">Comments</a>",
            "url": "https://gist.github.com/gritzko/6e81b5391eacb585ae207f5e634db07e",
            "title": "Git is a file system. We need a database for the code",
            "date_modified": "2026-02-15T09:09:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47022238\">Comments</a>",
            "url": "https://gist.github.com/gritzko/6e81b5391eacb585ae207f5e634db07e",
            "title": "SCM as a database for the code",
            "date_modified": "2026-02-15T09:09:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=47001871\">Comments</a>",
            "url": "https://monosketch.io/",
            "title": "Monosketch",
            "date_modified": "2026-02-13T12:18:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46997133\">Comments</a>",
            "url": "https://github.com/aws/aws-sdk-go-v2/commit/3dca5e45d5ad05460b93410087833cbaa624754e",
            "title": "AWS Adds support for nested virtualization",
            "date_modified": "2026-02-13T00:07:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46961345\">Comments</a>",
            "url": "https://entire.io/blog/hello-entire-world/",
            "title": "Ex-GitHub CEO Launches a New Developer Platform for AI Agents",
            "date_modified": "2026-02-10T15:44:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46957629\">Comments</a>",
            "url": "https://code.storage/",
            "title": "Code Storage by the Pierre Computer Company",
            "date_modified": "2026-02-10T10:12:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46949452\">Comments</a>",
            "url": "https://www.githubstatus.com/incidents/lcw3tg2f6zsd",
            "title": "Another GitHub outage in the same day",
            "date_modified": "2026-02-09T19:07:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46924711\">Comments</a>",
            "url": "https://simonwillison.net/2026/Feb/7/software-factory/",
            "title": "StrongDM's AI team build serious software without even looking at the code",
            "date_modified": "2026-02-07T15:41:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46924426\">Comments</a>",
            "url": "https://factory.strongdm.ai/",
            "title": "Software factories and the agentic moment",
            "date_modified": "2026-02-07T15:05:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46874097\">Comments</a>",
            "url": "https://deno.com/blog/introducing-deno-sandbox",
            "title": "Deno Sandbox",
            "date_modified": "2026-02-03T17:33:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46871473\">Comments</a>",
            "url": "https://blog.rbby.dev/posts/github-ai-contribution-blame-for-pull-requests/",
            "title": "GitHub Browser Plugin for AI Contribution Blame in Pull Requests",
            "date_modified": "2026-02-03T14:35:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46870917\">Comments</a>",
            "url": "https://github.com/oug-t/difi",
            "title": "Show HN: difi – A Git diff TUI with Neovim integration (written in Go)",
            "date_modified": "2026-02-03T13:47:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46861842\">Comments</a>",
            "url": "https://www.githubstatus.com?todayis=2026-02-02",
            "title": "GitHub experience various partial-outages/degradations",
            "date_modified": "2026-02-02T21:28:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46859054\">Comments</a>",
            "url": "https://openai.com/index/introducing-the-codex-app/",
            "title": "The Codex App",
            "date_modified": "2026-02-02T18:02:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46792194\">Comments</a>",
            "url": "https://matklad.github.io/2026/01/27/make-ts.html",
            "title": "Make.ts",
            "date_modified": "2026-01-28T07:35:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46783017\">Comments</a>",
            "url": "https://allenai.org/blog/open-coding-agents",
            "title": "AI2: Open Coding Agents",
            "date_modified": "2026-01-27T17:17:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46782091\">Comments</a>",
            "url": "https://tailscale.com/blog/aperture-private-alpha",
            "title": "A first look at Aperture by Tailscale (private alpha)",
            "date_modified": "2026-01-27T16:23:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46750767\">Comments</a>",
            "url": "https://9to5mac.com/2026/01/23/tiktok-is-officially-us-owned-for-american-users-heres-whats-changing/",
            "title": "TikTok is officially US-owned for American users, here's what's changing",
            "date_modified": "2026-01-25T04:37:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46743908\">Comments</a>",
            "url": "https://twitter.com/NicerInPerson/status/2014989679796347375",
            "title": "Claude Code's new hidden feature: Swarms",
            "date_modified": "2026-01-24T14:35:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46731748\">Comments</a>",
            "url": "https://tuananh.net/2026/01/20/what-has-docker-become/",
            "title": "What has Docker become?",
            "date_modified": "2026-01-23T12:36:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46713387\">Comments</a>",
            "url": "https://lix.dev/blog/introducing-lix/",
            "title": "Lix – universal version control system for binary files",
            "date_modified": "2026-01-21T23:55:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46711589\">Comments</a>",
            "url": "https://cognition.ai/blog/devin-review",
            "title": "Devin Review: AI to Stop Slop",
            "date_modified": "2026-01-21T21:09:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46654085\">Comments</a>",
            "url": "https://github.com/trasta298/keifu",
            "title": "Keifu – A TUI for navigating commit graphs with color and clarity",
            "date_modified": "2026-01-17T00:32:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46620673\">Comments</a>",
            "url": "https://www.zettalane.com/blog/openzfs-summit-2025-mayanas-objbacker.html",
            "title": "Native ZFS VDEV for Object Storage (OpenZFS Summit)",
            "date_modified": "2026-01-14T18:49:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46617744\">Comments</a>",
            "url": "https://usetero.com/blog/the-question-your-observability-vendor-wont-answer",
            "title": "I built Vector. Now I'm answering the question your observability vendor won't",
            "date_modified": "2026-01-14T16:07:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46617668\">Comments</a>",
            "url": "https://starlink.com/support/article/58c9c8b7-474e-246f-7e3c-06db3221d34d",
            "title": "Roam 50GB is now Roam 100GB",
            "date_modified": "2026-01-14T16:03:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46614558\">Comments</a>",
            "url": "https://xlii.space/eng/i-hate-github-actions-with-passion/",
            "title": "I Hate GitHub Actions with Passion",
            "date_modified": "2026-01-14T10:53:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46602591\">Comments</a>",
            "url": "https://www.greptile.com/blog/github-ids",
            "title": "Every GitHub object has two IDs",
            "date_modified": "2026-01-13T15:52:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46597023\">Comments</a>",
            "url": "https://aicoding.leaflet.pub/3mcbiyal7jc2y",
            "title": "Provenance Is the New Version Control",
            "date_modified": "2026-01-13T03:26:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46580136\">Comments</a>",
            "url": "https://jakobemmerling.de/posts/fuse-is-all-you-need/",
            "title": "FUSE is All You Need – Giving agents access to anything via filesystems",
            "date_modified": "2026-01-11T21:12:45.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/i9nrlh/continuous_snapshotting_filesystem\">Comments</a></p>",
            "url": "https://nilfs.sourceforge.io/en/index.html",
            "title": "Continuous Snapshotting Filesystem",
            "date_modified": "2026-01-10T12:15:20.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/osmrzx/how_i_use_jujutsu\">Comments</a></p>",
            "url": "https://abhinavsarkar.net/posts/jj-usage/",
            "title": "How I use Jujutsu",
            "date_modified": "2026-01-10T11:55:32.000Z"
        },
        {
            "content_html": "<p>Search engine optimization, or SEO, is a big business. While some SEO practices are useful, much of the day-to-day SEO wisdom you see online amounts to superstition. An increasingly popular approach geared toward LLMs called \"content chunking\" may fall into that category. In the latest installment of Google's <a href=\"https://search-off-the-record.libsyn.com/seo-aio-geo-your-site-third-party-support-to-optimize-for-llms\"><em>Search Off the Record</em> podcast</a>, John Mueller and Danny Sullivan say that breaking content down into bite-sized chunks for LLMs like Gemini is a bad idea.</p>\n<p>You've probably seen websites engaging in content chunking and scratched your head, and for good reason—this content isn't made for you. The idea is that if you split information into smaller paragraphs and sections, it is more likely to be ingested and cited by generative AI bots like Gemini. So you end up with short paragraphs, sometimes with just one or two sentences, and lots of subheds formatted like questions one might ask a chatbot.</p>\n<p>According to Sullivan, this is a misconception, and Google doesn't use such signals to improve ranking. \"One of the things I keep seeing over and over in some of the advice and guidance and people are trying to figure out what do we do with the LLMs or whatever, is that turn your content into bite-sized chunks, because LLMs like things that are really bite size, right?\" said Sullivan. \"So... we don't want you to do that.\"</p><p><a href=\"https://arstechnica.com/google/2026/01/google-dont-make-bite-sized-content-for-llms-if-you-care-about-search-rank/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/google/2026/01/google-dont-make-bite-sized-content-for-llms-if-you-care-about-search-rank/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/google/2026/01/google-dont-make-bite-sized-content-for-llms-if-you-care-about-search-rank/",
            "title": "Google: Don’t make “bite-sized” content for LLMs if you care about search rank",
            "date_modified": "2026-01-09T20:27:34.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/seun8j/how_github_monopoly_is_destroying_open\">Comments</a></p>",
            "url": "https://ploum.net/2026-01-05-unteaching_github.html",
            "title": "How GitHub monopoly is destroying the open source ecosystem",
            "date_modified": "2026-01-05T14:25:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46491749\">Comments</a>",
            "url": "https://github.com/huseyinbabal/taws",
            "title": "Show HN: Terminal UI for AWS",
            "date_modified": "2026-01-04T20:17:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46430815\">Comments</a>",
            "url": "https://github.com/quickemu-project/quickemu",
            "title": "Quickemu: Quickly create and run optimised Windows, macOS and Linux VMs",
            "date_modified": "2025-12-30T08:19:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46416863\">Comments</a>",
            "url": "https://rocketedge.com/2025/12/29/vibe-coding-for-ctos-the-real-cost-of-100-lines-of-code-ai-agents-vs-human-developers-without-losing-control/",
            "title": "Vibe Coding for CTOs: The Real Cost of 100 Lines of Code",
            "date_modified": "2025-12-29T02:33:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46397609\">Comments</a>",
            "url": "https://exe.dev/",
            "title": "Exe.dev/",
            "date_modified": "2025-12-26T23:42:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46391514\">Comments</a>",
            "url": "https://nesbitt.io/2025/12/24/package-managers-keep-using-git-as-a-database.html",
            "title": "Package managers keep using Git as a database, it never works out",
            "date_modified": "2025-12-26T12:46:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46372771\">Comments</a>",
            "url": "https://www.hobson.space/posts/nixcross/",
            "title": "Custom Cross Compiler with Nix",
            "date_modified": "2025-12-24T05:31:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46354737\">Comments</a>",
            "url": "https://willmcgugan.github.io/toad-released/",
            "title": "Toad is a unified experience for AI in the terminal",
            "date_modified": "2025-12-22T15:12:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46346796\">Comments</a>",
            "url": "https://loggingsucks.com/",
            "title": "Logging Sucks",
            "date_modified": "2025-12-21T18:09:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46327325\">Comments</a>",
            "url": "https://graphite.com/blog/graphite-joins-cursor",
            "title": "Cursor Acquires Graphite",
            "date_modified": "2025-12-19T16:07:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46291500\">Comments</a>",
            "url": "https://www.blacksmith.sh/blog/actions-pricing",
            "title": "The GitHub Actions control plane is no longer free",
            "date_modified": "2025-12-16T17:37:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46291156\">Comments</a>",
            "url": "https://resources.github.com/actions/2026-pricing-changes-for-github-actions/",
            "title": "Pricing Changes for GitHub Actions",
            "date_modified": "2025-12-16T17:12:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46257442\">Comments</a>",
            "url": "https://jyn.dev/what-is-a-build-system-anyway/",
            "title": "What is a build system, anyway?",
            "date_modified": "2025-12-13T19:58:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46245238\">Comments</a>",
            "url": "https://hatchet.run/blog/durable-execution",
            "title": "How to think about durable execution",
            "date_modified": "2025-12-12T15:49:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46222165\">Comments</a>",
            "url": "https://github.com/hashicorp/terraform-cdk",
            "title": "The future of Terraform CDK",
            "date_modified": "2025-12-10T19:14:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46207852\">Comments</a>",
            "url": "https://detail.dev/",
            "title": "Show HN: Detail, a Bug Finder",
            "date_modified": "2025-12-09T17:35:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46202624\">Comments</a>",
            "url": "https://dagger.io/",
            "title": "Dagger: Define software delivery workflows and dev environments",
            "date_modified": "2025-12-09T08:32:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46189692\">Comments</a>",
            "url": "https://nesbitt.io/2025/12/06/github-actions-package-manager.html",
            "title": "GitHub Actions Has a Package Manager, and It Might Be the Worst",
            "date_modified": "2025-12-08T08:15:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46189692\">Comments</a>",
            "url": "https://nesbitt.io/2025/12/06/github-actions-package-manager.html",
            "title": "GitHub Actions has a package manager, and it might be the worst",
            "date_modified": "2025-12-08T08:15:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46177664\">Comments</a>",
            "url": "https://github.com/observIQ/otel-distro-builder",
            "title": "OpenTelemetry Distribution Builder",
            "date_modified": "2025-12-06T23:41:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46162656\">Comments</a>",
            "url": "https://blog.cloudflare.com/5-december-2025-outage/",
            "title": "Cloudflare outage on December 5, 2025",
            "date_modified": "2025-12-05T15:35:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46160315\">Comments</a>",
            "url": "https://about.netflix.com/en/news/netflix-to-acquire-warner-bros",
            "title": "Netflix to Acquire Warner Bros. In an $82.7B Deal",
            "date_modified": "2025-12-05T12:21:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46157594\">Comments</a>",
            "url": "https://blog.ui.com/article/introducing-unifi-5g",
            "title": "UniFi 5G",
            "date_modified": "2025-12-05T07:06:38.000Z"
        },
        {
            "content_html": "Use Supabase as a platform for your own business and tools",
            "url": "https://supabase.com/blog/introducing-supabase-for-platforms",
            "title": "Introducing Supabase for Platforms",
            "date_modified": "2025-12-05T07:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46138238\">Comments</a>",
            "url": "https://mitchellh.com/writing/ghostty-non-profit",
            "title": "Ghostty Is Now Non-Profit",
            "date_modified": "2025-12-03T18:40:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46124267\">Comments</a>",
            "url": "https://bun.com/blog/bun-joins-anthropic",
            "title": "Anthropic acquires Bun",
            "date_modified": "2025-12-02T18:05:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46123921\">Comments</a>",
            "url": "https://devblogs.microsoft.com/typescript/progress-on-typescript-7-december-2025/",
            "title": "Progress on TypeScript 7 – December 2025",
            "date_modified": "2025-12-02T17:37:06.000Z"
        },
        {
            "content_html": "<p>Modern applications increasingly require complex and long-running coordination between services, such as multi-step payment processing, AI agent orchestration, or approval processes awaiting human decisions. Building these traditionally required significant effort to implement state management, handle failures, and integrate multiple infrastructure services.</p> \n<p>Starting today, you can use <a href=\"https://aws.amazon.com/lambda/lambda-durable-functions/\">AWS Lambda durable functions</a> to build reliable multi-step applications directly within the familiar AWS Lambda experience. Durable functions are regular Lambda functions with the same event handler and integrations you already know. You write sequential code in your preferred programming language, and durable functions track progress, automatically retry on failures, and suspend execution for up to one year at defined points,&nbsp;without paying for idle compute during waits.</p> \n<p>AWS Lambda durable functions use a checkpoint and replay mechanism, known as durable execution, to deliver these capabilities. After enabling a function for durable execution, you add the new open source durable execution SDK to your function code. You then use SDK primitives like “steps” to add automatic checkpointing and retries to your business logic and “waits” to efficiently suspend execution without compute charges. When execution terminates unexpectedly, Lambda resumes from the last checkpoint, replaying your event handler from the beginning while skipping completed operations.</p> \n<p><strong><span>Getting started with AWS Lambda durable functions<br> </span></strong>Let me walk you through how to use durable functions.</p> \n<p>First, I create a new <a href=\"https://console.aws.amazon.com/lambda?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">Lambda function in the console</a> and select <strong>Author from scratch</strong>. In the <strong>Durable execution</strong> section, I select <strong>Enable</strong>. Note that, durable function setting can only be set during function creation and currently can’t be modified for existing Lambda functions.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/25/2025-news-durable-function-4.png\" width=\"1066\" height=\"889\"></p> \n<p>After I create my Lambda durable function, I can get started with the provided code.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/25/2025-news-durable-function-5.png\" width=\"1311\" height=\"1405\"></p> \n<p>Lambda durable functions introduces two core primitives that handle state management and recovery:</p> \n<ul> \n <li><strong>Steps</strong>—The <code>context.step()</code> method adds automatic retries and checkpointing to your business logic. After a step is completed, it will be skipped during replay.</li> \n <li><strong>Wait</strong>—The <code>context.wait()</code> method pauses execution for a specified duration, terminating the function, suspending and resuming execution without compute charges.</li> \n</ul> \n<p>Additionally, Lambda durable functions provides other operations for more complex patterns: <code>create_callback()</code> creates a callback that you can use to await results for external events like API responses or human approvals, <code>wait_for_condition()</code> pauses until a specific condition is met like polling a REST API for process completion, and <code>parallel()</code> or <code>map()</code> operations for advanced concurrency use cases.</p> \n<p><span><strong>Building a production-ready order processing workflow<br> </strong></span>Now let’s expand the default example to build a production-ready order processing workflow. This demonstrates how to use callbacks for external approvals, handle errors properly, and configure retry strategies. I keep the code intentionally concise to focus on these core concepts. In a full implementation, you could enhance the validation step with <a href=\"https://console.aws.amazon.com/bedrock?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">Amazon Bedrock</a> to add AI-powered order analysis.</p> \n<p>Here’s how the order processing workflow works:</p> \n<ul> \n <li>First, <code>validate_order()</code> checks order data to ensure all required fields are present.</li> \n <li>Next, <code>send_for_approval()</code> sends the order for external human approval and waits for a callback response, suspending execution without compute charges.</li> \n <li>Then, <code>process_order()</code> completes order processing.</li> \n <li>Throughout the workflow, try-catch error handling distinguishes between terminal errors that stop execution immediately and recoverable errors inside steps that trigger automatic retries.</li> \n</ul> \n<p>Here’s the complete order processing workflow with step definitions and the main handler:</p> \n<pre><code>import random\nfrom aws_durable_execution_sdk_python import (\n    DurableContext,\n    StepContext,\n    durable_execution,\n    durable_step,\n)\nfrom aws_durable_execution_sdk_python.config import (\n    Duration,\n    StepConfig,\n    CallbackConfig,\n)\nfrom aws_durable_execution_sdk_python.retries import (\n    RetryStrategyConfig,\n    create_retry_strategy,\n)\n\n\n@durable_step\ndef validate_order(step_context: StepContext, order_id: str) -&gt; dict:\n    \"\"\"Validates order data using AI.\"\"\"\n    step_context.logger.info(f\"Validating order: {order_id}\")\n    # In production: calls Amazon Bedrock to validate order completeness and accuracy\n    return {\"order_id\": order_id, \"status\": \"validated\"}\n\n\n@durable_step\ndef send_for_approval(step_context: StepContext, callback_id: str, order_id: str) -&gt; dict:\n    \"\"\"Sends order for approval using the provided callback token.\"\"\"\n    step_context.logger.info(f\"Sending order {order_id} for approval with callback_id: {callback_id}\")\n    \n    # In production: send callback_id to external approval system\n    # The external system will call Lambda SendDurableExecutionCallbackSuccess or\n    # SendDurableExecutionCallbackFailure APIs with this callback_id when approval is complete\n    \n    return {\n        \"order_id\": order_id,\n        \"callback_id\": callback_id,\n        \"status\": \"sent_for_approval\"\n    }\n\n\n@durable_step\ndef process_order(step_context: StepContext, order_id: str) -&gt; dict:\n    \"\"\"Processes the order with retry logic for transient failures.\"\"\"\n    step_context.logger.info(f\"Processing order: {order_id}\")\n    # Simulate flaky API that sometimes fails\n    if random.random() &gt; 0.4:\n        step_context.logger.info(\"Processing failed, will retry\")\n        raise Exception(\"Processing failed\")\n    return {\n        \"order_id\": order_id,\n        \"status\": \"processed\",\n        \"timestamp\": \"2025-11-27T10:00:00Z\",\n    }\n\n\n@durable_execution\ndef lambda_handler(event: dict, context: DurableContext) -&gt; dict:\n    try:\n        order_id = event.get(\"order_id\")\n        \n        # Step 1: Validate the order\n        validated = context.step(validate_order(order_id))\n        if validated[\"status\"] != \"validated\":\n            raise Exception(\"Validation failed\")  # Terminal error - stops execution\n        context.logger.info(f\"Order validated: {validated}\")\n        \n        # Step 2: Create callback\n        callback = context.create_callback(\n            name=\"awaiting-approval\",\n            config=CallbackConfig(timeout=Duration.from_minutes(3))\n        )\n        context.logger.info(f\"Created callback with id: {callback.callback_id}\")\n        \n        # Step 3: Send for approval with the callback_id\n        approval_request = context.step(send_for_approval(callback.callback_id, order_id))\n        context.logger.info(f\"Approval request sent: {approval_request}\")\n        \n        # Step 4: Wait for the callback result\n        # This blocks until external system calls SendDurableExecutionCallbackSuccess or SendDurableExecutionCallbackFailure\n        approval_result = callback.result()\n        context.logger.info(f\"Approval received: {approval_result}\")\n        \n        # Step 5: Process the order with custom retry strategy\n        retry_config = RetryStrategyConfig(max_attempts=3, backoff_rate=2.0)\n        processed = context.step(\n            process_order(order_id),\n            config=StepConfig(retry_strategy=create_retry_strategy(retry_config)),\n        )\n        if processed[\"status\"] != \"processed\":\n            raise Exception(\"Processing failed\")  # Terminal error\n        \n        context.logger.info(f\"Order successfully processed: {processed}\")\n        return processed\n        \n    except Exception as error:\n        context.logger.error(f\"Error processing order: {error}\")\n        raise error  # Re-raise to fail the execution\n</code></pre> \n<p>This code demonstrates several important concepts:</p> \n<ul> \n <li><strong>Error handling</strong>—The try-catch block handles terminal errors. When an unhandled exception is thrown outside of a step (like the validation check), it terminates the execution immediately. This is useful when there’s no point in retrying, such as invalid order data.</li> \n <li><strong>Step retries</strong>—Inside the <code>process_order</code> step, exceptions trigger automatic retries based on the default (step 1) or configured <code>RetryStrategy</code> (step 5). This handles transient failures like temporary API unavailability.</li> \n <li><strong>Logging</strong>—I use <code>context.logger</code> for the main handler and <code>step_context.logger</code> inside steps. The context logger suppresses duplicate logs during replay.</li> \n</ul> \n<p>Now I create a test event with <code>order_id</code> and invoke the function asynchronously to start the order workflow. I navigate to the <strong>Test</strong> tab and fill in the optional <strong>Durable execution name</strong> to identify this execution. Note that, durable functions provides built-in idempotency. If I invoke the function twice with the same execution name, the second invocation returns the existing execution result instead of creating a duplicate.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/28/2025-news-durable-function-rev-8-2.png\" width=\"1297\" height=\"1257\"></p> \n<p>I can monitor the execution by navigating to the <strong>Durable executions</strong> tab in the Lambda console:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/29/2025-news-durable-function-rev-9-1.png\" width=\"693\" height=\"624\"></p> \n<p>Here I can see each step’s status and timing. The execution shows <code>CallbackStarted</code> followed by <code>InvocationCompleted</code>, which indicates the function has terminated and execution is suspended to avoid idle charges while waiting for the approval callback.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/29/2025-news-durable-function-rev-3-1-1.png\" width=\"1397\" height=\"419\"></p> \n<p>I can now complete the callback directly from the console by choosing <strong>Send success</strong> or <strong>Send failure</strong>, or programmatically using the Lambda API.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/29/2025-news-durable-function-rev-3-2.png\" width=\"1645\" height=\"725\"></p> \n<p>I choose <strong>Send success</strong>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/27/2025-news-durable-function-rev-6.png\" width=\"1342\" height=\"967\"></p> \n<p>After the callback completes, the execution resumes and processes the order. If the <code>process_order</code> step fails due to the simulated flaky API, it automatically retries based on the configured strategy. Once all retries succeed, the execution completes successfully.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/29/2025-news-durable-function-rev-3-3.png\" width=\"1387\" height=\"834\"></p> \n<p><span><strong>Monitoring executions with Amazon EventBridge<br> </strong></span>You can also monitor durable function executions using Amazon EventBridge. Lambda automatically sends execution status change events to the default event bus, allowing you to build downstream workflows, send notifications, or integrate with other AWS services.</p> \n<p>To receive these events, create an EventBridge rule on the default event bus with this pattern:</p> \n<pre><code>{\n  \"source\": [\"aws.lambda\"],\n  \"detail-type\": [\"Durable Execution Status Change\"]\n}\n</code></pre> \n<p><strong><span>Things to know<br> </span></strong>Here are key points to note:</p> \n<ul> \n <li><strong>Availability</strong>—Lambda durable functions are now available in US East (Ohio) AWS Region. For the latest Region availability, visit the <a href=\"https://builder.aws.com/build/capabilities/explore?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">AWS Capabilities by Region</a> page.</li> \n <li><strong>Programming language support</strong>—At launch, AWS Lambda durable functions supports JavaScript/TypeScript (Node.js 22/24) and Python (3.13/3.14). We recommend bundling the durable execution SDK with your function code using your preferred package manager. The SDKs are fast-moving, so you can easily update dependencies as new features become available.</li> \n <li><strong>Using Lambda versions</strong>—When deploying durable functions to production, use Lambda versions to ensure replay always happens on the same code version. If you update your function code while an execution is suspended, replay will use the version that started the execution, preventing inconsistencies from code changes during long-running workflows.</li> \n <li><strong>Testing your durable functions</strong>—You can test durable functions locally without AWS credentials using the separate testing SDK with pytest integration and the <a href=\"https://aws.amazon.com/serverless/sam/?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">AWS Serverless Application Model (AWS SAM) command line interface (CLI)</a> for more complex integration testing.</li> \n <li><strong>Open source SDKs</strong>—The durable execution SDKs are open source for <a href=\"https://github.com/aws/aws-durable-execution-sdk-js\">JavaScript/TypeScript</a> and <a href=\"https://github.com/aws/aws-durable-execution-sdk-python\">Python</a>. You can review the source code, contribute improvements, and stay updated with the latest features.</li> \n <li><strong>Pricing</strong>—To learn more on AWS Lambda durable functions pricing, refer to the <a href=\"https://aws.amazon.com/lambda/pricing/?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">AWS Lambda pricing</a> page.</li> \n</ul> \n<p>Get started with AWS Lambda durable functions by visiting the <a href=\"https://console.aws.amazon.com/lambda?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">AWS Lambda console</a>. To learn more, refer to <a href=\"https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html?trk=c4ea046f-18ad-4d23-a1ac-cdd1267f942c&amp;sc_channel=el\">AWS Lambda durable functions</a> documentation page.</p> \n<p>Happy building!</p> \n<p>— <a href=\"https://www.linkedin.com/in/donnieprakoso\">Donnie</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/",
            "title": "Build multi-step applications and AI workflows with AWS Lambda durable functions",
            "date_modified": "2025-12-02T16:12:19.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing the public preview of AWS DevOps Agent, a <a href=\"https://aws.amazon.com/ai/frontier-agents\">frontier agent</a> that helps you respond to incidents, identify root causes, and prevent future issues through systematic analysis of past incidents and operational patterns.</p> \n<p>Frontier agents represent a new class of AI agents that are autonomous, massively scalable, and work for hours or days without constant intervention.</p> \n<p>When production incidents occur, on-call engineers face significant pressure to quickly identify root causes while managing stakeholder communications. They must analyze data across multiple monitoring tools, review recent deployments, and coordinate response teams. After service restoration, teams often lack bandwidth to transform incident learnings into systematic improvements.</p> \n<p>AWS DevOps Agent is your always-on, autonomous on-call engineer. When issues arise, it automatically correlates data across your operational toolchain, from metrics and logs to recent code deployments in GitHub or GitLab. It identifies probable root causes and recommends targeted mitigations, helping reduce mean time to resolution. The agent also manages incident coordination, using Slack channels for stakeholder updates and maintaining detailed investigation timelines.</p> \n<p>To get started, you connect AWS DevOps Agent to your existing tools through the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>. The agent works with popular services such as <a href=\"https://aws.amazon.com/cloudwatch/\">Amazon CloudWatch</a>, <a href=\"https://www.datadoghq.com/\">Datadog</a>, <a href=\"https://www.dynatrace.com/\">Dynatrace</a>, <a href=\"https://newrelic.com/\">New Relic</a>, and <a href=\"https://www.splunk.com/\">Splunk</a> for observability data, while integrating with GitHub Actions and GitLab CI/CD to track deployments and their impact on your cloud resources. Through the bring your own (BYO) <a href=\"https://modelcontextprotocol.io/docs/getting-started/intro\">Model Context Protocol (MCP)</a> server capability, you can also integrate additional tools such as your organization’s custom tools, specialized platforms or open source observability solutions, such as <a href=\"https://grafana.com/\">Grafana</a> and <a href=\"https://prometheus.io/\">Prometheus</a> into your investigations.</p> \n<p>The agent acts as a virtual team member and can be configured to automatically respond to incidents from your ticketing systems. It includes built-in support for <a href=\"https://www.servicenow.com/\">ServiceNow</a>, and through configurable <a href=\"https://en.wikipedia.org/wiki/Webhook\">webhooks</a>, can respond to events from other incident management tools like <a href=\"https://www.pagerduty.com/\">PagerDuty</a>. As investigations progress, the agent updates tickets and relevant Slack channels with its findings. All of this is powered by an intelligent application topology the agent builds—a comprehensive map of your system components and their interactions, including deployment history that helps identify potential deployment-related causes during investigations.</p> \n<p><span><strong>Let me show you how it works<br> </strong></span>To show you how it works, I deployed a straigthforward <a href=\"https://aws.amazon.com/lambda/\">AWS Lambda</a> function that intentionally generates errors when invoked. I deployed it in an <a href=\"https://aws.amazon.com/cloudformation/\">AWS CloudFormation</a> stack.</p> \n<p><strong>Step 1: Create an Agent Space</strong></p> \n<p>An Agent Space defines the scope of what AWS DevOps Agent can access as it performs tasks.</p> \n<p>You can organize Agent Spaces based on your operational model. Some teams align an Agent Space with a single application, others create one per on-call team managing multiple services, and some organizations use a centralized approach. For this demonstration, I’ll show you how to create an Agent Space for a single application. This setup helps isolate investigations and resources for that specific application, making it easier to track and analyze incidents within its context.</p> \n<p>In the AWS DevOps Agent section of the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>, I select <strong>Create Agent Space</strong>, enter a name for this space and create the <a href=\"https://aws.amazon.com/iam/\">AWS Identity and Access Management (IAM)</a> roles it uses to introspect AWS resources in my or others’ AWS accounts.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-15-01.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-15-01-1024x570.png\" alt=\"AWS DevOps Agent - Create an Agent Space\" width=\"1024\" height=\"570\"></a>For this demo, I choose to enable the AWS DevOps Agent web app; more about this later. This can be done at a later stage.</p> \n<p>When ready, I choose <strong>Create</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-15-07.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-15-07-1024x472.png\" alt=\"AWS DevOps Agent - Enable Web App\" width=\"1024\" height=\"472\"></a>After it has been created, I choose the <strong>Topology</strong> tab.</p> \n<p>This view shows the key resources, entities, and relationships AWS DevOps Agent has selected as a foundation for performing its tasks efficiently. It doesn’t represent everything AWS DevOps Agent can access or see, only what the Agent considers most relevant right now. By default, the Topology includes the AWS resources that are contained in my account. As your agent completes more tasks, it will discover and add new resources to this list.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-19-12.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-19-12-1024x650.png\" alt=\"AWS DevOps Agent - Topology\" width=\"1024\" height=\"650\"></a></p> \n<p><strong>Step 2: Configure the AWS DevOps web app for the operators</strong></p> \n<p>The AWS DevOps Agent web app provides a web interface for on-call engineers to manually trigger investigations, view investigation details including relevant topology elements, steer investigations, and ask questions about an investigation.</p> \n<p>I can access the web app directly from my Agent Space in the AWS console by choosing the <strong>Operator access</strong> link. Alternatively, I can use <a href=\"https://aws.amazon.com/iam/identity-center/\">AWS IAM Identity Center</a> to configure user access for my team. IAM Identity Center lets me manage users and groups directly or connect to an identity provider (IdP), providing a centralized way to control who can access the AWS DevOps Agent web app.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/28/2025-11-28_19-52-39.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/28/2025-11-28_19-52-39-1024x545.png\" alt=\"AWS DevOps Agent - web app access\" width=\"1024\" height=\"545\"></a></p> \n<p>At this stage, I have an Agent Space all set up to focus investigations and resources for this speciﬁc application, and I’ve enabled the DevOps team to initiate investigations using the web app.</p> \n<p>Now that the one-time setup for this application is done, I start invoking the faulty Lambda function. It generates errors at each invocation. The CloudWatch alarm associated with the Lambda errors count turns on to <strong>ALARM</strong> state. In real life, you might receive an alert from external services, such as ServiceNow. You can configure AWS DevOps Agent to automatically start investigations when receiving such alerts.</p> \n<p>For this demo, I manually start the investigation by selecting <strong>Start Investigation</strong>.</p> \n<p>You can also choose from several preconfigured starting points to quickly begin your investigation: Latest alarm to investigate your most recent triggered alarm and analyze the underlying metrics and logs to determine the root cause, High CPU usage to investigate high CPU utilization metrics across your compute resources and identify which processes or services are consuming excessive resources, or Error rate spike to investigate the recent increase in application error rates by analyzing metrics, application logs, and identifying the source of failures.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/28/2025-11-28_19-55-05.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/28/2025-11-28_19-55-05-1024x528.png\" alt=\"AWS DevOps Agent - web app\" width=\"1024\" height=\"528\"></a></p> \n<p>I enter some information, such as <strong>Investigation details</strong>, <strong>Investigation starting point</strong>, the <strong>Date and time of the incident</strong>, the <strong>AWS Account ID for the incident.</strong></p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-39-07-v3.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-39-07-v3-601x1024.png\" alt=\"- web app - start investigation\" width=\"601\" height=\"1024\"></a></p> \n<p>In the AWS DevOps Agent web app, you can watch the investigation unfold in real time. The agent identifies the application stack. It correlates metrics from CloudWatch, examines logs from CloudWatch Logs or external sources, such as Splunk, reviews recent code changes from GitHub, and analyzes traces from <a href=\"https://aws.amazon.com/x-ray/\">AWS X-Ray</a>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-45-49.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-45-49-1024x900.png\" alt=\"- web app - application stack\" width=\"1024\" height=\"900\"></a></p> \n<p>It identifies the error patterns and provides a detailed investigation summary. In the context of this demo, the investigation reveals that these are intentional test exceptions, shows the timeline of function invocations leading to the alarm, and even suggests monitoring improvements for error handling.</p> \n<p>The agent uses a dedicated incident channel in Slack, notifies on-call teams if needed, and provides real-time status updates to stakeholders. Through the investigation chat interface, you can interact directly with the agent by asking clarifying questions such as “which logs did you analyze?” or steering the investigation by providing additional context, such as “focus on these specific log groups and rerun your analysis.” If you need expert assistance, you can create an AWS Support case with a single click, automatically populating it with the agent’s findings, and engage with AWS Support experts directly through the investigation chat window.</p> \n<p>For this demo, the AWS DevOps Agent correctly identified manual activities in the Lambda console to invoke a function that intentionally triggers errors <img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/1f607.png\" alt=\"😇\">.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-50-57.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_08-50-57-1024x697.png\" alt=\"- web app - root cause\" width=\"1024\" height=\"697\"></a></p> \n<p>Beyond incident response, AWS DevOps Agent analyzes my recent incidents to identify high-impact improvements that prevent future issues.</p> \n<p>During active incidents, the agent offers immediate mitigation plans through its incident mitigations tab to help restore service quickly. Mitigation plans consist of specs that provide detailed implementation guidance for developers and agentic development tools like <a href=\"https://kiro.dev/\">Kiro</a>.</p> \n<p>For longer-term resilience, it identifies potential enhancements by examining gaps in observability, infrastructure configurations, and deployment pipeline. My straightforward demo that triggered intentional errors was not enough to generate relevant recommendations though.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_09-08-36.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/11/21/2025-11-21_09-08-36-1024x390.png\" alt=\"AWS DevOps Agent - web app - recommendations\" width=\"1024\" height=\"390\"></a></p> \n<p>For example, it might detect that a critical service lacks multi-AZ deployment and comprehensive monitoring. The agent then creates detailed recommendations with implementation guidance, considering factors like operational impact and implementation complexity. In an upcoming quick follow-up release, the agent will expand its analysis to include code bugs and testing coverage improvements.</p> \n<p><span><strong>Availability<br> </strong></span>You can try AWS DevOps Agent today in the US East (N. Virginia) Region. Although the agent itself runs in US East (N. Virginia) (<code>us-east-1</code>), it can monitor applications deployed in any Region, across multiple AWS accounts.</p> \n<p>During the preview period, you can use AWS DevOps Agent at no charge, but there will be a limit on the number of agent task hours per month.</p> \n<p>As someone who has spent countless nights debugging production issues, I’m particularly excited about how AWS DevOps Agent combines deep operational insights with practical, actionable recommendations. The service helps teams move from reactive firefighting to proactive system improvement.</p> \n<p>To learn more and sign up for the preview, visit <a href=\"https://aws.amazon.com/devops-agent\">AWS DevOps Agent</a>.&nbsp;I look forward to hearing how AWS DevOps Agent helps improve your operational efficiency.</p> \n<p><a href=\"https://linktr.ee/sebsto\">— seb</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/aws-devops-agent-helps-you-accelerate-incident-response-and-improve-system-reliability-preview/",
            "title": "AWS DevOps Agent helps you accelerate incident response and improve system reliability (preview)",
            "date_modified": "2025-12-02T16:05:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46119214\">Comments</a>",
            "url": "https://chrisebert.net/comparing-aws-lambda-arm64-vs-x86_64-performance-across-multiple-runtimes-in-late-2025/",
            "title": "Comparing AWS Lambda ARM64 vs. x86_64 Performance Across Runtimes in Late 2025",
            "date_modified": "2025-12-02T09:11:41.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/4wqk4q/benchmarking_read_latency_aws_s3_s3\">Comments</a></p>",
            "url": "https://nixiesearch.substack.com/p/benchmarking-read-latency-of-aws",
            "title": "Benchmarking read latency of AWS S3, S3 Express, EBS and Instance store",
            "date_modified": "2025-12-01T21:03:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46110842\">Comments</a>",
            "url": "https://github.com/coder/ghostty-web",
            "title": "Ghostty compiled to WASM with xterm.js API compatibility",
            "date_modified": "2025-12-01T18:17:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46103571\">Comments</a>",
            "url": "https://dineshpandiyan.com/blog/stacked-diffs-with-rebase-onto/",
            "title": "Stacked Diffs with git rebase —onto",
            "date_modified": "2025-12-01T04:47:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46098838\">Comments</a>",
            "url": "https://www.humanlayer.dev/blog/writing-a-good-claude-md",
            "title": "Writing a good Claude.md",
            "date_modified": "2025-11-30T17:56:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46065997\">Comments</a>",
            "url": "https://github.com/BinSquare/ERA",
            "title": "Show HN: Era – Open-source local sandbox for AI agents",
            "date_modified": "2025-11-27T05:28:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46045987\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=46045987",
            "title": "Launch HN: Onyx (YC W24) – Open-source chat UI",
            "date_modified": "2025-11-25T14:20:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46038047\">Comments</a>",
            "url": "https://www.anthropic.com/engineering/advanced-tool-use",
            "title": "Claude Advanced Tool Use",
            "date_modified": "2025-11-24T19:21:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=46037637\">Comments</a>",
            "url": "https://www.anthropic.com/news/claude-opus-4-5",
            "title": "Claude Opus 4.5",
            "date_modified": "2025-11-24T18:53:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45963949\">Comments</a>",
            "url": "https://www.cloudflarestatus.com/incidents/8gmgl950y3h7",
            "title": "Cloudflare Global Network experiencing issues",
            "date_modified": "2025-11-18T11:48:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45963780\">Comments</a>",
            "url": "https://www.cloudflarestatus.com/?t=1",
            "title": "Cloudflare, X, More are down",
            "date_modified": "2025-11-18T11:35:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45933900\">Comments</a>",
            "url": "https://excalidraw.com/#json=kMtNOJfH_UUOzBqt7WXx9,cyuXonQjb-Kor72f0F5YXg",
            "title": "Show HN: A visual guide to learning Jujutsu (JJ)",
            "date_modified": "2025-11-15T00:32:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45930598\">Comments</a>",
            "url": "https://www.claude.com/blog/structured-outputs-on-the-claude-developer-platform",
            "title": "Structured outputs on the Claude Developer Platform",
            "date_modified": "2025-11-14T19:04:23.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing the general availability of provisioned mode for <a href=\"https://aws.amazon.com/lambda\">AWS Lambda</a> with <a href=\"https://aws.amazon.com/sqs/\">Amazon Simple Queue Service (Amazon SQS)</a> <a href=\"https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html\">Event Source Mapping (ESM)</a>, a new feature that customers can use to optimize the throughput of their event-driven applications by configuring dedicated polling resources. Using this new capability, which provides 3x faster scaling, and 16x higher concurrency, you can process events with lower latency, handle sudden traffic spikes more effectively, and maintain precise control over your event processing resources.</p> \n<p>Modern applications increasingly rely on event-driven architectures where services communicate through events and messages. Amazon SQS is commonly used as an event source for Lambda functions, so developers can build loosely coupled, scalable applications. Although the SQS ESM automatically handles queue polling and function invocation, customers with stringent performance requirements have asked for more control over the polling behavior to handle spiky traffic patterns and maintain low processing latency.</p> \n<p>Provisioned mode for SQS ESM addresses these needs by introducing event pollers, which are dedicated resources that remain ready to handle expected traffic patterns. These event pollers can auto scale up to 1000 per concurrent executions per minute, more than three times faster than before to handle sudden spikes in event traffic and provide up to 20,000 concurrency–16 times higher capacity to process millions of events with Lambda functions. This enhanced scaling behavior helps customers maintain predictable low latency even during traffic surges.</p> \n<p>Enterprises across various industries, from financial services to gaming companies, are using AWS Lambda with Amazon SQS to process real-time events for their mission-critical applications. These organizations, which include some of the largest online gaming platforms and financial institutions, require consistent subsecond processing times for their event-driven workloads, particularly during periods of peak usage. Provisioned mode for SQS ESM is a capability you can use to meet your stringent performance requirements while maintaining cost controls.</p> \n<p><span><strong>Enhanced control and performance</strong></span></p> \n<p>With provisioned mode, you can configure both minimum and maximum numbers of event pollers for your SQS ESM. Each event poller represents a unit of compute that handles queue polling, event batching, and filtering before invoking Lambda functions. Each event poller can handle up to 1 MB/sec of throughput, up to 10 concurrent invokes, or up to 10 SQS polling API calls per second. By setting a minimum number of event pollers, you enable your application to maintain a baseline processing capacity that can immediately handle sudden traffic increases. We recommend that you set the minimum event pollers required to handle your known peak workload requirements. The optional maximum setting helps prevent overloading downstream systems by limiting the total processing throughput.</p> \n<p>The new mode delivers significant improvements in how your event-driven applications handle varying workloads. When traffic increases, your ESM detects the growing backlog within seconds and dynamically scales event pollers between your configured minimum and maximum values three times faster than before. This enhanced scaling capability is complemented by a substantial increase in processing capacity, with support for up to 2 GBps of aggregate traffic, and up to 20K concurrent requests—16x higher than previously possible. By maintaining a minimum number of ready-to-use event pollers, your application achieves predictable performance, handling sudden traffic spikes without the delay typically associated with scaling up resources. During low traffic periods, your ESM automatically scales down to your configured minimum number of event pollers, which means you can optimize costs while maintaining responsiveness.</p> \n<p><span><strong>Let’s try it out</strong></span></p> \n<p>Enabling provisioned mode is straightforward in the <a href=\"https://aws.amazon.com/console/\">AWS Management Console</a>. You need to already have an SQS queue configured and a Lambda function. To get started, in the <strong>Configuration</strong> tab for your Lambda function, choose <strong>Triggers</strong>, then <strong>Add trigger</strong>. This will bring up a user interface where you can configure your trigger. Choose <strong>SQS</strong> from the dropdown menu for source and then select the <strong>SQS queue</strong> you want to use.</p> \n<p>Under <strong>Event poller configuration</strong>, you will now see a new option called <strong>Provisioned mode</strong>. Select <strong>Configure</strong> to reveal settings for <strong>Minimum event pollers</strong> and <strong>Maximum event pollers</strong>, each with defaults and minimum and maximum values displayed.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/07/sqs-provision-01-1024x541.png\" alt=\"Configuration panel for SQS provisioned Mode\" width=\"1024\" height=\"541\"></p> \n<p>After you have configured <strong>Provisioned mode</strong>, you can save your trigger. If you need to make changes later, you can find the current configuration under the <strong>Triggers</strong> tab in the AWS Lambda configuration section, and you can modify your current settings there.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/07/sqs-provision-02-1024x475.png\" alt=\"SQS Provisioned Poller confiig\" width=\"1024\" height=\"475\"></p> \n<p><span><strong>Monitoring and observability</strong></span></p> \n<p>You can monitor your provisioned mode usage through Amazon CloudWatch metrics. The ProvisionedPollers metric shows the number of active event pollers processing events in one-minute windows.</p> \n<p><span><strong>Now available</strong></span></p> \n<p>Provisioned mode for Lambda SQS ESM is available today in all commercial <a href=\"https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#region\">AWS Regions</a>. You can start using this feature through the AWS Management Console, <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a>, or <a href=\"https://aws.amazon.com/developer/tools/\">AWS SDKs</a>. Pricing is based on the number of event pollers provisioned and the duration they’re provisioned for, measured in Event Poller Units (EPUs). Each EPU supports up to 1 MB per second throughput capacity per event poller, with minimum 2 event pollers per ESM. See the <a href=\"https://aws.amazon.com/lambda/pricing/\">AWS pricing page</a> for more information on EPU charges.</p> \n<p>To learn more about provisioned mode for SQS ESM, visit the AWS Lambda <a href=\"https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html\">documentation</a>. Start building more responsive event-driven applications today with enhanced control over your event processing resources.</p>",
            "url": "https://aws.amazon.com/blogs/aws/aws-lambda-enhances-sqs-processing-with-new-provisioned-mode-3x-faster-scaling-16x-higher-capacity/",
            "title": "AWS Lambda enhances event processing with provisioned mode for SQS event-source mapping",
            "date_modified": "2025-11-14T17:45:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45912785\">Comments</a>",
            "url": "https://seed.line.me/index_en.html",
            "title": "Seed. LINE's Custom Typeface",
            "date_modified": "2025-11-13T09:36:51.000Z"
        },
        {
            "content_html": "<div><p><span>Google and the cloud-native community have consistently strengthened Kubernetes to support modern applications. At KubeCon EU 2025 earlier this year, </span><span>we announced a series of enhancements</span><span> to Kubernetes </span><a href=\"https://cloud.google.com/blog/products/containers-kubernetes/google-bytedance-and-red-hat-improve-ai-on-kubernetes?e=48754805\"><span>to better support AI inference</span></a><span>. Today, at KubeCon NA 2025, we’re focused on making Kubernetes the most open and scalable platform for AI agents, with the introduction of </span><strong>Agent Sandbox</strong><span>.</span></p>\n<p><span>Consider the challenge that AI agents represent. AI agents help applications go from answering simple queries to performing complex, multi-step tasks to achieve the users objective. Provided a request like “visualize last quarters sales data”, the agent has to use one tool to query the data and another to process that data into a graph and return to the user.&nbsp; Where traditional software is predictable, AI agents can make their own decisions about when and how to use tools at their disposal to achieve a user's objective, including generating code, using computer terminals and even browsers.</span></p>\n<p><span>Without strong security and operational guardrails, orchestrating powerful, non-deterministic agents can introduce significant risks. Providing kernel-level isolation for agents that execute code and commands is non-negotiable. AI and agent-based workloads also have additional infrastructure needs compared to traditional applications. Most notably, they need to orchestrate thousands of sandboxes as ephemeral environments, rapidly creating and deleting them as needed while ensuring they have limited network access.&nbsp;&nbsp;</span></p>\n<p><span>With its maturity, security, and scalability, we believe Kubernetes provides the most suitable foundation for running AI agents. Yet it still needs to evolve to meet the needs of agent code execution and computer use scenarios. Agent Sandbox is a powerful first step in that direction.&nbsp;</span></p>\n<h3><strong>Strong isolation at scale</strong></h3>\n<p><span>Agentic code execution and computer use require an isolated sandbox to be provisioned for each task. Further, users expect infrastructure to keep pace even as thousands of sandboxes are scheduled in parallel.&nbsp;</span></p>\n<p><span>At its core, </span><span>Agent Sandbox is a new Kubernetes primitive built with the Kubernetes community that’s designed specifically for agent code execution and computer use, delivering the performance and scale needed for the next generation of agentic AI workloads. Foundationally built on gVisor with additional support for Kata Containers for runtime isolation, Agent Sandbox provides a secure boundary to reduce the risk of vulnerabilities that could lead to data loss, exfiltration or damage to production systems. We’re continuing our commitment to open source, building Agent Sandbox as a Cloud Native Computing Foundation (CNCF) project in the Kubernetes community. </span></p></div>\n<div>\n\n\n\n\n\n\n  \n    <div>\n      <div>\n  \n\n    <figure>\n\n      \n      \n        \n        <img src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/1_K1VZDUQ.max-1000x1000.jpg\" alt=\"1\">\n        \n        \n      \n    </figure>\n\n  \n      </div>\n    </div>\n  \n\n\n\n\n</div>\n<div><h3><strong>Enhanced performance on GKE</strong></h3>\n<p><span>At the same time, you need to optimize performance as you scale your agents to deliver the best agent user-experience at the lowest cost. When you use Agent Sandbox on Google Kubernetes Engine (GKE), you can leverage managed gVisor in </span><a href=\"https://docs.cloud.google.com/kubernetes-engine/docs/concepts/sandbox-pods\"><span>GKE Sandbox</span></a><span> and the </span><a href=\"https://cloud.google.com/blog/products/containers-kubernetes/container-optimized-compute-delivers-autoscaling-for-autopilot?e=48754805\"><span>container-optimized compute platform</span></a><span> to horizontally scale your sandboxes faster. Agent Sandbox also enables low-latency sandbox execution by enabling administrators to configure pre-warmed pools of sandboxes. With this feature, Agent Sandbox delivers sub-second latency for fully isolated agent workloads, up to a 90% improvement over cold starts.</span></p>\n<p><span>The same isolation property that makes a sandbox safe, makes it more susceptible to compute underutilization. Reinitializing each sandbox environment with a script can be brittle and slow, and idle sandboxes often waste valuable compute cycles. In a perfect world, you could take a snapshot of running sandbox environments to start them from a specific state.</span></p>\n<p><strong>Pod Snapshots</strong><span> is a new, GKE-exclusive feature that enables full checkpoint and restore of running pods. Pod Snapshots drastically reduces startup latency of agent and AI workloads. When combined with Agent Sandbox, Pod Snapshots lets teams provision sandbox environments from snapshots, so they can start up in seconds. GKE Pod Snapshots supports snapshot and restore of both CPU- and GPU-based workloads, bringing pod start times from minutes down to seconds. With Pod Snapshots, any idle sandbox can be snapshotted and suspended, saving significant compute cycles with little to no disruption for end-users.</span></p></div>\n<div>\n\n\n\n\n\n\n  \n    <div>\n      <div>\n  \n\n    <figure>\n\n      \n      \n        \n        <img src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/2_NJWlanH.max-1000x1000.jpg\" alt=\"2\">\n        \n        \n      \n    </figure>\n\n  \n      </div>\n    </div>\n  \n\n\n\n\n</div>\n<div><h3><strong>Built for AI engineers</strong></h3>\n<p><span>Teams building today’s agentic AI or reinforcement learning (RL) systems should not have to be infrastructure experts. We built Agent Sandbox with AI engineers in mind, designing an API and Python SDK that lets them manage the lifecycle of their sandboxes, without worrying about the underlying infrastructure.&nbsp; </span></p></div>\n<div><dl>\n    <dt>code_block</dt>\n    <dd>&lt;ListValue: [StructValue([('code', 'from agentic_sandbox import Sandbox\\r\\n\\r\\n# The SDK abstracts all YAML into a simple context manager \\r\\nwith Sandbox(template_name=\"python3-template\",namespace=\"ai-agents\") as sandbox:\\r\\n\\r\\n   # Execute a command inside the sandbox\\r\\n   result = sandbox.run(\"print(\\'Hello from inside the sandbox!\\')\")'), ('language', ''), ('caption', &lt;wagtail.rich_text.RichText object at 0x7fe6dcdaa070&gt;)])]&gt;</dd>\n</dl></div>\n<div><p><span>This separation of concern enables both an AI developer-friendly experience and the operational control and extensibility that Kubernetes administrators and operators expect.</span></p>\n<h3><strong>Get started today</strong></h3>\n<p><span>Agentic AI represents a profound shift for software development and infrastructure teams. Agent Sandbox and GKE can help&nbsp; deliver the isolation and performance your agents need. </span><span>Agent Sandbox is available in open source and can be </span><span>deployed on GKE today</span><span>. GKE Pod Snapshots is available in limited preview and will be available to all GKE customers later this year. To get started, check out the Agent Sandbox </span><a href=\"https://agent-sandbox.sigs.k8s.io/\" rel=\"noopener\" target=\"_blank\"><span>documentation</span></a><span>&nbsp; and </span><a href=\"https://docs.cloud.google.com/kubernetes-engine/docs/how-to/agent-sandbox\"><span>quick start</span></a><span>. We are excited to see what you build!</span></p></div>",
            "url": "https://cloud.google.com/blog/products/containers-kubernetes/agentic-ai-on-kubernetes-and-gke/",
            "title": "Introducing Agent Sandbox: Strong guardrails for agentic AI on Kubernetes and GKE",
            "date_modified": "2025-11-11T12:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45876308\">Comments</a>",
            "url": "https://maxdeviant.com/posts/2025/head-in-the-zed-cloud/",
            "title": "Head in the Zed Cloud",
            "date_modified": "2025-11-10T14:23:17.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/h6ikaz/head_zed_cloud\">Comments</a></p>",
            "url": "https://maxdeviant.com/posts/2025/head-in-the-zed-cloud/",
            "title": "Head in the Zed Cloud",
            "date_modified": "2025-11-10T14:22:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45860865\">Comments</a>",
            "url": "https://www.thedailybeast.com/air-traffic-controllers-start-resigning-as-shutdown-bites/",
            "title": "US Air Traffic Controllers Start Resigning as Shutdown Bites",
            "date_modified": "2025-11-08T23:06:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45824243\">Comments</a>",
            "url": "https://github.com/openpcc/openpcc",
            "title": "Open Source Implementation of Apple's Private Compute Cloud",
            "date_modified": "2025-11-05T15:52:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45813767\">Comments</a>",
            "url": "https://cognition.ai/blog/codemaps",
            "title": "Codemaps: Understand Code, Before You Vibe It",
            "date_modified": "2025-11-04T17:47:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45800955\">Comments</a>",
            "url": "https://blog.yakkomajuri.com/blog/python-to-node",
            "title": "Why We Migrated from Python to Node.js",
            "date_modified": "2025-11-03T16:35:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45774571\">Comments</a>",
            "url": "https://lil.law.harvard.edu/blog/2025/10/24/rethinking-data-discovery-for-libraries-and-digital-humanities/",
            "title": "Use DuckDB-WASM to query TB of data in browser",
            "date_modified": "2025-10-31T17:37:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45759572\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=v9Ob5yPpC0A",
            "title": "Jujutsu at Google [video]",
            "date_modified": "2025-10-30T13:00:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45749017\">Comments</a>",
            "url": "https://tailscale.com/blog/peer-relays-beta",
            "title": "Tailscale Peer Relays",
            "date_modified": "2025-10-29T16:21:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45748484\">Comments</a>",
            "url": "https://www.tigerdata.com/blog/fluid-storage-forkable-ephemeral-durable-infrastructure-age-of-agents",
            "title": "Replacing EBS and Rethinking Postgres Storage from First Principles",
            "date_modified": "2025-10-29T15:49:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45731848\">Comments</a>",
            "url": "https://tailscale.com/blog/services-beta",
            "title": "Tailscale Services",
            "date_modified": "2025-10-28T12:19:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45727557\">Comments</a>",
            "url": "https://www.iroh.computer/blog/iroh-blobs-0-95-new-features",
            "title": "Iroh-Blobs",
            "date_modified": "2025-10-27T23:28:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45719237\">Comments</a>",
            "url": "https://sealos.io/blog/reduce-container-image-size-case-study",
            "title": "We reduced a container image from 800GB to 2GB",
            "date_modified": "2025-10-27T10:15:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45717724\">Comments</a>",
            "url": "https://www.npmjs.com/package/vite-plugin-use-golang",
            "title": "Show HN: Write Go code in JavaScript files",
            "date_modified": "2025-10-27T05:36:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45710721\">Comments</a>",
            "url": "https://maurycyz.com/misc/easy_git/",
            "title": "You Already Have a Git Server",
            "date_modified": "2025-10-26T10:53:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45708884\">Comments</a>",
            "url": "https://tsdown.dev/",
            "title": "Tsdown – The Elegant Bundler for Libraries",
            "date_modified": "2025-10-26T03:19:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45702831\">Comments</a>",
            "url": "https://ducklake.select/2025/10/24/frozen-ducklake/",
            "title": "Frozen DuckLakes for Multi-User, Serverless Data Access",
            "date_modified": "2025-10-25T10:57:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45701404\">Comments</a>",
            "url": "https://www.seangoedecke.com/good-code-reviews/",
            "title": "Mistakes I see engineers making in their code reviews",
            "date_modified": "2025-10-25T04:42:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45672280\">Comments</a>",
            "url": "https://steveklabnik.com/writing/i-see-a-future-in-jj/",
            "title": "I see a future in jj",
            "date_modified": "2025-10-22T17:21:54.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/lhkipb/judo_jujutsu_gui\">Comments</a></p>",
            "url": "https://judojj.com/",
            "title": "Judo (Jujutsu GUI)",
            "date_modified": "2025-10-21T00:14:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45647166\">Comments</a>",
            "url": "https://www.anthropic.com/news/claude-code-on-the-web",
            "title": "Claude Code on the Web",
            "date_modified": "2025-10-20T18:12:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45647166\">Comments</a>",
            "url": "https://www.anthropic.com/news/claude-code-on-the-web",
            "title": "Claude Code on the web",
            "date_modified": "2025-10-20T18:12:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45646691\">Comments</a>",
            "url": "https://www.xtxmarkets.com/tech/2025-ternfs/#posix-shaped",
            "title": "TernFS – an exabyte scale, multi-region distributed filesystem",
            "date_modified": "2025-10-20T17:36:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45645120\">Comments</a>",
            "url": "https://judojj.com",
            "title": "Show HN: I created a cross-platform GUI for the JJ VCS (Git compatible)",
            "date_modified": "2025-10-20T15:35:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45640772\">Comments</a>",
            "url": "https://old.reddit.com/r/aws/comments/1obd3lx/dynamodb_down_useast1/",
            "title": "Major AWS Outage Happening",
            "date_modified": "2025-10-20T07:11:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45602428\">Comments</a>",
            "url": "https://elixir-lang.org/blog/2025/10/16/elixir-v1-19-0-released/",
            "title": "Elixir 1.19",
            "date_modified": "2025-10-16T07:31:25.000Z"
        },
        {
            "content_html": "<p>&nbsp;</p> \n<p>As someone that used to work at <a href=\"https://en.wikipedia.org/wiki/Sun_Microsystems\">Sun Microsystems</a>, where <a href=\"https://en.wikipedia.org/wiki/ZFS\">ZFS</a> was invented, I’ve always loved working with storage systems that offer instant volume copies for my development and testing needs.</p> \n<p>Today, I’m excited to share that AWS is bringing similar capabilities to <a href=\"https://aws.amazon.com/ebs/\">Amazon Elastic Block Store (Amazon EBS)</a> with the launch of Amazon EBS Volume Clones, a new capability that lets you create instant point-in-time copies of your EBS volumes within the same Availability Zone.</p> \n<p>Many customers need to create copies of their production data to support development and testing activities in a separate nonproduction environment. Until now, this process required taking an EBS snapshot (stored in <a href=\"https://aws.amazon.com/s3/\">Amazon Simple Storage Service (Amazon S3)</a>) and then creating a new volume from that snapshot. Although this approach works, the process creates operational overhead due to multiple steps.</p> \n<p>With Amazon EBS Volume Clones, you can now create copies of your EBS volumes with a single API call or console click. The copied volumes are available within seconds and provide immediate access to your data with single-digit millisecond latency. This makes Volume Clones particularly useful for quickly setting up test environments with production data or creating temporary copies of databases for development purposes.</p> \n<p><strong><span>Let me show you how Volume Clones works</span><br> </strong>For this post, I created a small <a href=\"https://aws.amazon.com/ec2/\">Amazon Elastic Compute Cloud (Amazon EC2)</a> instance, with an attached volume. I created a file on the root file system with the command <code>echo \"Hello CopyVolumes\" &gt; hello.txt</code>.</p> \n<p>To initiate the copy, I open a browser on the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a> and I navigate to <strong>EC2</strong>, <strong>Elastic Block Store</strong>, <strong>Volumes</strong>. I select the volume I want to copy.</p> \n<p>Note that, at the time of publication of this post, only encrypted volumes can be copied.</p> \n<p>On the <strong>Actions</strong> menu, I choose the <strong>Copy Volume</strong> option.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/10/07/2025-10-06_15-35-57.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/10/07/2025-10-06_15-35-57.png\" alt=\"Copy Volume - initiate\" width=\"800\" height=\"433\"></a></p> \n<p>Next, I choose the details of the target volume. I can change the <strong>Volume type</strong> and adjust the <strong>Size</strong>, <strong>IOPS</strong>, and <strong>Throughput</strong> parameters. I choose <strong>Copy volume</strong> to start the Volume Clone operation.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/10/07/2025-10-06_15-36-22.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/10/07/2025-10-06_15-36-22.png\" alt=\"Copy Volume - Parameters\" width=\"800\" height=\"807\"></a></p> \n<p>The copied volume enters the <strong>Creating</strong> state and becomes available within seconds. I can then attach it to an EC2 instance and start using it immediately.</p> \n<p>Data blocks are copied from the source volume and written to the volume copy in the background. The volume remains in the <strong>Initializing</strong> state until the process is complete. I can monitor its progress with the <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVolumeStatus.html\"><code>describe-volume-status</code> API</a>. The initializing operation doesn’t affect the performance of the source volume. I can continue using it normally during the copy process.</p> \n<p>I love that the copied volume is available immediately. I don’t need to wait for its initialization to complete. During the initialization phase, my copied volume delivers performance based on the lowest of: a baseline of 3,000 IOPS and 125 MiB/s, the source volume’s provisioned performance, or the copied volume’s provisioned performance.</p> \n<p>After initialization is completed, the copied volume becomes fully independent of the source volume and delivers its full provisioned performance.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/10/07/2025-10-07_11-12-41.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/10/07/2025-10-07_11-12-41.png\" alt=\"Copy Volume - Initializing\" width=\"800\" height=\"310\"></a>Alternatively, I can use the <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a> to initiate the copy:</p> \n<pre><code>aws ec2 copy-volumes                          \\\n     --source-volume-id vol-1234567890abcdef0 \\\n     --size 500                               \\\n     --volume-type gp3</code></pre> \n<p>After the volume copy is created, I attach it to my EC2 instance and mount it. I can check the file I created at start is present.</p> \n<p>First, I attach the volume from my laptop, using the <code>attach-volume</code> command:</p> \n<pre><code>aws ec2 attach-volume \\\n         --volume-id 'vol-09b700e3a23a9b4ad' \\\n         --instance-id 'i-079e6504ad25b029e'   \\\n         --device '/dev/sdb'</code></pre> \n<p>Then, I connect to the instance, and I type these commands:</p> \n<pre><code>$ sudo lsblk -f\nNAME          FSTYPE FSVER LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINTS\nnvme0n1                                                                              \n├─nvme0n1p1   xfs          /     49e26d9d-0a9d-4667-b93e-a23d1de8eacd    6.2G    22% /\n└─nvme0n1p128 vfat   FAT16       3105-2F44                               8.6M    14% /boot/efi\nnvme1n1                                                                              \n├─nvme1n1p1   xfs          /     49e26d9d-0a9d-4667-b93e-a23d1de8eacd                \n└─nvme1n1p128 vfat   FAT16       3105-2F44     \n\n$ sudo mount -t xfs /dev/nvme1n1p1 /data\n\n$ df -h\nFilesystem        Size  Used Avail Use% Mounted on\ndevtmpfs          4.0M     0  4.0M   0% /dev\ntmpfs             924M     0  924M   0% /dev/shm\ntmpfs             370M  476K  369M   1% /run\n/dev/nvme0n1p1    8.0G  1.8G  6.2G  22% /\ntmpfs             924M     0  924M   0% /tmp\n/dev/nvme0n1p128   10M  1.4M  8.7M  14% /boot/efi\ntmpfs             185M     0  185M   0% /run/user/1000\n/dev/nvme1n1p1    8.0G  1.8G  6.2G  22% /data\n\n$ cat /data/home/ec2-user/hello.txt \nHello CopyVolumes</code></pre> \n<p><strong><span>Things to know<br> </span></strong>Volume Clones creates copies within the same Availability Zone as your source volume. You can create copies from encrypted volumes only, and the size of your copy must be equal to or greater than the source volume.</p> \n<p>Volume Clones creates crash-consistent copies of your volumes, exactly like snapshots. For application consistency, you need to pause application I/O operations before creating the copy. For example, with PostgreSQL databases, you can use the <code>pg_start_backup()</code> and <code>pg_stop_backup()</code> functions to pause writes and create a consistent copy. At the operating system level on Linux with XFS, you can use the <code>xfs_freeze</code> command to temporarily suspend and resume access to the file system and ensure all cached updates are written to disk.</p> \n<p>Although Volume Clones creates point-in-time copies, it complements rather than replaces EBS snapshots for backup purposes. EBS snapshots remain the recommended solution for data backup and protection against AZ-level and volume failures. Snapshots provide incremental backups to Amazon S3 with 11 nines of durability, compared to Volume Clones which maintains EBS volume durability (99.999% for io2, 99.9% for other volume types). Consider using Volume Clones specifically for test and development environment scenarios where you need instant access to volume copies.</p> \n<p>Copied volumes exist independently of their source volumes and continue to incur standard EBS volume charges until you delete them. To manage costs effectively, implement governance rules to identify and remove copied volumes that are no longer needed for your development or testing activities.</p> \n<p><span><strong>Pricing and availability<br> </strong></span>Volume Clones supports all EBS volume types and works with volumes in the same AWS account and Availability Zone. This new capability is available in all AWS commercial <a href=\"https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#region\">Regions</a>, selected <a href=\"https://aws.amazon.com/about-aws/global-infrastructure/localzones/locations/\">Local Zones</a>, and in the <a href=\"https://aws.amazon.com/govcloud-us/\">AWS GovCloud (US)</a>.</p> \n<p>For pricing, you’re charged a one-time fee per GiB of data on the source volume at initiation and standard EBS pricing for the new volume.</p> \n<p>I find Volume Clones particularly valuable for database workloads and continuous integration (CI) scenarios. For instance, you can quickly create a copy of your production database for testing new features or troubleshooting issues without impacting your production environment or waiting for data to hydrate from Amazon S3.</p> \n<p>To get started with Amazon EBS Volume Clones, visit the <a href=\"https://console.aws.amazon.com/ec2/home#Volumes:\">Amazon EBS section on the console</a> or check out the <a href=\"https://docs.aws.amazon.com/ebs/latest/userguide/ebs-copying-volume.html\">EBS documentation</a>. I look forward to hearing how you use this capability to improve your development workflows.</p> \n<a href=\"https://linktr.ee/sebsto\">— seb</a>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-amazon-ebs-volume-clones-create-instant-copies-of-your-ebs-volumes/",
            "title": "Introducing Amazon EBS Volume Clones: Create instant copies of your EBS volumes",
            "date_modified": "2025-10-14T21:35:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45581761\">Comments</a>",
            "url": "https://mtlynch.io/notes/hold-off-on-litestream-0.5.0/",
            "title": "Hold Off on Litestream 0.5.0",
            "date_modified": "2025-10-14T16:10:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45568842\">Comments</a>",
            "url": "https://www.reuters.com/business/a16z-backed-data-firms-fivetran-dbt-labs-merge-all-stock-deal-2025-10-13/",
            "title": "A16Z-backed data firms Fivetran, dbt Labs to merge in all-stock deal",
            "date_modified": "2025-10-13T14:42:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45566421\">Comments</a>",
            "url": "https://www.stavros.io/posts/switch-to-jujutsu-already-a-tutorial/",
            "title": "Switch to Jujutsu Already: A Tutorial",
            "date_modified": "2025-10-13T09:22:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45561888\">Comments</a>",
            "url": "https://jmmv.dev/2025/09/bazel-remote-execution.html",
            "title": "Trusting builds with Bazel remote execution",
            "date_modified": "2025-10-12T21:03:03.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/gocddb/switch_jujutsu_already_tutorial\">Comments</a></p>",
            "url": "https://www.stavros.io/posts/switch-to-jujutsu-already-a-tutorial/",
            "title": "Switch to Jujutsu already: a tutorial",
            "date_modified": "2025-10-12T08:33:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45553995\">Comments</a>",
            "url": "https://github.com/acsandmann/rift",
            "title": "Show HN: Rift – A tiling window manager for macOS",
            "date_modified": "2025-10-12T00:22:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45543899\">Comments</a>",
            "url": "https://blog.tangled.org/intro",
            "title": "Tangled, a Git collaboration platform built on atproto",
            "date_modified": "2025-10-10T21:18:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45528735\">Comments</a>",
            "url": "https://www.githubstatus.com/incidents/k7bhmjkblcwp",
            "title": "GitHub Issues",
            "date_modified": "2025-10-09T15:05:52.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/83xufu/solving_git_s_pain_points_with_jujutsu\">Comments</a></p>",
            "url": "https://www.youtube.com/watch?v=ulJ_Pw8qqsE",
            "title": "Solving Git's Pain Points with Jujutsu",
            "date_modified": "2025-10-09T14:21:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45514213\">Comments</a>",
            "url": "https://www.geocod.io/code-and-coordinates/2025-10-02-from-millions-to-billions/",
            "title": "Scaling request logging with ClickHouse, Kafka, and Vector",
            "date_modified": "2025-10-08T09:56:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45496625\">Comments</a>",
            "url": "https://github.com/mbj/mutant",
            "title": "Automated code reviews via mutation testing",
            "date_modified": "2025-10-06T21:40:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/xmlpu8/saving_my_commit_with_jj_evolog\">Comments</a></p>",
            "url": "https://landaire.net/jj-evolog/",
            "title": "Saving My Commit With `jj evolog`",
            "date_modified": "2025-10-06T16:21:09.000Z"
        },
        {
            "content_html": "<p>At JetBrains, our mission is simple: help developers achieve engineering excellence without distractions or vendor lock-in. Today, we’re excited to share that we’re collaborating with Zed on the <a href=\"https://agentclientprotocol.com\" target=\"_blank\" rel=\"noopener\">Agent Client Protocol (ACP)</a> – an open protocol that lets AI coding agents work inside editors, including JetBrains IDEs, if both are ACP-compatible.</p>\n\n\n\n<h2><strong>Why this collaboration matters</strong></h2>\n\n\n\n<ul>\n<li><strong>No vendor lock-in:</strong> Use the agent you prefer inside the IDE you love.</li>\n\n\n\n<li><strong>Confidence and control:</strong> See exactly what the agent is planning, review diffs, and approve actions.</li>\n\n\n\n<li><strong>A great UX in your IDE:</strong> Streaming updates, clear intent, and changes that fit naturally into editor workflows.</li>\n</ul>\n\n\n\n<h2><strong>What developers get in JetBrains IDEs</strong></h2>\n\n\n\n<ul>\n<li><strong>Bring your agent.</strong> The ACP protocol makes agents editor-agnostic, so agents work anywhere you code.</li>\n\n\n\n<li><strong>Full control.</strong> Going forward, your IDE will mediate access to files, the terminal, and other tools via the ACP protocol – you decide what runs.</li>\n\n\n\n<li><strong>The same workflows you’re used to.</strong> Your environment stays yours by default.</li>\n\n\n\n<li><strong>Shorter feedback loops.</strong> Prompt, observe progress, and validate – all in one place.</li>\n</ul>\n\n\n\n<h2><strong>The JetBrains × Zed advantage</strong></h2>\n\n\n\n<p>Zed has quickly innovated in protocol design and seen real-world adoption. JetBrains brings decades of IDE expertise, including refactoring, debugging, navigation, and performance at scale. Together, we’re aligning on ACP-driven experiences that feel native in JetBrains IDEs while remaining open and portable across the ecosystem. The result is agents that are portable, powerful, and predictable inside your daily tools.</p>\n\n\n\n<h2><strong>What’s next</strong></h2>\n\n\n\n<p>We’re working closely with Zed on the next milestones for ACP-powered experiences in JetBrains IDEs. As work progresses, we’ll share details on early implementations, developer previews, and how partners can extend ACP-based integrations.</p>\n\n\n\n<p>Our goal hasn’t changed: empower developers with freedom of choice, clarity of control, and first-class productivity with AI agents that feel at home in JetBrains IDEs.</p>",
            "url": "https://blog.jetbrains.com/ai/2025/10/jetbrains-zed-open-interoperability-for-ai-coding-agents-in-your-ide/",
            "title": "JetBrains × Zed: Open Interoperability for AI Coding Agents in Your IDE",
            "date_modified": "2025-10-06T15:57:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45491621\">Comments</a>",
            "url": "https://github.com/jdx/mise/discussions/6564",
            "title": "Mise: Monorepo Tasks",
            "date_modified": "2025-10-06T14:07:46.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/by9vog/oswald_object_storage_write_ahead_log\">Comments</a></p>",
            "url": "https://nvartolomei.com/oswald/",
            "title": "OSWALD - Object Storage Write-Ahead Log Device",
            "date_modified": "2025-10-04T12:38:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45467751\">Comments</a>",
            "url": "https://blogsystem5.substack.com/p/you-are-holding-build-files-wrong",
            "title": "Build files are the best tool to represent software architecture",
            "date_modified": "2025-10-03T21:04:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45437112\">Comments</a>",
            "url": "https://gist.github.com/tkafka/e3eb63a5ec448e9be6701bfd1f1b1e58",
            "title": "Detect Electron apps on Mac that hasn't been updated to fix the system wide lag",
            "date_modified": "2025-10-01T12:54:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45436534\">Comments</a>",
            "url": "https://www.amplifypartners.com/blog-posts/why-tigerbeetle-is-the-most-interesting-database-in-the-world",
            "title": "Why TigerBeetle is the most interesting database in the world",
            "date_modified": "2025-10-01T11:33:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45433926\">Comments</a>",
            "url": "https://cognition.ai/blog/blockdiff",
            "title": "Blockdiff: We built our own file format for VM disk snapshots",
            "date_modified": "2025-10-01T03:13:16.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing Amazon ECS Managed Instances, a new compute option for <a href=\"https://aws.amazon.com/ecs/\">Amazon Elastic Container Service (Amazon ECS)</a> that enables developers to use the full range of <a href=\"https://aws.amazon.com/ec2\">Amazon Elastic Compute Cloud (Amazon EC2)</a> capabilities while offloading infrastructure management responsibilities to <a href=\"https://aws.amazon.com\">Amazon Web Service (AWS)</a>. This new offering combines the operational simplicity of offloading infrastructure with the flexibility and control of Amazon EC2, which means customers can focus on building applications that drive innovation, while reducing total cost of ownership (TCO) and maintaining AWS best practices.</p> \n<p>Amazon ECS Managed Instances provides a fully managed container compute environment that supports a broad range of EC2 instance types and deep integration with AWS services. By default, it automatically selects the most cost-optimized EC2 instances for your workloads, but you can specify particular instance attributes or types when needed. AWS handles all aspects of infrastructure management, including provisioning, scaling, security patching, and cost optimization, enabling you to concentrate on building and running your applications.</p> \n<p><span><strong>Let’s try it out</strong></span></p> \n<p>Looking at the <a href=\"https://aws.amazon.com/console/\">AWS Management Console</a> experience for creating a new Amazon ECS cluster, I can see the new option for using ECS Managed Instances. Let’s take a quick tour of all the new options.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/09/24/Screenshot-2025-09-24-at-10.51.19 AM-1024x502.png\" alt=\"Creating a ECS cluster with Managed Instances\" width=\"1024\" height=\"502\"></p> \n<p>After I’ve selected <strong>Fargate and Managed Instances</strong>, I’m presented with two options. If I select <strong>Use ECS default</strong>, Amazon ECS will choose general purpose instance types based on grouping together pending Tasks, and picking the optimum instance type based on cost and resilience metrics. This is the most straightforward and recommended way to get started. Selecting <strong>Use custom – advanced</strong> opens up additional configuration parameters, where I can fine-tune the attributes of instances Amazon ECS will use.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/09/24/Screenshot-2025-09-24-at-12.59.44 PM-1024x593.png\" alt=\"Creating a ECS cluster with Managed Instances\" width=\"1024\" height=\"593\"></p> \n<p>By default, I see <strong>CPU</strong> and <strong>Memory</strong> as attributes, but I can select from 20 additional attributes to continue to filter the list of available instance types Amazon ECS can access.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/09/30/Screenshot-2025-09-30-at-4.05.55 PM-1024x735.png\" alt=\"ECS Managed Instances\" width=\"1024\" height=\"735\"></p> \n<p>After I’ve made my attribute selections, I see a list of all the instance types that match my choices.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/09/24/Screenshot-2025-09-24-at-12.59.57 PM-1-1024x466.png\" alt=\"Creating a ECS cluster with Managed Instances\" width=\"1024\" height=\"466\"></p> \n<p>From here, I can create my ECS cluster as usual and Amazon ECS will provision instances for me on my behalf based on the attributes and criteria I’ve defined in the previous steps.</p> \n<p><span><strong>Key features of Amazon ECS Managed Instances</strong></span></p> \n<p>With Amazon ECS Managed Instances, AWS takes full responsibility for infrastructure management, handling all aspects of instance provisioning, scaling, and maintenance. This includes implementing regular security patches initiated every 14 days (due to instance connection draining, the actual lifetime of the instance may be longer), with the ability to schedule maintenance windows using Amazon EC2 event windows to minimize disruption to your applications.</p> \n<p>The service provides exceptional flexibility in instance type selection. Although it automatically selects cost-optimized instance types by default, you maintain the power to specify desired instance attributes when your workloads require specific capabilities. This includes options for GPU acceleration, CPU architecture, and network performance requirements, giving you precise control over your compute environment.</p> \n<p>To help optimize costs, Amazon ECS Managed Instances intelligently manages resource utilization by automatically placing multiple tasks on larger instances when appropriate. The service continually monitors and optimizes task placement, consolidating workloads onto fewer instances to dry up, utilize and terminate idle (empty) instances, providing both high availability and cost efficiency for your containerized applications.</p> \n<p>Integration with existing AWS services is seamless, particularly with Amazon EC2 features such as EC2 pricing options. This deep integration means that you can maximize existing capacity investments while maintaining the operational simplicity of a fully managed service.</p> \n<p>Security remains a top priority with Amazon ECS Managed Instances. The service runs on Bottlerocket, a purpose-built container operating system, and maintains your security posture through automated security patches and updates. You can see all the updates and patches applied to the Bottlerocket OS image on the <a href=\"https://bottlerocket.dev/en/os/\">Bottlerocket website</a>. This comprehensive approach to security keeps your containerized applications running in a secure, maintained environment.</p> \n<p><span><strong>Available now</strong></span></p> \n<p>Amazon ECS Managed Instances is available today in US East (North Virginia), US West (Oregon), Europe (Ireland), Africa (Cape Town), Asia Pacific (Singapore), and Asia Pacific (Tokyo) AWS Regions. You can start using Managed Instances through the AWS Management Console, AWS Command Line Interface (AWS CLI), or infrastructure as code (IaC) tools such as AWS Cloud Development Kit (AWS CDK) and AWS CloudFormation. You pay for the EC2 instances you use plus a management fee for the service.</p> \n<p>To learn more about Amazon ECS Managed Instances, visit the <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ManagedInstances.html\">documentation</a> and get started simplifying your container infrastructure today.</p>",
            "url": "https://aws.amazon.com/blogs/aws/announcing-amazon-ecs-managed-instances-for-containerized-applications/",
            "title": "Announcing Amazon ECS Managed Instances for containerized applications",
            "date_modified": "2025-09-30T18:46:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45427697\">Comments</a>",
            "url": "https://imbue.com/sculptor/",
            "title": "Show HN: Sculptor, the Missing UI for Claude Code",
            "date_modified": "2025-09-30T16:35:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45426490\">Comments</a>",
            "url": "https://blog.kagi.com/kagi-news",
            "title": "Kagi News",
            "date_modified": "2025-09-30T15:09:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45415814\">Comments</a>",
            "url": "https://www.greptile.com/blog/sandboxing-agents-at-the-kernel-level",
            "title": "Sandboxing AI Agents at the Kernel Level",
            "date_modified": "2025-09-29T16:40:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45409001\">Comments</a>",
            "url": "https://www.skeptrune.com/posts/use-the-accept-header-to-serve-markdown-instead-of-html-to-llms/",
            "title": "Use the Accept Header to Serve Markdown Instead of HTML to LLMs",
            "date_modified": "2025-09-28T23:33:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45399204\">Comments</a>",
            "url": "https://blog.cloudflare.com/code-mode/",
            "title": "Code Mode: the better way to use MCP",
            "date_modified": "2025-09-27T20:49:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45398468\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=iPoL03tFBtU",
            "title": "Docker Was Too Slow, So We Replaced It: Nix in Production [video]",
            "date_modified": "2025-09-27T18:56:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45395468\">Comments</a>",
            "url": "https://idiallo.com/blog/users-only-care-about-20-percent",
            "title": "Users only care about 20% of your application",
            "date_modified": "2025-09-27T13:15:37.000Z"
        },
        {
            "content_html": "<p>It turns out we've all been using MCP wrong.</p><p>Most agents today use MCP by directly exposing the \"tools\" to the <a href=\"https://www.cloudflare.com/learning/ai/what-is-large-language-model/\"><u>LLM</u></a>.</p><p>We tried something different: Convert the MCP tools into a TypeScript API, and then ask an LLM to write code that calls that API.</p><p>The results are striking:</p><ol><li><p>We found agents are able to handle many more tools, and more complex tools, when those tools are presented as a TypeScript API rather than directly. Perhaps this is because LLMs have an enormous amount of real-world TypeScript in their training set, but only a small set of contrived examples of tool calls.</p></li><li><p>The approach really shines when an agent needs to string together multiple calls. With the traditional approach, the output of each tool call must feed into the LLM's neural network, just to be copied over to the inputs of the next call, wasting time, energy, and tokens. When the LLM can write code, it can skip all that, and only read back the final results it needs.</p></li></ol><p>In short, LLMs are better at writing code to call MCP, than at calling MCP directly.</p>\n    <div>\n      <h2>What's MCP?</h2>\n      <a href=\"https://blog.cloudflare.com/code-mode/#whats-mcp\">\n        \n      </a>\n    </div>\n    <p>For those that aren't familiar: <a href=\"https://modelcontextprotocol.io/docs/getting-started/intro\"><u>Model Context Protocol</u></a> is a standard protocol for giving AI agents access to external tools, so that they can directly perform work, rather than just chat with you.</p><p>Seen another way, MCP is a uniform way to:</p><ul><li><p>expose an API for doing something,</p></li><li><p>along with documentation needed for an LLM to understand it,</p></li><li><p>with authorization handled out-of-band.</p></li></ul><p>MCP has been making waves throughout 2025 as it has suddenly greatly expanded the capabilities of AI agents.</p><p>The \"API\" exposed by an MCP server is expressed as a set of \"tools\". Each tool is essentially a remote procedure call (RPC) function – it is called with some parameters and returns a response. Most modern LLMs have <a href=\"https://developers.cloudflare.com/workers-ai/features/function-calling/\"><u>the capability to use \"tools\" (sometimes called \"function calling\")</u></a>, meaning they are trained to output text in a certain format when they want to invoke a tool. The program invoking the LLM sees this format and invokes the tool as specified, then feeds the results back into the LLM as input.</p>\n    <div>\n      <h3>Anatomy of a tool call</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#anatomy-of-a-tool-call\">\n        \n      </a>\n    </div>\n    <p>Under the hood, an LLM generates a stream of \"tokens\" representing its output. A token might represent a word, a syllable, some sort of punctuation, or some other component of text.</p><p>A tool call, though, involves a token that does <i>not</i> have any textual equivalent. The LLM is trained (or, more often, fine-tuned) to understand a special token that it can output that means \"the following should be interpreted as a tool call,\" and another special token that means \"this is the end of the tool call.\" Between these two tokens, the LLM will typically write tokens corresponding to some sort of JSON message that describes the call.</p><p>For instance, imagine you have connected an agent to an MCP server that provides weather info, and you then ask the agent what the weather is like in Austin, TX. Under the hood, the LLM might generate output like the following. Note that here we've used words in <code>&lt;|</code> and <code>|&gt;</code> to represent our special tokens, but in fact, these tokens do not represent text at all; this is just for illustration.</p><p>I will use the Weather MCP server to find out the weather in Austin, TX.</p>\n            <pre><code>I will use the Weather MCP server to find out the weather in Austin, TX.\n\n&lt;|tool_call|&gt;\n{\n  \"name\": \"get_current_weather\",\n  \"arguments\": {\n    \"location\": \"Austin, TX, USA\"\n  }\n}\n&lt;|end_tool_call|&gt;</code></pre>\n            <p>Upon seeing these special tokens in the output, the LLM's harness will interpret the sequence as a tool call. After seeing the end token, the harness pauses execution of the LLM. It parses the JSON message and returns it as a separate component of the structured API result. The agent calling the LLM API sees the tool call, invokes the relevant MCP server, and then sends the results back to the LLM API. The LLM's harness will then use another set of special tokens to feed the result back into the LLM:</p>\n            <pre><code>&lt;|tool_result|&gt;\n{\n  \"location\": \"Austin, TX, USA\",\n  \"temperature\": 93,\n  \"unit\": \"fahrenheit\",\n  \"conditions\": \"sunny\"\n}\n&lt;|end_tool_result|&gt;</code></pre>\n            <p>The LLM reads these tokens in exactly the same way it would read input from the user – except that the user cannot produce these special tokens, so the LLM knows it is the result of the tool call. The LLM then continues generating output like normal.</p><p>Different LLMs may use different formats for tool calling, but this is the basic idea.</p>\n    <div>\n      <h3>What's wrong with this?</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#whats-wrong-with-this\">\n        \n      </a>\n    </div>\n    <p>The special tokens used in tool calls are things LLMs have never seen in the wild. They must be specially trained to use tools, based on synthetic training data. They aren't always that good at it. If you present an LLM with too many tools, or overly complex tools, it may struggle to choose the right one or to use it correctly. As a result, MCP server designers are encouraged to present greatly simplified APIs as compared to the more traditional API they might expose to developers.</p><p>Meanwhile, LLMs are getting really good at writing code. In fact, LLMs asked to write code against the full, complex APIs normally exposed to developers don't seem to have too much trouble with it. Why, then, do MCP interfaces have to \"dumb it down\"? Writing code and calling tools are almost the same thing, but it seems like LLMs can do one much better than the other?</p><p>The answer is simple: LLMs have seen a lot of code. They have not seen a lot of \"tool calls\". In fact, the tool calls they have seen are probably limited to a contrived training set constructed by the LLM's own developers, in order to try to train it. Whereas they have seen real-world code from millions of open source projects.</p><p><b><i>Making an LLM perform tasks with tool calling is like putting Shakespeare through a month-long class in Mandarin and then asking him to write a play in it. It's just not going to be his best work.</i></b></p>\n    <div>\n      <h3>But MCP is still useful, because it is uniform</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#but-mcp-is-still-useful-because-it-is-uniform\">\n        \n      </a>\n    </div>\n    <p>MCP is designed for tool-calling, but it doesn't actually <i>have to</i> be used that way.</p><p>The \"tools\" that an MCP server exposes are really just an RPC interface with attached documentation. We don't really <i>have to</i> present them as tools. We can take the tools, and turn them into a programming language API instead.</p><p>But why would we do that, when the programming language APIs already exist independently? Almost every MCP server is just a wrapper around an existing traditional API – why not expose those APIs?</p><p>Well, it turns out MCP does something else that's really useful: <b>It provides a uniform way to connect to and learn about an API.</b></p><p>An AI agent can use an MCP server even if the agent's developers never heard of the particular MCP server, and the MCP server's developers never heard of the particular agent. This has rarely been true of traditional APIs in the past. Usually, the client developer always knows exactly what API they are coding for. As a result, every API is able to do things like basic connectivity, authorization, and documentation a little bit differently.</p><p>This uniformity is useful even when the AI agent is writing code. We'd like the AI agent to run in a sandbox such that it can only access the tools we give it. MCP makes it possible for the agentic framework to implement this, by handling connectivity and authorization in a standard way, independent of the AI code. We also don't want the AI to have to search the Internet for documentation; MCP provides it directly in the protocol.</p>\n    <div>\n      <h2>OK, how does it work?</h2>\n      <a href=\"https://blog.cloudflare.com/code-mode/#ok-how-does-it-work\">\n        \n      </a>\n    </div>\n    <p>We have already extended the <a href=\"https://developers.cloudflare.com/agents/\"><u>Cloudflare Agents SDK</u></a> to support this new model!</p><p>For example, say you have an app built with ai-sdk that looks like this:</p>\n            <pre><code>const stream = streamText({\n  model: openai(\"gpt-5\"),\n  system: \"You are a helpful assistant\",\n  messages: [\n    { role: \"user\", content: \"Write a function that adds two numbers\" }\n  ],\n  tools: {\n    // tool definitions \n  }\n})</code></pre>\n            <p>You can wrap the tools and prompt with the codemode helper, and use them in your app:&nbsp;</p>\n            <pre><code>import { codemode } from \"agents/codemode/ai\";\n\nconst {system, tools} = codemode({\n  system: \"You are a helpful assistant\",\n  tools: {\n    // tool definitions \n  },\n  // ...config\n})\n\nconst stream = streamText({\n  model: openai(\"gpt-5\"),\n  system,\n  tools,\n  messages: [\n    { role: \"user\", content: \"Write a function that adds two numbers\" }\n  ]\n})</code></pre>\n            <p>With this change, your app will now start generating and running code that itself will make calls to the tools you defined, MCP servers included. We will introduce variants for other libraries in the very near future. <a href=\"https://github.com/cloudflare/agents/blob/main/docs/codemode.md\"><u>Read the docs</u></a> for more details and examples.&nbsp;</p>\n    <div>\n      <h3>Converting MCP to TypeScript</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#converting-mcp-to-typescript\">\n        \n      </a>\n    </div>\n    <p>When you connect to an MCP server in \"code mode\", the Agents SDK will fetch the MCP server's schema, and then convert it into a TypeScript API, complete with doc comments based on the schema.</p><p>For example, connecting to the MCP server at <a href=\"https://gitmcp.io/cloudflare/agents\">gitmcp.io/cloudflare/agents</a>, will generate a TypeScript definition like this:</p>\n            <pre><code>interface FetchAgentsDocumentationInput {\n  [k: string]: unknown;\n}\ninterface FetchAgentsDocumentationOutput {\n  [key: string]: any;\n}\n\ninterface SearchAgentsDocumentationInput {\n  /**\n   * The search query to find relevant documentation\n   */\n  query: string;\n}\ninterface SearchAgentsDocumentationOutput {\n  [key: string]: any;\n}\n\ninterface SearchAgentsCodeInput {\n  /**\n   * The search query to find relevant code files\n   */\n  query: string;\n  /**\n   * Page number to retrieve (starting from 1). Each page contains 30\n   * results.\n   */\n  page?: number;\n}\ninterface SearchAgentsCodeOutput {\n  [key: string]: any;\n}\n\ninterface FetchGenericUrlContentInput {\n  /**\n   * The URL of the document or page to fetch\n   */\n  url: string;\n}\ninterface FetchGenericUrlContentOutput {\n  [key: string]: any;\n}\n\ndeclare const codemode: {\n  /**\n   * Fetch entire documentation file from GitHub repository:\n   * cloudflare/agents. Useful for general questions. Always call\n   * this tool first if asked about cloudflare/agents.\n   */\n  fetch_agents_documentation: (\n    input: FetchAgentsDocumentationInput\n  ) =&gt; Promise&lt;FetchAgentsDocumentationOutput&gt;;\n\n  /**\n   * Semantically search within the fetched documentation from\n   * GitHub repository: cloudflare/agents. Useful for specific queries.\n   */\n  search_agents_documentation: (\n    input: SearchAgentsDocumentationInput\n  ) =&gt; Promise&lt;SearchAgentsDocumentationOutput&gt;;\n\n  /**\n   * Search for code within the GitHub repository: \"cloudflare/agents\"\n   * using the GitHub Search API (exact match). Returns matching files\n   * for you to query further if relevant.\n   */\n  search_agents_code: (\n    input: SearchAgentsCodeInput\n  ) =&gt; Promise&lt;SearchAgentsCodeOutput&gt;;\n\n  /**\n   * Generic tool to fetch content from any absolute URL, respecting\n   * robots.txt rules. Use this to retrieve referenced urls (absolute\n   * urls) that were mentioned in previously fetched documentation.\n   */\n  fetch_generic_url_content: (\n    input: FetchGenericUrlContentInput\n  ) =&gt; Promise&lt;FetchGenericUrlContentOutput&gt;;\n};</code></pre>\n            <p>This TypeScript is then loaded into the agent's context. Currently, the entire API is loaded, but future improvements could allow an agent to search and browse the API more dynamically – much like an agentic coding assistant would.</p>\n    <div>\n      <h3>Running code in a sandbox</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#running-code-in-a-sandbox\">\n        \n      </a>\n    </div>\n    <p>Instead of being presented with all the tools of all the connected MCP servers, our agent is presented with just one tool, which simply executes some TypeScript code.</p><p>The code is then executed in a secure sandbox. The sandbox is totally isolated from the Internet. Its only access to the outside world is through the TypeScript APIs representing its connected MCP servers.</p><p>These APIs are backed by RPC invocation which calls back to the agent loop. There, the Agents SDK dispatches the call to the appropriate MCP server.</p><p>The sandboxed code returns results to the agent in the obvious way: by invoking <code>console.log()</code>. When the script finishes, all the output logs are passed back to the agent.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6DRERHP138FSj3GG0QYj3M/99e8c09b352560b7d4547ca299482c27/image2.png\">\n          </figure>\n    <div>\n      <h2>Dynamic Worker loading: no containers here</h2>\n      <a href=\"https://blog.cloudflare.com/code-mode/#dynamic-worker-loading-no-containers-here\">\n        \n      </a>\n    </div>\n    <p>This new approach requires access to a secure sandbox where arbitrary code can run. So where do we find one? Do we have to run containers? Is that expensive?</p><p>No. There are no containers. We have something much better: isolates.</p><p>The Cloudflare Workers platform has always been based on V8 isolates, that is, isolated JavaScript runtimes powered by the <a href=\"https://v8.dev/\"><u>V8 JavaScript engine</u></a>.</p><p><b>Isolates are far more lightweight than containers.</b> An isolate can start in a handful of milliseconds using only a few megabytes of memory.</p><p>Isolates are so fast that we can just create a new one for every piece of code the agent runs. There's no need to reuse them. There's no need to prewarm them. Just create it, on demand, run the code, and throw it away. It all happens so fast that the overhead is negligible; it's almost as if you were just eval()ing the code directly. But with security.</p>\n    <div>\n      <h3>The Worker Loader API</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#the-worker-loader-api\">\n        \n      </a>\n    </div>\n    <p>Until now, though, there was no way for a Worker to directly load an isolate containing arbitrary code. All Worker code instead had to be uploaded via the Cloudflare API, which would then deploy it globally, so that it could run anywhere. That's not what we want for Agents! We want the code to just run right where the agent is.</p><p>To that end, we've added a new API to the Workers platform: the <a href=\"https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/\"><u>Worker Loader API</u></a>. With it, you can load Worker code on-demand. Here's what it looks like:</p>\n            <pre><code>// Gets the Worker with the given ID, creating it if no such Worker exists yet.\nlet worker = env.LOADER.get(id, async () =&gt; {\n  // If the Worker does not already exist, this callback is invoked to fetch\n  // its code.\n\n  return {\n    compatibilityDate: \"2025-06-01\",\n\n    // Specify the worker's code (module files).\n    mainModule: \"foo.js\",\n    modules: {\n      \"foo.js\":\n        \"export default {\\n\" +\n        \"  fetch(req, env, ctx) { return new Response('Hello'); }\\n\" +\n        \"}\\n\",\n    },\n\n    // Specify the dynamic Worker's environment (`env`).\n    env: {\n      // It can contain basic serializable data types...\n      SOME_NUMBER: 123,\n\n      // ... and bindings back to the parent worker's exported RPC\n      // interfaces, using the new `ctx.exports` loopback bindings API.\n      SOME_RPC_BINDING: ctx.exports.MyBindingImpl({props})\n    },\n\n    // Redirect the Worker's `fetch()` and `connect()` to proxy through\n    // the parent worker, to monitor or filter all Internet access. You\n    // can also block Internet access completely by passing `null`.\n    globalOutbound: ctx.exports.OutboundProxy({props}),\n  };\n});\n\n// Now you can get the Worker's entrypoint and send requests to it.\nlet defaultEntrypoint = worker.getEntrypoint();\nawait defaultEntrypoint.fetch(\"http://example.com\");\n\n// You can get non-default entrypoints as well, and specify the\n// `ctx.props` value to be delivered to the entrypoint.\nlet someEntrypoint = worker.getEntrypoint(\"SomeEntrypointClass\", {\n  props: {someProp: 123}\n});</code></pre>\n            <p>You can start playing with this API right now when running <code>workerd</code> locally with Wrangler (<a href=\"https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/\"><u>check out the docs</u></a>), and you can <a href=\"https://forms.gle/MoeDxE9wNiqdf8ri9\"><u>sign up for beta access</u></a> to use it in production.</p>\n    <div>\n      <h2>Workers are better sandboxes</h2>\n      <a href=\"https://blog.cloudflare.com/code-mode/#workers-are-better-sandboxes\">\n        \n      </a>\n    </div>\n    <p>The design of Workers makes it unusually good at sandboxing, especially for this use case, for a few reasons:</p>\n    <div>\n      <h3>Faster, cheaper, disposable sandboxes</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#faster-cheaper-disposable-sandboxes\">\n        \n      </a>\n    </div>\n    <p><a href=\"https://developers.cloudflare.com/workers/reference/how-workers-works/\"><u>The Workers platform uses isolates instead of containers.</u></a> Isolates are much lighter-weight and faster to start up. It takes mere milliseconds to start a fresh isolate, and it's so cheap we can just create a new one for every single code snippet the agent generates. There's no need to worry about pooling isolates for reuse, prewarming, etc.</p><p>We have not yet finalized pricing for the Worker Loader API, but because it is based on isolates, we will be able to offer it at a significantly lower cost than container-based solutions.</p>\n    <div>\n      <h3>Isolated by default, but connected with bindings</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#isolated-by-default-but-connected-with-bindings\">\n        \n      </a>\n    </div>\n    <p>Workers are just better at handling isolation.</p><p>In Code Mode, we prohibit the sandboxed worker from talking to the Internet. The global <code>fetch()</code> and <code>connect()</code> functions throw errors.</p><p>But on most platforms, this would be a problem. On most platforms, the way you get access to private resources is, you <i>start</i> with general network access. Then, using that network access, you send requests to specific services, passing them some sort of API key to authorize private access.</p><p>But Workers has always had a better answer. In Workers, the \"environment\" (<code>env</code> object) doesn't just contain strings, <a href=\"https://blog.cloudflare.com/workers-environment-live-object-bindings/\"><u>it contains live objects</u></a>, also known as \"bindings\". These objects can provide direct access to private resources without involving generic network requests.</p><p>In Code Mode, we give the sandbox access to bindings representing the MCP servers it is connected to. Thus, the agent can specifically access those MCP servers <i>without</i> having network access in general.</p><p>Limiting access via bindings is much cleaner than doing it via, say, network-level filtering or HTTP proxies. Filtering is hard on both the LLM and the supervisor, because the boundaries are often unclear: the supervisor may have a hard time identifying exactly what traffic is legitimately necessary to talk to an API. Meanwhile, the LLM may have difficulty guessing what kinds of requests will be blocked. With the bindings approach, it's well-defined: the binding provides a JavaScript interface, and that interface is allowed to be used. It's just better this way.</p>\n    <div>\n      <h3>No API keys to leak</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#no-api-keys-to-leak\">\n        \n      </a>\n    </div>\n    <p>An additional benefit of bindings is that they hide API keys. The binding itself provides an already-authorized client interface to the MCP server. All calls made on it go to the agent supervisor first, which holds the access tokens and adds them into requests sent on to MCP.</p><p>This means that the AI cannot possibly write code that leaks any keys, solving a common security problem seen in AI-authored code today.</p>\n    <div>\n      <h2>Try it now!</h2>\n      <a href=\"https://blog.cloudflare.com/code-mode/#try-it-now\">\n        \n      </a>\n    </div>\n    \n    <div>\n      <h3>Sign up for the production beta</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#sign-up-for-the-production-beta\">\n        \n      </a>\n    </div>\n    <p>The Dynamic Worker Loader API is in closed beta. To use it in production, <a href=\"https://forms.gle/MoeDxE9wNiqdf8ri9\"><u>sign up today</u></a>.</p>\n    <div>\n      <h3>Or try it locally</h3>\n      <a href=\"https://blog.cloudflare.com/code-mode/#or-try-it-locally\">\n        \n      </a>\n    </div>\n    <p>If you just want to play around, though, Dynamic Worker Loading is fully available today when developing locally with Wrangler and <code>workerd</code> – check out the docs for <a href=\"https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/\"><u>Dynamic Worker Loading</u></a> and <a href=\"https://github.com/cloudflare/agents/blob/main/docs/codemode.md\"><u>code mode in the Agents SDK</u></a> to get started.</p>",
            "url": "https://blog.cloudflare.com/code-mode/",
            "title": "Code Mode: the better way to use MCP",
            "date_modified": "2025-09-26T13:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45381810\">Comments</a>",
            "url": "https://news.sky.com/video/all-british-adults-to-require-a-digital-id-brit-card-13438041",
            "title": "All British adults to require a digital ID 'Brit Card'",
            "date_modified": "2025-09-26T02:09:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45377641\">Comments</a>",
            "url": "https://ollama.com/blog/web-search",
            "title": "Ollama Web Search",
            "date_modified": "2025-09-25T19:21:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45371283\">Comments</a>",
            "url": "https://meks.quest/blogs/the-theatre-of-pull-requests-and-code-review",
            "title": "The Theatre of Pull Requests and Code Review",
            "date_modified": "2025-09-25T10:35:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45362425\">Comments</a>",
            "url": "https://zed.dev/blog/pricing-change-llm-usage-is-now-token-based",
            "title": "Zed's Pricing Has Changed: LLM Usage Is Now Token-Based",
            "date_modified": "2025-09-24T16:13:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45347532\">Comments</a>",
            "url": "https://github.com/humanlayer/advanced-context-engineering-for-coding-agents/blob/main/ace-fca.md",
            "title": "Getting AI to work in complex codebases",
            "date_modified": "2025-09-23T14:27:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45347117\">Comments</a>",
            "url": "https://mitchellh.com/writing/libghostty-is-coming",
            "title": "Libghostty is coming",
            "date_modified": "2025-09-23T13:56:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45333021\">Comments</a>",
            "url": "https://marcobambini.substack.com/p/why-local-first-apps-havent-become",
            "title": "Why haven't local-first apps become popular?",
            "date_modified": "2025-09-22T13:17:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45329080\">Comments</a>",
            "url": "https://blog.val.town/vtlsp",
            "title": "Building a better online editor for TypeScript",
            "date_modified": "2025-09-22T04:22:32.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ahvcd2/nixcon_2025_trip_report\">Comments</a></p>",
            "url": "https://michael.stapelberg.ch/posts/2025-09-21-nixcon-2025-trip-report/",
            "title": "NixCon 2025 Trip Report",
            "date_modified": "2025-09-21T07:51:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45310529\">Comments</a>",
            "url": "https://www.seangoedecke.com/ai-agents-and-code-review/",
            "title": "If you are good at code review, you will be good at using AI agents",
            "date_modified": "2025-09-20T04:59:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45290245\">Comments</a>",
            "url": "https://www.xtxmarkets.com/tech/2025-ternfs/",
            "title": "TernFS – An exabyte scale, multi-region distributed filesystem",
            "date_modified": "2025-09-18T14:36:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45273352\">Comments</a>",
            "url": "https://stategraph.dev/blog/why-stategraph/",
            "title": "Stategraph: Terraform state as a distributed systems problem",
            "date_modified": "2025-09-17T08:38:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45251375\">Comments</a>",
            "url": "https://blog.asciinema.org/post/three-point-o/",
            "title": "Asciinema CLI 3.0 rewritten in Rust, adds live streaming, upgrades file format",
            "date_modified": "2025-09-15T16:06:30.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/dnfqkp/waterpark_transforming_healthcare_with\">Comments</a></p>",
            "url": "https://youtu.be/hdBm4K-vvt0",
            "title": "Waterpark: Transforming Healthcare with Distributed Actors",
            "date_modified": "2025-09-15T13:55:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45201703\">Comments</a>",
            "url": "https://haystackeditor.com",
            "title": "Show HN: Haystack – Review pull requests like you wrote them yourself",
            "date_modified": "2025-09-10T18:21:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45199378\">Comments</a>",
            "url": "https://mergify.com/blog/the-origin-story-of-merge-queues",
            "title": "The Origin Story of Merge Queues",
            "date_modified": "2025-09-10T15:43:43.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/h6peie/origin_story_merge_queues\">Comments</a></p>",
            "url": "https://mergify.com/blog/the-origin-story-of-merge-queues",
            "title": "The Origin Story of Merge Queues",
            "date_modified": "2025-09-10T15:43:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45196568\">Comments</a>",
            "url": "https://til.andrew-quinn.me/posts/tarsnap-is-cozy/",
            "title": "Tarsnap is cozy",
            "date_modified": "2025-09-10T12:17:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45174724\">Comments</a>",
            "url": "https://github.com/Barre/ZeroFS",
            "title": "Show HN: ZeroFS, the Filesystem That Makes S3 Your Primary Storage",
            "date_modified": "2025-09-08T22:13:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45157223\">Comments</a>",
            "url": "https://github.com/BeaconBay/ck",
            "title": "Show HN: Semantic grep with local embeddings",
            "date_modified": "2025-09-07T11:20:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45139122\">Comments</a>",
            "url": "https://bigtechpr.substack.com/p/why-browser-co-610m-is-cheap",
            "title": "Why Browser Company at $610M is cheap",
            "date_modified": "2025-09-05T14:38:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45137525\">Comments</a>",
            "url": "https://codesmash.dev/why-i-ditched-docker-for-podman-and-you-should-too",
            "title": "I Ditched Docker for Podman (and You Should Too)",
            "date_modified": "2025-09-05T11:56:59.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/gsmdft/radicle_1_4_0_lily\">Comments</a></p>",
            "url": "https://radicle.xyz/2025/09/04/radicle-1.4.0",
            "title": "Radicle 1.4.0 – Lily",
            "date_modified": "2025-09-05T07:03:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45128889\">Comments</a>",
            "url": "https://github.com/browseros-ai/BrowserOS/issues/99",
            "title": "Ask HN: What Arc/Dia features should we prioritize?",
            "date_modified": "2025-09-04T16:13:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45126358\">Comments</a>",
            "url": "https://www.atlassian.com/blog/announcements/atlassian-acquires-the-browser-company",
            "title": "The Browser Company (Arc, Dia) Has Been Acquired by Atlassian",
            "date_modified": "2025-09-04T12:12:31.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ksxhza/jujutsu_v0_33_0_released\">Comments</a></p>",
            "url": "https://github.com/jj-vcs/jj/releases/tag/v0.33.0",
            "title": "jujutsu v0.33.0 released",
            "date_modified": "2025-09-04T09:21:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45116978\">Comments</a>",
            "url": "https://www.warp.dev/blog/introducing-warp-code-prompt-to-prod",
            "title": "Warp Code: the fastest way from prompt to production",
            "date_modified": "2025-09-03T15:31:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45116688\">Comments</a>",
            "url": "https://zed.dev/blog/claude-code-via-acp",
            "title": "Claude Code: Now in Beta in Zed",
            "date_modified": "2025-09-03T15:07:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45110075\">Comments</a>",
            "url": "https://morningcoffee.io/parallel-ai-agents-are-a-game-changer.html",
            "title": "Parallel AI agents are a game changer",
            "date_modified": "2025-09-02T22:44:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45107962\">Comments</a>",
            "url": "https://www.sanity.io/blog/first-attempt-will-be-95-garbage",
            "title": "A staff engineer's journey with Claude Code",
            "date_modified": "2025-09-02T19:34:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45083952\">Comments</a>",
            "url": "https://jj-for-everyone.github.io/",
            "title": "Jujutsu for Everyone",
            "date_modified": "2025-08-31T15:31:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45083952\">Comments</a>",
            "url": "https://jj-for-everyone.github.io/",
            "title": "Jujutsu for everyone",
            "date_modified": "2025-08-31T15:31:04.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/s4fffm/jujutsu_for_everyone\">Comments</a></p>",
            "url": "https://jj-for-everyone.github.io/",
            "title": "Jujutsu for everyone",
            "date_modified": "2025-08-31T15:29:08.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/mqbcib/aws_has_finally_made_sqs_viable_queuing\">Comments</a></p>",
            "url": "https://aws.amazon.com/blogs/compute/building-resilient-multi-tenant-systems-with-amazon-sqs-fair-queues/",
            "title": "AWS has finally made SQS a viable queuing solution",
            "date_modified": "2025-08-30T15:56:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45074147\">Comments</a>",
            "url": "https://agentclientprotocol.com/overview/introduction",
            "title": "Agent Client Protocol",
            "date_modified": "2025-08-30T12:42:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45066070\">Comments</a>",
            "url": "https://marcoapp.io/blog/offline-first-landscape",
            "title": "Offline-First Landscape – 2025",
            "date_modified": "2025-08-29T16:20:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45054202\">Comments</a>",
            "url": "https://github.com/DeluxeOwl/chronicle",
            "title": "Chronicle – Idiomatic, type safe event sourcing framework for Go",
            "date_modified": "2025-08-28T16:37:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45050090\">Comments</a>",
            "url": "https://claude-checkpoints.com/",
            "title": "Claude Code Checkpoints",
            "date_modified": "2025-08-28T09:16:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45037904\">Comments</a>",
            "url": "https://monodraw.helftone.com/",
            "title": "Monodraw",
            "date_modified": "2025-08-27T10:54:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45037365\">Comments</a>",
            "url": "https://github.com/orgs/community/discussions/170758",
            "title": "The GitHub website is slow on Safari",
            "date_modified": "2025-08-27T09:43:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45036418\">Comments</a>",
            "url": "https://poor.dev/blog/building-zellij-web-terminal/",
            "title": "Terminal sessions you can bookmark",
            "date_modified": "2025-08-27T07:16:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=45010876\">Comments</a>",
            "url": "https://jakub.kr/components/oklch-colors",
            "title": "What are OKLCH colors?",
            "date_modified": "2025-08-25T06:32:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44990680\">Comments</a>",
            "url": "https://github.com/mbuhot/glyn",
            "title": "Glyn: Type-safe PubSub and Registry for Gleam actors with distributed clustering",
            "date_modified": "2025-08-22T22:29:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44988530\">Comments</a>",
            "url": "https://git.vuxu.org/nitro/about/",
            "title": "Nitro: A tiny but flexible init system and process supervisor",
            "date_modified": "2025-08-22T19:06:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44986251\">Comments</a>",
            "url": "https://devenv.sh/blog/2025/08/22/closing-the-nix-gap-from-environments-to-packaged-applications-for-rust/",
            "title": "Closing the Nix gap: From environments to packaged applications for rust",
            "date_modified": "2025-08-22T16:06:15.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/dlobhb/optique_type_safe_combinatorial_cli\">Comments</a></p>",
            "url": "https://optique.dev/",
            "title": "Optique: Type-safe combinatorial CLI parser for TypeScript",
            "date_modified": "2025-08-21T03:08:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44967469\">Comments</a>",
            "url": "https://tigerbeetle.com/blog/2025-08-04-code-review-can-be-better/",
            "title": "Code review can be better",
            "date_modified": "2025-08-20T23:10:37.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/muv9ja/let_jj_absorb_help_you_keep_clean_commit\">Comments</a></p>",
            "url": "https://www.pauladamsmith.com/blog/2025/08/jj-absorb.html",
            "title": "Let `jj absorb` help you keep a clean commit history",
            "date_modified": "2025-08-20T21:04:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44964916\">Comments</a>",
            "url": "https://github.com/zedless-editor/zed",
            "title": "Zedless: Zed fork focused on privacy and being local-first",
            "date_modified": "2025-08-20T18:47:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44962844\">Comments</a>",
            "url": "https://www.lastweekinaws.com/blog/aws-in-2025-the-stuff-you-think-you-know-thats-now-wrong/",
            "title": "AWS in 2025: Stuff you think you know that's now wrong",
            "date_modified": "2025-08-20T15:30:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44961172\">Comments</a>",
            "url": "https://zed.dev/blog/sequoia-backs-zed",
            "title": "Sequoia backs Zed",
            "date_modified": "2025-08-20T12:13:16.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/zxglnn/code_review_can_be_better\">Comments</a></p>",
            "url": "https://tigerbeetle.com/blog/2025-08-04-code-review-can-be-better/",
            "title": "Code Review Can Be Better",
            "date_modified": "2025-08-20T11:03:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44958400\">Comments</a>",
            "url": "https://gregoryszorc.com/blog/2021/04/07/modern-ci-is-too-complex-and-misdirected/",
            "title": "Modern CI is too complex and misdirected (2021)",
            "date_modified": "2025-08-20T03:30:06.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/oqhuo2/git_rfc_introduce_jj_inspired_git_history\">Comments</a></p>",
            "url": "https://lore.kernel.org/git/20250819-b4-pks-history-builtin-v1-0-9b77c32688fe@pks.im/",
            "title": "Git RFC: Introduce jj-inspired git-history(1) command for easy history editing",
            "date_modified": "2025-08-20T00:38:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44957443\">Comments</a>",
            "url": "https://agents.md/",
            "title": "AGENTS.md – Open format for guiding coding agents",
            "date_modified": "2025-08-20T00:15:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44953032\">Comments</a>",
            "url": "https://research.kudelskisecurity.com/2025/08/19/how-we-exploited-coderabbit-from-a-simple-pr-to-rce-and-write-access-on-1m-repositories/",
            "title": "How we exploited CodeRabbit: From simple PR to RCE and write access on 1M repos",
            "date_modified": "2025-08-19T15:55:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44950600\">Comments</a>",
            "url": "https://www.theverge.com/news/761240/uk-apple-us-encryption-back-door-demands-dropped",
            "title": "UK drops demand for backdoor into Apple encryption",
            "date_modified": "2025-08-19T11:58:34.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/re6xka/using_nix_as_library\">Comments</a></p>",
            "url": "https://fzakaria.com/2025/08/17/using-nix-as-a-library",
            "title": "Using Nix as a library",
            "date_modified": "2025-08-18T21:01:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44944241\">Comments</a>",
            "url": "https://trychroma.com/cloud",
            "title": "Show HN: Chroma Cloud – serverless search database for AI",
            "date_modified": "2025-08-18T19:20:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44937550\">Comments</a>",
            "url": "https://roguesecurity.dev/blog/systemd-hardening",
            "title": "SystemD Service Hardening",
            "date_modified": "2025-08-18T04:57:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44922383\">Comments</a>",
            "url": "https://goose.icu/lambda/",
            "title": "Eliminating JavaScript cold starts on AWS Lambda",
            "date_modified": "2025-08-16T11:44:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44916783\">Comments</a>",
            "url": "https://tylercipriani.com/blog/2025/08/15/git-lfs/",
            "title": "The Future of Large Files in Git Is Git",
            "date_modified": "2025-08-15T20:07:06.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/l4rowa/github_actions_safe_sleep_sh\">Comments</a></p>",
            "url": "https://github.com/actions/runner/blob/v2.328.0/src/Misc/layoutroot/safe_sleep.sh",
            "title": "GitHub Actions safe_sleep.sh",
            "date_modified": "2025-08-15T10:07:53.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/1v1slg/jujutsu_with_radicle\">Comments</a></p>",
            "url": "https://radicle.xyz/2025/08/14/jujutsu-with-radicle",
            "title": "Jujutsu with Radicle",
            "date_modified": "2025-08-14T18:22:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44892209\">Comments</a>",
            "url": "https://astral.sh/blog/introducing-pyx",
            "title": "PYX: The next step in Python packaging",
            "date_modified": "2025-08-13T18:42:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44876289\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=44876289",
            "title": "Ask HN: What alternatives to GitHub are you using?",
            "date_modified": "2025-08-12T13:59:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44874945\">Comments</a>",
            "url": "https://radicle.xyz/2025/08/12/radicle-1.3.0",
            "title": "Radicle 1.3.0",
            "date_modified": "2025-08-12T11:33:42.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/jrutdz/commit_mono\">Comments</a></p>",
            "url": "https://commitmono.com/",
            "title": "Commit Mono",
            "date_modified": "2025-08-11T17:28:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44865560\">Comments</a>",
            "url": "https://www.theverge.com/news/757461/microsoft-github-thomas-dohmke-resignation-coreai-team-transition",
            "title": "GitHub is no longer independent at Microsoft after CEO resignation",
            "date_modified": "2025-08-11T15:47:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44864929\">Comments</a>",
            "url": "https://github.blog/news-insights/company-news/goodbye-github/",
            "title": "Auf Wiedersehen, GitHub – CEO Steps Down",
            "date_modified": "2025-08-11T15:01:39.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/t2qits/gleam_s_interoperability_with_erlang\">Comments</a></p>",
            "url": "https://www.youtube.com/watch?v=63Z2oNW1Bf4",
            "title": "Gleam’s Interoperability with Erlang and Elixir",
            "date_modified": "2025-08-11T10:31:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44855868\">Comments</a>",
            "url": "https://github.com/liquidmetal-dev/flintlock",
            "title": "Flintlock – Create and manage the lifecycle of MicroVMs, backed by containerd",
            "date_modified": "2025-08-10T15:30:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/5qq2k2/why_is_github_ui_getting_so_much_slower\">Comments</a></p>",
            "url": "https://yoyo-code.com/why-is-github-ui-getting-so-much-slower/",
            "title": "Why is GitHub UI getting so much slower?",
            "date_modified": "2025-08-09T02:44:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44833834\">Comments</a>",
            "url": "https://bytemash.net/posts/i-went-down-the-linear-rabbit-hole/",
            "title": "Linear sent me down a local-first rabbit hole",
            "date_modified": "2025-08-08T05:45:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44830221\">Comments</a>",
            "url": "https://cursor.com/cli",
            "title": "Cursor CLI",
            "date_modified": "2025-08-07T20:53:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44826484\">Comments</a>",
            "url": "https://tomscii.sig7.se/2024/01/Ditching-GitHub",
            "title": "Ditching GitHub (2024)",
            "date_modified": "2025-08-07T16:18:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44815405\">Comments</a>",
            "url": "https://github.com/gleam-lang/gleam/blob/main/changelog/v1.12.md",
            "title": "Gleam v1.12.0 Released",
            "date_modified": "2025-08-06T17:57:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44815405\">Comments</a>",
            "url": "https://github.com/gleam-lang/gleam/blob/main/changelog/v1.12.md",
            "title": "Gleam v1.12",
            "date_modified": "2025-08-06T17:57:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44799861\">Comments</a>",
            "url": "https://yoyo-code.com/why-is-github-ui-getting-so-much-slower/",
            "title": "Why is GitHub UI getting so much slower?",
            "date_modified": "2025-08-05T16:07:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44799861\">Comments</a>",
            "url": "https://yoyo-code.com/why-is-github-ui-getting-so-much-slower/",
            "title": "Why is GitHub UI getting slower?",
            "date_modified": "2025-08-05T16:07:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44798166\">Comments</a>",
            "url": "https://deepmind.google/discover/blog/genie-3-a-new-frontier-for-world-models/",
            "title": "Genie 3: A new frontier for world models",
            "date_modified": "2025-08-05T14:08:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44795825\">Comments</a>",
            "url": "https://apps.apple.com/cn/app/ublock-origin-lite/id6745342698",
            "title": "uBlock Origin Lite now available for Safari",
            "date_modified": "2025-08-05T09:01:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44784570\">Comments</a>",
            "url": "https://underjord.io/500-virtual-linux-devices-on-arm64.html",
            "title": "Virtual Linux Devices on ARM64",
            "date_modified": "2025-08-04T11:51:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44777086\">Comments</a>",
            "url": "https://automerge.org/blog/automerge-3/",
            "title": "Automerge 3.0",
            "date_modified": "2025-08-03T15:08:58.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/6b3ete/jujutsu_for_busy_devs_part_2_how_do_i\">Comments</a></p>",
            "url": "https://maddie.wtf/posts/2025-07-21-jujutsu-for-busy-devs/entry/1",
            "title": "Jujutsu For Busy Devs, Part 2: \"How Do I...?\"",
            "date_modified": "2025-08-02T04:26:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44746415\">Comments</a>",
            "url": "https://streamnative.io/products/ursa",
            "title": "Ursa: A leaderless, object storage–based alternative to Kafka",
            "date_modified": "2025-07-31T15:01:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44745045\">Comments</a>",
            "url": "https://codspeed.io/blog/benchmarks-in-ci-without-noise",
            "title": "Benchmarks in CI: Escaping the Cloud Chaos",
            "date_modified": "2025-07-31T12:40:05.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/qwaniu/vanity_import_paths_go_2019\">Comments</a></p>",
            "url": "https://sagikazarmark.hu/blog/vanity-import-paths-in-go/",
            "title": "Vanity import paths in Go (2019)",
            "date_modified": "2025-07-31T12:32:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44700815\">Comments</a>",
            "url": "https://github.com/jkoppel/jj-workshop",
            "title": "The JJ VCS workshop: A zero-to-hero speedrun",
            "date_modified": "2025-07-27T12:23:31.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/m8ytbz/jj_vcs_workshop_zero_hero_speedrun\">Comments</a></p>",
            "url": "https://github.com/jkoppel/jj-workshop",
            "title": "The JJ VCS Workshop: A Zero-to-Hero Speedrun",
            "date_modified": "2025-07-27T12:22:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44678535\">Comments</a>",
            "url": "https://www.anthropic.com/news/how-anthropic-teams-use-claude-code",
            "title": "How Anthropic teams use Claude Code",
            "date_modified": "2025-07-25T01:43:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44661785\">Comments</a>",
            "url": "https://buf.build/blog/hyperpb",
            "title": "Hyperpb: 10x faster dynamic Protobuf parsing that's faster than generated code",
            "date_modified": "2025-07-23T17:32:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44658973\">Comments</a>",
            "url": "https://blog.ydb.tech/the-surprising-grpc-client-bottleneck-in-low-latency-networks-and-how-to-get-around-it-69d6977a1d02",
            "title": "The Surprising gRPC Client Bottleneck in Low-Latency Networks",
            "date_modified": "2025-07-23T13:23:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44658820\">Comments</a>",
            "url": "https://radicle.xyz/2025/07/23/using-radicle-ci-for-development",
            "title": "Using Radicle CI",
            "date_modified": "2025-07-23T13:09:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44651766\">Comments</a>",
            "url": "https://plane.so/blog/everything-you-need-to-know-about-plane-air-gapped",
            "title": "We built an air-gapped Jira alternative for regulated industries",
            "date_modified": "2025-07-22T19:17:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44641961\">Comments</a>",
            "url": "https://maddie.wtf/posts/2025-07-21-jujutsu-for-busy-devs",
            "title": "Jujutsu for busy devs",
            "date_modified": "2025-07-22T00:21:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44629123\">Comments</a>",
            "url": "https://github.com/alefore/duende",
            "title": "Show HN: Duende: Web UX for guiding Gemini as it improves your source code",
            "date_modified": "2025-07-20T20:41:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44617408\">Comments</a>",
            "url": "https://clickhouse.com/blog/postgres-to-clickhouse-data-modeling-tips-v2",
            "title": "Postgres to ClickHouse: Data Modeling Tips",
            "date_modified": "2025-07-19T17:24:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44617184\">Comments</a>",
            "url": "https://www.notcheckmark.com/2025/07/rethinking-cli-interfaces-for-ai/",
            "title": "Rethinking CLI interfaces for AI",
            "date_modified": "2025-07-19T16:58:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44610925\">Comments</a>",
            "url": "https://github.com/ryoppippi/ccusage",
            "title": "Ccusage: A CLI tool for analyzing Claude Code usage from local JSONL files",
            "date_modified": "2025-07-18T23:22:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44594584\">Comments</a>",
            "url": "https://conductor.build/",
            "title": "Show HN: Conductor, a Mac app that lets you run a bunch of Claude Codes at once",
            "date_modified": "2025-07-17T15:43:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44586427\">Comments</a>",
            "url": "https://neon.com/blog/an-apology-and-a-recap-on-may-june-stability",
            "title": "A Recap on May/June Stability at Neon",
            "date_modified": "2025-07-16T20:22:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44586064\">Comments</a>",
            "url": "https://drew.silcock.dev/blog/artisanal-git/",
            "title": "Artisanal Handcrafted Git Repositories",
            "date_modified": "2025-07-16T19:45:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44586064\">Comments</a>",
            "url": "https://drew.silcock.dev/blog/artisanal-git/",
            "title": "Artisanal handcrafted Git repositories",
            "date_modified": "2025-07-16T19:45:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44585002\">Comments</a>",
            "url": "https://www.instantdb.com/essays/agents",
            "title": "How and where will agents ship software?",
            "date_modified": "2025-07-16T17:47:08.000Z"
        },
        {
            "content_html": "<p><a href=\"https://aws.amazon.com/s3/features/metadata/\">Amazon S3 Metadata</a> now provides complete visibility into all your existing objects in your <a href=\"https://aws.amazon.com/s3/\">Amazon Simple Storage Service (Amazon S3)</a> buckets, expanding beyond new objects and changes. With this expanded coverage, you can analyze and query metadata for your entire S3 storage footprint.</p> \n<p>Today, many customers rely on Amazon S3 to store unstructured data at scale. To understand what’s in a bucket, you often need to build and maintain custom systems that scan for objects, track changes, and manage metadata over time. These systems are expensive to maintain and hard to keep up to date as data grows.</p> \n<p>Since <a href=\"https://aws.amazon.com/blogs/aws/introducing-queryable-object-metadata-for-amazon-s3-buckets-preview/\">the launch of S3 Metadata at re:Invent 2024</a>, you’ve been able to query new and updated object metadata using metadata tables instead of relying on <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-inventory.html\">Amazon S3 Inventory</a> or object-level APIs such as <code>ListObjects</code>, <code>HeadObject</code>, and <code>GetObject</code>—which can introduce latency and impact downstream workflows.</p> \n<p>To make it easier for you to work with this expanded metadata, S3 Metadata introduces live inventory tables that work with familiar SQL-based tools. After your existing objects are backfilled into the system, any updates like uploads or deletions typically appear within an hour in your live inventory tables.</p> \n<p>With <strong>S3 Metadata live inventory tables</strong>, you get a fully managed Apache Iceberg table that provides a complete and current snapshot of the objects and their metadata in your bucket, including existing objects, thanks to backfill support. These tables are refreshed automatically within an hour of changes such as uploads or deletions, so you stay up to date. You can use them to identify objects with specific properties—like unencrypted data, missing tags, or particular storage classes—and to support analytics, cost optimization, auditing, and governance.</p> \n<p><strong>S3 Metadata journal tables</strong>, previously known as <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/metadata-tables-overview.html\">S3 Metadata tables</a>, are automatically enabled when you configure live inventory tables, provide a near real-time view of object-level changes in your bucket—including uploads, deletions, and metadata updates. These tables are ideal for auditing activity, tracking the lifecycle of objects, and generating event-driven insights. For example, you can use them to find out which objects were deleted in the past 24 hours, identify the requester making the most <code>PUT</code> operations, or monitor updates to object metadata over time.</p> \n<p>S3 Metadata tables are created in a namespace name that is similar to your bucket name for easier discovery. The tables are stored in AWS table buckets, grouped by account and <a href=\"https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#region\">Region</a>. After you enable S3 Metadata for a general purpose S3 bucket, the system creates and maintains these tables for you. You don’t need to manage compaction or garbage collection processes—<a href=\"https://aws.amazon.com/s3/features/tables/\">S3 Tables</a> takes care of <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-maintenance.html\">table maintenance</a> tasks in the background.</p> \n<p>These new tables help avoid waiting for metadata discovery before processing can begin, making them ideal for large-scale analytics and <a href=\"https://aws.amazon.com/ai/machine-learning/\">machine learning (ML)</a> workloads. By querying metadata ahead of time, you can schedule GPU jobs more efficiently and reduce idle time in compute-intensive environments.</p> \n<p><span><strong>Let’s see how it works<br> </strong></span>To see how this works in practice, I configure S3 Metadata for a general purpose bucket using the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/03/2025-07-03_09-39-10.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/03/2025-07-03_09-39-10.png\" alt=\"S3 Metadata, start from general purpose bucket\" width=\"800\" height=\"523\"></a></p> \n<p>After choosing a general purpose bucket, I choose the <strong>Metadata</strong> tab, then I choose <strong>Create metadata configuration</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/08/2025-07-08_12-23-39.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/08/2025-07-08_12-23-39.png\" alt=\"S3 Metadata, configure journal and inventory table\" width=\"800\" height=\"822\"></a>For <strong>Journal table</strong>, I can choose the <strong>Server-side encryption</strong> option and the <strong>Record expiration</strong> period. For <strong>Live Inventory table</strong>, I choose <strong>Enabled</strong> and I can select the <strong>Server-side encryption</strong> options.</p> \n<p>I configure <strong>Record expiration</strong> on the journal table. Journal table records expire after the specified number of days, 365 days (one year) in my example.</p> \n<p>Then, I choose <strong>Create metadata configuration</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/16/MetadataBlog-rev1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/16/MetadataBlog-rev1.png\" width=\"996\" height=\"582\"></a></p> \n<p>S3 Metadata creates the live inventory table and journal table. In the <strong>Live Inventory table</strong> section, I can observe the <strong>Table status</strong>: the system immediately starts to <strong>backfill</strong> the table with existing object metadata. It can take between minutes to hours. The exact time depends on the quantity of objects you have in your S3 bucket.</p> \n<p>While waiting, I also upload and delete objects to generate data in the journal table.</p> \n<p>Then, I navigate to <a href=\"https://aws.amazon.com/athena\">Amazon Athena</a> to start querying the new tables.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/16/MetadataBlog-rev2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/16/MetadataBlog-rev2.png\" width=\"355\" height=\"290\"></a></p> \n<p>I choose <strong>Query table with Athena</strong> to start querying the table. I can choose between a couple of default queries on the console.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/16/MetadataBlog-rev3.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/07/16/MetadataBlog-rev3.png\" alt=\"MetadataBlog-rev3\" width=\"428\" height=\"662\"></a></p> \n<p>In Athena, I observe the structure of the tables in the <strong>AWSDataCatalog</strong> <strong>Data source</strong> and I start with a short query to check how many records are available in the journal table. I already have 6,488 entries:</p> \n<pre><code>SELECT count(*) FROM \"b_aws-news-blog-metadata-inventory\".\"journal\";\n\n# _col0\n1 6488</code></pre> \n<p>Here are a couple of example queries I tried on the journal table:</p> \n<pre><code># Query deleted objects in last 24 hours\n# Use is_delete_marker=true for versioned buckets and record_type='DELETE' otherwise\nSELECT bucket, key, version_id, last_modified_date\nFROM \"s3tablescatalog/aws-s3\".\"b_aws-news-blog-metadata-inventory\".\"journal\"\nWHERE last_modified_date &gt;= (current_date - interval '1' day) AND is_delete_marker = true;\n\n# bucket key version_id last_modified_date is_delete_marker\n1 aws-news-blog-metadata-inventory .build/index-build/arm64-apple-macosx/debug/index/store/v5/records/G0/NSURLSession.h-JET61D329FG0 \n2 aws-news-blog-metadata-inventory .build/index-build/arm64-apple-macosx/debug/index/store/v5/records/G5/cdefs.h-PJ21EUWKMWG5 \n3 aws-news-blog-metadata-inventory .build/index-build/arm64-apple-macosx/debug/index/store/v5/records/FX/buf.h-25EDY57V6ZXFX \n4 aws-news-blog-metadata-inventory .build/index-build/arm64-apple-macosx/debug/index/store/v5/records/G6/NSMeasurementFormatter.h-3FN8J9CLVMYG6 \n5 aws-news-blog-metadata-inventory .build/index-build/arm64-apple-macosx/debug/index/store/v5/records/G8/NSXMLDocument.h-1UO2NUJK0OAG8 \n\n# Query recent PUT requests IP addresses\nSELECT source_ip_address, count(source_ip_address)\nFROM \"s3tablescatalog/aws-s3\".\"b_aws-news-blog-metadata-inventory\".\"journal\"\nGROUP BY source_ip_address;\n\n#\tsource_ip_address\t_col1\n1\tmy_laptop_IP_address\t12488\n\n# Query S3 Lifecycle expired objects in last 7 days\nSELECT bucket, key, version_id, last_modified_date, record_timestamp\nFROM \"s3tablescatalog/aws-s3\".\"b_aws-news-blog-metadata-inventory\".\"journal\"\nWHERE requester = 's3.amazonaws.com' AND record_type = 'DELETE' AND record_timestamp &gt; (current_date - interval '7' day);\n\n(not applicable to my demo bucket)</code></pre> \n<p>The results helped me track the specific objects that were removed, including their timestamps.</p> \n<p>Now, I look at the live inventory table:</p> \n<pre><code># Distribution of object tags\nSELECT object_tags, count(object_tags)\nFROM \"s3tablescatalog/aws-s3\".\"b_aws-news-blog-metadata-inventory\".\"inventory\"\nGROUP BY object_tags;\n\n# object_tags    _col1\n1 {Source=Swift} 1\n2 {Source=swift} 1\n3 {}             12486\n\n# Query storage class and size for specific tags\nSELECT storage_class, count(*) as count, sum(size) / 1024 / 1024 as usage\nFROM \"s3tablescatalog/aws-s3\".\"b_aws-news-blog-metadata-inventory\".\"inventory\"\nGROUP BY object_tags['pii=true'], storage_class;\n\n# storage_class count   usage\n1 STANDARD      124884  165\n\n# Find objects with specific user defined metadata\nSELECT key, last_modified_date, user_metadata\nFROM \"s3tablescatalog/aws-s3\".\"b_aws-news-blog-metadata-inventory\".\"inventory\"\nWHERE cardinality(user_metadata) &gt; 0 ORDER BY last_modified_date DESC;\n\n(not applicable to my demo bucket)</code></pre> \n<p>These are just a few examples of what is possible with S3 Metadata. Your preferred queries will depend on your use cases. Refer to <a href=\"https://aws.amazon.com/blogs/storage/analyzing-amazon-s3-metadata-with-amazon-athena-and-amazon-quicksight/\">Analyzing Amazon S3 Metadata with Amazon Athena and Amazon QuickSight</a> in the <a href=\"https://aws.amazon.com/blogs/storage/\">AWS Storage Blog</a> for more examples.</p> \n<p><span><strong>Pricing and availability<br> </strong></span>S3 Metadata live inventory and journal tables are available today in US East (N. Virginia), US East (Ohio), and US West (Oregon).</p> \n<p>The journal tables are charged $0.30 per million updates. This is a 33 percent drop from our previous price.</p> \n<p>For inventory tables, there’s a one-time backfill cost of $0.30 for a million objects to set up the table and generate metadata for existing objects. There are no additional costs if your bucket has less than one billion objects. For buckets with more than a billion objects, there is a monthly fee of $0.10 per million objects per month.</p> \n<p>As usual, the <a href=\"https://aws.amazon.com/s3/pricing/\">Amazon S3 pricing page</a> has all the details.</p> \n<p>With S3 Metadata live inventory and journal tables, you can reduce the time and effort required to explore and manage large datasets. You get an up-to-date view of your storage and a record of changes, and both are available as Iceberg tables you can query on demand. You can discover data faster, power compliance workflows, and optimize your ML pipelines.</p> \n<p>You can get started by enabling metadata inventory on your S3 bucket through the AWS console, <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a>, or <a href=\"https://aws.amazon.com/tools/\">AWS SDKs</a>. When they’re enabled, the journal and live inventory tables are automatically created and updated. To learn more, visit the <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html\">S3 Metadata Documentation page</a>.</p> \n<p><a href=\"https://linktr.ee/sebsto\">— seb</a></p> \n<p><em>Update 7/15/2025: Revised some code and updated Region list.</em></p>",
            "url": "https://aws.amazon.com/blogs/aws/amazon-s3-metadata-now-supports-metadata-for-all-your-s3-objects/",
            "title": "Amazon S3 Metadata now supports metadata for all your S3 objects",
            "date_modified": "2025-07-15T23:33:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44569296\">Comments</a>",
            "url": "https://www.lesswrong.com/posts/dxiConBZTd33sFaRC/field-notes-from-shipping-real-code-with-claude",
            "title": "Field Notes on Shipping with Claude Code",
            "date_modified": "2025-07-15T09:01:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44536988\">Comments</a>",
            "url": "https://www.theverge.com/openai/705999/google-windsurf-ceo-openai",
            "title": "OpenAI's Windsurf deal is off, and its CEO is going to Google",
            "date_modified": "2025-07-11T21:35:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44524707\">Comments</a>",
            "url": "https://simonwillison.net/2025/Jul/10/grok-4/",
            "title": "Grok 4",
            "date_modified": "2025-07-10T19:43:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44524430\">Comments</a>",
            "url": "https://h0x0er.github.io/blog/2025/06/29/ebpf-connecting-with-container-runtimes/",
            "title": "eBPF: Connecting with Container Runtimes",
            "date_modified": "2025-07-10T19:10:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44520438\">Comments</a>",
            "url": "https://github.com/afnanenayet/diffsitter",
            "title": "Diffsitter – A Tree-sitter based AST difftool to get meaningful semantic diffs",
            "date_modified": "2025-07-10T12:51:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44517055\">Comments</a>",
            "url": "https://twitter.com/xai/status/1943158495588815072",
            "title": "Grok 4 Launch [video]",
            "date_modified": "2025-07-10T04:02:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44513769\">Comments</a>",
            "url": "https://comet.perplexity.ai/?a=b",
            "title": "Perplexity Comet",
            "date_modified": "2025-07-09T19:14:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44511770\">Comments</a>",
            "url": "https://www.beam.cloud/blog/agentic-apps",
            "title": "The Architecture Behind Lovable and Bolt",
            "date_modified": "2025-07-09T16:10:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44490863\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=44490863",
            "title": "Launch HN: Morph (YC S23) – Apply AI code edits at 4,500 tokens/sec",
            "date_modified": "2025-07-07T14:40:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44489690\">Comments</a>",
            "url": "https://arxiv.org/abs/2506.17298",
            "title": "Mercury: Ultra-Fast Language Models Based on Diffusion",
            "date_modified": "2025-07-07T12:31:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44485649\">Comments</a>",
            "url": "https://github.com/JFryy/systemd-lsp",
            "title": "Show HN: A Language Server Implementation for SystemD Unit Files",
            "date_modified": "2025-07-07T00:57:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44481286\">Comments</a>",
            "url": "https://www.indragie.com/blog/i-shipped-a-macos-app-built-entirely-by-claude-code",
            "title": "Building a Mac app with Claude code",
            "date_modified": "2025-07-06T14:55:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44472062\">Comments</a>",
            "url": "https://9to5mac.com/2025/07/04/apple-just-released-a-weirdly-interesting-coding-language-model/",
            "title": "Apple just released a weirdly interesting coding language model",
            "date_modified": "2025-07-05T11:44:05.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/7abrl9/500x_faster_four_different_ways_speed_up\">Comments</a></p>",
            "url": "https://pythonspeed.com/articles/different-ways-speed/",
            "title": "500× faster: Four different ways to speed up your code",
            "date_modified": "2025-07-02T18:12:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44441530\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=44441530",
            "title": "Ask HN: Why there is no demand for my SaaS when competition is killing it?",
            "date_modified": "2025-07-02T09:05:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44435716\">Comments</a>",
            "url": "https://jamesbvaughan.com/bidirectional-editing/",
            "title": "Code⇄GUI bidirectional editing via LSP",
            "date_modified": "2025-07-01T16:43:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44432272\">Comments</a>",
            "url": "https://branching.app",
            "title": "Version Control for AI Coding",
            "date_modified": "2025-07-01T10:00:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44429225\">Comments</a>",
            "url": "https://docs.anthropic.com/en/docs/claude-code/hooks",
            "title": "Claude Code now supports Hooks",
            "date_modified": "2025-07-01T00:01:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44429225\">Comments</a>",
            "url": "https://docs.anthropic.com/en/docs/claude-code/hooks",
            "title": "Claude Code now supports hooks",
            "date_modified": "2025-07-01T00:01:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44426233\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=44426233",
            "title": "Ask HN: What's the 2025 stack for a self-hosted photo library with local AI?",
            "date_modified": "2025-06-30T18:10:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44424256\">Comments</a>",
            "url": "https://tesseral.com/blog/b2b-auth-isnt-that-similar-to-b2c-auth",
            "title": "Auth for B2B SaaS: it's not like auth for consumer software",
            "date_modified": "2025-06-30T15:06:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44421776\">Comments</a>",
            "url": "https://untested.sonnet.io/notes/new-enso-first-public-beta/",
            "title": "Show HN: New Ensō – first public beta",
            "date_modified": "2025-06-30T11:02:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44416421\">Comments</a>",
            "url": "https://not-matthias.github.io/posts/anticheat-update-tracking/",
            "title": "Anticheat Update Tracking",
            "date_modified": "2025-06-29T21:06:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44407708\">Comments</a>",
            "url": "https://www.dolthub.com/blog/2025-06-03-people-keep-inventing-prolly-trees/",
            "title": "People Keep Inventing Prolly Trees",
            "date_modified": "2025-06-28T20:01:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44392304\">Comments</a>",
            "url": "https://hiandrewquinn.github.io/til-site/posts/save-your-disk-write-files-directly-into-ram-with-dev-shm/",
            "title": "Save your disk, write files directly into RAM with /dev/shm",
            "date_modified": "2025-06-26T23:04:30.000Z"
        },
        {
            "content_html": "How we use data lineage to optimize ClickHouse table deployments and avoid unnecessary and expensive data migrations.",
            "url": "https://www.tinybird.co/blog-posts/when-not-to-migrate-your-data",
            "title": "How we automatically handle ClickHouse schema migrations",
            "date_modified": "2025-06-26T22:56:12.656Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44389926\">Comments</a>",
            "url": "https://depot.dev/blog/container-security-at-scale-building-untrusted-images-safely",
            "title": "Building untrusted container images safely at scale",
            "date_modified": "2025-06-26T18:22:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44389202\">Comments</a>",
            "url": "https://developers.googleblog.com/en/introducing-gemma-3n-developer-guide/",
            "title": "Introducing Gemma 3n",
            "date_modified": "2025-06-26T17:03:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44386887\">Comments</a>",
            "url": "https://www.cubic.dev/blog/learnings-from-building-ai-agents",
            "title": "Learnings from Building AI Agents",
            "date_modified": "2025-06-26T12:45:04.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ng5imb/docs_for_ai_agents\">Comments</a></p>",
            "url": "https://technicalwriting.dev/ai/agents/",
            "title": "Docs for AI agents",
            "date_modified": "2025-06-25T23:31:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44376919\">Comments</a>",
            "url": "https://github.com/google-gemini/gemini-cli",
            "title": "Gemini CLI",
            "date_modified": "2025-06-25T13:10:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44365620\">Comments</a>",
            "url": "https://github.com/useautumn/autumn",
            "title": "Show HN: Autumn – Open-source infra over Stripe",
            "date_modified": "2025-06-24T12:48:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/s9xgbs/json_evolution_go_from_v1_v2\">Comments</a></p>",
            "url": "https://antonz.org/go-json-v2/",
            "title": "JSON evolution in Go: from v1 to v2",
            "date_modified": "2025-06-23T13:19:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44353490\">Comments</a>",
            "url": "https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code",
            "title": "Claude Code for VSCode",
            "date_modified": "2025-06-23T08:07:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44336015\">Comments</a>",
            "url": "https://clickhouse.com/blog/scaling-observability-beyond-100pb-wide-events-replacing-otel",
            "title": "ClickHouse scales beyond 100 petabytes of logs",
            "date_modified": "2025-06-21T09:23:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44329102\">Comments</a>",
            "url": "https://github.com/hatchet-dev/pickaxe",
            "title": "Show HN: Pickaxe – a TypeScript library for building AI agents",
            "date_modified": "2025-06-20T16:07:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44328855\">Comments</a>",
            "url": "https://support.hashicorp.com/hc/en-us/articles/41802449287955-HCP-Vault-Secrets-End-Of-Life",
            "title": "HCP Vault Secrets End of Life",
            "date_modified": "2025-06-20T15:47:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44328326\">Comments</a>",
            "url": "https://fly.io/blog/phoenix-new-the-remote-ai-runtime/",
            "title": "Phoenix.new – Remote AI Runtime for Phoenix",
            "date_modified": "2025-06-20T14:57:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44317242\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=44317242",
            "title": "Show HN: I'm building an app to replace Overleaf and Notion",
            "date_modified": "2025-06-19T10:33:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44314423\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=LCEmiRjPEtQ",
            "title": "Andrej Karpathy: Software in the era of AI [video]",
            "date_modified": "2025-06-19T00:33:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44310763\">Comments</a>",
            "url": "https://airpass.tiagoalves.me/",
            "title": "Airpass – easily overcome WiFi time limits",
            "date_modified": "2025-06-18T15:29:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44301809\">Comments</a>",
            "url": "https://www.anthropic.com/engineering/building-effective-agents",
            "title": "Building Effective AI Agents",
            "date_modified": "2025-06-17T17:50:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44297349\">Comments</a>",
            "url": "https://omarabid.com/claude-magic",
            "title": "Why Claude Code feels like magic?",
            "date_modified": "2025-06-17T09:53:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44290331\">Comments</a>",
            "url": "https://coroot.com/blog/opentelemetry-for-go-measuring-the-overhead/",
            "title": "OpenTelemetry for Go: Measuring overhead costs",
            "date_modified": "2025-06-16T15:09:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44282591\">Comments</a>",
            "url": "https://github.com/noghartt/container-compose",
            "title": "Show HN: Container-compose – A Docker-compose like tool for Apple containers",
            "date_modified": "2025-06-15T14:53:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44264362\">Comments</a>",
            "url": "https://substack.com/home/post/p-165651243",
            "title": "Being a Force Multiplier",
            "date_modified": "2025-06-12T23:38:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44247020\">Comments</a>",
            "url": "https://signoz.io/blog/cicd-observability-with-opentelemetry/",
            "title": "CI/CD Observability with OpenTelemetry Step by Step Guide",
            "date_modified": "2025-06-11T12:42:50.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/8tgpqm/field_notes_from_shipping_real_code_with\">Comments</a></p>",
            "url": "https://diwank.space/field-notes-from-shipping-real-code-with-claude",
            "title": "Field Notes From Shipping Real Code With Claude",
            "date_modified": "2025-06-09T02:16:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/bxpwqt/i_read_all_cloudflare_s_claude_generated\">Comments</a></p>",
            "url": "https://www.maxemitchell.com/writings/i-read-all-of-cloudflares-claude-generated-commits/",
            "title": "I Read All Of Cloudflare's Claude-Generated Commits",
            "date_modified": "2025-06-07T12:00:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44196417\">Comments</a>",
            "url": "https://github.com/possibilities/claude-composer",
            "title": "Show HN: Claude Composer",
            "date_modified": "2025-06-05T22:53:21.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing a new publicly available source of API models for <a href=\"https://aws.amazon.com/\">Amazon Web Services (AWS)</a>. We are now publishing AWS API models on a daily basis to <a href=\"https://central.sonatype.com/search?namespace=software.amazon.api.models&amp;sort=name\">Maven Central</a> and providing open source access to a new repository on <a href=\"https://github.com/aws/api-models-aws\">GitHub</a>. This repository includes a definitive, up-to-date source of <a href=\"https://smithy.io/\">Smithy API models</a> that define AWS public interface definitions and behaviors.</p> \n<p>These Smithy models can be used to better understand AWS services and build developer tools like custom SDKs and command line interfaces (CLIs) for connecting to AWS or testing tools for validating your application integrations on AWS.</p> \n<p>Since 2018, we have been generating SDK clients and CLI tools using <a href=\"https://aws.amazon.com/blogs/developer/tag/smithy/\">Smithy models</a>. All AWS services are modeled in Smithy to thoroughly document the API contract including operations and behaviors like protocols, authentication, request and response types, and errors.</p> \n<p>With this public resource, you can build and test your own applications that can integrate directly with AWS services with confidence such as:</p> \n<ul> \n <li><strong>Generate SDK clients</strong> – You can build your own, purpose-built SDKs for language communities without <a href=\"https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">official AWS SDK support</a> and client code generator using Smithy toolchain to <a href=\"https://smithy.io/2.0/tutorials/full-stack-tutorial.html#generating-the-client\">generate client SDK libraries</a>.</li> \n <li><strong>Generating API implementations</strong> – You can generate server stubs for language-specific framework, even <a href=\"https://modelcontextprotocol.io/introduction\">model context protocol (MCP)</a> server configurations for your AI agents. You have built-in validation to ensure you adhere to your own API standards.</li> \n <li><strong>Build your own developer tools</strong> – You can build your own tools on top of AWS such as mock testing tools, IAM policy generators, or higher-level abstractions for connecting to AWS.</li> \n <li><strong>Understand AWS API behaviors</strong> – You can concisely and easily investigate your artifact to quickly review and understand how SDKs interpret API calls and the behaviors to expect with those calls.</li> \n</ul> \n<p><strong><u>Learn about AWS API models</u></strong><br> You can browse the AWS service models directly on GitHub by accessing the <code>api-models-aws</code> repository. This repository contains Smithy models with the <a href=\"https://smithy.io/2.0/spec/json-ast.html\">JSON AST</a> format for all public AWS API services. All Smithy models consist of shapes and traits. <a href=\"https://smithy.io/2.0/spec/model.html#shapes\">Shapes</a> are instances of <code>types</code> and <a href=\"https://smithy.io/2.0/spec/model.html#traits\">traits</a> are used to add more information to shapes that might be useful for clients, servers, or documentation.</p> \n<p>The AWS models repository contains:</p> \n<ul> \n <li>Top-level service directories are named using the <code>&lt;sdk-id&gt;</code> of the service, where <code>&lt;sdk-id&gt;</code> is the value of the model’s <a href=\"https://smithy.io/2.0/aws/aws-core.html#sdkid\">sdkId</a>, lowercased and with spaces converted to hyphens</li> \n <li>Each service directory contains one directory per <code>&lt;version&gt;</code> of the service, where <code>&lt;version&gt;</code> is the value of the service shape’s <a href=\"https://smithy.io/2.0/spec/service-types.html#service\">version property</a>.</li> \n <li>Contained within a service-version directory, a model file named &lt;<code>sdk-id&gt;-&lt;version&gt;.json</code> will be present</li> \n</ul> \n<p>For example, when you want to define a <code>RunInstances</code> API in <a href=\"https://aws.amazon.com/ec2\">Amazon EC2</a> service, the model uses <code>service</code> type, an entry point of an API that aggregates resources and operations together. The shape referenced by a member is called its <code>target</code>.</p> \n<pre><code>com.amazonaws.ec2#AmazonEC2\": {\n      \"type\": \"service\",\n      \"version\": \"2016-11-15\",\n      \"operations\": [\n....\n        {\n          \"target\": \"com.amazonaws.ec2#RunInstances\"\n        },\n....\n\t  ]</code></pre> \n<p>The <code>operation</code> type represents the input, output, traits, and possible errors of an API operation. Operation shapes are bound to <a href=\"https://smithy.io/2.0/spec/service-types.html#resource\">resource</a> shapes and <a href=\"https://smithy.io/2.0/spec/service-types.html#service\">service</a> shapes. An operation is defined in the IDL using an <a href=\"https://smithy.io/2.0/spec/idl.html#idl-operation\">operation_statement</a>. In the traits, you can find detailed API information such as documentation, examples, and so on.</p> \n<pre><code>\"com.amazonaws.ec2#RunInstances\": {\n      \"type\": \"operation\",\n      \"input\": {\n        \"target\": \"com.amazonaws.ec2#RunInstancesRequest\"\n      },\n      \"output\": {\n        \"target\": \"com.amazonaws.ec2#Reservation\"\n      },\n      \"traits\": {\n        \"smithy.api#documentation\": \"&lt;p&gt;Launches the specified number of instances using an AMI for which you have....\",\n        smithy.api#examples\": [\n          {\n            \"title\": \"To launch an instance\",\n            \"documentation\": \"This example launches an instance using the specified AMI, instance type, security group, subnet, block device mapping, and tags.\",\n            \"input\": {\n              \"BlockDeviceMappings\": [\n                {\n                  \"DeviceName\": \"/dev/sdh\",\n                  \"Ebs\": {\n                    \"VolumeSize\": 100\n                  }\n                }\n              ],\n              \"ImageId\": \"ami-abc12345\",\n              \"InstanceType\": \"t2.micro\",\n              \"KeyName\": \"my-key-pair\",\n              \"MaxCount\": 1,\n              \"MinCount\": 1,\n              \"SecurityGroupIds\": [\n                \"sg-1a2b3c4d\"\n              ],\n              \"SubnetId\": \"subnet-6e7f829e\",\n              \"TagSpecifications\": [\n                {\n                  \"ResourceType\": \"instance\",\n                  \"Tags\": [\n                    {\n                      \"Key\": \"Purpose\",\n                      \"Value\": \"test\"\n                    }\n                  ]\n                }\n              ]\n            },\n            \"output\": {}\n          }\n        ]\n      }\n    },</code></pre> \n<p>We use Smithy extensively to model our service APIs and provide the daily releases of the <a href=\"https://aws.amazon.com/developer/tools/\">AWS SDKs</a> and <a href=\"https://aws.amazon.com/cli/\">AWS CLI</a>. AWS API models can be helpful for implementing server stubs to interact with AWS services.</p> \n<p><strong><u>How to build with AWS API models<br> </u></strong>Smithy API models provide <a href=\"https://github.com/smithy-lang/awesome-smithy\">building resources</a> such as build tools, client or server code generators, IDE support, and implementations. For example, with <a href=\"https://smithy.io/2.0/guides/smithy-cli/index.html\">Smithy CLI</a>, you can easily build your models, run ad-hoc validation, compare models for differences, query models, and more. The Smithy CLI makes it easy to get started working with Smithy without setting up Java or using the <a href=\"https://smithy.io/2.0/guides/gradle-plugin/index.html#smithy-gradle-plugin\"><span>Smithy Gradle Plugins</span></a>.</p> \n<p>I want to show two examples how to build your own applications with AWS API models and Smithy build tools.</p> \n<ul> \n <li><strong>Build a minimal SDK client</strong> – This sample project provides a template to get started using <a href=\"https://github.com/smithy-lang/smithy-typescript/\">Smithy TypeScript</a> to create a minimal AWS SDK client for Amazon DynamoDB. You can build the minimal SDK from the Smithy model, and then run the example code. To learn more, visit the <a href=\"https://github.com/smithy-lang/smithy-examples/tree/main/smithy-typescript-examples/minimal-aws-sdk-client\">example project</a> here.</li> \n <li><strong>Build MCP servers </strong>– This sample project provides a template to generate a fat jar which contains all the dependencies required to run an MCP <code>StdIO</code> server using the Smithy CLI. You can find&nbsp;<code>MCPServerExample</code> to build an MCP server by modeling tools as Smithy APIs and <code>ProxyMCPExample</code> to create a proxy MCP Server for any Smithy service.&nbsp;To learn more, visit the <a href=\"https://github.com/smithy-lang/smithy-java/tree/main/examples/mcp-server\">GitHub repository</a>.</li> \n</ul> \n<p><strong><u>Now available<br> </u></strong>You can now access AWS API models on a daily basis providing open-source access on the <a href=\"https://github.com/aws/api-models-aws\">AWS API models repository</a> and service model packages available on <a href=\"https://central.sonatype.com/search?namespace=software.amazon.api.models&amp;sort=name\">Maven Central</a>. You can import models and add dependencies using the maven package of your choice.</p> \n<p>To learn more about the AWS preferred API modeling language, visit <a href=\"https://smithy.io/\">Smithy.io</a> and its <a href=\"https://smithy.io/2.0/guides/using-code-generation/index.html\">code generation guide</a>. To learn more each AWS SDKs, visit <a href=\"https://aws.amazon.com/developer/tools/\">Tools to Build on AWS</a> and its <a href=\"https://github.com/aws\">respective repository</a> for SDK specific support or through your usual AWS Support contacts.</p> \n<p>— <a href=\"https://twitter.com/channyun\">Channy</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-aws-api-models-and-publicly-available-resources-for-aws-api-definitions/",
            "title": "Introducing AWS API models and publicly available resources for AWS API definitions",
            "date_modified": "2025-06-05T20:18:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44194871\">Comments</a>",
            "url": "https://www.nytimes.com/2025/05/21/climate/ai-weather-models-aurora-microsoft.html",
            "title": "Aurora, a foundation model for the Earth system",
            "date_modified": "2025-06-05T19:17:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44194082\">Comments</a>",
            "url": "https://github.com/hyperdxio/hyperdx",
            "title": "Show HN: ClickStack – Open-source Datadog alternative by ClickHouse and HyperDX",
            "date_modified": "2025-06-05T18:01:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44188839\">Comments</a>",
            "url": "https://aavetis.github.io/ai-pr-watcher/",
            "title": "Tracking Copilot vs. Codex vs. Cursor vs. Devin PR Performance",
            "date_modified": "2025-06-05T06:22:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44177824\">Comments</a>",
            "url": "https://help.openai.com/en/articles/11428266-codex-changelog",
            "title": "Codex Changelog: agent internet access, voice dictation and update existing PRs",
            "date_modified": "2025-06-04T06:44:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44170967\">Comments</a>",
            "url": "https://steipete.me/posts/2025/claude-code-is-my-computer",
            "title": "Claude Code Is My Computer",
            "date_modified": "2025-06-03T15:14:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44163063\">Comments</a>",
            "url": "https://fly.io/blog/youre-all-nuts/",
            "title": "My AI skeptic friends are all nuts",
            "date_modified": "2025-06-02T21:09:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44162443\">Comments</a>",
            "url": "https://www.wsj.com/articles/snowflake-to-buy-crunchy-data-for-250-million-233543ab",
            "title": "Snowflake to Buy Crunchy Data for $250M",
            "date_modified": "2025-06-02T20:01:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44159166\">Comments</a>",
            "url": "https://github.com/cloudflare/workers-oauth-provider/commits/main/",
            "title": "Cloudlflare builds OAuth with Claude and publishes all the prompts",
            "date_modified": "2025-06-02T14:24:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44159166\">Comments</a>",
            "url": "https://github.com/cloudflare/workers-oauth-provider/",
            "title": "Cloudlflare builds OAuth with Claude and publishes all the prompts",
            "date_modified": "2025-06-02T14:24:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44150093\">Comments</a>",
            "url": "https://github.com/openai/codex/discussions/1174",
            "title": "Codex CLI is going native",
            "date_modified": "2025-06-01T11:22:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44141887\">Comments</a>",
            "url": "https://commaok.xyz/post/simple-backoff/",
            "title": "Simpler Backoff",
            "date_modified": "2025-05-31T04:43:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44136539\">Comments</a>",
            "url": "https://jamesin.substack.com/p/whats-working-for-yc-companies-since",
            "title": "What's Working for YC Companies Since the AI Boom",
            "date_modified": "2025-05-30T14:24:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44135977\">Comments</a>",
            "url": "https://github.com/microsandbox/microsandbox",
            "title": "Microsandbox: Virtual Machines that feel and perform like containers",
            "date_modified": "2025-05-30T13:20:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44128751\">Comments</a>",
            "url": "https://tailscale.com/blog/grants-ga",
            "title": "A new generation of Tailscale access controls",
            "date_modified": "2025-05-29T18:23:04.000Z"
        },
        {
            "content_html": "<p>Cloudflare Workers Builds is our <a href=\"https://en.wikipedia.org/wiki/CI/CD\"><u>CI/CD</u></a> product that makes it easy to build and deploy Workers applications every time code is pushed to GitHub or GitLab. What makes Workers Builds special is that projects can be built and deployed with minimal configuration.<a href=\"https://developers.cloudflare.com/workers/ci-cd/builds/#get-started\"> <u>Just hook up your project and let us take care of the rest!</u></a></p><p>But what happens when things go wrong, such as failing to install tools or dependencies? What usually happens is that we don’t fix the problem until a customer contacts us about it, at which point many other customers have likely faced the same issue. This can be a frustrating experience for both us and our customers because of the lag time between issues occurring and us fixing them.</p><p>We want Workers Builds to be reliable, fast, and easy to use so that developers can focus on building, not dealing with our bugs. That’s why we recently started building an error detection system that can detect, categorize, and surface all build issues occurring on Workers Builds, enabling us to proactively fix issues and add missing features.</p><p>It’s also no secret that we’re big fans of being “<a href=\"https://www.cloudflare.com/the-net/top-of-mind-security/customer-zero/\">Customer Zero</a>” at Cloudflare, and Workers Builds is itself a product that’s built end-to-end on our <a href=\"https://www.cloudflare.com/developer-platform/\"><u>Developer Platform</u></a> using <a href=\"https://developers.cloudflare.com/workers/\"><u>Workers</u></a>, <a href=\"https://developers.cloudflare.com/durable-objects/\"><u>Durable Objects</u></a>, <a href=\"https://developers.cloudflare.com/hyperdrive/\"><u>Hyperdrive</u></a>, <a href=\"https://blog.cloudflare.com/cloudflare-containers-coming-2025/\"><u>Containers</u></a>, <a href=\"https://developers.cloudflare.com/queues/\"><u>Queues</u></a>, <a href=\"https://developers.cloudflare.com/kv/\"><u>Workers KV</u></a>, <a href=\"https://developers.cloudflare.com/r2/\"><u>R2</u></a>, and <a href=\"https://developers.cloudflare.com/workers/observability/\"><u>Workers Observability</u></a>.</p><p>In this post, we will dive into how we used the <a href=\"https://www.cloudflare.com/developer-platform/\">Cloudflare Developer Platform</a> to check for issues across more than <b>1 million Durable Objects</b>.</p>\n          <div>\n            <h2>Background: Workers Builds architecture</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#background-workers-builds-architecture\">\n              \n            </a>\n          </div>\n          <p>Back in October 2024, we wrote about<a href=\"https://blog.cloudflare.com/workers-builds-integrated-ci-cd-built-on-the-workers-platform/\"> <u>how we built Workers Builds entirely on the Workers platform</u></a>. To recap, Builds is built using Workers, Durable Objects, Workers KV, R2, Queues, Hyperdrive, and a Postgres database. Some of these things were not present when launched back in October (for example, Queues and KV). But the core of the architecture is the same.</p><p>A client Worker receives GitHub/GitLab webhooks and stores build metadata in Postgres (via Hyperdrive). A build management Worker uses two Durable Object classes: a Scheduler class to find builds in Postgres that need scheduling, and a class called BuildBuddy to manage the lifecycle of a build. When a build needs to be started, Scheduler creates a new BuildBuddy instance which is responsible for creating a container for the build (using<a href=\"https://blog.cloudflare.com/container-platform-preview/\"> <u>Cloudflare Containers</u></a>), monitoring the container with health checks, and receiving build logs so that they can be viewed in the Cloudflare Dashboard.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Zf6QSXafUJOxn6isLsqar/fd8eaa3428185c3da2ef96ddd1fdc43c/image2.png\">\n          </figure><p>In addition to this core scheduling logic, we have several Workers Queues for background work such as sending PR comments to GitHub/GitLab.</p>\n          <div>\n            <h2>The problem: builds are failing</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#the-problem-builds-are-failing\">\n              \n            </a>\n          </div>\n          <p>While this architecture has worked well for us so far, we found ourselves with a problem: compared to<a href=\"https://developers.cloudflare.com/pages/\"> <u>Cloudflare Pages</u></a>, a concerning percentage of builds were failing. We needed to dig deeper and figure out what was wrong, and understand how we could improve Workers Builds so that developers can focus more on shipping instead of build failures.</p>\n          <div>\n            <h2>Types of build failures</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#types-of-build-failures\">\n              \n            </a>\n          </div>\n          <p>Not all build failures are the same. We have several categories of failures that we monitor:</p><ul><li><p>Initialization failures: when the container fails to start.</p></li><li><p>Clone failures: failing to clone the repository from GitHub/GitLab.</p></li><li><p>Build timeouts: builds that ran past the limit and were terminated by BuildBuddy.</p></li><li><p>Builds failing health checks: the container stopped responding to health checks, e.g. the container crashed for an unknown reason.</p></li><li><p>Failure to install tools or dependencies.</p></li><li><p>Failed user build/deploy commands.</p></li></ul><p>The first few failure types were straightforward, and we’ve been able to track down and fix issues in our build system and control plane to improve what we call “build completion rate”. We define build completion as the following:</p><ol><li><p>We successfully started the build.</p></li><li><p>We attempted to install tools/dependencies (considering failures as “user error”).</p></li><li><p>We attempted to run the user-defined build/deploy commands (again, considering failures as “user error”).</p></li><li><p>We successfully marked the build as stopped in our database.</p></li></ol><p>For example, we had a bug where builds for a deleted Worker would attempt to run and continuously fail, which affected our build completion rate metric.</p>\n          <div>\n            <h3>User error</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#user-error\">\n              \n            </a>\n          </div>\n        <p>We’ve made a lot of progress improving the reliability of build and container orchestration, but we had a significant percentage of build failures in the “user error” metric. We started asking ourselves “is this actually user error? Or is there a problem with the product itself?”</p><p>This presented a challenge because questions like “did the build command fail due to a bug in the build system, or user error?” are a lot harder to answer than pass/fail issues like failing to create a container for the build. To answer these questions, we had to build something new, something smarter.</p>\n          <div>\n            <h3>Build logs</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#build-logs\">\n              \n            </a>\n          </div>\n        <p>The most obvious way to determine why a build failed is to look at its logs. When spot-checking build failures, we can typically identify what went wrong. For example, some builds fail to install dependencies because of an out of date lockfile (e.g. package-lock.json out of date with package.json). But looking through build failures one by one doesn’t scale. We didn’t want engineers looking through customer build logs without at least suspecting that there was an issue with our build system that we could fix.</p>\n          <div>\n            <h2>Automating error detection</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#automating-error-detection\">\n              \n            </a>\n          </div>\n          <p>At this point, next steps were clear: we needed an automated way to identify why a build failed based on build logs, and provide a way for engineers to see what the top issues were while ensuring privacy (e.g. removing account-specific identifiers and file paths from the aggregate data).</p>\n          <div>\n            <h3>Detecting errors in build logs using Workers Queues</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#detecting-errors-in-build-logs-using-workers-queues\">\n              \n            </a>\n          </div>\n        <p>The first thing we needed was a way to categorize build errors after a build fails. To do this, we created a queue named BuildErrorsQueue to process builds and look for errors. After a build fails, BuildBuddy will send the build ID to BuildErrorsQueue which fetches the logs, checks for issues, and saves results to Postgres.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3423WCenScTudEv27TCMnJ/86b621a957d4249449c99db43a43bb9a/image7.png\">\n          </figure><p>We started out with a few static patterns to match things like Wrangler errors in log lines:</p>\n            <pre><code>export const DetectedErrorCodes = {\n  wrangler_error: {\n    detect: async (lines: LogLines) =&gt; {\n      const errors: DetectedError[] = []\n      for (const line of lines) {\n        if (line[2].trim().startsWith('✘ [ERROR]')) {\n          errors.push({\n            error_code: 'wrangler_error',\n            error_group: getWranglerLogGroupFromLogLine(line, wranglerRegexMatchers),\n            detected_on: new Date(),\n            lines_matched: [line],\n          })\n        }\n      }\n      return errors\n    },\n  },\n  installing_tools_or_dependencies_failed: { ... },\n}</code></pre>\n            <p>It wouldn’t be useful if all Wrangler errors were grouped under a single generic “wrangler_error” code, so we further grouped them by normalizing the log lines into groups:</p>\n            <pre><code>function getWranglerLogGroupFromLogLine(\n  logLine: LogLine,\n  regexMatchers: RegexMatcher[]\n): string {\n  const original = logLine[2].trim().replaceAll(/[\\t\\n\\r]+/g, ' ')\n  let message = original\n  let group = original\n  for (const { mustMatch, patterns, stopOnMatch, name, useNameAsGroup } of regexMatchers) {\n    if (mustMatch !== undefined) {\n      const matched = matchLineToRegexes(message, mustMatch)\n      if (!matched) continue\n    }\n    if (patterns) {\n      for (const [pattern, mask] of patterns) {\n        message = message.replaceAll(pattern, mask)\n      }\n    }\n    if (useNameAsGroup === true) {\n      group = name\n    } else {\n      group = message\n    }\n    if (Boolean(stopOnMatch) &amp;&amp; message !== original) break\n  }\n  return group\n}\n\nconst wranglerRegexMatchers: RegexMatcher[] = [\n  {\n    name: 'could_not_resolve',\n    // ✘ [ERROR] Could not resolve \"./balance\"\n    // ✘ [ERROR] Could not resolve \"node:string_decoder\" (originally \"string_decoder/\")\n    mustMatch: [/^✘ \\[ERROR\\] Could not resolve \"[@\\w :/\\\\.-]*\"/i],\n    stopOnMatch: true,\n    patterns: [\n      [/(?&lt;=^✘ \\[ERROR\\] Could not resolve \")[@\\w :/\\\\.-]*(?=\")/gi, '&lt;MODULE&gt;'],\n      [/(?&lt;=\\(originally \")[@\\w :/\\\\.-]*(?=\")/gi, '&lt;MODULE&gt;'],\n    ],\n  },\n  {\n    name: 'no_matching_export_for_import',\n    // ✘ [ERROR] No matching export in \"src/db/schemas/index.ts\" for import \"someCoolTable\"\n    mustMatch: [/^✘ \\[ERROR\\] No matching export in \"/i],\n    stopOnMatch: true,\n    patterns: [\n      [/(?&lt;=^✘ \\[ERROR\\] No matching export in \")[@~\\w:/\\\\.-]*(?=\")/gi, '&lt;MODULE&gt;'],\n      [/(?&lt;=\" for import \")[\\w-]*(?=\")/gi, '&lt;IMPORT&gt;'],\n    ],\n  },\n  // ...many more added over time\n]</code></pre>\n            <p>Once we had our error detection matchers and normalizing logic in place, implementing the BuildErrorsQueue consumer was easy:</p>\n            <pre><code>export async function handleQueue(\n  batch: MessageBatch,\n  env: Bindings,\n  ctx: ExecutionContext\n): Promise&lt;void&gt; {\n  ...\n  await pMap(batch.messages, async (msg) =&gt; {\n    try {\n      const { build_id } = BuildErrorsQueueMessageBody.parse(msg.body)\n      await store.buildErrors.deleteErrorsByBuildId({ build_id })\n      const bb = getBuildBuddy(env, build_id)\n      const errors: DetectedError[] = []\n      let cursor: LogsCursor | undefined\n      let hasMore = false\n\n      do {\n        using maybeNewLogs = await bb.getLogs(cursor, false)\n        const newLogs = LogsWithCursor.parse(maybeNewLogs)\n        cursor = newLogs.cursor\n        const newErrors = await detectErrorsInLogLines(newLogs.lines)\n        errors.push(...newErrors)\n        hasMore = Boolean(cursor) &amp;&amp; newLogs.lines.length &gt; 0\n      } while (hasMore)\n\n      if (errors.length &gt; 0) {\n        await store.buildErrors.insertErrors(\n          errors.map((e) =&gt; ({\n            build_id,\n            error_code: e.error_code,\n            error_group: e.error_group,\n          }))\n        )\n      }\n      msg.ack()\n    } catch (e) {\n      msg.retry()\n      sentry.captureException(e)\n    }\n  })\n}</code></pre>\n            <p>Here, we’re fetching logs from each build’s BuildBuddy Durable Object, detecting why it failed using the matchers we wrote, and saving errors to the Postgres DB. We also delete any existing errors for when we improve our error detection patterns to prevent subsequent runs from adding duplicate data to our database.</p>\n          <div>\n            <h2>What about historical builds?</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#what-about-historical-builds\">\n              \n            </a>\n          </div>\n          <p>The BuildErrorsQueue was great for new builds, but this meant we still didn’t know why all the previous build failures happened other than “user error”. We considered only tracking errors in new builds, but this was unacceptable because it would significantly slow down our ability to improve our error detection system because each iteration would require us to wait days to identify issues we need to prioritize.</p>\n          <div>\n            <h3>Problem: logs are stored across one million+ Durable Objects</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#problem-logs-are-stored-across-one-million-durable-objects\">\n              \n            </a>\n          </div>\n        <p>Remember how every build has an associated BuildBuddy DO to store logs? This is a great design for ensuring our logging pipeline scales with our customers, but it presented a challenge when trying to aggregate issues based on logs because something would need to go through all historical builds (&gt;1 million at the time) to fetch logs and detect why they failed.</p><p>If we were using Go and Kubernetes, we might solve this using a long-running container that goes through all builds and runs our error detection. But how do we solve this in Workers?</p>\n          <div>\n            <h3>How do we backfill errors for historical builds?</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#how-do-we-backfill-errors-for-historical-builds\">\n              \n            </a>\n          </div>\n        <p>At this point, we already had the Queue to process new builds. If we could somehow send all of the old build IDs to the queue, it could scan them all quickly using<a href=\"https://developers.cloudflare.com/queues/configuration/consumer-concurrency/\"> <u>Queues concurrent consumers</u></a> to quickly work through all builds. We thought about hacking together a local script to fetch all of the log IDs and sending them to an API to put them on a queue. But we wanted something more secure and easier to use so that running a new backfill was as simple as an API call.</p><p>That’s when an idea hit us: what if we used a Durable Object with alarms to fetch a range of builds and send them to BuildErrorsQueue? At first, it seemed far-fetched, given that Durable Object alarms have a limited amount of work they can do per invocation. But wait, if<a href=\"https://agents.cloudflare.com/\"> <u>AI Agents built on Durable Objects</u></a> can manage background tasks, why can’t we fetch millions of build IDs and forward them to queues?</p>\n          <div>\n            <h3>Building a Build Errors Agent with Durable Objects</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#building-a-build-errors-agent-with-durable-objects\">\n              \n            </a>\n          </div>\n        <p>The idea was simple: create a Durable Object class named BuildErrorsAgent and run a single instance that loops through the specified range of builds in the database and sends them to BuildErrorsQueue.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3kmsS4LACzLUUoECSJT08g/b6a9ccffcbe8a41c300a74546a17ba85/image5.png\">\n          </figure><p>The first thing we did was set up an RPC method to start a backfill and save the parameters in<a href=\"https://developers.cloudflare.com/durable-objects/api/storage-api/#kv-api\"> <u>Durable Object KV storage</u></a> so that it can be read each time the alarm executes:</p>\n            <pre><code>async start({\n  min_build_id,\n  max_build_id,\n}: {\n  min_build_id: BuildRecord['build_id']\n  max_build_id: BuildRecord['build_id']\n}): Promise&lt;void&gt; {\n  logger.setTags({ handler: 'start', environment: this.env.ENVIRONMENT })\n  try {\n    if (min_build_id &lt; 0) throw new Error('min_build_id cannot be negative')\n    if (max_build_id &lt; min_build_id) {\n      throw new Error('max_build_id cannot be less than min_build_id')\n    }\n    const [started_on, stopped_on] = await Promise.all([\n      this.kv.get('started_on'),\n      this.kv.get('stopped_on'),\n    ])\n    await match({ started_on, stopped_on })\n      .with({ started_on: P.not(null), stopped_on: P.nullish }, () =&gt; {\n        throw new Error('BuildErrorsAgent is already running')\n      })\n      .otherwise(async () =&gt; {\n        // delete all existing data and start queueing failed builds\n        await this.state.storage.deleteAlarm()\n        await this.state.storage.deleteAll()\n        this.kv.put('started_on', new Date())\n        this.kv.put('config', { min_build_id, max_build_id })\n        void this.state.storage.setAlarm(this.getNextAlarmDate())\n      })\n  } catch (e) {\n    this.sentry.captureException(e)\n    throw e\n  }\n}</code></pre>\n            <p>The most important part of the implementation is the alarm that runs every second until the job is complete. Each alarm invocation has the following steps:</p><ol><li><p>Set a new alarm (always first to ensure an error doesn’t cause it to stop).</p></li><li><p>Retrieve state from KV.</p></li><li><p>Validate that the agent is supposed to be running:</p><ol><li><p>Ensure the agent is supposed to be running.</p></li><li><p>Ensure we haven’t reached the max build ID set in the config.</p></li></ol></li><li><p>Finally, queue up another batch of builds by querying Postgres and sending to the BuildErrorsQueue.</p></li></ol>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6Ab6VC49luyio3t5QamgMD/273c77158ff4ac7af662669360d5f485/image6.png\">\n          </figure>\n            <pre><code>async alarm(): Promise&lt;void&gt; {\n  logger.setTags({ handler: 'alarm', environment: this.env.ENVIRONMENT })\n  try {\n    void this.state.storage.setAlarm(Date.now() + 1000)\n    const kvState = await this.getKVState()\n    this.sentry.setContext('BuildErrorsAgent', kvState)\n    const ctxLogger = logger.withFields({ state: JSON.stringify(kvState) })\n\n    await match(kvState)\n      .with({ started_on: P.nullish }, async () =&gt; {\n        ctxLogger.info('BuildErrorsAgent is not started, cancelling alarm')\n        await this.state.storage.deleteAlarm()\n      })\n      .with({ stopped_on: P.not(null) }, async () =&gt; {\n        ctxLogger.info('BuildErrorsAgent is stopped, cancelling alarm')\n        await this.state.storage.deleteAlarm()\n      })\n      .with(\n        // we should never have started_on set without config set, but just in case\n        { started_on: P.not(null), config: P.nullish },\n        async () =&gt; {\n          const msg =\n            'BuildErrorsAgent started but config is empty, stopping and cancelling alarm'\n          ctxLogger.error(msg)\n          this.sentry.captureException(new Error(msg))\n          this.kv.put('stopped_on', new Date())\n          await this.state.storage.deleteAlarm()\n        }\n      )\n      .when(\n        // make sure there are still builds to enqueue\n        (s) =&gt;\n          s.latest_build_id !== null &amp;&amp;\n          s.config !== null &amp;&amp;\n          s.latest_build_id &gt;= s.config.max_build_id,\n        async () =&gt; {\n          ctxLogger.info('BuildErrorsAgent job complete, cancelling alarm')\n          this.kv.put('stopped_on', new Date())\n          await this.state.storage.deleteAlarm()\n        }\n      )\n      .with(\n        {\n          started_on: P.not(null),\n          stopped_on: P.nullish,\n          config: P.not(null),\n          latest_build_id: P.any,\n        },\n        async ({ config, latest_build_id }) =&gt; {\n          // 1. select batch of ~1000 builds\n          // 2. send them to Queues 100 at a time, updating\n          //    latest_build_id after each batch is sent\n          const failedBuilds = await this.store.builds.selectFailedBuilds({\n            min_build_id: latest_build_id !== null ? latest_build_id + 1 : config.min_build_id,\n            max_build_id: config.max_build_id,\n            limit: 1000,\n          })\n          if (failedBuilds.length === 0) {\n            ctxLogger.info(`BuildErrorsAgent: ran out of builds, stopping and cancelling alarm`)\n            this.kv.put('stopped_on', new Date())\n            await this.state.storage.deleteAlarm()\n          }\n\n          for (\n            let i = 0;\n            i &lt; BUILDS_PER_ALARM_RUN &amp;&amp; i &lt; failedBuilds.length;\n            i += QUEUES_BATCH_SIZE\n          ) {\n            const batch = failedBuilds\n              .slice(i, QUEUES_BATCH_SIZE)\n              .map((build) =&gt; ({ body: build }))\n\n            if (batch.length === 0) {\n              ctxLogger.info(`BuildErrorsAgent: ran out of builds in current batch`)\n              break\n            }\n            ctxLogger.info(\n              `BuildErrorsAgent: sending ${batch.length} builds to build errors queue`\n            )\n            await this.env.BUILD_ERRORS_QUEUE.sendBatch(batch)\n            this.kv.put(\n              'latest_build_id',\n              Math.max(...batch.map((m) =&gt; m.body.build_id).concat(latest_build_id ?? 0))\n            )\n\n            this.kv.put(\n              'total_builds_processed',\n              ((await this.kv.get('total_builds_processed')) ?? 0) + batch.length\n            )\n          }\n        }\n      )\n      .otherwise(() =&gt; {\n        const msg = 'BuildErrorsAgent has nothing to do - this should never happen'\n        this.sentry.captureException(msg)\n        ctxLogger.info(msg)\n      })\n  } catch (e) {\n    this.sentry.captureException(e)\n    throw e\n  }\n}</code></pre>\n            <p>Using pattern matching with <a href=\"https://github.com/gvergnaud/ts-pattern\"><u>ts-pattern</u></a> made it much easier to understand what states we were expecting and what will happen compared to procedural code. We considered using a more powerful library like <a href=\"https://stately.ai/docs/xstate\"><u>XState</u></a>, but decided on ts-pattern due to its simplicity.</p>\n          <div>\n            <h3>Running the backfill</h3>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#running-the-backfill\">\n              \n            </a>\n          </div>\n        <p>Once everything rolled out, we were able to trigger an errors backfill for over a million failed builds in a couple of hours with a single API call, categorizing 80% of failed builds on the first run. With a fast backfill process, we were able to iterate on our regex matchers to further refine our error detection and improve error grouping. Here’s what the error list looks like in our staging environment:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5rdNvB1SpjGpeiCOCs86Tj/74141402e67fbd9ced673a98cb3c57f6/image4.png\">\n          </figure>\n          <div>\n            <h2>Fixes and improvements</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#fixes-and-improvements\">\n              \n            </a>\n          </div>\n          <p>Having a better understanding of what’s going wrong has already enabled us to make several improvements:</p><ul><li><p>Wrangler now shows a<a href=\"https://github.com/cloudflare/workers-sdk/pull/8534\"> <u>clearer error message when no config file is found</u></a>.</p></li><li><p>Fixed multiple edge-cases where the wrong package manager was used in TypeScript/JavaScript projects.</p></li><li><p>Added support for bun.lock (previously only checked for bun.lockb).</p></li><li><p>Fixed several edge cases where build caching did not work in monorepos.</p></li><li><p>Projects that use a runtime.txt file to specify a Python version no longer fail.</p></li><li><p>….and more!</p></li></ul><p>We’re still working on fixing other bugs we’ve found, but we’re making steady progress. Reliability is a feature we’re striving for in Workers Builds, and this project has helped us make meaningful progress towards that goal. Instead of waiting for people to contact support for issues, we’re able to proactively identify and fix issues (and catch regressions more easily).</p><p>One of the great things about building on the Developer Platform is how easy it is to ship things. The core of this error detection pipeline (the Queue and Durable Object) <b>only took two days to build</b>, which meant we could spend more time working on improving Workers Builds instead of spending weeks on the error detection pipeline itself.</p>\n          <div>\n            <h2>What’s next?</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#whats-next\">\n              \n            </a>\n          </div>\n          <p>In addition to continuing to improve build reliability and speed, we’ve also started thinking about other ways to help developers build their applications on Workers. For example, we built a<a href=\"https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/workers-builds\"> <u>Builds MCP server</u></a> that allows users to debug builds directly in Cursor/Claude/etc. We’re also thinking about ways we can expose these detected issues in the Cloudflare Dashboard so that users can identify issues more easily without scrolling through hundreds of logs.</p>\n          <div>\n            <h2>Ready to get started?</h2>\n            <a href=\"https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/#ready-to-get-started\">\n              \n            </a>\n          </div>\n          <p>Building applications on Workers has never been easier! Try deploying a Durable Object-backed <a href=\"https://github.com/cloudflare/templates/tree/main/durable-chat-template\"><u>chat application</u></a> with Workers Builds:&nbsp;</p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/durable-chat-template\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a><p></p>",
            "url": "https://blog.cloudflare.com/detecting-workers-builds-errors-across-1-million-durable-durable-objects/",
            "title": "Let’s DO this: detecting Workers Builds errors across 1 million Durable Objects",
            "date_modified": "2025-05-29T13:00:00.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/tweioa/building_distributed_cache_for_s3\">Comments</a></p>",
            "url": "https://clickhouse.com/blog/building-a-distributed-cache-for-s3",
            "title": "Building a Distributed Cache for S3",
            "date_modified": "2025-05-29T06:52:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44123131\">Comments</a>",
            "url": "https://github.com/wzhudev/reverse-linear-sync-engine",
            "title": "Reverse engineering of Linear's sync engine",
            "date_modified": "2025-05-29T04:29:30.000Z"
        },
        {
            "content_html": "<p>Yeah I know this place is generally super anti-AI. But I figured it’s dishonest to not also post it here. I’d love to see more nuanced posts on this topic here.</p>\n<p><a href=\"https://lobste.rs/s/42qb2p/i_am_disappointed_ai_discourse\">Comments</a></p>",
            "url": "https://steveklabnik.com/writing/i-am-disappointed-in-the-ai-discourse/",
            "title": "I am disappointed in the AI discourse",
            "date_modified": "2025-05-28T17:35:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44106934\">Comments</a>",
            "url": "https://ducklake.select/",
            "title": "DuckLake is an integrated data lake and catalog format",
            "date_modified": "2025-05-27T13:43:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44105878\">Comments</a>",
            "url": "https://www.allthingsdistributed.com/2025/05/just-make-it-scale-an-aurora-dsql-story.html",
            "title": "Just make it scale: An Aurora DSQL story",
            "date_modified": "2025-05-27T11:31:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44098605\">Comments</a>",
            "url": "https://maych.in/blog/its-time-to-give-nix-a-chance/",
            "title": "I think it's time to give Nix a chance",
            "date_modified": "2025-05-26T15:56:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44093675\">Comments</a>",
            "url": "https://www.aluxian.com/claude-code-does-our-releases-now/",
            "title": "Claude Code does our releases now",
            "date_modified": "2025-05-26T03:22:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44090302\">Comments</a>",
            "url": "https://chrlschn.dev/blog/2025/05/beware-the-complexity-merchants/",
            "title": "Beware the Complexity Merchants",
            "date_modified": "2025-05-25T19:25:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44081338\">Comments</a>",
            "url": "https://sean.heelan.io/2025/05/22/how-i-used-o3-to-find-cve-2025-37899-a-remote-zeroday-vulnerability-in-the-linux-kernels-smb-implementation/",
            "title": "I used o3 to find a remote zeroday in the Linux SMB implementation",
            "date_modified": "2025-05-24T14:25:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44071900\">Comments</a>",
            "url": "https://www.wordonfire.org/articles/remembering-alasdair-macintyre-1929-2025/",
            "title": "Alasdair MacIntyre Has Died",
            "date_modified": "2025-05-23T11:37:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44063703\">Comments</a>",
            "url": "https://www.anthropic.com/news/claude-4",
            "title": "Claude 4",
            "date_modified": "2025-05-22T16:34:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44060711\">Comments</a>",
            "url": "https://sean.heelan.io/2025/05/22/how-i-used-o3-to-find-cve-2025-37899-a-remote-zeroday-vulnerability-in-the-linux-kernels-smb-implementation/",
            "title": "How I used o3 to find a remote 0-day vulnerability in the Linux kernel (ksmbd)",
            "date_modified": "2025-05-22T10:44:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/q1vcrq/encore_s_mcp_server_enables_your_ai_tools\">Comments</a></p>",
            "url": "https://encore.dev/blog/mcp-server",
            "title": "Encore's MCP Server enables your AI tools to introspect your application",
            "date_modified": "2025-05-22T07:56:32.000Z"
        },
        {
            "content_html": "<p>I’m pleased to announce developers can now programmatically disable Apple <a href=\"https://support.apple.com/en-us/102149\">System Integrity Protection (SIP)</a> on their <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-mac-instances.html?trk=4b29643c-e00f-4ab6-ab9c-b1fb47aa1708&amp;sc_channel=blog\">Amazon EC2 Mac</a> instances. System Integrity Protection (SIP), also known as rootless, is a security feature introduced by Apple in <a href=\"https://wikipedia.org/wiki/OS_X_El_Capitan\">OS X El Capitan</a> (2015, version 10.11). It’s designed to protect the system from potentially harmful software by restricting the power of the root user account. SIP is enabled by default on macOS.</p> \n<p>SIP safeguards the system by preventing modification of protected files and folders, restricting access to system-owned files and directories, and blocking unauthorized software from selecting a startup disk. The primary goal of SIP is to address the security risk linked to unrestricted root access, which could potentially allow malware to gain full control of a device with just one password or vulnerability. By implementing this protection, Apple aims to ensure a higher level of security for macOS users, especially considering that many users operate on administrative accounts with weak or no passwords.</p> \n<p>While SIP provides excellent protection against malware for everyday use, developers might occasionally need to temporarily disable it for development and testing purposes. For instance, when creating a new device driver or system extension, disabling SIP is necessary to install and test the code. Additionally, SIP might block access to certain system settings required for your software to function properly. Temporarily disabling SIP grants you the necessary permissions to fine-tune programs for macOS. However, it’s crucial to remember that this is akin to briefly disabling the vault door for authorized maintenance, not leaving it permanently open.</p> \n<p><a href=\"https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection\">Disabling SIP</a> on a Mac requires physical access to the machine. You have to <a href=\"https://support.apple.com/en-gb/guide/mac-help/mchl82829c17/mac\">restart the machine in recovery mode</a>, then disable SIP with the <code>csrutil</code> command line tool, then restart the machine again.</p> \n<p>Until today, you had to operate with the standard SIP settings on EC2 Mac instances. The physical access requirement and the need to boot in recovery mode made integrating SIP with the Amazon EC2 control plane and EC2 API challenging. But that’s no longer the case! You can now disable and re-enable SIP at will on your Amazon EC2 Mac instances. Let me show you how.</p> \n<p><span><strong>Let’s see how it works<br> </strong></span>Imagine I have an Amazon EC2 Mac instance started. It’s a <code>mac2-m2.metal</code> instance, running on an Apple silicon M2 processor. Disabling or enabling SIP is as straightforward as calling a new EC2 API: <code>CreateMacSystemIntegrityProtectionModificationTask</code>. This API is asynchronous; it starts the process of changing the SIP status on your instance. You can monitor progress using another new EC2 API: <code>DescribeMacModificationTasks</code>. All I need to know is the instance ID of the machine I want to work with.</p> \n<p><span><strong>Prerequisites<br> </strong></span>On Apple silicon based EC2 Mac instances and more recent type of machines, before calling the new EC2 API, I must set the <code>ec2-user</code> user password and enable <a href=\"https://support.apple.com/en-gb/guide/deployment/dep24dbdcf9e/web\">secure token</a> for that user on macOS. This requires connecting to the machine and typing two commands in the terminal.</p> \n<pre><code># on the target EC2 Mac instance\n# Set a password for the ec2-user user\n~ % sudo /usr/bin/dscl . -passwd /Users/ec2-user\nNew Password: (MyNewPassw0rd)\n\n# Enable secure token, with the same password, for the ec2-user\n# old password is the one you just set with dscl\n~ % sysadminctl -newPassword MyNewPassw0rd -oldPassword MyNewPassw0rd\n2025-03-05 13:16:57.261 sysadminctl[3993:3033024] Attempting to change password for ec2-user…\n2025-03-05 13:16:58.690 sysadminctl[3993:3033024] SecKeychainCopyLogin returned -25294\n2025-03-05 13:16:58.690 sysadminctl[3993:3033024] Failed to update keychain password (-25294)\n2025-03-05 13:16:58.690 sysadminctl[3993:3033024] - Done\n\n# The error about the KeyChain is expected. I never connected with the GUI on this machine, so the Login keychain does not exist\n# you can ignore this error.  The command below shows the list of keychains active in this session\n~ % security list\n    \"/Library/Keychains/System.keychain\"\n\n# Verify that the secure token is ENABLED\n~ % sysadminctl -secureTokenStatus ec2-user\n2025-03-05 13:18:12.456 sysadminctl[4017:3033614] Secure token is ENABLED for user ec2-user\n</code></pre> \n<p><span><strong>Change the SIP status<br> </strong></span>I don’t need to connect to the machine to toggle the SIP status. I only need to know its instance ID. I open a terminal on my laptop and use the <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a> to retrieve the Amazon EC2 Mac instance ID.</p> \n<pre><code> aws ec2 describe-instances \\\n         --query \"Reservations[].Instances[?InstanceType == 'mac2-m2.metal' ].InstanceId\" \\\n         --output text\n\ni-012a5de8da47bdff7</code></pre> \n<p>Now, still from the terminal on my laptop, I disable SIP with the <code>create-mac-system-integrity-protection-modification-task</code> command:</p> \n<pre><code>echo '{\"rootVolumeUsername\":\"ec2-user\",\"rootVolumePassword\":\"MyNewPassw0rd\"}' &gt; tmpCredentials\naws ec2 create-mac-system-integrity-protection-modification-task \\\n--instance-id \"i-012a5de8da47bdff7\" \\\n--mac-credentials file://./tmpCredentials \\\n--mac-system-integrity-protection-status \"disabled\" &amp;&amp; rm tmpCredentials\n\n{\n    \"macModificationTask\": {\n        \"instanceId\": \"i-012a5de8da47bdff7\",\n        \"macModificationTaskId\": \"macmodification-06a4bb89b394ac6d6\",\n        \"macSystemIntegrityProtectionConfig\": {},\n        \"startTime\": \"2025-03-14T14:15:06Z\",\n        \"taskState\": \"pending\",\n        \"taskType\": \"sip-modification\"\n    }\n}</code></pre> \n<p>After the task is started, I can check its status with the <code>aws ec2 describe-mac-modification-tasks</code> command.</p> \n<pre><code>{\n    \"macModificationTasks\": [\n        {\n            \"instanceId\": \"i-012a5de8da47bdff7\",\n            \"macModificationTaskId\": \"macmodification-06a4bb89b394ac6d6\",\n            \"macSystemIntegrityProtectionConfig\": {\n                \"debuggingRestrictions\": \"\",\n                \"dTraceRestrictions\": \"\",\n                \"filesystemProtections\": \"\",\n                \"kextSigning\": \"\",\n                \"nvramProtections\": \"\",\n                \"status\": \"disabled\"\n            },\n            \"startTime\": \"2025-03-14T14:15:06Z\",\n            \"tags\": [],\n            \"taskState\": \"in-progress\",\n            \"taskType\": \"sip-modification\"\n        },\n...</code></pre> \n<p>The instance initiates the process and a series of reboots, during which it becomes unreachable. This process can take 60–90 minutes to complete. After that, when I see the status in the console becoming available again, I <a href=\"https://community.aws/content/2duUtYq4ENzOLGLdEg0A3aeyCuj/ec2-mac-02-connect-to-an-ec2-mac-instance?lang=en?trk=4b29643c-e00f-4ab6-ab9c-b1fb47aa1708&amp;sc_channel=el\">connect to the machine through SSH or EC2 Instance Connect</a>, as usual.</p> \n<pre><code>➜  ~ ssh ec2-user@54.99.9.99\nWarning: Permanently added '54.99.9.99' (ED25519) to the list of known hosts.\nLast login: Mon Feb 26 08:52:42 2024 from 1.1.1.1\n\n    ┌───┬──┐   __|  __|_  )\n    │ ╷╭╯╷ │   _|  (     /\n    │  └╮  │  ___|\\___|___|\n    │ ╰─┼╯ │  Amazon EC2\n    └───┴──┘  macOS Sonoma 14.3.1\n\n➜  ~ uname -a\nDarwin Mac-mini.local 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:27 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T8103 arm64\n\n➜ ~ csrutil --status \nSystem Integrity Protection status: disabled.\n</code></pre> \n<p><span><strong>When to disable SIP<br> </strong></span>Disabling SIP should be approached with caution because it opens up the system to potential security risks. However, as I mentioned in the introduction of this post, you might need to disable SIP when developing device drivers or kernel extensions for macOS. Some older applications might also not function correctly when SIP is enabled.</p> \n<p>Disabling SIP is also required to turn off Spotlight indexing. Spotlight can help you quickly find apps, documents, emails and other items on your Mac. It’s very convenient on desktop machines, but not so much on a server. When there is no need to index your documents as they change, turning off Spotlight <a href=\"https://eclecticlight.co/2022/12/08/spotlight-problems-mds_stores-and-mdworker-in-trouble/\">will release some CPU cycles and disk I/O</a>.</p> \n<p><span><strong>Things to know<br> </strong></span>There are a couple of additional things to know about disabling SIP on Amazon EC2 Mac:</p> \n<ul> \n <li>Disabling SIP is available through the API and <a href=\"https://aws.amazon.com/tools/\">AWS SDKs</a>, the <a href=\"https://aws.amazon.com/cli/?trk=4b29643c-e00f-4ab6-ab9c-b1fb47aa1708&amp;sc_channel=blog\">AWS CLI</a>, and the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>.</li> \n <li>On Apple silicon, the setting is volume based. So if you <a href=\"https://aws.amazon.com/blogs/compute/new-reset-amazon-ec2-mac-instances-to-a-known-state-using-replace-root-volume-capability/?trk=4b29643c-e00f-4ab6-ab9c-b1fb47aa1708&amp;sc_channel=blog\">replace the root volume</a>, you need to disable SIP again. On Intel, the setting is Mac host based, so if you replace the root volume, SIP will still be disabled.</li> \n <li>After disabling SIP, it will be enabled again if you stop and start the instance. Rebooting an instance doesn’t change its SIP status.</li> \n <li>SIP status isn’t transferable between EBS volumes. This means SIP will be disabled again after you restore an instance from an EBS snapshot or if you create an AMI from an instance where SIP is enabled.</li> \n</ul> \n<p>These new APIs are available in <a href=\"https://github.com/aws-samples/amazon-ec2-mac-getting-started/blob/main/ec2-macos.md\">all Regions where Amazon EC2 Mac is available</a>, at no additional cost. Try them today.</p> \n<a href=\"https://linktr.ee/sebsto\">— seb</a>",
            "url": "https://aws.amazon.com/blogs/aws/configure-system-integrity-protection-sip-on-amazon-ec2-mac-instances/",
            "title": "Configure System Integrity Protection (SIP) on Amazon EC2 Mac instances",
            "date_modified": "2025-05-21T17:36:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44053754\">Comments</a>",
            "url": "https://github.com/lastmile-ai/mcp-agent/tree/main/examples/mcp_agent_server",
            "title": "Show HN: Representing Agents as MCP Servers",
            "date_modified": "2025-05-21T17:19:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44053744\">Comments</a>",
            "url": "https://jngiam.bearblog.dev/mcp-large-data/",
            "title": "LLM function calls don't scale; code orchestration is simpler, more effective",
            "date_modified": "2025-05-21T17:18:52.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/6psu9y/hypervisor_as_library\">Comments</a></p>",
            "url": "https://seiya.me/blog/hypervisor-as-a-library",
            "title": "Hypervisor as a Library",
            "date_modified": "2025-05-20T15:17:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44042070\">Comments</a>",
            "url": "https://zackproser.com/blog/openai-codex-review",
            "title": "OpenAI Codex Review",
            "date_modified": "2025-05-20T14:29:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44042070\">Comments</a>",
            "url": "https://zackproser.com/blog/openai-codex-review",
            "title": "OpenAI Codex hands-on review",
            "date_modified": "2025-05-20T14:29:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44038279\">Comments</a>",
            "url": "https://seiya.me/blog/hypervisor-as-a-library",
            "title": "Hypervisor as a Library",
            "date_modified": "2025-05-20T06:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44038168\">Comments</a>",
            "url": "https://en.wikipedia.org/wiki/Cleo_(mathematician)",
            "title": "Cleo, the mathematician that tricked Stack Exchange",
            "date_modified": "2025-05-20T05:47:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44035876\">Comments</a>",
            "url": "https://cloudcoding.ai/",
            "title": "Show HN: Claude Code in the Cloud",
            "date_modified": "2025-05-19T23:02:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44034918\">Comments</a>",
            "url": "https://jules.google/",
            "title": "Jules: An Asynchronous Coding Agent",
            "date_modified": "2025-05-19T21:12:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44032777\">Comments</a>",
            "url": "https://docs.anthropic.com/en/docs/claude-code/sdk",
            "title": "Claude Code SDK – Anthropic",
            "date_modified": "2025-05-19T18:04:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44032777\">Comments</a>",
            "url": "https://docs.anthropic.com/en/docs/claude-code/sdk",
            "title": "Claude Code SDK",
            "date_modified": "2025-05-19T18:04:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44031432\">Comments</a>",
            "url": "https://github.blog/changelog/2025-05-19-github-copilot-coding-agent-in-public-preview/",
            "title": "GitHub Copilot Coding Agent",
            "date_modified": "2025-05-19T16:17:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44030560\">Comments</a>",
            "url": "https://github.com/crhuber/kelp",
            "title": "Kelp – simple replacement for homebrew on macOS",
            "date_modified": "2025-05-19T14:55:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44028616\">Comments</a>",
            "url": "https://airport.query.farm/",
            "title": "Airport for DuckDB",
            "date_modified": "2025-05-19T11:25:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44022736\">Comments</a>",
            "url": "https://nghiant3223.github.io/2025/04/15/go-scheduler.html",
            "title": "Understanding the Go Scheduler",
            "date_modified": "2025-05-18T17:03:55.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/goqzff/push_ifs_up_fors_down_2023\">Comments</a></p>",
            "url": "https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html",
            "title": "Push Ifs Up And Fors Down (2023)",
            "date_modified": "2025-05-17T20:08:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44013071\">Comments</a>",
            "url": "https://martincapodici.com/2025/05/13/production-tests-a-guidebook-for-better-systems-and-more-sleep/",
            "title": "Production tests: a guidebook for better systems and more sleep",
            "date_modified": "2025-05-17T09:13:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=44006345\">Comments</a>",
            "url": "https://openai.com/index/introducing-codex/",
            "title": "A Research Preview of Codex",
            "date_modified": "2025-05-16T15:02:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43992908\">Comments</a>",
            "url": "https://xata.io/blog/xata-postgres-with-data-branching-and-pii-anonymization",
            "title": "Postgres with data branching and PII anonymization",
            "date_modified": "2025-05-15T08:14:29.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yidvhu/keeping_time_on_stream\">Comments</a></p>",
            "url": "https://s2.dev/blog/timestamping",
            "title": "Keeping time on a stream",
            "date_modified": "2025-05-14T18:11:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43962992\">Comments</a>",
            "url": "https://tailscale.com/blog/4via6-connectivity-to-edge-devices",
            "title": "Tailscale 4via6 – Connect Edge Deployments at Scale",
            "date_modified": "2025-05-12T14:00:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43959710\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=43959710",
            "title": "Ask HN: Cursor or Windsurf?",
            "date_modified": "2025-05-12T04:41:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43931566\">Comments</a>",
            "url": "https://warpstreamlabs.github.io/bento/",
            "title": "Bento gets a makeover",
            "date_modified": "2025-05-08T21:30:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43931409\">Comments</a>",
            "url": "https://support.anthropic.com/en/articles/11145838-using-claude-code-with-your-max-plan",
            "title": "A flat pricing subscription for Claude Code",
            "date_modified": "2025-05-08T21:12:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43927926\">Comments</a>",
            "url": "https://github.com/voideditor/void",
            "title": "Void: Open-source Cursor alternative",
            "date_modified": "2025-05-08T16:35:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43927914\">Comments</a>",
            "url": "https://ghiculescu.substack.com/p/nobody-codes-here-anymore",
            "title": "Notes on rolling out Cursor and Claude Code",
            "date_modified": "2025-05-08T16:34:39.000Z"
        },
        {
            "content_html": "<p>Today we are announcing the general availability of <a href=\"https://aws.amazon.com/ebs/?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Amazon Elastic Block Store (Amazon EBS)</a> Provisioned Rate for Volume Initialization, a feature that accelerates the transfer of data from an EBS snapshot, a highly durable backup of volumes stored in <a href=\"https://aws.amazon.com/s3/?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Amazon Simple Storage Service (Amazon S3)</a> to a new EBS volume.</p> \n<p>With Amazon EBS Provisioned Rate for Volume Initialization, you can create fully performant EBS volumes within a predictable amount of time. You can use this feature to speed up the initialization of hundreds of concurrent volumes and instances. You can also use this feature when you need to recover from an existing EBS Snapshot and need your EBS volume to be created and initialized as quickly as possible. You can use this feature to quickly create copies of EBS volumes with EBS Snapshots in a different Availability Zone, AWS Region, or AWS account. Provisioned Rate for Volume Initialization for each volume is charged based on the full snapshot size and the specified volume initialization rate.</p> \n<p>This new feature expedites the volume initialization process by fetching the data from an EBS Snapshot to an EBS volume at a consistent rate that you specify between 100 MiB/s and 300 MiB/s. You can specify this volume initialization rate at which the snapshot blocks are to be downloaded from Amazon S3 to the volume.</p> \n<p>With specifying the volume initialization rate, you can create a fully performant volume in a predictable time, enabling increased operational efficiency and visibility on the expected time of completion. If you run utilities like <code>fio</code>/<code>dd</code> to expedite volume initialization for your workflows like application recovery and volume copy for testing and development, it will remove the operational burden of managing such scripts with the consistency and predictability to your workflows.</p> \n<p><strong><u>Get started with specifying the volume initialization rate</u></strong><br> To get started, you can choose the volume initialization rate when you launch your EC2 instance or create your volume from the snapshot.</p> \n<p><strong>1. Create a volume in the EC2 launch wizard</strong><br> When launching new EC2 instances in the launch wizard of <a href=\"https://console.aws.amazon.com/ec2?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">EC2 console</a>, you can enter a desired <strong>Volume initialization rate</strong> in the <strong>Storage (volumes)</strong> section.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/03/20/2025-ebs-volume-initialization-rate-ec2-launch.png\" width=\"2282\" height=\"1200\"></p> \n<p>You can also set the volume initialization rate when creating and modifying the <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">EC2 Launch Templates</a>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/03/20/2025-ebs-volume-initialization-rate-ec2-launch-template.png\" width=\"2496\" height=\"1280\"></p> \n<p>In the <a href=\"https://aws.amazon.com/cli/?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">AWS Command Line Interface (AWS CLI)</a>, you can add <code>VolumeInitializationRate</code> parameter to the block device mappings when you call <code>run-instances</code> command.</p> \n<pre><code>aws ec2 run-instances \\\n    --image-id ami-0abcdef1234567890 \\\n    --instance-type t2.micro \\\n    --subnet-id subnet-08fc749671b2d077c \\\n    --security-group-ids sg-0b0384b66d7d692f9 \\\n    --key-name MyKeyPair \\\n    --block-device-mappings file://mapping.json</code></pre> \n<p>Contents of <code><span>mapping.json</span></code>. This example adds <code><span>/dev/sdh</span></code> an empty EBS volume with a size of 8 GiB.</p> \n<pre><code>[\n    {\n        \"DeviceName\": \"/dev/sdh\",\n        \"Ebs\": {\n            \"VolumeSize\": 8,\n            \"VolumeType\": \"gp3\",            \n            \"VolumeInitializationRate\": 300\n\t\t } \n     } \n]</code></pre> \n<p>To learn more, visit <a href=\"https://docs.aws.amazon.com/cli/latest/reference/ec2/run-instances.html#options?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">block device mapping options</a>, which defines the EBS volumes and instance store volumes to attach to the instance at launch.</p> \n<p><strong>2. Create a volume from snapshots</strong><br> When you create a volume from snapshots, you can also choose <strong>Create volume </strong>in the <a href=\"https://console.aws.amazon.com/ec2/?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">EC2 console</a> and specify the <strong>Volume initialization rate</strong>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/01/14/2025-ebs-volume-initialization-rate.jpg\" width=\"1200\" height=\"1983\"></p> \n<p>Confirm your new volume with the initialization rate.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2025/01/14/2025-ebs-volume-initialization-rate-2.jpg\" width=\"2560\" height=\"915\"></p> \n<p>In the AWS CLI, you can use <code>VolumeInitializationRate</code> parameter and when calling <code>create-volume</code> command.</p> \n<pre><code>aws ec2 create-volume --region us-east-1 --cli-input-json '{\n    \"AvailabilityZone\": \"us-east-1a\",\n    \"VolumeType\": \"gp3\",\n    \"SnapshotId\": \"snap-07f411eed12ef613a\",\n    \"VolumeInitializationRate\": 300\n}'</code></pre> \n<p>If the command is run successfully, you will receive the result below.</p> \n<pre><code>{\n    \"AvailabilityZone\": \"us-east-1a\",\n    \"CreateTime\": \"2025-01-03T21:44:53.000Z\",\n    \"Encrypted\": false,\n    \"Size\": 100,\n    \"SnapshotId\": \"snap-07f411eed12ef613a\",\n    \"State\": \"creating\",\n    \"VolumeId\": \"vol-0ba4ed2a280fab5f9\",\n    \"Iops\": 300,\n    \"Tags\": [],\n    \"VolumeType\": \"gp2\",\n    \"MultiAttachEnabled\": false,\n    \"VolumeInitializationRate\": 300\n}</code></pre> \n<p>You can also set the volume initialization rate when replacing root volumes of EC2 instances and provisioning EBS volumes using the <a href=\"https://github.com/kubernetes-sigs/aws-ebs-csi-driver/\">EBS Container Storage Interface (CSI) driver</a>.</p> \n<p>After creation of the volume, EBS will keep track of the hydration progress and publish an <a href=\"https://docs.aws.amazon.com/ebs/latest/userguide/ebs-cloud-watch-events.html?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Amazon EventBridge notification for EBS</a> to your account when the hydration completes so that they can be certain when their volume is fully performant.</p> \n<p>To learn more, visit <a href=\"https://docs.aws.amazon.com/ebs/latest/userguide/ebs-creating-volume.html?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Create an Amazon EBS volume</a> and <a href=\"https://docs.aws.amazon.com/ebs/latest/userguide/initalize-volume.html?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Initialize Amazon EBS volumes</a> in the Amazon EBS User Guide.</p> \n<p><strong><u>Now available</u></strong><br> Amazon EBS Provisioned Rate for Volume Initialization is now available and supported for all EBS volume types today. You will be charged based on the full snapshot size and the specified volume initialization rate.&nbsp;To learn more, visit <a href=\"https://aws.amazon.com/ebs/pricing/?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Amazon EBS Pricing</a> page.</p> \n<p>To learn more about Amazon EBS including this feature, take the free digital course on the AWS Skill Builder portal. Course includes use cases, architecture diagrams and demos.</p> \n<p>Give this feature a try in the <a href=\"https://console.aws.amazon.com/ec2/home?#CreateVolume:?trk=769a1a2b-8c19-4976-9c45-b6b1226c7d20&amp;sc_channel=el\">Amazon EC2 console</a>&nbsp;today and send feedback to&nbsp;<a href=\"https://repost.aws/tags/TAQngny0JOSqmKzdPtGiZZ2w/amazon-elastic-block-store\">AWS re:Post for Amazon EBS</a>&nbsp;or through your usual AWS Support contacts.</p> \n<p>—&nbsp;<a href=\"https://twitter.com/channyun\">Channy</a></p> \n<hr> \n<p>How is the News Blog doing? Take this <a href=\"https://amazonmr.au1.qualtrics.com/jfe/form/SV_eyD5tC5xNGCdCmi\">1 minute survey</a>!</p> \n<p><em>(This <a href=\"https://amazonmr.au1.qualtrics.com/jfe/form/SV_eyD5tC5xNGCdCmi\">survey</a> is hosted by an external company. AWS handles your information as described in the <a href=\"https://aws.amazon.com/privacy/\">AWS Privacy Notice</a>. AWS will own the data gathered via this survey and will not share the information collected with survey respondents.)</em></p>",
            "url": "https://aws.amazon.com/blogs/aws/accelerate-the-transfer-of-data-from-an-amazon-ebs-snapshot-to-a-new-ebs-volume/",
            "title": "Accelerate the transfer of data from an Amazon EBS snapshot to a new EBS volume",
            "date_modified": "2025-05-06T21:52:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43908846\">Comments</a>",
            "url": "https://www.sourcebot.dev/blog/review-agent-learnings",
            "title": "I built an AI code review agent in a few hours, here's what I learned",
            "date_modified": "2025-05-06T19:39:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43901190\">Comments</a>",
            "url": "https://www.wiz.io/blog/github-actions-security-guide",
            "title": "How to Harden GitHub Actions: The Unofficial Guide",
            "date_modified": "2025-05-06T02:07:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43901190\">Comments</a>",
            "url": "https://www.wiz.io/blog/github-actions-security-guide",
            "title": "How to harden GitHub Actions",
            "date_modified": "2025-05-06T02:07:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43899236\">Comments</a>",
            "url": "https://blog.yaakov.online/replacing-kubernetes-with-systemd/",
            "title": "Replacing Kubernetes with systemd (2024)",
            "date_modified": "2025-05-05T20:40:14.000Z"
        },
        {
            "content_html": "<p>Has your browsing experience ever been disrupted by this error page? Sometimes Cloudflare returns <a href=\"https://developers.cloudflare.com/support/troubleshooting/cloudflare-errors/troubleshooting-cloudflare-5xx-errors/#error-500-internal-server-error\"><u>\"Error 500\"</u></a> when our servers cannot respond to your web request. This inability to respond could have several potential causes, including problems caused by a bug in one of the services that make up Cloudflare's software stack.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1rIrBE5GP2IJwbn2k6mVTV/fd796ed1ef591fb8bd95395aa8f604d1/1.png\">\n          </figure><p>We know that our testing platform will inevitably miss <a href=\"https://blog.cloudflare.com/pipefail-how-a-missing-shell-option-slowed-cloudflare-down/\"><u>some software bugs</u></a>, so we built guardrails to gradually and safely release new code before a feature reaches all users. Health Mediated Deployments (HMD) is Cloudflare’s data-driven solution to automating software updates across our <a href=\"https://www.cloudflare.com/network/\"><u>global network</u></a>. HMD works by querying <a href=\"https://thanos.io/\"><u>Thanos</u></a>, a system for storing and scaling <a href=\"https://blog.cloudflare.com/how-cloudflare-runs-prometheus-at-scale/\"><u>Prometheus</u></a> metrics. Prometheus collects detailed data about the performance of our services, and Thanos makes that data accessible across our distributed network. HMD uses these metrics to determine whether new code should continue to roll out, pause for further evaluation, or be automatically reverted to prevent widespread issues.</p><p>Cloudflare engineers configure signals from their service, such as alerting rules or <a href=\"https://sre.google/workbook/implementing-slos/\"><u>Service Level Objectives (SLOs)</u></a>. For example, the following Service Level Indicator (SLI) checks the rate of HTTP 500 errors over 10 minutes returned from a service in our software stack.</p>\n            <pre><code>sum(rate(http_request_count{code=\"500\"}[10m])) / sum(rate(http_request_count[10m]))</code></pre>\n            <p>An SLO is a combination of an SLI and an objective threshold. For example, the service returns 500 errors &lt;0.1% of the time.</p><p>If the success rate is unexpectedly decreasing where the new code is running, HMD reverts the change in order to stabilize the system, reacting before humans even know what Cloudflare service was broken. Below, HMD recognizes the degradation in signal in an early release stage and reverts the code back to the prior version to limit the blast radius.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2O6gCfhZsoU1QCf3lu0QMl/bb4377cccbf982b607ce3564e4bf9fbd/2.png\">\n          </figure><p>\nCloudflare’s network serves millions of requests per second across diverse geographies. How do we know that HMD will react quickly the next time we accidentally release code that contains a bug? HMD performs a testing strategy called <a href=\"https://en.wikipedia.org/wiki/Backtesting\"><u>backtesting</u></a>, outside the release process, which uses historical incident data to test how long it would take to react to degrading signals in a future release.</p><p>We use Thanos to join thousands of small Prometheus deployments into a single unified query layer while keeping our monitoring reliable and cost-efficient. To backfill historical incident metric data that has fallen out of Prometheus’ retention period, we use our object storage solution, <a href=\"https://blog.cloudflare.com/tag/cloudflare-r2/\"><u>R2</u></a>.</p><p>Today, we store 4.5 billion distinct time series for a year of retention, which results in roughly 8 petabytes of data in 17 million objects distributed all over the globe.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5TlfqQIPqS7TVxFB38PztG/65dc562db7af5304562b3fed9ab6486d/3.png\">\n          </figure>\n          <div>\n            <h2>Making it work at scale</h2>\n            <a href=\"https://blog.cloudflare.com/safe-change-at-any-scale/#making-it-work-at-scale\">\n              \n            </a>\n          </div>\n          <p>To give a sense of scale, we can estimate the impact of a batch of backtests:</p><ul><li><p>Each backtest run is made up of multiple SLOs to evaluate a service's health.</p></li><li><p>Each SLO is evaluated using multiple queries containing batches of data centers.</p></li><li><p>Each data center issues anywhere from tens to thousands of requests to R2.</p></li></ul><p>Thus, in aggregate, a batch can translate to hundreds of thousands of <a href=\"https://prometheus.io/docs/prometheus/latest/querying/basics/\"><u>PromQL queries</u></a> and millions of requests to R2. Initially, batch runs would take about 30 hours to complete but through blood, sweat, and tears, we were able to cut this down to 2 hours.</p><p>Let’s review how we made this processing more efficient.</p>\n          <div>\n            <h3>Recording rules</h3>\n            <a href=\"https://blog.cloudflare.com/safe-change-at-any-scale/#recording-rules\">\n              \n            </a>\n          </div>\n        <p>HMD slices our fleet of machines across multiple dimensions. For the purposes of this post, let’s refer to them as “tier” and “color”. Given a pair of tier and color, we would use the following PromQL expression to find the machines that make up this combination:</p>\n            <pre><code>group by (instance, datacenter, tier, color) (\n  up{job=\"node_exporter\"}\n  * on (datacenter) group_left(tier) datacenter_metadata{tier=\"tier3\"}\n  * on (instance) group_left(color) server_metadata{color=\"green\"}\n  unless on (instance) (machine_in_maintenance == 1)\n  unless on (datacenter) (datacenter_disabled == 1)\n)</code></pre>\n            <p>Most of these series have a cardinality of approximately the number of machines in our fleet. That’s a substantial amount of data we need to fetch from object storage and transmit home for query evaluation, as well as a significant number of series we need to decode and join together.</p><p>Since this is a fairly common query that is issued in every HMD run, it makes sense to precompute it. In the Prometheus ecosystem, this is commonly done with <a href=\"https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/\"><u>recording rules</u></a>:</p>\n            <pre><code>hmd:release_scopes:info{tier=\"tier3\", color=\"green\"}</code></pre>\n            <p>Aside from looking much cleaner, this also reduces the load at query time significantly. Since all the joins involved can only have matches within a data center, it is well-defined to evaluate those rules directly in the Prometheus instances inside the data center itself.</p><p>Compared to the original query, the cardinality we need to deal with now scales with the size of the release scope instead of the size of the entire fleet.</p><p>This is significantly cheaper and also less likely to be affected by network issues along the way, which in turn reduces the amount that we need to retry the query, on average.&nbsp;</p>\n          <div>\n            <h3>Distributed query processing</h3>\n            <a href=\"https://blog.cloudflare.com/safe-change-at-any-scale/#distributed-query-processing\">\n              \n            </a>\n          </div>\n        \n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2X1dlDO1DYXfFo29DRIBeX/c2d8a7f88d24dc4b562d068a4774a6dd/4.png\">\n          </figure><p>HMD and the Thanos Querier, depicted above, are stateless components that can run anywhere, with highly available deployments in North America and Europe. Let us quickly recap what happens when we evaluate the SLI expression from HMD in our introduction:</p>\n            <pre><code>sum(rate(http_request_count{code=\"500\"}[10m]))\n/ \nsum(rate(http_request_count[10m]))</code></pre>\n            <p>Upon receiving this query from HMD, the Thanos Querier will start requesting raw time series data for the “http_requests_total” metric from its connected <a href=\"https://thanos.io/v0.4/components/sidecar/\"><u>Thanos Sidecar</u></a> and <a href=\"https://thanos.io/tip/components/store.md/\"><u>Thanos Store</u></a> instances all over the world, wait for all the data to be transferred to it, decompress it, and finally compute its result:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4jcK7cvQfMtqeQMeuFnTMz/3bdca2132a4e700050512dc15d823ef3/5.png\">\n          </figure><p>While this works, it is not optimal for several reasons. We have to wait for raw data from thousands of data sources all over the world to arrive in one location before we can even start to decompress it, and then we are limited by all the data being processed by one instance. If we double the number of data centers, we also need to double the amount of memory we allocate for query evaluation.</p><p>Many SLIs come in the form of simple aggregations, typically to boil down some aspect of the service's health to a number, such as the percentage of errors. As with the aforementioned recording rule, those aggregations are often distributive — we can evaluate them inside the data center and coalesce the sub-aggregations again to arrive at the same result.</p><p>To illustrate, if we had a recording rule per data center, we could rewrite our example like this:</p>\n            <pre><code>sum(datacenter:http_request_count:rate10m{code=\"500\"})\n/ \nsum(datacenter:http_request_count:rate10m)</code></pre>\n            <p>This would solve our problems, because instead of requesting raw time series data for high-cardinality metrics, we would request pre-aggregated query results. Generally, these pre-aggregated results are an order of magnitude less data that needs to be sent over the network and processed into a final result.</p><p>However, recording rules come with a steep write-time cost in our architecture, evaluated frequently across thousands of Prometheus instances in production, just to speed up a less frequent ad-hoc batch process. Scaling recording rules alongside our growing set of service health SLIs quickly would be unsustainable. So we had to go back to the drawing board.</p><p>It would be great if we could evaluate data center-scoped queries remotely and coalesce their result back again — for arbitrary queries and at runtime. To illustrate, we would like to evaluate our example like this:</p>\n            <pre><code>(sum(rate(http_requests_total{status=\"500\", datacenter=\"dc1\"}[10m])) + ...)\n/\n(sum(rate(http_requests_total{datacenter=\"dc1\"}[10m])) + ...)</code></pre>\n            <p>This is exactly what Thanos’ <a href=\"https://thanos.io/tip/proposals-done/202301-distributed-query-execution.md/\"><u>distributed query engine</u></a> is capable of doing. Instead of requesting raw time series data, we request data center scoped aggregates and only need to send those back home where they get coalesced back again into the full query result:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/41XYc4JFFrNsmr3p0nYD2h/e3719dbe8fb8055cbb8f72c88729dfd9/6.png\">\n          </figure><p>Note that we ensure all the expensive data paths are as short as possible by utilizing R2 <a href=\"https://developers.cloudflare.com/r2/reference/data-location/#location-hints\"><u>location hints</u></a> to specify the primary access region.\n</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6H31Ad1XCjJWpuAQGPYSlt/ddaa971e4fa59bffdf283e10d0be0b8c/7.png\">\n          </figure>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1kxTrCfN0wZiNu90MNaOm8/8c3df56f9b8724b0ec01e6e9270bb989/8.png\">\n          </figure><p>To measure the effectiveness of this approach, we used <a href=\"https://cloudprober.org/\"><u>Cloudprober</u></a> and wrote probes that evaluate the relatively cheap, but still global, query <code>count(node_uname_info)</code>.</p>\n            <pre><code>sum(thanos_cloudprober_latency:rate6h{component=\"thanos-central\"})\n/\nsum(thanos_cloudprober_latency:rate6h{component=\"thanos-distributed\"})</code></pre>\n            <p>In the graph below, the y-axis represents the speedup of the distributed execution deployment relative to the centralized deployment. On average, distributed execution responds 3–5 times faster to probes.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1FzUb7uthpVG0yeSEFQxnd/6061eb1bc585565a47017ed9ddddae0a/9.png\">\n          </figure><p>Anecdotally, even slightly more complex queries quickly time out or even crash our centralized deployment, but they still can be comfortably computed by the distributed one. For a slightly more expensive query like <code>count(up)</code> for about 17 million scrape jobs, we had difficulty getting the centralized querier to respond and had to scope it to a single region, which took about 42 seconds:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3lhd497YLjRAhmSz55jGlG/2b258c6f634dc8a435f78703e38ec56c/10.png\">\n          </figure><p>Meanwhile, our distributed queriers were able to return the full result in about 8 seconds:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4tr2sKnLeKrzXLnMsfRViZ/675a2aade0d922548fc07a0bd8ad5fc5/11.png\">\n          </figure>\n          <div>\n            <h3>Congestion control</h3>\n            <a href=\"https://blog.cloudflare.com/safe-change-at-any-scale/#congestion-control\">\n              \n            </a>\n          </div>\n        <p>HMD batch processing leads to spiky load patterns that are hard to provision for. In a perfect world, it would issue a steady and predictable stream of queries. At the same time, HMD batch queries have lower priority to us than the queries that on-call engineers issue to triage production problems. We tackle both of those problems by introducing an adaptive priority-based concurrency control mechanism. After reading Netflix’s work on <a href=\"https://netflixtechblog.medium.com/performance-under-load-3e6fa9a60581\"><u>adaptive concurrency limits</u></a>, we implemented a similar proxy to dynamically limit batch request flow when Thanos SLOs start to degrade. For example, one such SLO is its cloudprober failure rate over the last minute:</p>\n            <pre><code>sum(thanos_cloudprober_fail:rate1m)\n/\n(sum(thanos_cloudprober_success:rate1m) + sum(thanos_cloudprober_fail:rate1m))</code></pre>\n            <p>We apply jitter, a random delay, to smooth query spikes inside the proxy. Since batch processing prioritizes overall query throughput over individual query latency, jitter helps HMD send a burst of queries, while allowing Thanos to process queries gradually over several minutes. This reduces instantaneous load on Thanos, improving overall throughput, even if individual query latency increases. Meanwhile, HMD encounters fewer errors, minimizing retries and boosting batch efficiency.</p><p>Our solution simulates how TCP’s congestion control algorithm, <a href=\"https://en.wikipedia.org/wiki/Additive_increase/multiplicative_decrease\"><u>additive increase/multiplicative decrease</u></a>, works. When the proxy server receives a successful request from Thanos, it allows one more concurrent request through next time. If backpressure signals breach defined thresholds, the proxy limits the congestion window proportional to the failure rate.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4M4s0lmq8h3bmZumLPUfXu/c3a967d51b367d155c26f4d95c673cd1/12.png\">\n          </figure><p>As the failure rate increases past the “warn” threshold, approaching the “emergency” threshold, the proxy gets exponentially closer to allowing zero additional requests through the system. However, to prevent bad signals from halting all traffic, we cap the loss with a configured minimum request rate.</p>\n          <div>\n            <h3>Columnar experiments</h3>\n            <a href=\"https://blog.cloudflare.com/safe-change-at-any-scale/#columnar-experiments\">\n              \n            </a>\n          </div>\n        <p>Because Thanos deals with Prometheus TSDB blocks that were never designed for being read over a slow medium like object storage, it does a lot of random I/O. Inspired by <a href=\"https://www.youtube.com/watch?v=V8Y4VuUwg8I\"><u>this excellent talk</u></a>, we started storing our time series data in <a href=\"https://parquet.apache.org/\"><u>Parquet</u></a> files, with some promising preliminary results. This project is still too early to draw any robust conclusions, but we wanted to share our implementation with the Prometheus community, so we are publishing our experimental object storage gateway as <a href=\"https://github.com/cloudflare/parquet-tsdb-poc\"><u>parquet-tsdb-poc</u></a> on GitHub.</p>\n          <div>\n            <h2>Conclusion</h2>\n            <a href=\"https://blog.cloudflare.com/safe-change-at-any-scale/#conclusion\">\n              \n            </a>\n          </div>\n          <p>We built Health Mediated Deployments (HMD) to enable safe and reliable software releases while pushing the limits of our observability infrastructure. Along the way, we significantly improved Thanos’ ability to handle high-load queries, reducing batch runtimes by 15x.</p><p>But this is just the beginning. We’re excited to continue working with the observability, resiliency, and R2 teams to push our infrastructure to its limits — safely and at scale. As we explore new ways to enhance observability, one exciting frontier is optimizing time series storage for object storage.</p><p>We’re sharing this work with the community as an open-source proof of concept. If you’re interested in exploring Parquet-based time series storage and its potential for large-scale observability, check out the GitHub project linked above.</p>",
            "url": "https://blog.cloudflare.com/safe-change-at-any-scale/",
            "title": "Scaling with safety: Cloudflare's approach to global service health metrics and software releases",
            "date_modified": "2025-05-05T13:20:57.162Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43872094\">Comments</a>",
            "url": "https://www.ubicloud.com/blog/building-burstables-cpu-slicing-with-cgroups",
            "title": "Building Burstables: CPU slicing with cgroups",
            "date_modified": "2025-05-02T16:45:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43869353\">Comments</a>",
            "url": "https://suno.com/explore/",
            "title": "Suno v4.5",
            "date_modified": "2025-05-02T13:18:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43859536\">Comments</a>",
            "url": "https://www.anthropic.com/news/integrations",
            "title": "Claude can now connect to your world",
            "date_modified": "2025-05-01T16:02:14.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/cswpbf/minidisc_zero_config_service_discovery\">Comments</a></p>",
            "url": "https://github.com/mscheidegger/minidisc",
            "title": "minidisc: Zero-config service discovery for Tailscale networks",
            "date_modified": "2025-05-01T14:53:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43847432\">Comments</a>",
            "url": "https://github.com/deepseek-ai/DeepSeek-Prover-V2",
            "title": "DeepSeek-Prover-V2",
            "date_modified": "2025-04-30T16:23:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43835948\">Comments</a>",
            "url": "https://github.com/neuroxhq/helm-chart-neurox-control",
            "title": "Show HN: Neurox – GPU Observability for AI Infra",
            "date_modified": "2025-04-29T18:02:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43833195\">Comments</a>",
            "url": "https://jepsen.io/analyses/amazon-rds-for-postgresql-17.4",
            "title": "Jepsen: Amazon RDS for PostgreSQL 17.4",
            "date_modified": "2025-04-29T14:30:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43832662\">Comments</a>",
            "url": "https://medium.com/gitconnected/mission-impossible-managing-ai-agents-in-the-real-world-f8e7834833af",
            "title": "Mission Impossible: Managing AI Agents in the Real World",
            "date_modified": "2025-04-29T13:54:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43823851\">Comments</a>",
            "url": "https://rnikhil.com/2025/04/25/sales-outbound-ai-dead",
            "title": "Is outbound going to die?",
            "date_modified": "2025-04-28T17:28:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43812500\">Comments</a>",
            "url": "https://github.com/mr-karan/logchef",
            "title": "Show HN: Logchef – Schema-agnostic log viewer for ClickHouse",
            "date_modified": "2025-04-27T15:15:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43791941\">Comments</a>",
            "url": "https://catalins.tech/how-i-setup-new-macbooks/",
            "title": "Easily setup new MacBooks",
            "date_modified": "2025-04-25T09:57:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43782406\">Comments</a>",
            "url": "https://motherduck.com/blog/introducing-instant-sql/",
            "title": "Instant SQL for results as you type in DuckDB UI",
            "date_modified": "2025-04-24T13:23:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43774327\">Comments</a>",
            "url": "https://iamcharliegraham.substack.com/publish/post/161906169",
            "title": "The Future of MCPs",
            "date_modified": "2025-04-23T17:12:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43773976\">Comments</a>",
            "url": "https://docs.fiveonefour.com/moose",
            "title": "Show HN: Moose – OSS framework to build analytical back ends with ClickHouse",
            "date_modified": "2025-04-23T16:41:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43773813\">Comments</a>",
            "url": "https://koomen.dev/essays/horseless-carriages/",
            "title": "AI Horseless Carriages",
            "date_modified": "2025-04-23T16:19:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43773777\">Comments</a>",
            "url": "https://www.featureform.com/post/deploy-mcp-on-aws-lambda-with-mcpengine",
            "title": "MCP on AWS Lambda with MCPEngine",
            "date_modified": "2025-04-23T16:17:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43766200\">Comments</a>",
            "url": "https://blog.atuin.sh/atuin-desktop-runbooks-that-run/",
            "title": "Atuin Desktop: Runbooks That Run",
            "date_modified": "2025-04-22T20:54:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43765011\">Comments</a>",
            "url": "https://github.com/alexykn/sapphire",
            "title": "Sapphire: Rust based package manager for macOS",
            "date_modified": "2025-04-22T18:39:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43763688\">Comments</a>",
            "url": "https://clickhouse.com/blog/clickhouse-gets-lazier-and-faster-introducing-lazy-materialization",
            "title": "ClickHouse gets lazier (and faster): Introducing lazy materialization",
            "date_modified": "2025-04-22T16:03:32.000Z"
        },
        {
            "content_html": "<p>Software development moves fast – really fast. It can also involve multiple teams working from different locations around the world. However, while speed and collaboration can be great for developers and businesses, they can also create security challenges.&nbsp;</p>\n\n\n\n<p>With more entry points and less time to catch potential threats, each commit, build, and <a href=\"https://www.jetbrains.com/teamcity/ci-cd-guide/continuous-deployment/\" target=\"_blank\" rel=\"noopener\">deployment</a> is another opportunity for something to go wrong. Whether that’s a security breach, malicious attack, or accidental exposure, the impact can ripple through your chain and burden every application.</p>\n\n\n\n<p>That’s where CI/CD security comes in. Learn what securing your <a href=\"https://www.jetbrains.com/teamcity/ci-cd-guide/ci-cd-pipeline/\" target=\"_blank\" rel=\"noopener\">CI/CD pipeline</a> means for your team, the main risks you need to be aware of, and the practical steps to safeguard your flow.</p>\n\n\n\n<h2>What is CI/CD security, and why is it important?</h2>\n\n\n\n<p>CI/CD security is a set of <a href=\"https://www.jetbrains.com/teamcity/ci-cd-guide/ci-cd-best-practices/\" target=\"_blank\" rel=\"noopener\">practices</a> and controls that protects the entire software delivery process. It prioritizes keeping your code safe from the very start, is built in rather than a separate phase, and is integral to <a href=\"https://www.jetbrains.com/teamcity/ci-cd-guide/what-is-devsecops/\" target=\"_blank\" rel=\"noopener\">DevSecOps</a>.</p>\n\n\n\n<p>Your CI/CD pipeline has access to tons of sensitive information, including codebases, credentials, and production environments. If compromised, attackers could inject malicious code, steal data, or even gain access to your systems (as they did in the <a href=\"https://www.techtarget.com/whatis/feature/SolarWinds-hack-explained-Everything-you-need-to-know\" target=\"_blank\" rel=\"noopener\">SolarWinds attack</a>).</p>\n\n\n\n<p>Aside from these catastrophic breaches, proper CI/CD security helps prevent mistakes, which could expose sensitive data or introduce vulnerabilities. Malicious employee or contractor behavior shouldn’t be overlooked here, either – <a href=\"https://www.statista.com/statistics/1387393/loss-sensitive-information-organizations-cause-worldwide/\" target=\"_blank\" rel=\"noopener\">20% of businesses</a> cited this as a cause of their data breaches. CI/CD security is both a shield and a safety net in one.</p>\n\n\n\n<figure><img width=\"2560\" height=\"1440\" src=\"https://blog.jetbrains.com/wp-content/uploads/2025/04/ci-cd-security-20-percent.png\"></figure>\n\n\n\n<p>With development automation, changes can go from a laptop to production in minutes, and CI/CD security needs to ensure it doesn’t slow down the process. Acceleration is great for business agility, but giving attackers a fast track to your systems is hazardous. In fact, less than <a href=\"https://www.reversinglabs.com/hubfs/Documents/2022-RL-Flying-Blind-Software-Firms-Struggle-To-Detect-Supply-Chain-Hacks.pdf\" target=\"_blank\" rel=\"noopener\">10% of companies</a> in 2022 had implemented hack monitoring in their software development lifecycle.&nbsp;</p>\n\n\n\n<p>However, get CI/CD security right, and you can have both speed and reliability.</p>\n\n\n\n<h2>CI/CD pipeline security threats</h2>\n\n\n\n<p>Your CI/CD pipeline has several potential weak points, including:&nbsp;</p>\n\n\n\n<ul>\n<li><strong>Source-code repositories</strong>: Where your application code and configuration files live, the starting point of your pipeline.</li>\n</ul>\n\n\n\n<ul>\n<li><strong>Build servers</strong>: The systems that compile your code, run tests, and package your applications. They handle sensitive operations and often have elevated privileges.</li>\n</ul>\n\n\n\n<ul>\n<li><a href=\"https://www.jetbrains.com/teamcity/tutorials/general/working-with-artifacts/\" target=\"_blank\" rel=\"noopener\"><strong>Artifact</strong></a><strong> storage</strong>: Where your compiled applications, container images, and packages are stored before deployment.&nbsp;</li>\n\n\n\n<li><strong>Deployment environments</strong>: The staging and production systems where your applications run (including cloud platforms and traditional servers).</li>\n</ul>\n\n\n\n<p>These components face threats from various angles, such as:&nbsp;</p>\n\n\n\n<ul>\n<li><strong>Supply chain attacks</strong>: Harmful code can sneak in through compromised third-party tools, libraries, or dependencies used in your application.</li>\n</ul>\n\n\n\n<ul>\n<li><strong>Stolen passwords and secrets</strong>: Attackers may find exposed credentials in pipeline configurations or scripts. These threats can take a long time to identify and contain – 292 days, according to <a href=\"https://www.ibm.com/reports/data-breach\" target=\"_blank\" rel=\"noopener\">one report</a>.&nbsp;</li>\n</ul>\n\n\n\n<ul>\n<li><strong>Configuration mistakes</strong>: Small errors in setup can enable attackers to bypass security or gain more access than they should have.</li>\n</ul>\n\n\n\n<ul>\n<li><strong>Insider threats</strong>: Developers with pipeline access might accidentally or intentionally introduce vulnerabilities.</li>\n\n\n\n<li><strong>Server breaches</strong>: Attackers can get access to the computers that run your build and deployment process.</li>\n</ul>\n\n\n\n<p>The interconnected nature of <a href=\"https://www.jetbrains.com/teamcity/ci-cd-guide/\" target=\"_blank\" rel=\"noopener\">CI/CD</a> means that compromising just one part can affect everything in the system.</p>\n\n\n\n<figure><img width=\"2715\" height=\"1902\" src=\"https://blog.jetbrains.com/wp-content/uploads/2025/04/CICD-pipeline-security-threats.png\"></figure>\n\n\n\n<h2>Tips for securing your CI/CD pipeline</h2>\n\n\n\n<p>The most effective CI/CD security involves building multiple layers of protection throughout your pipeline. Rather than implementing a single tool or simply following a checklist, you should set up security checkpoints at every stage.</p>\n\n\n\n<h3>Employ CI/CD access controls</h3>\n\n\n\n<p>Protect your pipeline by implementing strict access controls and applying the principle of least privilege.&nbsp;</p>\n\n\n\n<p>Use role-based access control (RBAC) to ensure team members only have the access they absolutely need for their specific roles. To prevent unauthorized code changes, set up mandatory code reviews, enable branch protection rules, and use signed commits.</p>\n\n\n\n<p>Remember to regularly audit these permissions and remove access when team members leave.</p>\n\n\n\n<h3>Manage secrets effectively</h3>\n\n\n\n<p>Never, ever hardcode credentials into your pipeline configurations or code. Instead, use dedicated secrets management tools (such as HashiCorp Vault) to securely store and manage sensitive information.</p>\n\n\n\n<p>Rotate these credentials regularly (ideally automatically) and ensure secrets are encrypted both in transit and at rest. It’s also best to use temporary credentials where possible.</p>\n\n\n\n<h3>Integrated security testing</h3>\n\n\n\n<p>Make security testing a natural part of your pipeline by putting multiple testing layers in place.</p>\n\n\n\n<p>Certain tools can help you catch vulnerabilities before they reach production:</p>\n\n\n\n<ul>\n<li><strong><a href=\"https://blog.jetbrains.com/teamcity/2025/03/what-is-sast/\" data-type=\"link\" data-id=\"https://blog.jetbrains.com/teamcity/2025/03/what-is-sast/\">Static Application Security Testing (SAST)</a></strong> analyzes your source code for security vulnerabilities.</li>\n\n\n\n<li><a href=\"https://blog.jetbrains.com/teamcity/2025/02/what-is-dast/\"><strong>Dynamic Application Security Testing (DAST)</strong></a> tests running applications.</li>\n\n\n\n<li><strong>Interactive Application Security Testing (IAST)</strong> is used for runtime analysis.</li>\n\n\n\n<li><strong>Software Composition Analysis (SCA)</strong> checks third-party dependencies.</li>\n</ul>\n\n\n\n<p>Configure these tests to run automatically with each build and block deployments if any security issues are found.</p>\n\n\n\n<figure><img width=\"2412\" height=\"1602\" src=\"https://blog.jetbrains.com/wp-content/uploads/2025/04/Securing-CI_CD-Pipeline-Strategies-and-Tools.png\"></figure>\n\n\n\n<h3>Secure the development and deployment environment</h3>\n\n\n\n<p>Ensure your build environments are as secure as your production systems – they’re just as important, if not more.&nbsp;</p>\n\n\n\n<p>Harden your build servers by removing unnecessary services, keeping systems patched, and using minimal base images. Implement network segmentation to isolate build environments from each other and other systems.</p>\n\n\n\n<p>If you can, consider using temporary infrastructure. This method allows you to create fresh environments for each build and destroy them afterward.</p>\n\n\n\n<h3>Automate security scans</h3>\n\n\n\n<p>Set up automated security scanning throughout your pipeline. Use container scanners to check for vulnerabilities in container images, dependency checkers to identify known vulnerabilities in libraries, and registry scanners to ensure the security of stored artifacts.&nbsp;</p>\n\n\n\n<p>Establish vulnerability thresholds (what level is considered suspicious or a threat) and automatically stop deployments that don’t meet your security standards. Schedule regular scans of your artifacts to ensure you’re aware of new or emerging vulnerabilities.</p>\n\n\n\n<h3>Monitor and alert</h3>\n\n\n\n<p>Implement comprehensive monitoring for your CI/CD pipeline. Track all activities and watch for unusual patterns like builds at odd hours, unexpected configuration changes, strange resource usage, and deployment events.</p>\n\n\n\n<p>Use detailed logging and set up alerts, making sure your team knows how to respond if something suspicious is found. Security information and event management (SIEM) are great CI/CD security tools – they correlate security events and enable real-time threat detection and response.</p>\n\n\n\n<h3>Perform regular security audits and assessments</h3>\n\n\n\n<p>Regularly test your CI/CD pipeline security using different methods:</p>\n\n\n\n<ul>\n<li><strong>Penetration testing</strong> identifies potential vulnerabilities before attackers do.</li>\n\n\n\n<li><strong>Red team exercises</strong> simulate ‘real’ attacks, while <strong>blue team exercises</strong> let you practice your incident response.</li>\n\n\n\n<li><strong>Purple team exercises</strong> are used to improve both your offensive and defensive capabilities.</li>\n</ul>\n\n\n\n<p>Check your compliance with your local security standards and regulations, and update your controls based on the results of your assessments.</p>\n\n\n\n<h2>How TeamCity can help</h2>\n\n\n\n<p>Security in your CI/CD pipeline is a must for protecting your software supply chain. While the threats are real, with the right tools and practices, you can build and deploy software securely without slowing down your team or minimizing their efforts.</p>\n\n\n\n<p>TeamCity makes this easier with <a href=\"https://www.jetbrains.com/teamcity/features/security/\" target=\"_blank\" rel=\"noopener\">security features</a> that grow with your needs.</p>\n\n\n\n<figure><table><tbody><tr><td><strong>TeamCity On-Premises</strong></td><td><strong>TeamCity Cloud</strong></td></tr><tr><td>🖥️ Installed and fully managed by your team</td><td>☁️ Hosted and managed by JetBrains</td></tr><tr><td>🔐 Full control over infrastructure and network</td><td>🔒 Zero-maintenance, secure-by-default CI/CD environment</td></tr><tr><td>🗝️ SSH key management</td><td>🗝️ SSH key management</td></tr><tr><td>🔄 Custom secrets management integrations (e.g., HashiCorp Vault, AWS KMS)</td><td>🔄 Custom secrets management integrations (e.g., HashiCorp Vault, AWS KMS)</td></tr><tr><td>📦 Artifact storage and access managed internally</td><td>📦 Secure artifact storage with access control</td></tr><tr><td>🔍 Customizable logging and monitoring tools</td><td>📜 Built-in user audit logs and integrated monitoring</td></tr><tr><td>🔧 Highly customizable for specific compliance needs</td><td>✅ Compliant with industry standards and suitable for regulated industries</td></tr><tr><td>👥 Ideal for teams with strict infrastructure or data residency policies</td><td>🏢 Great for teams who want secure CI/CD without infrastructure management</td></tr></tbody></table></figure>\n\n\n\n<p>Deliver secure software without compromising on speed or performance. <a href=\"https://www.jetbrains.com/teamcity/download/\" target=\"_blank\" rel=\"noopener\">Try TeamCity for free now</a>.</p>\n\n\n<div>\n<p><span>💡See also: </span><a href=\"https://blog.jetbrains.com/teamcity/2023/05/cicd-security-whitepaper/\"><span>[Whitepaper] 9 Ways to Prevent a Supply Chain Attack on Your CI/CD Server</span></a></p>\n</div>",
            "url": "https://blog.jetbrains.com/teamcity/2025/04/ci-cd-security-best-practices/",
            "title": "CI/CD Security Best Practices",
            "date_modified": "2025-04-22T12:57:38.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yjypaz/event_hidden_architectures\">Comments</a></p>",
            "url": "https://skiplabs.io/blog/event-hidden-arch",
            "title": "Event-Hidden Architectures",
            "date_modified": "2025-04-22T12:53:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43735550\">Comments</a>",
            "url": "https://www.anthropic.com/engineering/claude-code-best-practices",
            "title": "Claude Code Best Practices",
            "date_modified": "2025-04-19T10:48:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43735443\">Comments</a>",
            "url": "https://spiraldb.com/post/so-you-want-to-use-object-storage",
            "title": "Achieveing lower latencies with S3 object storage",
            "date_modified": "2025-04-19T10:19:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43725815\">Comments</a>",
            "url": "https://www.bugsink.com/blog/why-i-gave-up-on-self-hosted-sentry/",
            "title": "I gave up on self-hosted Sentry",
            "date_modified": "2025-04-18T07:24:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43710576\">Comments</a>",
            "url": "https://github.com/plandex-ai/plandex",
            "title": "Show HN: Plandex v2 – open source AI coding agent for large projects and tasks",
            "date_modified": "2025-04-16T21:26:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43708725\">Comments</a>",
            "url": "https://www.bloomberg.com/news/articles/2025-04-16/openai-said-to-be-in-talks-to-buy-windsurf-for-about-3-billion",
            "title": "OpenAI in Talks to Buy Windsurf for About $3B",
            "date_modified": "2025-04-16T18:24:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43695723\">Comments</a>",
            "url": "https://devblogs.microsoft.com/oldnewthing/20250411-00/?p=111066",
            "title": "The case of the UI thread that hung in a kernel call",
            "date_modified": "2025-04-15T17:13:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43692476\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=43692476",
            "title": "Launch HN: Mrge.io (YC X25) – Cursor for code review",
            "date_modified": "2025-04-15T13:34:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43692476\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=43692476",
            "title": "Launch HN: mrge.io (YC X25) – Cursor for code review",
            "date_modified": "2025-04-15T13:34:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43691339\">Comments</a>",
            "url": "https://www.infoworld.com/article/3849245/github-suffers-a-cascading-supply-chain-attack-compromising-ci-cd-secrets.html",
            "title": "GitHub suffers a cascading supply chain attack compromising CI/CD secrets",
            "date_modified": "2025-04-15T11:31:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43682547\">Comments</a>",
            "url": "https://github.com/nathanhleung/protobuf-ts-types",
            "title": "Show HN: Zero-codegen, no-compile TypeScript type inference from Protobufs",
            "date_modified": "2025-04-14T15:41:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43680699\">Comments</a>",
            "url": "https://github.com/meilisearch/meilisearch",
            "title": "Meilisearch – search engine API bringing AI-powered hybrid search",
            "date_modified": "2025-04-14T12:46:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43677174\">Comments</a>",
            "url": "https://github.com/basecamp/gh-signoff",
            "title": "Local CI. Sign off on your own work",
            "date_modified": "2025-04-14T01:12:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43659876\">Comments</a>",
            "url": "https://substack.com/home/post/p-161145826",
            "title": "Dev Tools Honeytrap: Why We Can't Stop Building Tools Nobody Buys",
            "date_modified": "2025-04-11T23:37:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43659505\">Comments</a>",
            "url": "https://github.com/firecracker-microvm/firecracker/blob/main/docs/snapshotting/random-for-clones.md",
            "title": "Firecracker Entropy for VM Clones",
            "date_modified": "2025-04-11T22:46:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43655221\">Comments</a>",
            "url": "https://stevana.github.io/erlangs_not_about_lightweight_processes_and_message_passing.html",
            "title": "Erlang's not about lightweight processes and message passing",
            "date_modified": "2025-04-11T15:50:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43655221\">Comments</a>",
            "url": "https://stevana.github.io/erlangs_not_about_lightweight_processes_and_message_passing.html",
            "title": "Erlang's not about lightweight processes and message passing (2023)",
            "date_modified": "2025-04-11T15:50:49.000Z"
        },
        {
            "content_html": "<p>At re:Invent 2023, we <a href=\"https://aws.amazon.com/blogs/aws/new-amazon-s3-express-one-zone-high-performance-storage-class/\">introduced</a> <a href=\"https://aws.amazon.com/s3/storage-classes/express-one-zone/\">Amazon S3 Express One Zone</a>, a high-performance, single-Availability Zone (AZ) storage class purpose-built to deliver consistent single-digit millisecond data access for your most frequently accessed data and latency-sensitive applications.</p> \n<p>S3 Express One Zone delivers data access speed up to 10 times faster than S3 Standard, and it can support up to 2 million <code>GET</code> transactions per second (TPS) and up to 200,000 <code>PUT</code> TPS per directory bucket. This makes it ideal for performance-intensive&nbsp;workloads such as interactive data analytics, data streaming, media rendering and transcoding, high performance computing (HPC), and AI/ML trainings. Using S3 Express One Zone, customers like <a href=\"https://aws.amazon.com/blogs/storage/fundrise-uses-amazon-s3-express-one-zone-to-accelerate-investment-data-processing/\">Fundrise</a>, <a href=\"https://aws.amazon.com/blogs/storage/how-aura-improves-database-performance-using-amazon-s3-express-one-zone-for-caching/\">Aura</a>, <a href=\"https://aws.amazon.com/blogs/storage/lyrebird-improves-performance-and-reduces-costs-for-generative-ai-workloads-using-amazon-s3-express-one-zone/\">Lyrebird</a>, <a href=\"https://aws.amazon.com/blogs/storage/how-vivian-health-is-using-amazon-s3-express-one-zone-to-accelerate-healthcare-hiring/\">Vivian Health</a>, and <a href=\"https://aws.amazon.com/blogs/storage/how-fetch-reduces-latency-on-image-uploads-using-amazon-s3-express-one-zone/\">Fetch</a> improved the performance and reduced the costs of their data-intensive workloads.</p> \n<p>Since launch, we’ve introduced <a href=\"https://aws.amazon.com/about-aws/whats-new/storage/?whats-new-content.q=S3%2BExpress%2BOne%2BZone\">a number of features</a> for our customers using S3 Express One Zone. For example, S3 Express One Zone started to support <a href=\"https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-express-one-zone-s3-lifecycle-expirations/\">object expiration using S3 Lifecycle</a> to expire objects based on age to help you automatically optimize storage costs. In addition, your log-processing or media-broadcasting applications can directly <a href=\"https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-express-one-zone-append-data-object/\">append new data to the end of existing objects</a> and then immediately read the object, all within S3 Express One Zone.</p> \n<p>Today we’re announcing that, effective April 10, 2025, S3 Express One Zone has reduced storage prices by 31 percent, <code>PUT</code> request prices by 55 percent, and <code>GET</code> request prices by 85 percent. In addition, S3 Express One Zone has reduced the per-GB charges for data uploads and retrievals by 60 percent, and these charges now apply to all bytes transferred rather than just portions of requests greater than 512 KB.</p> \n<p>Here is a price reduction table in the US East (N. Virginia) Region:</p> \n<table> \n <tbody> \n  <tr> \n   <td><strong>Price</strong></td> \n   <td><strong>Previous</strong></td> \n   <td><strong>New</strong></td> \n   <td><strong>Price reduction</strong></td> \n  </tr> \n  <tr> \n   <td><strong>Storage<br> </strong>(per GB-Month)</td> \n   <td>$0.16</td> \n   <td>$0.11</td> \n   <td>31%</td> \n  </tr> \n  <tr> \n   <td><strong>Writes<br> </strong>(<code>PUT</code> requests)</td> \n   <td>$0.0025 per 1,000 requests up to 512 KB</td> \n   <td>$0.00113 per 1,000 requests</td> \n   <td>55%</td> \n  </tr> \n  <tr> \n   <td><strong>Reads<br> </strong>(<code>GET</code> requests)</td> \n   <td>$0.0002 per 1,000 requests up to 512 KB</td> \n   <td>$0.00003 per 1,000 requests</td> \n   <td>85%</td> \n  </tr> \n  <tr> \n   <td><strong>Data upload<br> </strong>(per GB)</td> \n   <td>$0.008</td> \n   <td>$0.0032</td> \n   <td>60%</td> \n  </tr> \n  <tr> \n   <td><strong>Data retrievals<br> </strong>(per GB)</td> \n   <td>$0.0015</td> \n   <td>$0.0006</td> \n   <td>60%</td> \n  </tr> \n </tbody> \n</table> \n<p>For S3 Express One Zone pricing examples, go to the <a href=\"https://aws.amazon.com/s3/faqs/#S3_Express_One_Zone\">S3 billing FAQs</a> or use the <a href=\"https://calculator.aws/\">AWS Pricing Calculator</a>.</p> \n<p>These pricing reductions apply to <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-Endpoints.html\">S3 Express One Zone in all AWS Regions</a> where the storage class is available: US East (N. Virginia), US East (Ohio), US West (Oregon), Asia Pacific (Mumbai), Asia Pacific (Tokyo), Europe (Ireland), and Europe (Stockholm) Regions. To learn more, visit the <a href=\"https://aws.amazon.com/s3/pricing/\">Amazon S3 pricing page</a> and <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-bucket-high-performance.html\">S3 Express One Zone</a> in the AWS Documentation.</p> \n<p>Give S3 Express One Zone a try in the <a href=\"https://console.aws.amazon.com/s3\">S3 console</a> today and send feedback to <a href=\"https://repost.aws/tags/TADSTjraA0Q4-a1dxk6eUYaw/amazon-simple-storage-service\">AWS re:Post for Amazon S3</a> or through your usual AWS Support contacts.</p> \n<p>— <a href=\"https://x.com/channyun\">Channy</a></p> \n<hr> \n<p>How is the News Blog doing? Take this <a href=\"https://amazonmr.au1.qualtrics.com/jfe/form/SV_eyD5tC5xNGCdCmi\">1 minute survey</a>!</p> \n<p><em>(This <a href=\"https://amazonmr.au1.qualtrics.com/jfe/form/SV_eyD5tC5xNGCdCmi\">survey</a> is hosted by an external company. AWS handles your information as described in the <a href=\"https://aws.amazon.com/privacy/?trk=4b29643c-e00f-4ab6-ab9c-b1fb47aa1708&amp;sc_channel=blog\">AWS Privacy Notice</a>. AWS will own the data gathered via this survey and will not share the information collected with survey respondents.)</em></p>",
            "url": "https://aws.amazon.com/blogs/aws/up-to-85-price-reductions-for-amazon-s3-express-one-zone/",
            "title": "Announcing up to 85% price reductions for Amazon S3 Express One Zone",
            "date_modified": "2025-04-10T21:04:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43644173\">Comments</a>",
            "url": "https://blog.cloudflare.com/r2-data-catalog-public-beta/",
            "title": "Cloudflare R2 Data Catalog: Managed Apache Iceberg tables with zero egress fees",
            "date_modified": "2025-04-10T14:30:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43643944\">Comments</a>",
            "url": "https://www.docker.com/blog/introducing-docker-model-runner/",
            "title": "Docker Model Runner",
            "date_modified": "2025-04-10T14:10:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43634005\">Comments</a>",
            "url": "https://withaqua.com",
            "title": "Show HN: Aqua Voice 2 – Fast Voice Input for Mac and Windows",
            "date_modified": "2025-04-09T16:31:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43624888\">Comments</a>",
            "url": "https://netflixtechblog.com/how-netflix-accurately-attributes-ebpf-flow-logs-afe6d644a3bc",
            "title": "How Netflix Accurately Attributes eBPF Flow Logs",
            "date_modified": "2025-04-08T18:21:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43623820\">Comments</a>",
            "url": "https://github.com/coroot/coroot",
            "title": "Show HN: Coroot – eBPF-based, open source observability with actionable insights",
            "date_modified": "2025-04-08T16:49:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43617493\">Comments</a>",
            "url": "https://yossarian.net/til/post/any-program-can-be-a-github-actions-shell/",
            "title": "Any program can be a GitHub Actions shell",
            "date_modified": "2025-04-08T01:20:10.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/teutud/beautiful_ci_for_bazel\">Comments</a></p>",
            "url": "https://narang99.github.io/2025-03-22-monorepo-bazel-jenkins/",
            "title": "Beautiful CI for Bazel",
            "date_modified": "2025-04-07T22:34:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43615095\">Comments</a>",
            "url": "https://www.designboom.com/design/severance-closer-look-mid-century-brutalist-retro-futuristic-universe-lumon-03-21-2025/",
            "title": "Severance: A closer look into the mid-century, brutalist universe of Lumon",
            "date_modified": "2025-04-07T19:40:39.000Z"
        },
        {
            "content_html": "<p>It’s not a secret that at Cloudflare <a href=\"https://blog.cloudflare.com/build-ai-agents-on-cloudflare/\"><u>we are bullish</u></a> on the future of agents. We’re excited about a future where AI can not only co-pilot alongside us, but where we can actually start to delegate entire tasks to AI.&nbsp;</p><p>While it hasn’t been too long since we <a href=\"https://blog.cloudflare.com/build-ai-agents-on-cloudflare/\"><u>first announced</u></a> our Agents SDK to make it easier for developers to build agents, building towards an agentic future requires continuous delivery towards this goal. Today, we’re making several announcements to help accelerate agentic development, including:</p><ul><li><p><b>New Agents SDK capabilities:</b> Build remote MCP clients, with transport and authentication built-in, to allow AI agents to connect to external services.&nbsp;</p></li><li><p><a href=\"https://developers.cloudflare.com/agents/model-context-protocol/authorization/#3-bring-your-own-oauth-provider\"><b><u>BYO Auth provider for MCP</u></b></a><b>:</b> Integrations with <a href=\"https://stytch.com/\"><u>Stytch</u></a>, <a href=\"https://auth0.com/\"><u>Auth0</u></a>, and <a href=\"https://workos.com/\"><u>WorkOS</u></a> to add authentication and authorization to your remote MCP server.&nbsp;</p></li><li><p><a href=\"https://developers.cloudflare.com/agents/model-context-protocol/mcp-agent-api/#hibernation-support\"><b><u>Hibernation for McpAgent</u></b></a><b>:</b> Automatically sleep stateful, remote MCP servers when inactive and wake them when needed. This allows you to maintain connections for long-running sessions while ensuring you’re not paying for idle time.&nbsp;</p></li><li><p><a href=\"https://developers.cloudflare.com/changelog/2025-04-07-durable-objects-free-tier\"><b><u>Durable Objects free tier</u></b></a><b>:</b> We view Durable Objects as a key component for building agents, and if you’re using our Agents SDK, you need access to it. Until today, Durable Objects was only accessible as part of our paid plans, and today we’re excited to include it in our free tier.</p></li><li><p><a href=\"https://blog.cloudflare.com/workflows-ga-production-ready-durable-execution\"><b><u>Workflows GA</u></b></a><b>:</b> Enables you to ship production-ready, long-running, multi-step actions in agents.</p></li><li><p><a href=\"https://blog.cloudflare.com/introducing-autorag-on-cloudflare\"><b><u>AutoRAG</u></b></a><b>:</b> Helps you integrate context-aware AI into your applications, in just a few clicks</p></li><li><p><a href=\"https://agents.cloudflare.com\"><b><u>agents.cloudflare.com</u></b></a><b>:</b> our new landing page for all things agents.</p></li></ul>\n          <div>\n            <h2>New MCP capabilities in Agents SDK</h2>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#new-mcp-capabilities-in-agents-sdk\">\n              \n            </a>\n          </div>\n          <p>AI agents can now connect to and interact with external services through MCP (<a href=\"https://modelcontextprotocol.io/introduction\"><u>Model Context Protocol</u></a>). We’ve updated the Agents SDK to allow you to build a remote MCP client into your AI agent, with all the components —&nbsp;authentication flows, tool discovery, and connection management —&nbsp;built-in for you.</p><p>This allows you to build agents that can:</p><ol><li><p>Prompt the end user to grant access to a 3rd party service (MCP server).</p></li><li><p>Use tools from these external services, acting on behalf of the end user.</p></li><li><p>Call MCP servers from Workflows, scheduled tasks, or any part of your agent.</p></li><li><p>Connect to multiple MCP servers and automatically discover new tools or capabilities presented by the 3rd party service.</p></li></ol>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/X3RvQHewsVwJhq3TVOD0w/bbc5690d2d687f7a390f91474b3eb385/1.png\">\n          </figure><p>MCP (Model Context Protocol) —&nbsp;<a href=\"https://www.anthropic.com/news/model-context-protocol\"><u>first introduced by Anthropic</u></a> —&nbsp;is quickly becoming the standard way for AI agents to interact with external services, with providers like OpenAI, Cursor, and Copilot adopting the protocol.</p><p>We <a href=\"https://blog.cloudflare.com/remote-model-context-protocol-servers-mcp/\"><u>recently announced</u></a> support for <a href=\"https://developers.cloudflare.com/agents/guides/remote-mcp-server/\"><u>building remote MCP servers</u></a> on Cloudflare, and added an <code>McpAgent</code> class to our Agents SDK that automatically handles the remote aspects of MCP: transport and authentication/authorization. Now, we’re excited to extend the same capabilities to agents acting as MCP clients.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3nxl3bIRTbfRzpdLhHF720/41bea06c9e48b7d356d11a6f254b76ef/2.png\">\n          </figure><p>Want to see it in action? Use the button below to deploy a fully remote MCP client that can be used to connect to remote MCP servers.</p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/mcp-client\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a>\n<p></p>\n          <div>\n            <h2>AI Agents can now act as remote MCP clients, with transport and auth included</h2>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#ai-agents-can-now-act-as-remote-mcp-clients-with-transport-and-auth-included\">\n              \n            </a>\n          </div>\n          <p>AI agents need to connect to external services to access tools, data, and capabilities beyond their built-in knowledge. That means AI agents need to be able to act as remote MCP clients, so they can connect to remote MCP servers that are hosting these tools and capabilities.&nbsp;</p><p>We’ve added a new class, <code>MCPClientManager</code>, into the Agents SDK to give you all the tooling you need to allow your AI agent to make calls to external services via MCP. The <code>MCPClientManager</code> class automatically handles:&nbsp;</p><ul><li><p><b>Transport: </b>Connect to remote MCP servers over SSE and HTTP, with support for <a href=\"https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http\"><u>Streamable HTTP</u></a> coming soon.&nbsp;</p></li><li><p><b>Connection management: </b>The client tracks the state of all connections and automatically reconnects if a connection is lost.</p></li><li><p><b>Capability discovery: </b>Automatically discovers all capabilities, tools, resources, and prompts presented by the MCP server.</p></li><li><p><b>Real-time updates</b>: When a server's tools, resources, or prompts change, the client automatically receives notifications and updates its internal state.</p></li><li><p><b>Namespacing: </b>When connecting to multiple MCP servers, all tools and resources are automatically namespaced to avoid conflicts.</p></li></ul>\n          <div>\n            <h3>Granting agents access to tools with built-in auth check for MCP Clients</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#granting-agents-access-to-tools-with-built-in-auth-check-for-mcp-clients\">\n              \n            </a>\n          </div>\n        <p>We've integrated the complete OAuth authentication flow directly into the Agents SDK, so your AI agents can securely connect and authenticate to any remote MCP server without you having to build authentication flow from scratch.</p><p>This allows you to give users a secure way to log in and explicitly grant access to allow the agent to act on their behalf by automatically:&nbsp;</p><ul><li><p>Supporting the OAuth 2.1 protocol.</p></li><li><p>Redirecting users to the service’s login page.</p></li><li><p>Generating the code challenge and exchanging an authorization code for an access token.</p></li><li><p>Using the access token to make authenticated requests to the MCP server.</p></li></ul><p>Here is an example of an agent that can securely connect to MCP servers by initializing the client manager, adding the server, and handling the authentication callbacks:</p>\n            <pre><code>async onStart(): Promise&lt;void&gt; {\n  // initialize MCPClientManager which manages multiple MCP clients with optional auth\n  this.mcp = new MCPClientManager(\"my-agent\", \"1.0.0\", {\n    baseCallbackUri: `${serverHost}/agents/${agentNamespace}/${this.name}/callback`,\n    storage: this.ctx.storage,\n  });\n}\n\nasync addMcpServer(url: string): Promise&lt;string&gt; {\n  // Add one MCP client to our MCPClientManager\n  const { id, authUrl } = await this.mcp.connect(url);\n  // Return authUrl to redirect the user to if the user is unauthorized\n  return authUrl\n}\n\nasync onRequest(req: Request): Promise&lt;void&gt; {\n  // handle the auth callback after being finishing the MCP server auth flow\n  if (this.mcp.isCallbackRequest(req)) {\n    await this.mcp.handleCallbackRequest(req);\n    return new Response(\"Authorized\")\n  }\n  \n  // ...\n}</code></pre>\n            <p>Connecting to multiple MCP servers and discovering what capabilities they offer</p><p>You can use the Agents SDK to connect an MCP client to multiple MCP servers simultaneously. This is particularly useful when you want your agent to access and interact with tools and resources served by different service providers.&nbsp;</p><p>The <code>MCPClientManager</code> class maintains connections to multiple MCP servers through the <code>mcpConnections</code> object, a dictionary that maps unique server names to their respective <code>MCPClientConnection</code> instances.&nbsp;</p><p>When you register a new server connection using <code>connect()</code>, the manager:&nbsp;</p><ol><li><p>Creates a new connection instance with server-specific authentication.</p></li><li><p>Initializes the connections and registers for server capability notifications.</p></li></ol>\n            <pre><code>async onStart(): Promise&lt;void&gt; {\n  // Connect to an image generation MCP server\n  await this.mcp.connect(\"https://image-gen.example.com/mcp/sse\");\n  \n  // Connect to a code analysis MCP server\n  await this.mcp.connect(\"https://code-analysis.example.org/sse\");\n  \n  // Now we can access tools with proper namespacing\n  const allTools = this.mcp.listTools();\n  console.log(`Total tools available: ${allTools.length}`);\n}</code></pre>\n            <p>Each connection manages its own authentication context, allowing one AI agent to authenticate to multiple servers simultaneously. In addition, <code>MCPClientManager</code> automatically handles namespacing to prevent collisions between tools with identical names from different servers.&nbsp;</p><p>For example, if both an “Image MCP Server” and “Code MCP Server” have a tool named “analyze”, they will both be independently callable without any naming conflicts.</p>\n          <div>\n            <h2>Use Stytch, Auth0, and WorkOS to bring authentication &amp; authorization to your MCP server&nbsp;</h2>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#use-stytch-auth0-and-workos-to-bring-authentication-authorization-to-your-mcp-server\">\n              \n            </a>\n          </div>\n          <p>With MCP, users will have a new way of interacting with your application, no longer relying on the dashboard or API as the entrypoint. Instead, the service will now be accessed by AI agents that are acting on a user’s behalf. To ensure users and agents can connect to your service securely, you’ll need to extend your existing authentication and authorization system to support these agentic interactions, implementing login flows, permissions scopes, consent forms, and access enforcement for your MCP server.&nbsp;</p><p>We’re adding integrations with <a href=\"https://stytch.com/\"><u>Stytch</u></a>, <a href=\"https://auth0.com/\"><u>Auth0</u></a>, and <a href=\"https://workos.com/\"><u>WorkOS</u></a> to make it easier for anyone building an MCP server to configure authentication &amp; authorization for their MCP server.&nbsp;</p><p>You can leverage our MCP server integration with Stytch, Auth0, and WorkOS to:&nbsp;</p><ul><li><p>Allow users to authenticate to your MCP server through email, social logins, SSO (single sign-on), and MFA (multi-factor authentication).</p></li><li><p>Define scopes and permissions that directly map to your MCP tools.</p></li><li><p>Present users with a consent page corresponding with the requested permissions.</p></li></ul><p>Enforce the permissions so that agents can only invoke permitted tools.&nbsp;</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6oYchjMwoMxwYxsqq4PObk/381937e89c249b87c1930295b407faf6/3.png\">\n          </figure><p>Get started with the examples below by using the “Deploy to Cloudflare” button to deploy the demo MCP servers in your Cloudflare account. These demos include pre-configured authentication endpoints, consent flows, and permission models that you can tailor to fit your needs. Once you deploy the demo MCP servers, you can use the <a href=\"https://playground.ai.cloudflare.com/\"><u>Workers AI playground</u></a>, a browser-based remote MCP client, to test out the end-to-end user flow.&nbsp;</p>\n          <div>\n            <h3>Stytch</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#stytch\">\n              \n            </a>\n          </div>\n        <p><a href=\"https://stytch.com/docs/guides/connected-apps/mcp-servers\"><u>Get started</u></a> with a remote MCP server that uses Stytch to allow users to sign in with email, Google login or enterprise SSO and authorize their AI agent to view and manage their company’s OKRs on their behalf. Stytch will handle restricting the scopes granted to the AI agent based on the user’s role and permissions within their organization. When authorizing the MCP Client, each user will see a consent page that outlines the permissions that the agent is requesting that they are able to grant based on their role.</p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/mcp-stytch-b2b-okr-manager\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a>\n<p></p><p>For more consumer use cases, deploy a remote MCP server for a To Do app that uses Stytch for authentication and MCP client authorization. Users can sign in with email and immediately access the To Do lists associated with their account, and grant access to any AI assistant to help them manage their tasks.</p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/mcp-stytch-consumer-todo-list\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a>\n<p></p><p>Regardless of use case, Stytch allows you to easily turn your application into an OAuth 2.0 identity provider and make your remote MCP server into a Relying Party so that it can easily inherit identity and permissions from your app. To learn more about how Stytch is enabling secure authentication to remote MCP servers, read their <a href=\"http://stytch.com/blog/remote-mcp-stytch-cloudflare\"><u>blog post</u></a>.</p><blockquote><p><i>“One of the challenges of realizing the promise of AI agents is enabling those agents to securely and reliably access data from other platforms. Stytch Connected Apps is purpose-built for these agentic use cases, making it simple to turn your app into an OAuth 2.0 identity provider to enable secure access to remote MCP servers. By combining Cloudflare Workers with Stytch Connected Apps, we're removing the barriers for developers, enabling them to rapidly transition from AI proofs-of-concept to secure, deployed implementations.” — Julianna Lamb, Co-Founder &amp; CTO, Stytch.</i></p></blockquote>\n          <div>\n            <h3>Auth0</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#auth0\">\n              \n            </a>\n          </div>\n        <p>Get started with a remote MCP server that uses Auth0 to authenticate users through email, social logins, or enterprise SSO to interact with their todos and personal data through AI agents. The MCP server securely connects to API endpoints on behalf of users, showing exactly which resources the agent will be able to access once it gets consent from the user. In this implementation, access tokens are automatically refreshed during long running interactions.</p><p>To set it up, first deploy the protected API endpoint: </p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-auth0/todos-api\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a>\n<p></p><p>Then, deploy the MCP server that handles authentication through Auth0 and securely connects AI agents to your API endpoint.&nbsp;</p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-auth0/mcp-auth0-oidc\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a>\n<p></p><blockquote><p><i>\"Cloudflare continues to empower developers building AI products with tools like AI Gateway, Vectorize, and Workers AI. The recent addition of Remote MCP servers further demonstrates that Cloudflare Workers and Durable Objects are a leading platform for deploying serverless AI. We’re very proud that Auth0 can help solve the authentication and authorization needs for these cutting-edge workloads.\" — Sandrino Di Mattia, Auth0 Sr. Director, Product Architecture.</i></p></blockquote>\n          <div>\n            <h3>WorkOS</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#workos\">\n              \n            </a>\n          </div>\n        <p>Get started with a remote MCP server that uses WorkOS's AuthKit to authenticate users and manage the permissions granted to AI agents. In this example, the MCP server dynamically exposes tools based on the user's role and access rights. All authenticated users get access to the <code>add</code> tool, but only users who have been assigned the <code>image_generation</code> permission in WorkOS can grant the AI agent access to the image generation tool. This showcases how MCP servers can conditionally expose capabilities to AI agents based on the authenticated user's role and permission.</p><a href=\"https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authkit\"><img src=\"https://deploy.workers.cloudflare.com/button\"></a>\n<p></p><blockquote><p><i>“MCP is becoming the standard for AI agent integration, but authentication and authorization are still major gaps for enterprise adoption. WorkOS Connect enables any application to become an OAuth 2.0 authorization server, allowing agents and MCP clients to securely obtain tokens for fine-grained permission authorization and resource access. With Cloudflare Workers, developers can rapidly deploy remote MCP servers with built-in OAuth and enterprise-grade access control. Together, WorkOS and Cloudflare make it easy to ship secure, enterprise-ready agent infrastructure.” —&nbsp;Michael Grinich, CEO of WorkOS.</i></p></blockquote>\n          <div>\n            <h2>Hibernate-able WebSockets: put AI agents to sleep when they’re not in use</h2>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#hibernate-able-websockets-put-ai-agents-to-sleep-when-theyre-not-in-use\">\n              \n            </a>\n          </div>\n          <p>Starting today, a new improvement is landing in the McpAgent class: support for the <a href=\"https://developers.cloudflare.com/durable-objects/best-practices/websockets/#websocket-hibernation-api\"><u>WebSockets Hibernation API</u></a> that allows your MCP server to go to sleep when it’s not receiving requests and instantly wake up when it’s needed. That means that you now only pay for compute when your agent is actually working.</p><p>We <a href=\"https://blog.cloudflare.com/remote-model-context-protocol-servers-mcp/\"><u>recently introduced</u></a> the <a href=\"https://developers.cloudflare.com/agents/model-context-protocol/tools/?cf_history_state=%7B%22guid%22%3A%22C255D9FF78CD46CDA4F76812EA68C350%22%2C%22historyId%22%3A11%2C%22targetId%22%3A%22DF3E523E0077ACCB6730439891CDD7D4%22%7D\"><u>McpAgent class</u></a>, which allows developers to build remote MCP servers on Cloudflare by using Durable Objects to maintain stateful connections for every client session. We decided to build McpAgent to be stateful from the start, allowing developers to build servers that can remember context, user preferences, and conversation history. But maintaining client connections means that the session can remain active for a long time, even when it’s not being used.&nbsp;</p>\n          <div>\n            <h3>MCP Agents are hibernate-able by default</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#mcp-agents-are-hibernate-able-by-default\">\n              \n            </a>\n          </div>\n        <p>You don’t need to change your code to take advantage of hibernation. With our latest SDK update, all McpAgent instances automatically include hibernation support, allowing your stateful MCP servers to sleep during inactive periods and wake up with their state preserved when needed.&nbsp;</p>\n          <div>\n            <h3>How it works</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#how-it-works\">\n              \n            </a>\n          </div>\n        <p>When a request comes in on the Server-Sent Events endpoint, /sse, the Worker initializes a WebSocket connection to the appropriate Durable Object for the session and returns an SSE stream back to the client. All responses flow over this stream.</p><p>The implementation leverages the WebSocket Hibernation API within Durable Objects. When periods of inactivity occur, the Durable Object can be evicted from memory while keeping the WebSocket connection open. If the WebSocket later receives a message, the runtime recreates the Durable Object and delivers the message to the appropriate handler.</p>\n          <div>\n            <h2>Durable Objects on free tier</h2>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#durable-objects-on-free-tier\">\n              \n            </a>\n          </div>\n          <p>To help you build AI agents on Cloudflare, we’re making <a href=\"http://developers.cloudflare.com/durable-objects/what-are-durable-objects/\"><u>Durable Objects</u></a> available on the free tier, so you can start with zero commitment. With Agents SDK, your AI agents deploy to Cloudflare running on Durable Objects.</p><p>Durable Objects offer compute alongside durable storage, that when combined with Workers, unlock stateful, serverless applications. Each Durable Object is a stateful coordinator for handling client real-time interactions, making requests to external services like LLMs, and creating agentic “memory” through state persistence in <a href=\"https://blog.cloudflare.com/sqlite-in-durable-objects/\"><u>zero-latency SQLite storage</u></a> — all tasks required in an AI agent. Durable Objects scale out to millions of agents effortlessly, with each agent created near the user interacting with their agent for fast performance, all managed by Cloudflare.&nbsp;</p><p>Zero-latency SQLite storage in Durable Objects was <a href=\"https://blog.cloudflare.com/sqlite-in-durable-objects/\"><u>introduced in public beta</u></a> September 2024 for Birthday Week. Since then, we’ve focused on missing features and robustness compared to pre-existing key-value storage in Durable Objects. We are excited to make SQLite storage generally available, with a 10 GB SQLite database per Durable Object, and recommend SQLite storage for all new Durable Object classes. Durable Objects free tier can only access SQLite storage.</p><p>Cloudflare’s free tier allows you to build real-world applications. On the free plan, every Worker request can call a Durable Object. For <a href=\"https://developers.cloudflare.com/durable-objects/platform/pricing/\"><u>usage-based pricing</u></a>, Durable Objects incur compute and storage usage with the following free tier limits.</p><div>\n    <figure>\n        <table>\n            <colgroup>\n                <col>\n                <col>\n                <col>\n            </colgroup>\n            <tbody>\n                <tr>\n                    <td>&nbsp;</td>\n                    <td>\n                        <p><span><span><strong>Workers Free</strong></span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span><strong>Workers Paid</strong></span></span></p>\n                    </td>\n                </tr>\n                <tr>\n                    <td>\n                        <p><span><span>Compute: Requests</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>100,000 / day</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>1 million / month included</span></span></p>\n                        <p><span><span>+ $0.15 / million</span></span></p>\n                    </td>\n                </tr>\n                <tr>\n                    <td>\n                        <p><span><span>Compute: Duration</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>13,000 GB-s / day</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>400,000 GB-s / month&nbsp; included&nbsp;</span></span></p>\n                        <p><span><span>+ $12.50 / million GB-s</span></span></p>\n                    </td>\n                </tr>\n                <tr>\n                    <td>\n                        <p><span><span>Storage: Rows read</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>5 million / day</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>25 billion / month included</span></span></p>\n                        <p><span><span>+ $0.001 / million&nbsp;</span></span></p>\n                    </td>\n                </tr>\n                <tr>\n                    <td>\n                        <p><span><span>Storage: Rows written</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>100,000 / day</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>50 million / month included</span></span></p>\n                        <p><span><span>+ $1.00 / million</span></span></p>\n                    </td>\n                </tr>\n                <tr>\n                    <td>\n                        <p><span><span>Storage: SQL stored data</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>5 GB (total)</span></span></p>\n                    </td>\n                    <td>\n                        <p><span><span>5 GB-month included</span></span></p>\n                        <p><span><span>+ $0.20 / GB-month</span></span></p>\n                    </td>\n                </tr>\n            </tbody>\n        </table>\n    </figure>\n</div>\n          <div>\n            <h3>Find us at agents.cloudflare.com</h3>\n            <a href=\"https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/#find-us-at-agents-cloudflare-com\">\n              \n            </a>\n          </div>\n        <p>We realize this is a lot of information to take in, but don’t worry. Whether you’re new to agents as a whole, or looking to learn more about how Cloudflare can help you build agents, today we launched a new site to help get you started —&nbsp;<a href=\"https://agents.cloudflare.com\"><u>agents.cloudflare.com</u></a>.&nbsp;</p><p>Let us know what you build!</p>",
            "url": "https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/",
            "title": "Piecing together the Agent puzzle: MCP, authentication & authorization, and Durable Objects free tier",
            "date_modified": "2025-04-07T13:12:21.944Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43610566\">Comments</a>",
            "url": "https://tigerbeetle.com/blog/2025-02-27-why-we-designed-tigerbeetles-docs-from-scratch/",
            "title": "We Designed TigerBeetle's Docs from Scratch",
            "date_modified": "2025-04-07T12:35:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43601356\">Comments</a>",
            "url": "https://blogsystem5.substack.com/p/bazel-next-generation",
            "title": "The next generation of Bazel builds",
            "date_modified": "2025-04-06T13:40:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43601026\">Comments</a>",
            "url": "https://thewisenerd.com/blog/ext4-readdir/",
            "title": "The order of files in your ext4 filesystem does not matter",
            "date_modified": "2025-04-06T12:39:40.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/wyjtwq/done_with_github_actions_supply_chain\">Comments</a></p>",
            "url": "https://huijzer.xyz/posts/jas/",
            "title": "Done with GitHub Actions Supply Chain Attacks",
            "date_modified": "2025-04-06T07:07:44.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ngijxb/curious_case_binfmt_for_x86_emulation_for\">Comments</a></p>",
            "url": "https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/",
            "title": "The curious case of binfmt for x86 emulation for ARM Docker",
            "date_modified": "2025-04-04T06:28:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43568036\">Comments</a>",
            "url": "https://github.com/awslabs/mcp",
            "title": "AWS MCP Servers",
            "date_modified": "2025-04-03T11:24:10.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/0jxkj4/introducing_nix_ninja_open_source_ninja\">Comments</a></p>",
            "url": "https://github.com/pdtpartners/nix-ninja",
            "title": "Introducing Nix Ninja – open-source Ninja-compatible build system for Nix",
            "date_modified": "2025-04-03T11:10:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43567701\">Comments</a>",
            "url": "https://github.com/pdtpartners/nix-ninja",
            "title": "Show HN: Nix Ninja – open-source Ninja-compatible build system for Nix",
            "date_modified": "2025-04-03T10:47:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43558517\">Comments</a>",
            "url": "https://docs.mermaidchart.com/blog/posts/mermaid-chart-vs-code-plugin-create-and-edit-mermaid-js-diagrams-in-visual-studio-code",
            "title": "Show HN: Mermaid Chart VS Code Plugin: Mermaid.js Diagrams in Visual Studio Code",
            "date_modified": "2025-04-02T16:33:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43558352\">Comments</a>",
            "url": "https://pears.com/news/introducing-bare-actually-run-javascript-everywhere/",
            "title": "Bare: Run JavaScript Everywhere",
            "date_modified": "2025-04-02T16:18:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43554214\">Comments</a>",
            "url": "https://github.com/aftertheflood/sparks",
            "title": "Sparks – A typeface for creating sparklines in text without code",
            "date_modified": "2025-04-02T06:44:17.000Z"
        },
        {
            "content_html": "<p>Apple spent years ignoring RCS, allowing iPhones to offer a degraded messaging experience with Android users. This made Android folks unwelcome in many a group chat, but Apple finally started rectifying this issue last year with the addition of RCS support in iOS. It has been a slow rollout, though, with Google's mobile service only now getting support.</p>\n<p>While Apple supports RCS messaging on iPhones now, it has not exactly been enthusiastic about it. Anyone using Google Fi on an iPhone was left in the lurch even after Apple changed course. The first RCS update rolled out in iOS 18 last fall, but it only supported postpaid plans on the big three carriers. Most other wireless subscribers had to wait, including those on Google Fi, as <a href=\"https://arstechnica.com/gadgets/2024/09/ios-18-brings-rcs-to-major-carrier-iphones-but-prepaid-plans-are-still-waiting/\">confirmed to Ars</a> last year. It was a suitably amusing outcome, considering Google is largely responsible for reviving the RCS standard and runs the Jibe back-end servers through which many iPhone RCS messages flow.</p>\n<p>Slowly but surely, Apple is making good on its promises to enable RCS as it gets the necessary data from carriers. The company <a href=\"https://arstechnica.com/gadgets/2025/03/apple-updates-all-its-operating-systems-brings-apple-intelligence-to-vision-pro/\">released iOS 18.4 this week</a>, and hiding amid the control center tweaks and priority notifications is support for RCS on Google Fi and other T-Mobile MVNOs. Some users spotted this feature in the recent beta releases, but the servers that handle RCS for Google's mobile service were not yet connectable. With the final release, <a href=\"https://support.google.com/fi/thread/335006509/rcs-messages-is-now-available-for-ios\">Google has confirmed</a> that RCS is ready at last.</p><p><a href=\"https://arstechnica.com/gadgets/2025/04/google-fi-users-on-iphone-finally-get-rcs-messaging/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/gadgets/2025/04/google-fi-users-on-iphone-finally-get-rcs-messaging/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/gadgets/2025/04/google-fi-users-on-iphone-finally-get-rcs-messaging/",
            "title": "Apple enables RCS messaging for Google Fi subscribers at last",
            "date_modified": "2025-04-01T20:22:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43547593\">Comments</a>",
            "url": "https://queue.acm.org/detail.cfm?id=3712057",
            "title": "Systems Correctness Practices at AWS: Leveraging Formal and Semi-Formal Methods",
            "date_modified": "2025-04-01T14:59:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43535558\">Comments</a>",
            "url": "https://www.aboutamazon.com/news/innovation-at-amazon/amazon-nova-website-sdk",
            "title": "Amazon introduces Nova Chat, entering the arena with ChatGPT, Claude, Grok",
            "date_modified": "2025-03-31T14:36:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43519459\">Comments</a>",
            "url": "https://www.dolthub.com/blog/2025-03-29-vibin/",
            "title": "Vibe Coding with Cursor",
            "date_modified": "2025-03-29T22:52:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43517375\">Comments</a>",
            "url": "https://pagedout.institute/?page=blog.php#entry-2025-03-29",
            "title": "Paged Out #6 is out",
            "date_modified": "2025-03-29T18:12:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43506574\">Comments</a>",
            "url": "https://depot.dev/blog/uncovering-disk-io-bottlenecks-github-actions-ci",
            "title": "Disk I/O bottlenecks in GitHub Actions",
            "date_modified": "2025-03-28T15:22:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43489698\">Comments</a>",
            "url": "https://world.hey.com/dhh/it-s-five-grand-a-day-to-miss-our-s3-exit-b8293563",
            "title": "It's five grand a day to miss our S3 exit",
            "date_modified": "2025-03-27T02:02:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43486997\">Comments</a>",
            "url": "https://edera.dev/stories/styrolite",
            "title": "Building a Linux Container Runtime from Scratch",
            "date_modified": "2025-03-26T20:35:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43486647\">Comments</a>",
            "url": "https://iximiuz.com/en/posts/iximiuz-labs-story/",
            "title": "Building a Firecracker-Powered Course Platform to Learn Docker and Kubernetes",
            "date_modified": "2025-03-26T20:08:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43485740\">Comments</a>",
            "url": "https://github.com/microsoft/playwright-mcp",
            "title": "Playwright Tools for MCP",
            "date_modified": "2025-03-26T19:07:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43473623\">Comments</a>",
            "url": "https://alexwlchan.net/2025/github-actions-audit/",
            "title": "Whose code am I running in GitHub Actions?",
            "date_modified": "2025-03-25T17:17:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43459240\">Comments</a>",
            "url": "https://www.npmjs.com/package/apidog-mcp-server",
            "title": "Show HN: We made an MCP Server so that Cursor can build anything from API Docs",
            "date_modified": "2025-03-24T10:21:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43454341\">Comments</a>",
            "url": "https://github.com/tsl0922/ttyd",
            "title": "Ttyd – Share your terminal over the web",
            "date_modified": "2025-03-23T17:26:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43439939\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=43439939",
            "title": "Ask HN: What is the simplest data orchestration tool you've worked with?",
            "date_modified": "2025-03-21T19:24:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43438206\">Comments</a>",
            "url": "https://www.bigscreenvr.com/",
            "title": "Bigscreen Beyond 2",
            "date_modified": "2025-03-21T17:03:27.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/patoqt/what_comes_after_github_actions\">Comments</a></p>",
            "url": "https://garnix.io/blog/what-comes-after-gha",
            "title": "What Comes After GitHub Actions?",
            "date_modified": "2025-03-21T16:26:34.000Z"
        },
        {
            "content_html": "<p>Connections made over cleartext HTTP ports risk exposing sensitive information because the data is transmitted unencrypted and can be intercepted by network intermediaries, such as ISPs, Wi-Fi hotspot providers, or malicious actors on the same network. It’s common for servers to either <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections\"><u>redirect</u></a> or return a <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403\"><u>403 (Forbidden)</u></a> response to close the HTTP connection and enforce the use of HTTPS by clients. However, by the time this occurs, it may be too late, because sensitive information, such as an API token, may have already been <a href=\"https://jviide.iki.fi/http-redirects\"><u>transmitted in cleartext</u></a> in the initial client request. This data is exposed before the server has a chance to redirect the client or reject the connection.</p><p>A better approach is to refuse the underlying cleartext connection by closing the <a href=\"https://developers.cloudflare.com/fundamentals/reference/network-ports/\"><u>network ports</u></a> used for plaintext HTTP, and that’s exactly what we’re going to do for our customers.</p><p><b>Today we’re announcing that we’re closing all of the </b><a href=\"https://developers.cloudflare.com/fundamentals/reference/network-ports/#network-ports-compatible-with-cloudflares-proxy:~:text=HTTP%20ports%20supported%20by%20Cloudflare\"><b><u>HTTP ports</u></b></a><b> on api.cloudflare.com.</b> We’re also making changes so that api.cloudflare.com can change IP addresses dynamically, in line with on-going efforts to <a href=\"https://blog.cloudflare.com/addressing-agility/\"><u>decouple names from IP addresses</u></a>, and reliably <a href=\"https://blog.cloudflare.com/topaz-policy-engine-design/\"><u>managing</u></a> addresses in our authoritative DNS. This will enhance the agility and flexibility of our API endpoint management. Customers relying on static IP addresses for our API endpoints will be notified in advance to prevent any potential availability issues.</p><p>In addition to taking this first step to secure Cloudflare API traffic, we’ll release the ability for customers to opt-in to safely disabling all HTTP port traffic for their websites on Cloudflare. We expect to make this free security feature available in the last quarter of 2025.</p><p>We have <a href=\"https://blog.cloudflare.com/introducing-universal-ssl/\"><u>consistently</u></a> <a href=\"https://blog.cloudflare.com/enforce-web-policy-with-hypertext-strict-transport-security-hsts/\"><u>advocated</u></a> for <a href=\"https://blog.cloudflare.com/post-quantum-for-all/\"><u>strong encryption standards</u></a> to safeguard users’ data and privacy online. As part of our ongoing commitment to enhancing Internet security, this blog post details our efforts to <i>enforce</i> HTTPS-only connections across our global network.&nbsp;</p>\n          <div>\n            <h3>Understanding the problem</h3>\n            <a href=\"https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/#understanding-the-problem\">\n              \n            </a>\n          </div>\n        <p>We already provide an “<a href=\"https://developers.cloudflare.com/ssl/edge-certificates/additional-options/always-use-https/\"><u>Always Use HTTPS</u></a>” setting that can be used to redirect all visitor traffic on our customers’ domains (and subdomains) from HTTP (plaintext) to HTTPS (encrypted). For instance, when a user clicks on an HTTP version of the URL on the site (http://www.example.com), we issue an HTTP 3XX redirection status code to immediately redirect the request to the corresponding HTTPS version (https://www.example.com) of the page. While this works well for most scenarios, there’s a subtle but important risk factor: What happens if the initial plaintext HTTP request (before the redirection) contains sensitive user information?</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2mXYZL0JRZOb8J6Tqm4mCj/1b24b76335ad9cf3f3b630ef31868f6c/1.png\">\n          </figure><p><sup><i>Initial plaintext HTTP request is exposed to the network before the server can redirect to the secure HTTPS connection.</i></sup></p><p>Third parties or intermediaries on shared networks could intercept sensitive data from the first plaintext HTTP request, or even carry out a <a href=\"https://blog.cloudflare.com/monsters-in-the-middleboxes/\"><u>Monster-in-the-Middle (MITM)</u></a> attack by impersonating the web server.</p><p>One may ask if <a href=\"https://developers.cloudflare.com/ssl/edge-certificates/additional-options/http-strict-transport-security/\"><u>HTTP Strict Transport Security (HSTS)</u></a> would partially alleviate this concern by ensuring that, after the first request, visitors can only access the website over HTTPS without needing a redirect. While this does reduce the window of opportunity for an adversary, the first request still remains exposed. Additionally, HSTS is not applicable by default for most non-user-facing use cases, such as API traffic from stateless clients. Many API clients don’t retain browser-like state or remember HSTS headers they've encountered. It is quite <a href=\"https://jviide.iki.fi/http-redirects\"><u>common practice</u></a> for API calls to be redirected from HTTP to HTTPS, and hence have their initial request exposed to the network.</p><p>Therefore, in line with our <a href=\"https://blog.cloudflare.com/dogfooding-from-home/\"><u>culture of dogfooding</u></a>, we evaluated the accessibility of the Cloudflare API (<a href=\"https://api.cloudflare.com\"><u>api.cloudflare.com</u></a>) over <a href=\"https://developers.cloudflare.com/fundamentals/reference/network-ports/#:~:text=ports%20listed%20below.-,HTTP,-ports%20supported%20by\"><u>HTTP ports (80, and others)</u></a>. In that regard, imagine a client making an initial request to our API endpoint that includes their <i>secret API key</i>. While we outright reject all plaintext connections with a 403 Forbidden response instead of redirecting for API traffic — clearly indicating that “<i>Cloudflare API is only accessible over TLS”</i> — this rejection still happens at the <a href=\"https://www.cloudflare.com/learning/ddos/what-is-layer-7/\">application layer</a>. By that point, the API key may have already been exposed over the network before we can even reject the request. We do have a notification mechanism in place to alert customers and rotate their API keys accordingly, but a stronger approach would be to eliminate the exposure entirely. We have an opportunity to improve!</p>\n          <div>\n            <h3>A better approach to API security</h3>\n            <a href=\"https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/#a-better-approach-to-api-security\">\n              \n            </a>\n          </div>\n        <p>Any API key or token exposed in plaintext on the public Internet should be considered compromised. We can either address exposure after it occurs or prevent it entirely. The reactive approach involves continuously tracking and revoking compromised credentials, requiring active management to rotate each one. For example, when a plaintext HTTP request is made to our API endpoints, we detect exposed tokens by scanning for 'Authorization' header values.</p><p>In contrast, a preventive approach is stronger and more effective, stopping exposure before it happens. Instead of relying on the API service application to react after receiving potentially sensitive cleartext data, we can preemptively refuse the underlying connection at the <a href=\"https://www.cloudflare.com/learning/ddos/glossary/open-systems-interconnection-model-osi/\"><u>transport layer</u></a>, before any HTTP or application-layer data is exchanged. The <i>preventative </i>approach can be achieved by closing all plaintext HTTP ports for API traffic on our global network. The added benefit is that this is operationally much simpler: by eliminating cleartext traffic, there's no need for key rotation.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1PYo3GMZjQ4LbfUHXNOVpj/2341da1d926e077624563358bd5034ef/2.png\">\n          </figure><p><sup><i>The transport layer carries the application layer data on top.</i></sup></p><p>To explain why this works: an application-layer request requires an underlying transport connection, like TCP or QUIC, to be established first. The combination of a port number and an IP address serves as a transport layer identifier for creating the underlying transport channel. Ports direct network traffic to the correct application-layer process — for example, port 80 is designated for plaintext HTTP, while port 443 is used for encrypted HTTPS. By disabling the HTTP cleartext server-side port, we prevent that transport channel from being established during the initial \"<a href=\"https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/\"><u>handshake</u></a>\" phase of the connection — before any application data, such as a secret API key, leaves the client’s machine.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/13fKw8cHkHlsLOzlXJYenr/c9156f67ae99cfdc74dc5917ebc1e5bb/3.png\">\n          </figure><p><sup><i>Both TCP and QUIC transport layer handshakes are a pre-requisite for HTTPS application data exchange on the web.</i></sup></p><p>Therefore, closing the HTTP interface entirely for API traffic gives a strong and visible <b>fast-failure</b> signal to developers that might be mistakenly accessing <code>http://… </code>instead of <code>https://…</code> with their secret API keys in the first request — a simple one-letter omission, but one with serious implications.</p><p>In theory, this is a simple change, but at Cloudflare’s global scale, implementing it required careful planning and execution. We’d like to share the steps we took to make this transition.</p>\n          <div>\n            <h3>Understanding the scope</h3>\n            <a href=\"https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/#understanding-the-scope\">\n              \n            </a>\n          </div>\n        <p>In an ideal scenario, we could simply close all cleartext HTTP ports on our network. However, two key challenges prevent this. First, as shown in the <a href=\"https://radar.cloudflare.com/adoption-and-usage#http-vs-https\"><u>Cloudflare Radar</u></a> figure below, about 2-3% of requests from “likely human” clients to our global network are over plaintext HTTP. While modern browsers prominently warn users about insecure HTTP connections and <a href=\"https://support.mozilla.org/en-US/kb/https-only-prefs\"><u>offer features to silently upgrade to HTTPS</u></a>, this protection doesn't extend to the broader ecosystem of connected devices. IoT devices with limited processing power, automated API clients, or legacy software stacks often lack such safeguards entirely. In fact, when filtering on plaintext HTTP traffic that is “likely automated”, the share <a href=\"https://radar.cloudflare.com/explorer?dataSet=http&amp;groupBy=http_protocol&amp;filters=botClass%253DLikely_Automated\"><u>rises to over 16%</u></a>! We continue to see a wide variety of legacy clients accessing resources over plaintext connections. This trend is not confined to specific networks, but is observable globally.</p><p>Closing HTTP ports, like port 80, across our entire IP address space would block such clients entirely, causing a major disruption in services. While we plan to cautiously start by implementing the change on Cloudflare's API IP addresses, it’s not enough. Therefore, our goal is to ensure all of our customers’ API traffic benefits from this change as well.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1OfUjwkP9iMdjymjtJX7tL/4cd278faf71f610c43239cc41d8f6fba/4.png\">\n          </figure><p><sup><i>Breakdown of HTTP and HTTPS for ‘human’ connections</i></sup></p><p>The second challenge relates to limitations posed by the longstanding <a href=\"https://en.wikipedia.org/wiki/Berkeley_sockets\"><u>BSD Sockets API</u></a> at the server-side, which we have addressed using <a href=\"https://blog.cloudflare.com/tubular-fixing-the-socket-api-with-ebpf/\"><u>Tubular</u></a>, a tool that inspects every connection terminated by a server and decides which application should receive it. Operators historically have faced a challenging dilemma: either listen to the same ports across many IP addresses using a single socket (scalable but inflexible), or maintain individual sockets for each IP address (flexible but unscalable). Luckily, Tubular has allowed us to <a href=\"https://blog.cloudflare.com/its-crowded-in-here/\"><u>resolve this using 'bindings'</u></a>, which decouples sockets from specific IP:port pairs. This creates efficient pathways for managing endpoints throughout our systems at scale, enabling us to handle both HTTP and HTTPS traffic intelligently without the traditional limitations of socket architecture.</p><p>Step 0, then, is about provisioning both IPv4 and IPv6 address space on our network that by default has all HTTP ports closed. Tubular enables us to configure and manage these IP addresses differently than others for our endpoints. Additionally, <a href=\"https://blog.cloudflare.com/addressing-agility/\"><u>Addressing Agility</u></a> and <a href=\"https://blog.cloudflare.com/topaz-policy-engine-design/\"><u>Topaz</u></a> enable us to assign these addresses dynamically, and safely, for opted-in domains.</p>\n          <div>\n            <h3>Moving from strategy to execution</h3>\n            <a href=\"https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/#moving-from-strategy-to-execution\">\n              \n            </a>\n          </div>\n        <p>In the past, our legacy stack would have made this transition challenging, but today’s Cloudflare possesses the appropriate tools to deliver a scalable solution, rather than addressing it on a domain-by-domain basis.</p><p>Using Tubular, we were able to bind our new set of anycast IP prefixes to our TLS-terminating proxies across the globe. To ensure that no plaintext HTTP traffic is served on these IP addresses, we extended our global <a href=\"https://en.wikipedia.org/wiki/Iptables\"><u>iptables</u></a> firewall configuration to reject any inbound packets on HTTP ports.</p>\n            <pre><code>iptables -A INPUT -p tcp -d &lt;IP_ADDRESS_BLOCK&gt; --dport &lt;HTTP_PORT&gt; -j REJECT \n--reject-with tcp-reset\n\niptables -A INPUT -p udp -d &lt;IP_ADDRESS_BLOCK&gt; --dport &lt;HTTP_PORT&gt; -j REJECT \n--reject-with icmp-port-unreachable</code></pre>\n            <p>As a result, any connections to these IP addresses on HTTP ports are filtered and rejected at the transport layer, eliminating the need for state management at the application layer by our web proxies.</p><p>The next logical step is to update the <a href=\"https://www.cloudflare.com/learning/dns/what-is-dns/\"><u>DNS assignments</u></a> so that API traffic is routed over the <i>correct</i> IP addresses. In our case, we encoded a new DNS policy for API traffic for the HTTPS-only interface as a declarative <a href=\"https://blog.cloudflare.com/topaz-policy-engine-design/\"><u>Topaz program</u></a> in our authoritative DNS server:</p>\n            <pre><code>- name: https_only\n exclusive: true \n config: |\n    (config\n      ([traffic_class \"API\"]\n       [ipv4 (ipv4_address “192.0.2.1”)] # Example IPv4 address\n       [ipv6 (ipv6_address “2001:DB8::1:1”)] # Example IPv6 address\n       [t (ttl 300]))\n  match: |\n    (= query_domain_class traffic_class)\n  response: |\n    (response (list ipv4) (list ipv6) t)</code></pre>\n            <p>The above policy encodes that for any DNS query targeting the ‘API traffic’ class, we return the respective HTTPS-only interface IP addresses. Topaz’s safety guarantees ensure <i>exclusivity</i>, preventing other DNS policies from inadvertently matching the same queries and misrouting plaintext HTTP expected domains to HTTPS-only IPs\n\napi.cloudflare.com is the first domain to be added to our HTTPS-only API traffic class, with other applicable endpoints to follow.</p>\n          <div>\n            <h3>Opting-in your API endpoints</h3>\n            <a href=\"https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/#opting-in-your-api-endpoints\">\n              \n            </a>\n          </div>\n        <p>As we said above, we've started with api.cloudflare.com and our internal API endpoints to thoroughly monitor any side effects on our own systems before extending this feature to customer domains. We have deployed these changes gradually across all data centers, leveraging Topaz’s flexibility to target subsets of traffic, minimizing disruptions, and ensuring a smooth transition.</p><p>To monitor unencrypted connections for your domains, before blocking access using the feature, you can review the relevant analytics on the Cloudflare dashboard. Log in, select your account and domain, and navigate to the \"<a href=\"https://developers.cloudflare.com/analytics/types-of-analytics/#account-analytics-beta\"><u>Analytics &amp; Logs</u></a>\" section. There, under the \"<i>Traffic Served Over SSL</i>\" subsection, you will find a breakdown of encrypted and unencrypted traffic for your site. That data can help provide a baseline for assessing the volume of plaintext HTTP connections for your site that will be blocked when you opt in. After opting in, you would expect no traffic for your site will be served over plaintext HTTP, and therefore that number should go down to zero.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4YjvOU3XQqj1Y2Kfv2jIL3/97178a99d17f8938bc3ec53704bbc4b8/5.png\">\n          </figure><p><i>Snapshot of ‘Traffic Served Over SSL’ section on Cloudflare dashboard</i></p><p>Towards the last quarter of 2025, we will provide customers the ability to opt in their domains using the dashboard or API (similar to enabling the Always Use HTTPS feature). Stay tuned!</p>\n          <div>\n            <h3>Wrapping up</h3>\n            <a href=\"https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/#wrapping-up\">\n              \n            </a>\n          </div>\n        <p>Starting today, any unencrypted connection to api.cloudflare.com will be completely rejected. Developers should <b>not</b> expect a 403 Forbidden response any longer for HTTP connections, as we will prevent the underlying connection to be established by closing the HTTP interface entirely. Only secure HTTPS connections will be allowed to be established.</p><p>We are also making updates to transition api.cloudflare.com away from its static IP addresses in the future. As part of that change, we will be discontinuing support for <a href=\"https://developers.cloudflare.com/ssl/reference/browser-compatibility/#non-sni-support\"><u>non-SNI</u></a> legacy clients for Cloudflare API specifically — currently, an average of just 0.55% of TLS connections to the Cloudflare API do not include an <a href=\"https://www.cloudflare.com/learning/ssl/what-is-sni/\">SNI</a> value. These non-SNI connections are initiated by a small number of accounts. We are committed to coordinating this transition and will work closely with the affected customers before implementing the change. This initiative aligns with our goal of enhancing the agility and reliability of our API endpoints.</p><p>Beyond the Cloudflare API use case, we're also exploring other areas where it's safe to close plaintext traffic ports. While the long tail of unencrypted traffic may persist for a while, it shouldn’t be forced on every site.\n\nIn the meantime, a small step like this can allow us to have a big impact in helping make a better Internet, and we are working hard to reliably bring this feature to your domains. We believe security should be free for all!</p>",
            "url": "https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/",
            "title": "HTTPS-only for Cloudflare APIs: shutting the door on cleartext traffic",
            "date_modified": "2025-03-20T13:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43419701\">Comments</a>",
            "url": "https://www.feldera.com/blog/the-pain-that-is-github-actions",
            "title": "The Pain That Is GitHub Actions",
            "date_modified": "2025-03-20T03:37:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43401421\">Comments</a>",
            "url": "https://duckdb.org/2025/03/14/preview-amazon-s3-tables.html",
            "title": "Preview: Amazon S3 Tables and Lakehouse in DuckDB",
            "date_modified": "2025-03-18T16:36:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43399811\">Comments</a>",
            "url": "https://planetscale.com/blog/the-real-fail-rate-of-ebs",
            "title": "The Failure Rate of EBS",
            "date_modified": "2025-03-18T14:24:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43399811\">Comments</a>",
            "url": "https://planetscale.com/blog/the-real-fail-rate-of-ebs",
            "title": "The real failure rate of EBS",
            "date_modified": "2025-03-18T14:24:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43396450\">Comments</a>",
            "url": "https://www.ycombinator.com/companies/depot/jobs/307RqGp-founding-developer-marketer",
            "title": "Depot (YC W23) is hiring a founding developer marketer (EU/US remote)",
            "date_modified": "2025-03-18T07:00:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43388133\">Comments</a>",
            "url": "https://twitter.com/parkerconrad/status/1901615179718406276",
            "title": "Rippling sues Deel over spying",
            "date_modified": "2025-03-17T13:03:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43378701\">Comments</a>",
            "url": "https://github.com/macos-fuse-t/scorpi",
            "title": "Scorpi – A Modern Hypervisor (For macOS)",
            "date_modified": "2025-03-16T13:01:31.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/7z4gp2/mojo_may_be_biggest_programming_language\">Comments</a></p>",
            "url": "https://www.fast.ai/posts/2023-05-03-mojo-launch.html",
            "title": "Mojo may be the biggest programming language advance in decades (2023)",
            "date_modified": "2025-03-16T01:14:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43358980\">Comments</a>",
            "url": "https://info.varnish-software.com/blog/tinykvm-the-fastest-sandbox",
            "title": "TinyKVM: Fast sandbox that runs on top of Varnish",
            "date_modified": "2025-03-14T02:12:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43337703\">Comments</a>",
            "url": "https://github.com/ZachGoldberg/Startup-CTO-Handbook/blob/main/StartupCTOHandbook.md",
            "title": "The Startup CTO's Handbook",
            "date_modified": "2025-03-11T22:18:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43332830\">Comments</a>",
            "url": "https://devblogs.microsoft.com/typescript/typescript-native-port/",
            "title": "A 10x Faster TypeScript",
            "date_modified": "2025-03-11T14:32:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43307159\">Comments</a>",
            "url": "https://github.com/johnbean393/Sidekick",
            "title": "Sidekick: Local-first native macOS LLM app",
            "date_modified": "2025-03-09T08:08:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43305919\">Comments</a>",
            "url": "https://github.com/PostHog/posthog/blob/master/.cursorrules",
            "title": "Posthog/.cursorrules",
            "date_modified": "2025-03-09T03:30:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43305375\">Comments</a>",
            "url": "https://www.telegraph.co.uk/us/politics/2025/03/08/us-to-cease-all-future-military-exercises-in-europe-reports/",
            "title": "US 'to cease all future military exercises in Europe'",
            "date_modified": "2025-03-09T01:45:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43304420\">Comments</a>",
            "url": "https://www.msn.com/en-us/money/companies/military-tech-startups-vie-for-billions-as-hegseth-shakes-up-pentagon-spending/ar-AA1zYgzZ",
            "title": "SpaceX teams up with Thiel's Palantir, Anduril on American Golden Dome",
            "date_modified": "2025-03-08T23:06:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43304253\">Comments</a>",
            "url": "https://havenweb.org/2022/11/02/facebook-lie.html",
            "title": "The Lie That Facebook Sold You",
            "date_modified": "2025-03-08T22:44:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43302495\">Comments</a>",
            "url": "https://github.com/bypirob/airo",
            "title": "Deploy from local to production (self-hosted)",
            "date_modified": "2025-03-08T18:54:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43294566\">Comments</a>",
            "url": "https://pola.rs/posts/polars-cloud-what-we-are-building/",
            "title": "Polars Cloud: The Distributed Cloud Architecture to Run Polars Anywhere",
            "date_modified": "2025-03-07T20:57:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43289885\">Comments</a>",
            "url": "https://rxdb.info/articles/local-first-future.html",
            "title": "Why Local-First Software Is the Future and Its Limitations",
            "date_modified": "2025-03-07T13:12:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43283669\">Comments</a>",
            "url": "https://github.com/warewulf/warewulf",
            "title": "Warewulf is a stateless and diskless container OS provisioning system",
            "date_modified": "2025-03-06T18:45:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43276038\">Comments</a>",
            "url": "https://simonwillison.net/2025/Mar/6/aider-using-uv-as-an-installer/",
            "title": "Aider: Using Uv as an Installer",
            "date_modified": "2025-03-06T03:18:06.000Z"
        },
        {
            "content_html": "<p>One thing&nbsp;<a href=\"https://store.steampowered.com/app/2536960/Shadowveil_Legend_of_The_Five_Rings/\"><em>Shadowveil: Legend of the Five Rings</em></a> does well is invoke terror. Not just the terror of an overwhelming mass of dark energy encroaching on your fortress, which is what the story suggests; more so, the terror of hoping your little computer-controlled fighters will do the smart thing and then being forced to watch, helpless, as they are consumed by algorithmic choices, bad luck, your strategies, or some combination of all three.</p>\n<p><em>Shadowveil,</em> the first video game based on the <a href=\"https://en.wikipedia.org/wiki/Legend_of_the_Five_Rings\">more than 30-year-old <em>Legend of the Five Rings </em>fantasy franchise</a>, is a roguelite auto-battler. You pick your Crab Clan hero (berserker hammer-wielder or tactical support type), train up some soldiers, and assign all of them abilities, items, and buffs you earn as you go. When battle starts, you choose which hex to start your fighters on, double-check your load-outs, then click to start and watch what happens. You win and march on, or you lose and regroup at base camp, buying some upgrades with your last run's goods.</p>\n<div><div src=\"https://www.youtube.com/embed/D2CL3dEpJgk?start=0&amp;wmode=transparent\"></div><div>\n    <div></div>\n    <div>\n      <em>Shadowveil: Legend of the Five Rings</em> launch trailer.\n\n          </div>\n  </div>\n</div>\n<p>In my impressions after roughly seven hours of playing, <em>Shadowveil </em>could do more to soften its learning curve, but it presents a mostly satisfying mix of overwhelming odds and achievement. What's irksome now could get patched, and what's already there is intriguing, especially for the price.</p><p><a href=\"https://arstechnica.com/gaming/2025/03/shadowveil-is-a-stylish-tough-single-player-auto-battler/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/gaming/2025/03/shadowveil-is-a-stylish-tough-single-player-auto-battler/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/gaming/2025/03/shadowveil-is-a-stylish-tough-single-player-auto-battler/",
            "title": "Shadowveil is a stylish, tough single-player auto-battler",
            "date_modified": "2025-03-05T12:00:24.000Z"
        },
        {
            "content_html": "<div><div src=\"https://www.youtube.com/embed/QIw6ITiwgBU?start=0&amp;wmode=transparent\"></div><div>\n    <div></div>\n    <div>\n      Millie Bobby Brown and Chris Pratt star in the Netflix original film <em>The Electric State</em>.\n\n          </div>\n  </div>\n</div>\n<p>Anthony and Joe Russo have their hands full these days with the Marvel films <em>Avengers: Doomsday</em> and <em>Avengers: Secret War</em>, slated for 2026 and 2027 releases, respectively. But we'll get a chance to see another, smaller film from the directors this month on Netflix: <em>The Electric State</em>, adapted from the graphic novel by Swedish artist/designer <a href=\"https://en.wikipedia.org/wiki/Simon_St%C3%A5lenhag\">Simon&nbsp;Stålenhag</a>.</p>\n<p>Stålenhag's stunningly surreal neofuturistic art—featured in his narrative art books, 2014's <em>Tales from the Loop</em> and 2016's <em>Things From the Flood</em>—inspired the 2020 eight-episode series <a href=\"https://arstechnica.com/gaming/2020/04/tech-takes-a-back-seat-to-interconnected-human-stories-in-tales-from-the-loop/\"><em>Tales From the Loop</em></a>, in which residents of a rural town find themselves grappling with strange occurrences thanks to the presence of an underground particle accelerator. That adaptation captured the mood and tone of the art that inspired it and received Emmy nominations for cinematography and special visual effects.</p>\n<p><em>The Electric State</em> was Stålenhag's third such book, published in 2018 and set in a similar dystopian, ravaged landscape. Paragraphs of text, accompanied by larger artworks, tell the story of a teen girl named Michelle who must travel across the country with her robot companion to find her long-lost brother, while being pursued by a federal agent. The Russo brothers acquired the rights early on and initially intended to make the film with Universal, but when the studio decided it would not be giving the film a theatrical release, Netflix bought the distribution rights.</p><p><a href=\"https://arstechnica.com/culture/2025/03/humans-and-bots-take-on-the-system-in-the-electric-state-trailer/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/culture/2025/03/humans-and-bots-take-on-the-system-in-the-electric-state-trailer/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/culture/2025/03/humans-and-bots-take-on-the-system-in-the-electric-state-trailer/",
            "title": "Netflix drops trailer for the Russo brothers’ The Electric State",
            "date_modified": "2025-03-03T19:21:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43230510\">Comments</a>",
            "url": "https://xeiaso.net/blog/2025/yoke-k8s/",
            "title": "Yoke: Infrastructure as code, but actually",
            "date_modified": "2025-03-02T13:56:01.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/xjnx95/distributed_systems_programming_has\">Comments</a></p>",
            "url": "https://www.shadaj.me/writing/distributed-programming-stalled",
            "title": "Distributed Systems Programming Has Stalled",
            "date_modified": "2025-02-28T04:57:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43160612\">Comments</a>",
            "url": "https://app.laravel.cloud/",
            "title": "Laravel Cloud",
            "date_modified": "2025-02-24T15:26:54.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/vtufn0/scrap_your_orm_replace_your_orm_with\">Comments</a></p>",
            "url": "https://youtu.be/SKXEppEZp9M",
            "title": "Scrap Your ORM—Replace Your ORM With Relational Algebra",
            "date_modified": "2025-02-22T18:40:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43140754\">Comments</a>",
            "url": "https://blog.trailofbits.com/2025/02/21/the-1.5b-bybit-hack-the-era-of-operational-security-failures-has-arrived/",
            "title": "The $1.5B Bybit Hack: The Era of Operational Security Failures Has Arrived",
            "date_modified": "2025-02-22T17:05:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43129301\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=43129301",
            "title": "Launch HN: Massdriver (YC W22) – Self-serve cloud infra without the red tape",
            "date_modified": "2025-02-21T16:19:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43125176\">Comments</a>",
            "url": "https://benjamintoll.com/2022/02/04/on-running-systemd-nspawn-containers/",
            "title": "Running Systemd-Nspawn Containers",
            "date_modified": "2025-02-21T08:00:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43125089\">Comments</a>",
            "url": "https://docs.docker.com/docker-hub/usage/",
            "title": "Docker limits unauthenticated pulls to 10/HR/IP from Docker Hub, from March 1",
            "date_modified": "2025-02-21T07:42:45.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/s3re6r/docker_limits_unauthenticated_pulls_10\">Comments</a></p>",
            "url": "https://docs.docker.com/docker-hub/usage/",
            "title": "Docker limits unauthenticated pulls to 10/hr/ip from Docker Hub, from March 1",
            "date_modified": "2025-02-21T07:40:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43117020\">Comments</a>",
            "url": "https://obsidian.md/blog/free-for-work/",
            "title": "Obsidian is now free for work",
            "date_modified": "2025-02-20T16:50:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43107506\">Comments</a>",
            "url": "https://yaak.app/blog/2025.1.1",
            "title": "Yaak 2.0 – Git, WebSockets, OAuth, and More",
            "date_modified": "2025-02-19T20:52:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43103073\">Comments</a>",
            "url": "https://github.com/mastra-ai/mastra",
            "title": "Show HN: Mastra – Open-source TypeScript agent framework",
            "date_modified": "2025-02-19T15:25:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43103073\">Comments</a>",
            "url": "https://github.com/mastra-ai/mastra",
            "title": "Show HN: Mastra – Open-source JS agent framework, by the creators of Gatsby",
            "date_modified": "2025-02-19T15:25:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43101430\">Comments</a>",
            "url": "https://www.ubicloud.com/blog/debugging-hetzner-uncovering-failures-with-powerstat-sensors-and-dmidecode",
            "title": "Debugging Hetzner: Uncovering failures with powerstat, sensors, and dmidecode",
            "date_modified": "2025-02-19T12:40:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43053844\">Comments</a>",
            "url": "https://fly.io/blog/wrong-about-gpu/",
            "title": "We were wrong about GPUs",
            "date_modified": "2025-02-14T22:36:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43045713\">Comments</a>",
            "url": "https://docs.google.com/presentation/d/e/2PACX-1vSmIbSwh1_DXKEMU5YKgYpt5_b4yfOfpfEOKS5_cvtLdiHsX6zt-gNeisamRuCtDtCb2SbTafTI8V47/pub?start=false&loop=false&delayms=3000",
            "title": "On Bloat",
            "date_modified": "2025-02-14T07:12:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43039347\">Comments</a>",
            "url": "https://monzo.com/blog/tolerating-full-cloud-outages-with-monzo-stand-in",
            "title": "Tolerating full cloud outages with Monzo Stand-in",
            "date_modified": "2025-02-13T18:23:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43022398\">Comments</a>",
            "url": "https://elevenreader.io",
            "title": "ElevenReader by ElevenLabs",
            "date_modified": "2025-02-12T06:10:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43013248\">Comments</a>",
            "url": "https://www.runportcullis.co/blog/bulk-data-clickhouse/",
            "title": "Bulk inserts on ClickHouse: How to avoid overstuffing your instance",
            "date_modified": "2025-02-11T14:43:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43008920\">Comments</a>",
            "url": "https://cacm.acm.org/research/metas-hyperscale-infrastructure-overview-and-insights/",
            "title": "Meta's Hyperscale Infrastructure: Overview and Insights",
            "date_modified": "2025-02-11T04:19:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=43007336\">Comments</a>",
            "url": "https://github.com/VKCOM/nocc",
            "title": "Nocc – A Distributed C++ Compiler",
            "date_modified": "2025-02-11T00:35:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42980150\">Comments</a>",
            "url": "https://www.docker.com/blog/ga-launch-docker-bake/",
            "title": "Docker Bake Is Now Generally Available",
            "date_modified": "2025-02-08T03:39:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42980150\">Comments</a>",
            "url": "https://www.docker.com/blog/ga-launch-docker-bake/",
            "title": "Docker Bake is now generally available",
            "date_modified": "2025-02-08T03:39:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42960291\">Comments</a>",
            "url": "https://www.cloudflarestatus.com",
            "title": "Cloudflare R2 Global Outage",
            "date_modified": "2025-02-06T08:27:37.000Z"
        },
        {
            "content_html": "<p>A mirror proxy Google runs on behalf of developers of the Go programming language pushed a backdoored package for more than three years until Monday, after researchers who spotted the malicious code petitioned for it to be taken down twice.</p>\n<p>The service, known as the <a href=\"https://proxy.golang.org/\">Go Module Mirror</a>, caches open source packages available on GitHub and elsewhere so that downloads are faster and to ensure they are compatible with the rest of the Go ecosystem. By default, when someone uses command-line tools built into Go to download or install packages, requests are routed through the service. A description on the site says the proxy is provided by the Go team and “run by Google.”</p>\n<h2>Caching in</h2>\n<p>Since November 2021, the Go Module Mirror has been hosting a backdoored version of a widely used module, security firm Socket <a href=\"https://socket.dev/blog/malicious-package-exploits-go-module-proxy-caching-for-persistence\">said Monday</a>. The file uses “typosquatting,” a technique that gives malicious files names similar to widely used legitimate ones and plants them in popular repositories. In the event someone makes a typo or even a minor variation from the correct name when fetching a file with the command line, they land on the malicious file instead of the one they wanted. (A similar typosquatting scheme is common with domain names, too.)</p><p><a href=\"https://arstechnica.com/security/2025/02/backdoored-package-in-go-mirror-site-went-unnoticed-for-3-years/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/security/2025/02/backdoored-package-in-go-mirror-site-went-unnoticed-for-3-years/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/security/2025/02/backdoored-package-in-go-mirror-site-went-unnoticed-for-3-years/",
            "title": "Go Module Mirror served backdoor to devs for 3+ years",
            "date_modified": "2025-02-05T12:25:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42945191\">Comments</a>",
            "url": "https://github.com/maurobaraldi/terraform-workspaces-aws-multi-account",
            "title": "Using Terraform Workspace for AWS multi account archtetctures",
            "date_modified": "2025-02-05T07:36:01.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/7b3udw/deploying_openvms_x86_on_amazon_ec2\">Comments</a></p>",
            "url": "https://aws.amazon.com/blogs/migration-and-modernization/deploying-openvms-x86-on-amazon-ec2/",
            "title": "Deploying OpenVMS x86 on Amazon EC2",
            "date_modified": "2025-02-05T05:48:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42916034\">Comments</a>",
            "url": "https://github.com/lsd-so/lsd-mcp",
            "title": "Show HN: Gave Claude LSD SQL",
            "date_modified": "2025-02-03T08:11:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42899703\">Comments</a>",
            "url": "https://www.swift.org/blog/the-next-chapter-in-swift-build-technologies/",
            "title": "Apple is open sourcing Swift Build",
            "date_modified": "2025-02-01T16:44:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42895049\">Comments</a>",
            "url": "https://www.admin-magazine.com/HPC/Articles/Linux-Writecache",
            "title": "Setting up a Linux writecache as a RAM disk",
            "date_modified": "2025-02-01T02:25:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42877995\">Comments</a>",
            "url": "https://www.githubstatus.com",
            "title": "GitHub Is Down",
            "date_modified": "2025-01-30T14:29:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42877886\">Comments</a>",
            "url": "https://www.dbos.dev/blog/what-is-lightweight-durable-execution",
            "title": "Why Durable Execution Should Be Lightweight",
            "date_modified": "2025-01-30T14:19:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42867657\">Comments</a>",
            "url": "https://www.instantdb.com/essays/pg_upgrade",
            "title": "A Major Postgres Upgrade with Zero Downtime",
            "date_modified": "2025-01-29T16:57:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42867050\">Comments</a>",
            "url": "https://github.com/lastmile-ai/mcp-agent",
            "title": "Show HN: Mcp-Agent – Build effective agents with Model Context Protocol",
            "date_modified": "2025-01-29T16:26:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42845323\">Comments</a>",
            "url": "https://www.jvt.me/posts/2025/01/27/go-tools-124/",
            "title": "Go 1.24's go tool is one of the best additions to the ecosystem in years",
            "date_modified": "2025-01-27T20:33:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42814373\">Comments</a>",
            "url": "https://www.ubicloud.com/blog/cloud-virtualization-red-hat-aws-firecracker-and-ubicloud-internals",
            "title": "Cloud Virtualization: Red Hat, AWS Firecracker, and Ubicloud internals",
            "date_modified": "2025-01-24T15:59:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42811146\">Comments</a>",
            "url": "https://blog.railway.com/p/slack-overflow",
            "title": "How we scaled Slack to support 1000s of developers",
            "date_modified": "2025-01-24T07:10:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42801370\">Comments</a>",
            "url": "https://bun.sh/blog/bun-v1.2",
            "title": "Bun 1.2 Is Released",
            "date_modified": "2025-01-23T06:50:28.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/im1wnu/tailwind_css_v4_0\">Comments</a></p>",
            "url": "https://tailwindcss.com/blog/tailwindcss-v4",
            "title": "Tailwind CSS v4.0",
            "date_modified": "2025-01-23T03:24:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42799540\">Comments</a>",
            "url": "https://www.trae.ai/home",
            "title": "Trae: An AI-powered IDE by ByteDance",
            "date_modified": "2025-01-23T01:21:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42799136\">Comments</a>",
            "url": "https://tailwindcss.com/blog/tailwindcss-v4",
            "title": "Tailwind CSS v4.0",
            "date_modified": "2025-01-23T00:33:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42764762\">Comments</a>",
            "url": "https://ninkovic.dev/blog/2025/think-twice-before-using-github-actions",
            "title": "I'll think twice before using GitHub Actions again",
            "date_modified": "2025-01-20T03:41:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42755251\">Comments</a>",
            "url": "https://www.economist.com/finance-and-economics/2025/01/16/the-traitors-a-reality-tv-show-offers-a-useful-economics-lesson",
            "title": "“The Traitors”, a reality TV show, offers a useful economics lesson",
            "date_modified": "2025-01-19T08:56:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/s2rr1t/brood_war_korean_translations\">Comments</a></p>",
            "url": "https://blog.sourcedive.net/brood-war-korean-translations/",
            "title": "Brood War Korean Translations",
            "date_modified": "2025-01-17T17:13:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42738479\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=42738479",
            "title": "Ask HN: Google forcibly enabled Gemini in our Corp Org. How to disable?",
            "date_modified": "2025-01-17T15:25:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42724621\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=itpcsQQvgAQ",
            "title": "Nintendo announces the Switch 2 [video]",
            "date_modified": "2025-01-16T13:08:14.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/viyhqi/nix_death_by_thousand_cuts\">Comments</a></p>",
            "url": "https://www.dgt.is/blog/2025-01-10-nix-death-by-a-thousand-cuts/",
            "title": "Nix - Death by a thousand cuts",
            "date_modified": "2025-01-15T00:52:30.000Z"
        },
        {
            "content_html": "<p>Microsoft CEO Satya Nadella has <a href=\"https://blogs.microsoft.com/blog/2025/01/13/introducing-core-ai-platform-and-tools/\">announced</a> a dramatic restructuring of the company's engineering organization, which is pivoting the company's focus to developing the tools that will underpin agentic AI.</p>\n<p>Dubbed \"CoreAI - Platform and Tools,\" the new division rolls the existing AI platform team and the previous developer division (responsible for everything from .NET to Visual Studio) along with some other teams into one big group.</p>\n<p>As for what this group will be doing specifically, it's basically everything that's mission-critical to Microsoft in 2025, as Nadella tells it:</p><p><a href=\"https://arstechnica.com/gadgets/2025/01/amid-a-flurry-of-hype-microsoft-reorganizes-entire-dev-team-around-ai/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/gadgets/2025/01/amid-a-flurry-of-hype-microsoft-reorganizes-entire-dev-team-around-ai/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/gadgets/2025/01/amid-a-flurry-of-hype-microsoft-reorganizes-entire-dev-team-around-ai/",
            "title": "Amid a flurry of hype, Microsoft reorganizes entire dev team around AI",
            "date_modified": "2025-01-14T21:00:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42696691\">Comments</a>",
            "url": "https://kevinmunger.substack.com/p/in-the-belly-of-the-mrbeast",
            "title": "In the belly of the MrBeast",
            "date_modified": "2025-01-14T13:05:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42696477\">Comments</a>",
            "url": "https://sliplane.io",
            "title": "Show HN: Simple Docker Hosting",
            "date_modified": "2025-01-14T12:35:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42691184\">Comments</a>",
            "url": "https://www.githubstatus.com/incidents/qd96yfgvmcf9",
            "title": "GitHub Git Operations Are Down",
            "date_modified": "2025-01-13T23:47:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42679679\">Comments</a>",
            "url": "http://134.0.119.41",
            "title": "Disco Elysium Explorer",
            "date_modified": "2025-01-13T03:11:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42672724\">Comments</a>",
            "url": "http://muratbuffalo.blogspot.com/2023/08/distributed-transactions-at-scale-in.html",
            "title": "Distributed Transactions at Scale in Amazon DynamoDB",
            "date_modified": "2025-01-12T11:12:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42666851\">Comments</a>",
            "url": "https://www.dgt.is/blog/2025-01-10-nix-death-by-a-thousand-cuts/",
            "title": "Nix – Death by a Thousand Cuts",
            "date_modified": "2025-01-11T16:19:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42645012\">Comments</a>",
            "url": "https://varoa.net/2025/01/09/serverless.html",
            "title": "Why aren't we all serverless yet?",
            "date_modified": "2025-01-09T13:12:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42610797\">Comments</a>",
            "url": "https://miguelcarranza.es/cto-year-7",
            "title": "Year 7 as a CTO",
            "date_modified": "2025-01-06T14:27:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42606773\">Comments</a>",
            "url": "https://futureboy.us/blog/twofifty.html",
            "title": "Engineer eats efficiently for $2.50 a day (2016)",
            "date_modified": "2025-01-06T01:56:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42602603\">Comments</a>",
            "url": "https://blog.mggross.com/intercepting-syscalls/",
            "title": "Reliable system call interception",
            "date_modified": "2025-01-05T15:58:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42584610\">Comments</a>",
            "url": "https://www.texasmonthly.com/food/yemeni-coffee-shops-booming-in-texas/",
            "title": "Yemeni Coffee Shops in Texas",
            "date_modified": "2025-01-03T11:11:39.000Z"
        },
        {
            "content_html": "<p>Doesn’t link directly to video, but to collection of videos.</p>\n<p><a href=\"https://lobste.rs/s/exgzeu/erlang_master_classes\">Comments</a></p>",
            "url": "https://www.cs.kent.ac.uk/ErlangMasterClasses/",
            "title": "Erlang master classes",
            "date_modified": "2025-01-02T15:51:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42571963\">Comments</a>",
            "url": "https://borretti.me/article/how-i-use-claude",
            "title": "How I Use Claude",
            "date_modified": "2025-01-02T05:35:06.000Z"
        },
        {
            "content_html": "<p>Andy rises from the ashes of his dead startup and discusses what happened in 2024 in the database game.</p>",
            "url": "https://www.cs.cmu.edu/~pavlo/blog/2025/01/2024-databases-retrospective.html",
            "title": "Databases in 2024: A Year in Review",
            "date_modified": "2025-01-01T00:32:11.864Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42559882\">Comments</a>",
            "url": "https://hardcoresoftware.learningbyshipping.com/p/225-systems-ideas-that-sound-good",
            "title": "Systems ideas that sound good but almost never work",
            "date_modified": "2024-12-31T16:47:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42548480\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=42548480",
            "title": "Is there such a thing as \"private, interactive databases\" for SaaS's",
            "date_modified": "2024-12-30T11:49:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42532353\">Comments</a>",
            "url": "https://blog.flippercloud.io/per-seat-pricing-sucks/",
            "title": "Per Seat Pricing Sucks",
            "date_modified": "2024-12-28T16:58:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42528521\">Comments</a>",
            "url": "https://www.permissionlessbook.com",
            "title": "Permissionless. A Manifesto for the Future of Everything",
            "date_modified": "2024-12-28T04:17:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42527355\">Comments</a>",
            "url": "https://mitchellh.com/writing/ghostty-1-0-reflection",
            "title": "Ghostty: Reflecting on Reaching 1.0",
            "date_modified": "2024-12-28T00:16:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42517447\">Comments</a>",
            "url": "https://ghostty.org/",
            "title": "Ghostty 1.0",
            "date_modified": "2024-12-26T20:14:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42513809\">Comments</a>",
            "url": "https://engineering.prezi.com/how-using-availability-zones-can-eat-up-your-budget-our-journey-from-prometheus-to-be8a816f7efe",
            "title": "Using AZs can eat up your budget – From Prometheus to VictoriaMetrics",
            "date_modified": "2024-12-26T08:09:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42497338\">Comments</a>",
            "url": "https://jeremymorrell.dev/blog/a-practitioners-guide-to-wide-events/",
            "title": "A Practitioner's Guide to Wide Events",
            "date_modified": "2024-12-23T20:29:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42487269\">Comments</a>",
            "url": "https://github.com/fish-shell/fish-shell/releases/tag/4.0b1",
            "title": "Fish has been ported to Rust",
            "date_modified": "2024-12-22T16:20:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42485498\">Comments</a>",
            "url": "https://buildingslack.com/the-death-of-glitch-the-birth-of-slack/",
            "title": "The death of Glitch, the birth of Slack",
            "date_modified": "2024-12-22T10:20:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42480105\">Comments</a>",
            "url": "https://s2.dev/blog/intro",
            "title": "Introducing S2",
            "date_modified": "2024-12-21T15:11:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42469945\">Comments</a>",
            "url": "https://github.com/libriscv/multi_tenant_drogon",
            "title": "Show HN: Ephemeral VMs in 1 Microsecond",
            "date_modified": "2024-12-20T10:43:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42469708\">Comments</a>",
            "url": "https://wordpress.org/news/2024/12/holiday-break/",
            "title": "Matt Mullenweg temporarily shuts down some Wordpress.org functions",
            "date_modified": "2024-12-20T10:07:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42461734\">Comments</a>",
            "url": "https://lwn.net/Articles/1002820/",
            "title": "Fish shell announces 4.0 release",
            "date_modified": "2024-12-19T14:36:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42434947\">Comments</a>",
            "url": "https://go.dev/blog/protobuf-opaque",
            "title": "Go Protobuf: The New Opaque API",
            "date_modified": "2024-12-16T20:18:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42431144\">Comments</a>",
            "url": "https://rootly.com/blog/new-blog-series-rescueops",
            "title": "Rootly New Blog Series: RescueOps",
            "date_modified": "2024-12-16T14:17:02.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/zf7vxg/death_developer_relations\">Comments</a></p>",
            "url": "https://leebriggs.co.uk/blog/2024/12/10/the-death-of-devrel",
            "title": "The Death of Developer Relations",
            "date_modified": "2024-12-15T13:29:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42399701\">Comments</a>",
            "url": "https://kubespec.dev/",
            "title": "Show HN: Kubernetes Spec Explorer",
            "date_modified": "2024-12-12T15:02:14.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/o0jzbc/ucacher_speeding_up_github_actions_via\">Comments</a></p>",
            "url": "https://earthly.dev/blog/ucacher/",
            "title": "Ucacher: Speeding up GitHub Actions via syscall instrumentation",
            "date_modified": "2024-12-11T17:44:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42370744\">Comments</a>",
            "url": "https://github.com/bazelbuild/bazel/releases/tag/8.0.0",
            "title": "Bazel 8.0 Released",
            "date_modified": "2024-12-09T21:33:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42348855\">Comments</a>",
            "url": "https://airport.revolvertype.com/",
            "title": "Now Boarding: The Story of Airport",
            "date_modified": "2024-12-07T10:48:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42347917\">Comments</a>",
            "url": "https://github.com/jdx/mise",
            "title": "Mise: Dev tools, env vars, task runner",
            "date_modified": "2024-12-07T07:21:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42341506\">Comments</a>",
            "url": "https://terriblesoftware.org/2024/12/04/the-6-mistakes-youre-going-to-make-as-a-new-manager/",
            "title": "Mistakes You're Going to Make as a New Manager",
            "date_modified": "2024-12-06T16:56:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42333053\">Comments</a>",
            "url": "https://www.acton-lang.org/",
            "title": "The Acton Programming Language",
            "date_modified": "2024-12-05T21:36:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42331207\">Comments</a>",
            "url": "https://github.com/facebook/react/blob/main/CHANGELOG.md",
            "title": "React v19 has been released",
            "date_modified": "2024-12-05T18:50:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42331207\">Comments</a>",
            "url": "https://github.com/facebook/react/blob/main/CHANGELOG.md",
            "title": "React 19",
            "date_modified": "2024-12-05T18:50:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42330732\">Comments</a>",
            "url": "https://openai.com/index/introducing-chatgpt-pro/",
            "title": "ChatGPT Pro",
            "date_modified": "2024-12-05T18:09:31.000Z"
        },
        {
            "content_html": "<p>Today, we are announcing <a href=\"https://aws.amazon.com/marketplace/features/buy-with-aws\">Buy with AWS</a>, a new way to discover and purchase solutions available in <a href=\"https://aws.amazon.com/mp/marketplace-service/overview/\">AWS Marketplace</a> from <a href=\"https://aws.amazon.com/partners/\">AWS Partner</a> sites. You can use Buy with AWS to accelerate and streamline your product procurement process on websites outside of <a href=\"https://aws.amazon.com/\">Amazon Web Services (AWS)</a>. This feature provides you the ability to find, try, and buy solutions from Partner websites using your AWS account</p> \n<p>AWS Marketplace is a curated digital store for you to find, buy, deploy, and manage cloud solutions from Partners. Buy with AWS is another step towards AWS Marketplace making it easy for you to find and procure the right Partner solutions, when and where you need them. You can conveniently find and procure solutions in AWS Marketplace, through integrated AWS service consoles, and now on Partner websites.</p> \n<p><span><strong>Accelerate cloud solution discovery and evaluation</strong></span></p> \n<p>You can now discover solutions from Partners available for purchase through AWS Marketplace as you explore solutions on the web beyond AWS.</p> \n<p>Look for products that are “Available in AWS Marketplace” when browsing on Partner sites, then accelerate your evaluation process with fast access to free trials, demo requests, and inquiries for custom pricing.</p> \n<p>For example, I want to evaluate <a href=\"https://www.wiz.io/\">Wiz</a> to see how it can help with my cloud security requirements. While browsing the Wiz website, I come across a <a href=\"https://www.wiz.io/partners/aws\">page where I see “Connect Wiz with Amazon Web Services (AWS)”</a>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Wiz-1-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Wiz-1-2.png\" alt=\"Wiz webpage featuring Buy With AWS\" width=\"1307\" height=\"785\"></a></p> \n<p>I choose <strong>Try with AWS</strong>. It asks me to sign in to my AWS account if I’m not signed in already. I’m then presented with a Wiz and AWS co-branded page for me to sign up for the free trial.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Wiz-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Wiz-2.png\" alt=\"Wiz and AWS co-branded page to sign up for free trial using Buy with AWS through AWS Marketplace\" width=\"904\" height=\"458\"></a></p> \n<p>The discovery experience that you see will vary depending on type of the Partner website you’re shopping from. Wiz is an example of how Buy with AWS can be implemented by an independent software vendor (ISV). Now, let’s look at an example of an AWS Marketplace Channel Partner, or reseller, who operates a storefront of their own.</p> \n<p>I browse to the <a href=\"https://marketplace-aws.bytes.co.uk/products\">Bytes storefront</a> with product listings from AWS Marketplace. I have the option to filter and search from the curated product listings, which are available in AWS Marketplace, on the Bytes site.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Bytes-1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Bytes-1.png\" alt=\"Bytes storefront with product listings from AWS Marketplace\" width=\"904\" height=\"634\"></a></p> \n<p>I choose <strong>View Details</strong> for Fortinet and see an option to <strong>Request Private Offer</strong> from AWS.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Bytes-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/Bytes-2.png\" alt=\"Bytes storefront with option to Request Private Offer for Fortinet from AWS Marketplace\" width=\"904\" height=\"404\"></a></p> \n<p>As you can tell, on a Channel Partner site, you can browse curated product listings available in AWS Marketplace, filter products, and request custom pricing directly from their website.</p> \n<p><span><strong>Streamline product procurement on AWS Partner sites</strong></span><br> I had a seamless experience using Buy with AWS to access a free trial for Wiz and browse through the Bytes storefront to request a private offer.</p> \n<p>Now I want to try <a href=\"https://www.databricks.com/\">Databricks</a> for one of the applications I’m building. I sign up for a <a href=\"http://signup.databricks.com/\">Databricks trial</a> through their website.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-1.png\" alt=\"Database homepage after login with option to Upgrade\" width=\"1443\" height=\"500\"></a></p> \n<p>I chose <strong>Upgrade</strong> and see Databricks is available in AWS Marketplace, which gives me the option to <strong>Buy with AWS</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-2.png\" alt=\"Option to upgrade to Databricks premium using Buy with AWS feature of AWS marketplace\" width=\"1426\" height=\"882\"></a></p> \n<p>I choose <strong>Buy with AWS</strong>, and after I sign in to my AWS account, I land on a Databricks and AWS Marketplace co-branded procurement page.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-3.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-3.png\" alt=\"Databricks and AWS co-branded page to subscribe using Buy with AWS\" width=\"1433\" height=\"888\"></a></p> \n<p>I complete the purchase on the co-branded procurement page and continue to set up my Databricks account.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-4.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/DB-4.png\" alt=\"Databricks and AWS co-branded page after subscribing using Buy with AWS\" width=\"1431\" height=\"892\"></a></p> \n<p>As you can tell, I didn’t have to navigate the challenge of managing procurement processes for multiple vendors. I also didn’t have to speak with a sales representative or onboard a new vendor in my billing system, which would have required multiple approvals and delayed the overall process.</p> \n<p><span><strong>Access centralized billing and benefits through AWS Marketplace</strong></span><br> Because Buy with AWS purchases are transacted through and managed in AWS Marketplace, you also benefit from the post-purchase experience of AWS Marketplace, including consolidated AWS billing, centralized subscription management, and access to cost optimization tools.</p> \n<p>For example, through the <a href=\"https://aws.amazon.com/aws-cost-management/aws-billing/\">AWS Billing and Cost Management console</a>, I can centrally manage all my AWS purchases, including Buy with AWS purchases, from one dashboard. I can easily access and process invoices for all of my organization’s AWS purchases. I also need to have valid <a href=\"https://docs.aws.amazon.com/marketplace/latest/buyerguide/buyer-iam-users-groups-policies.html\">AWS Identity and Access Management (IAM) permissions</a> to manage subscriptions and make a purchase through AWS Marketplace.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/billing-1-1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/24/billing-1-1.png\" width=\"1453\" height=\"703\"></a></p> \n<p>AWS Marketplace not only simplifies my billing but also helps in maintaining governance over spending by helping me manage purchasing authority and subscription access for my organization with centralized visibility and controls. I can manage my budget with pricing flexibility, cost transparency, and AWS cost management tools.</p> \n<p><span><strong>Buy with AWS for Partners</strong></span><br> Buy with AWS enables Partners who sell or resell products in AWS Marketplace to create new solution discovery and buying experiences for customers on their own websites. By adding call to action (CTA) buttons to their websites such as “Buy with AWS”, “Try free with AWS”, “Request private offer”, and “Request demo”, Partners can help accelerate product evaluation and the path-to-purchase for customers.</p> \n<p>By integrating with <a href=\"https://docs.aws.amazon.com/marketplace/latest/APIReference/welcome.html\">AWS Marketplace APIs</a>, Partners can display products from the AWS Marketplace catalog, allow customers to sort and filter products, and streamline private offers. Partners implementing Buy with AWS can access AWS Marketplace creative and messaging resources for guidance on building their own web experiences. Partners who implement Buy with AWS can access metrics for insights into engagement and conversion performance.</p> \n<p>The&nbsp;<a href=\"https://aws.amazon.com/marketplace/management/homepage?pageType=awsmpmp%3Acustomer\">Buy with AWS onboarding guide in the AWS Marketplace Management Portal</a> details how Partners can get started.</p> \n<p><span><strong>Learn more</strong></span><br> Visit the <a href=\"https://aws.amazon.com/marketplace/features/buy-with-aws\">Buy with AWS page</a> to learn more and explore Partner sites that offer Buy with AWS.</p> \n<p>To learn more about selling or reselling products using Buy with AWS on your website, visit:</p> \n<ul> \n <li><a href=\"https://aws.amazon.com/partners/marketplace/buy-with-aws/\">Buy with AWS seller page</a></li> \n <li><a href=\"https://aws.amazon.com/marketplace/management/homepage?pageType=awsmpmp%3Acustomer\">Buy with AWS onboarding guide in the AWS Marketplace Management Portal</a></li> \n</ul> \n<p>– <a href=\"https://www.linkedin.com/in/kprasadrao/\">Prasad</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-buy-with-aws-an-accelerated-procurement-experience-on-aws-partner-sites-powered-by-aws-marketplace/",
            "title": "Introducing Buy with AWS: an accelerated procurement experience on AWS Partner sites, powered by AWS Marketplace",
            "date_modified": "2024-12-04T23:30:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42317968\">Comments</a>",
            "url": "https://www.cnn.com/2024/12/04/us/brian-thompson-united-healthcare-death/index.html",
            "title": "UnitedHealthcare CEO fatally shot in midtown Manhattan",
            "date_modified": "2024-12-04T14:52:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42313258\">Comments</a>",
            "url": "https://blog.kerollmops.com/meilisearch-is-too-slow",
            "title": "Drawbacks and solutions for the Meilisearch document indexer",
            "date_modified": "2024-12-04T00:21:45.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/0rsd7r/amazon_s3_tables\">Comments</a></p>",
            "url": "https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-s3-tables-apache-iceberg-tables-analytics-workloads/",
            "title": "Amazon S3 Tables",
            "date_modified": "2024-12-03T19:47:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42308716\">Comments</a>",
            "url": "https://aws.amazon.com/rds/aurora/dsql/",
            "title": "Amazon Aurora DSQL",
            "date_modified": "2024-12-03T17:30:41.000Z"
        },
        {
            "content_html": "<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/12/s3_astro_1.png\" width=\"450\" height=\"450\">AWS customers make use of <a href=\"https://aws.amazon.com/s3/\">Amazon Simple Storage Service (Amazon S3)</a> at an incredible scale, regularly creating individual buckets that contain billions or trillions of objects! At that scale, finding the objects which meet particular criteria — objects with keys that match a pattern, objects of a particular size, or objects with a specific tag — becomes challenging. Our customers have had to build systems that capture, store, and query for this information. These systems can become complex and hard to scale, and can fall out of sync with the actual state of the bucket and the objects within.</p> \n<p><span><strong>Rich Metadata</strong></span><br> Today we are enabling in preview automatic generation of metadata that is captured when S3 objects are added or modified, and stored in fully managed <a href=\"https://iceberg.apache.org/\">Apache Iceberg</a> tables. This allows you to use Iceberg-compatible tools such as <a href=\"https://aws.amazon.com/athena\">Amazon Athena</a>, <a href=\"https://aws.amazon.com/redshift/\">Amazon Redshift</a>, <a href=\"https://quicksight.aws\">Amazon QuickSight</a>, and <a href=\"https://spark.apache.org/\">Apache Spark</a> to easily and efficiently query the metadata (and find the objects of interest) at any scale. As a result, you can quickly find the data that you need for your analytics, data processing, and AI training workloads.</p> \n<p>For video inference responses stored in S3, <a href=\"https://aws.amazon.com/bedrock/\">Amazon Bedrock</a> will annotate the content it generates with metadata that will allow you to identify the content as AI-generated, and to know which model was used to generate it.</p> \n<p>The metadata schema contains over 20 elements including the bucket name, object key, creation/modification time, storage class, encryption status, tags, and user metadata. You can also store additional, application-specific descriptive information in a separate table and then join it with the metadata table as part of your query.</p> \n<p><span><strong>How it Works</strong></span><br> You can enable capture of rich metadata for any of your S3 buckets by specifying the location (an S3 table bucket and a table name) where you want the metadata to be stored. Capture of updates (object creations, object deletions, and changes to object metadata) begins right away and will be stored in the table within minutes. Each update generates a new row in the table, with a record type (<code>CREATE</code>, <code>UPDATE_METADATA</code>, or <code>DELETE</code>) and a sequence number. You can retrieve the historical record for a given object by running a query that orders the results by sequence number.</p> \n<p><span><strong>Enabling and Querying Metadata</strong></span><br> I start by creating a table bucket for my metadata using the <code>create-table-bucket</code> command (this can also be done from the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a> or with an API call):</p> \n<div> \n <pre><code>$ aws s3tables create-table-bucket --name jbarr-table-bucket-1 --region us-east-2\n--------------------------------------------------------------------------------\n|                               CreateTableBucket                              |\n+-----+------------------------------------------------------------------------+\n|  arn|  arn:aws:s3tables:us-east-2:123456789012:bucket/jbarr-table-bucket-1   |\n+-----+------------------------------------------------------------------------+\n</code></pre> \n</div> \n<p>Then I specify the table bucket (by ARN) and the desired table name by putting this JSON into a file (I’ll call it <code>config.json</code>):</p> \n<div> \n <pre><code>{\n  \"S3TablesDestination\": {\n    \"TableBucketArn\": \"arn:aws:s3tables:us-east-2:123456789012:bucket/jbarr-table-bucket-1\",\n    \"TableName\": \"jbarr_data_bucket_1_table\"\n  }\n}</code></pre> \n</div> \n<p>And then I attach this configuration to my data bucket (the one that I want to capture metadata for):</p> \n<div> \n <pre><code>$ aws s3api create-bucket-metadata-table-configuration \\\n  --bucket jbarr-data-bucket-1 \\\n  --metadata-table-configuration file://./config.json \\\n  --region us-east-2</code></pre> \n</div> \n<p>For testing purposes I installed Apache Spark on an EC2 instance and after a little bit of setup I was able to run queries by referencing the <strong>Amazon S3 Tables Catalog for Apache Iceberg</strong> package and adding the metadata table (as <code>mytablebucket</code>) to the command line:</p> \n<div> \n <pre><code>$ bin/spark-shell \\\n--packages org.apache.iceberg:iceberg-spark-runtime-3.4_2.12:1.6.0 \\\n--jars ~/S3TablesCatalog.jar \\\n--master yarn \\\n--conf \"spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions\" \\\n--conf \"spark.sql.catalog.mytablebucket=org.apache.iceberg.spark.SparkCatalog\" \\\n--conf \"spark.sql.catalog.mytablebucket.catalog-impl=com.amazon.s3tables.iceberg.S3TablesCatalog\" \\\n--conf \"spark.sql.catalog.mytablebucket.warehouse=arn:aws:s3tables:us-east-2:123456789012:bucket/jbarr-table-bucket-1\"</code></pre> \n</div> \n<p>Here is the current schema for the Iceberg table:</p> \n<div> \n <pre><code>scala&gt; spark.sql(\"describe table mytablebucket.aws_s3_metadata.jbarr_data_bucket_1_table\").show(100,35)\n\n+---------------------+------------------+-----------------------------------+\n|             col_name|         data_type|                            comment|\n+---------------------+------------------+-----------------------------------+\n|               bucket|            string|   The general purpose bucket name.|\n|                  key|            string|The object key name (or key) tha...|\n|      sequence_number|            string|The sequence number, which is an...|\n|          record_type|            string|The type of this record, one of ...|\n|     record_timestamp|     timestamp_ntz|The timestamp that's associated ...|\n|           version_id|            string|The object's version ID. When yo...|\n|     is_delete_marker|           boolean|The object's delete marker statu...|\n|                 size|            bigint|The object size in bytes, not in...|\n|   last_modified_date|     timestamp_ntz|The object creation date or the ...|\n|                e_tag|            string|The entity tag (ETag), which is ...|\n|        storage_class|            string|The storage class that's used fo...|\n|         is_multipart|           boolean|The object's upload type. If the...|\n|    encryption_status|            string|The object's server-side encrypt...|\n|is_bucket_key_enabled|           boolean|The object's S3 Bucket Key enabl...|\n|          kms_key_arn|            string|The Amazon Resource Name (ARN) f...|\n|   checksum_algorithm|            string|The algorithm that's used to cre...|\n|          object_tags|map&lt;string,string&gt;|The object tags that are associa...|\n|        user_metadata|map&lt;string,string&gt;|The user metadata that's associa...|\n|            requester|            string|The AWS account ID of the reques...|\n|    source_ip_address|            string|The source IP address of the req...|\n|           request_id|            string|The request ID. For records that...|\n+---------------------+------------------+-----------------------------------+\n</code></pre> \n</div> \n<p>Here’s a simple query that shows some of the metadata for the ten most recent updates:</p> \n<div> \n <pre><code>scala&gt; spark.sql(\"SELECT key,size, storage_class,encryption_status \\\n  FROM mytablebucket.aws_s3_metadata.jbarr_data_bucket_1_table \\\n  order by last_modified_date DESC LIMIT 10\").show(false)\n+--------------------+------+-------------+-----------------+                   \n|key                 |size  |storage_class|encryption_status|\n+--------------------+------+-------------+-----------------+\n|wnt_itco_2.png      |36923 |STANDARD     |SSE-S3           |\n|wnt_itco_1.png      |37274 |STANDARD     |SSE-S3           |\n|wnt_imp_new_1.png   |15361 |STANDARD     |SSE-S3           |\n|wnt_imp_change_3.png|67639 |STANDARD     |SSE-S3           |\n|wnt_imp_change_2.png|67639 |STANDARD     |SSE-S3           |\n|wnt_imp_change_1.png|71182 |STANDARD     |SSE-S3           |\n|wnt_email_top_4.png |135164|STANDARD     |SSE-S3           |\n|wnt_email_top_2.png |117171|STANDARD     |SSE-S3           |\n|wnt_email_top_3.png |55913 |STANDARD     |SSE-S3           |\n|wnt_email_top_1.png |140937|STANDARD     |SSE-S3           |\n+--------------------+------+-------------+-----------------+</code></pre> \n</div> \n<p>In a real-world situation I would query the table using one of the AWS or open source analytics tools that I mentioned earlier.</p> \n<p><span><strong>Console Access</strong></span><br> I can also set up and manage the metadata configuration for my buckets using the Amazon S3 Console by clicking the <strong>Metadata</strong> tab:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/mdt_metadata_2-1.png\" width=\"894\" height=\"759\"></p> \n<p><span><strong>Available Now</strong></span><br> <a href=\"https://aws.amazon.com/s3/features/metadata/\">Amazon S3 Metadata</a> is available in preview now and you can start using it today in the US East (Ohio, N. Virginia) and US West (Oregon) AWS Regions.</p> \n<p>Integration with <a href=\"https://docs.aws.amazon.com/glue/latest/dg/catalog-and-crawler.html\">AWS Glue Data Catalog</a> is in preview, allowing you to query and visualize data—including S3 Metadata tables—using AWS Analytics services such as <a href=\"https://aws.amazon.com/athena\">Amazon Athena</a>, <a href=\"https://aws.amazon.com/redshift/\">Amazon Redshift</a>, <a href=\"https://aws.amazon.com/emr\">Amazon EMR</a>, and <a href=\"https://quicksight.aws\">Amazon QuickSight</a>.</p> \n<p>Pricing is based on the number updates (object creations, object deletions, and changes to object metadata) with an additional charge for storage of the metadata table. For more pricing information, visit the <a href=\"https://aws.amazon.com/s3/pricing\">S3 Pricing</a> page.</p> \n<p>I’m confident that you will be able to make use of this metadata in many powerful ways, and am looking forward to hearing about your use cases. Let me know what you think!</p> \n<p></p>\n<p>— <a href=\"https://twitter.com/jeffbarr\">Jeff</a>;</p>\n<p></p>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-queryable-object-metadata-for-amazon-s3-buckets-preview/",
            "title": "Introducing queryable object metadata for Amazon S3 buckets (preview)",
            "date_modified": "2024-12-03T16:47:12.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/xhhicr/next_platform\">Comments</a></p>",
            "url": "https://www.macchaffee.com/blog/2024/the-next-platform/",
            "title": "The next platform",
            "date_modified": "2024-12-03T02:52:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42299829\">Comments</a>",
            "url": "https://modal.com/blog/vprox",
            "title": "Static IPs for Serverless Containers",
            "date_modified": "2024-12-02T20:04:29.000Z"
        },
        {
            "content_html": "<p><a href=\"https://reinvent.awsevents.com/\">AWS re:Invent 2024</a>, our flagship an<a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/12/02/reinvent-2024-800x400-1.jpg\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/12/02/reinvent-2024-800x400-1-300x150.jpg\" alt=\"Image of large AWS logo with conference attendees moving in front of it in blurred motion\" width=\"300\" height=\"150\"></a>nual conference, is taking place Dec. 2-6, 2024, in Las Vegas. This premier cloud computing event brings together the global cloud computing community for a week of keynotes, technical sessions, product launches, and networking opportunities. As AWS continues to unveil its latest innovations and services throughout the conference, we’ll keep you updated here with all the major product announcements.</p> \n<p>Additional re:Invent resources:</p> \n<ul> \n <li><a href=\"https://aws.amazon.com/blogs/aws/\">AWS News Blog</a>: Chief Evangelist Jeff Barr and colleagues keep you posted on the biggest and best new AWS offerings.</li> \n <li><a href=\"https://aws.amazon.com/about-aws/whats-new/2024/\">What’s New with AWS</a>: A comprehensive list of all AWS launches.</li> \n <li><a href=\"https://aws.amazon.com/podcasts/aws-podcast/?podcast-list.sort-by=item.additionalFields.EpisodeNum&amp;podcast-list.sort-order=desc&amp;awsf.episode-type=*all&amp;awsf.tech-category-filter=*all&amp;awsf.product-filter=*all&amp;awsf.industry-filter=*all\">The Official AWS Podcast</a>: A podcast for developers and IT professionals looking for the latest news and trends from AWS.</li> \n <li><a href=\"https://www.twitch.tv/awsonair\">AWS On Air:</a> Live-streamed announcements and hands-on demos.</li> \n <li><a href=\"https://repost.aws/articles/ARv0eip0_MRcmzAjVilXglXA/get-ready-for-aws-re-invent-2024\">AWS re:Post:</a> Join the community in conversation through Q&amp;A.</li> \n</ul> \n<p>(This post was last updated:&nbsp; 8:26 a.m. PST, Dec. 3, 2024.)</p> \n<hr> \n<p><strong>Quick category links:</strong></p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Analytics\">Analytics</a> |&nbsp;<a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Application_Integration\">Application Integration</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Business_Applications\">Business Applications</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Compute\">Compute</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Containers\">Containers</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Database\">Database</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Generative_AI\">Generative AI / Machine Learning</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Management_&amp;_Governance\">Management &amp; Governance</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Migration_&amp;_Transfer_Services\">Migration &amp; Transfer Services</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Security\">Security, Identity, &amp; Compliance</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/#Storage\"> Storage</a></p> \n<div id=\"Analytics\"></div> \n<h3>Analytics</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/aws-clean-rooms-now-supports-multiple-clouds-and-data-sources/\">AWS Clean Rooms now supports multiple clouds and data sources</a><br> With expanded data sources, AWS Clean Rooms helps customers securely collaborate with their partners’ data across clouds, eliminating data movement, safeguarding sensitive information, promoting data freshness, and streamlining cross-company insights.</p> \n<div id=\"Application_Integration\"></div> \n<h3>Application Integration</h3> \n<div id=\"Business_Applications\">\n <a href=\"https://aws.amazon.com/blogs/aws/securely-share-aws-resources-across-vpc-and-account-boundaries-with-privatelink-vpc-lattice-eventbridge-and-step-functions/\">Securely share AWS resources across VPC and account boundaries with PrivateLink, VPC Lattice, EventBridge, and Step Functions</a>\n <br> Orchestrate hybrid workflows accessing private HTTPS endpoints – no more Lambda/SQS workarounds. EventBridge and Step Functions natively support private resources, simplifying cloud modernization.\n</div> \n<div></div> &nbsp;\n<p></p> \n<h3>Business Applications</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/newly-enhanced-amazon-connect-adds-generative-ai-whatsapp-business-and-secure-data-collection/\">Newly enhanced Amazon Connect adds generative AI, WhatsApp Business, and secure data collection</a><br> Use innovative tools like generative AI for segmentation and campaigns, WhatsApp Business, data privacy controls for chat, AI guardrails, conversational AI bot management, and enhanced analytics to elevate customer experiences securely and efficiently.</p> \n<div id=\"Compute\"></div> \n<h3>Compute</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/amazon-ec2-trn2-instances-and-trn2-ultraservers-for-aiml-training-and-inference-is-now-available/\">Amazon EC2 Trn2 Instances and Trn2 UltraServers for AI/ML training and inference are now available</a><br> With 4x faster speed, 4x more memory bandwidth, 3x higher memory capacity than predecessors, and 30% higher floating-point operations, these instances deliver unprecedented compute power for ML training and gen AI.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-ec2-p5en-instances-with-nvidia-h200-tensor-core-gpus-and-efav3-networking/\">New Amazon EC2 P5en instances with NVIDIA H200 Tensor Core GPUs and EFAv3 networking</a><br> Amazon EC2 P5en instances deliver up to 3,200 Gbps network bandwidth with EFAv3 for accelerating deep learning, generative AI, and HPC workloads with unmatched efficiency.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/introducing-storage-optimized-amazon-ec2-i8g-instances-powered-by-aws-graviton4-processors-and-3rd-gen-aws-nitro-ssds/\">Introducing storage optimized Amazon EC2 I8g instances powered by AWS Graviton4 processors and 3rd gen AWS Nitro SSDs</a><br> Elevate storage performance with AWS’s newest I8g instances, which deliver unparalleled speed and efficiency for I/O-intensive workloads.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/now-available-storage-optimized-amazon-ec2-i7ie-instances/\">Now available: Storage optimized Amazon EC2 I7ie instances</a><br> New AWS I7ie instances deliver unbeatable storage performance: up to 120TB NVMe, 40% better compute performance and up to 65% better real-time storage performance.</p> \n<div id=\"Containers\"></div> \n<h3>Containers</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/use-your-on-premises-infrastructure-in-amazon-eks-clusters-with-amazon-eks-hybrid-nodes/\">Use your on-premises infrastructure in Amazon EKS clusters with Amazon EKS Hybrid Nodes</a><br> Unify Kubernetes management across your cloud and on-premises environments with Amazon EKS Hybrid Nodes – use existing hardware while offloading control plane responsibilities to EKS for consistent operations.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/streamline-kubernetes-cluster-management-with-new-amazon-eks-auto-mode/\">Streamline Kubernetes cluster management with new Amazon EKS Auto Mode</a><br> With EKS Auto Mode, AWS simplifies Kubernetes cluster management, automating compute, storage, and networking, enabling higher agility and performance while reducing operational overhead.</p> \n<div id=\"Database\"></div> \n<h3>Database</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/amazon-memorydb-multi-region-is-now-generally-available/\">Amazon MemoryDB Multi-Region is now generally available</a><br> Build highly available, globally distributed apps with microsecond latencies across Regions, automatic conflict resolution, and up to 99.999% availability.</p> \n<div id=\"Generative_AI\"></div> \n<h3>Generative AI / Machine Learning</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-rag-evaluation-and-llm-as-a-judge-capabilities-in-amazon-bedrock/\">New RAG evaluation and LLM-as-a-judge capabilities in Amazon Bedrock</a><br> Evaluate AI models and applications efficiently with Amazon Bedrock’s new LLM-as-a-judge capability for model evaluation and RAG evaluation for Knowledge Bases, offering a variety of quality and responsible AI metrics at scale.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/enhance-your-productivity-with-new-extensions-and-integrations-in-amazon-q-business/\">Enhance your productivity with new extensions and integrations in Amazon Q Business</a><br> Seamlessly access AI assistance within work applications with Amazon Q Business’s new browser extensions and integrations.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-apis-in-amazon-bedrock-to-enhance-rag-applications-now-available/\">New APIs in Amazon Bedrock to enhance RAG applications, now available</a><br> With custom connectors and reranking models, you can enhance RAG applications by enabling direct ingestion to knowledge bases without requiring a full sync, and improving response relevance through advanced reranking models.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/introducing-new-partyrock-capabilities-and-free-daily-usage/\">Introducing new PartyRock capabilities and free daily usage</a><br> Unleash your creativity with PartyRock’s new AI capabilities: generate images, analyze visuals, search hundreds of thousands of apps, and process multiple docs simultaneously – no coding required.</p> \n<div id=\"Management_&amp;_Governance\">\n <a href=\"https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-q-business-extract-insights-visual-elements-documents/\">Amazon Q Business adds support to extract insights from visual elements within documents</a>\n</div> \n<div>\n Users can now query information embedded in various types of visuals, including diagrams, infographics, charts, and other image-based content.\n</div> \n<h3>Management &amp; Governance</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/container-insights-with-enhanced-observability-now-available-in-amazon-ecs/\">Container Insights with enhanced observability now available in Amazon ECS</a><br> With granular visibility into container workloads, CloudWatch Container Insights with enhanced observability for Amazon ECS enables proactive monitoring and faster troubleshooting, enhancing observability and improving application performance.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-database-insights-comprehensive-database-observability-from-fleets-to-instances/\">New Amazon CloudWatch Database Insights: Comprehensive database observability from fleets to instances</a><br> Monitor Amazon Aurora databases and gain comprehensive visibility into MySQL and PostgreSQL fleets and instances, analyze performance bottlenecks, track slow queries, set SLOs, and explore rich telemetry.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-and-amazon-opensearch-service-launch-an-integrated-analytics-experience/\">New Amazon CloudWatch and Amazon OpenSearch Service launch an integrated analytics experience</a><br> Unlock out-of-the-box OpenSearch dashboards and two additional query languages, OpenSearch SQL and PPL, for analyzing CloudWatch logs. OpenSearch customers can now analyze CloudWatch Logs without having to duplicate data.</p> \n<div id=\"Migration_&amp;_Transfer_Services\"></div> \n<h3>Migration &amp; Transfer Services</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/aws-data-migration-service-improves-database-schema-conversion-with-generative-ai/\">AWS Database Migration Service now automates time-intensive schema conversion tasks using generative AI</a><br> AWS DMS Schema Conversion converts up to 90% of your schema to accelerate your database migrations and reduce manual effort with the power of generative AI.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/announcing-aws-transfer-family-web-apps-for-fully-managed-amazon-s3-file-transfers/\">Announcing AWS Transfer Family web apps for fully managed Amazon S3 file transfers</a><br> AWS Transfer Family web apps are a new resource that you can use to create a simple interface for authorized line-of-business users to access data in Amazon S3 through a customizable web browser.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/introducing-default-data-integrity-protections-for-new-objects-in-amazon-s3/\">Introducing default data integrity protections for new objects in Amazon S3</a><br> Amazon S3 updates the default behavior of object upload requests with new data integrity protections that build upon S3’s existing durability posture.</p> \n<div id=\"Security\"></div> \n<h3>Security, Identity, &amp; Compliance</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-aws-security-incident-response-helps-organizations-respond-to-and-recover-from-security-events/\">New AWS Security Incident Response helps organizations respond to and recover from security events</a><br> AWS introduces a new service to streamline security event response, providing automated triage, coordinated communication, and expert guidance to recover from cybersecurity threats.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/introducing-amazon-guardduty-extended-threat-detection-aiml-attack-sequence-identification-for-enhanced-cloud-security/\">Introducing Amazon GuardDuty Extended Threat Detection: AI/ML attack sequence identification for enhanced cloud security</a><br> AWS extends GuardDuty with AI/ML capabilities to detect complex attack sequences across workloads, applications, and data, correlating multiple security signals over time for proactive cloud security.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/simplify-governance-with-declarative-policies/\">Simplify governance with declarative policies</a><br> With only a few steps, create declarative policies and enforce desired configuration for AWS services across your organization, reducing ongoing governance overhead and providing transparency for administrators and end users.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/aws-verified-access-now-supports-secure-access-to-resources-over-non-https-protocols/\">AWS Verified Access now supports secure access to resources over non-HTTP(S) protocols (preview)</a><br> With only a few steps, create declarative policies and enforce desired configuration for AWS services across your organization, reducing ongoing governance overhead and providing transparency for administrators and end users.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/introducing-amazon-opensearch-service-zero-etl-integration-for-amazon-security-lake/\">Introducing Amazon OpenSearch Service and Amazon Security Lake integration to simplify security analytics</a><br> Analyze security logs without data duplication; Amazon OpenSearch Service now offers zero-ETL integration with Amazon Security Lake for efficient threat hunting and investigations.</p> \n<div id=\"Storage\"></div> \n<h3>Storage</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/announcing-amazon-fsx-intelligent-tiering-a-new-storage-class-for-fsx-for-openzfs/\">Announcing Amazon FSx Intelligent-Tiering, a new storage class for FSx for OpenZFS</a><br> Delivering NAS capabilities with automatic data tiering among frequently accessed, infrequent, and archival storage tiers, Amazon FSx Intelligent-Tiering offers high performance up to 400K IOPS, 20 GB/s throughput, seamless integration with AWS services.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-physical-aws-data-transfer-terminals-let-you-upload-to-the-cloud-faster/\">New physical AWS Data Transfer Terminals let you upload to the cloud faster</a><br> Rapidly upload large datasets to AWS at blazing speeds with the new AWS Data Transfer Terminal, secure physical locations offering high throughput connection.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/connect-users-to-data-through-your-apps-with-storage-browser-for-amazon-s3/\">Connect users to data through your apps with Storage Browser for Amazon S3</a><br> Storage Browser for Amazon S3 is an open source interface component that you can add to your web applications to provide your authorized end users, such as customers, partners, and employees, with access to easily browse, upload, download, copy, and delete data in S3.</p>",
            "url": "https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/",
            "title": "Top announcements of AWS re:Invent 2024",
            "date_modified": "2024-12-02T05:06:42.000Z"
        },
        {
            "content_html": "<p>Last year, we announced <a href=\"https://aws.amazon.com/blogs/mt/new-container-insights-with-enhanced-observability-for-amazon-eks/\">enhanced observability in Amazon CloudWatch Container Insights</a>, a new capability to improve your observability for <a href=\"https://aws.amazon.com/eks/\">Amazon Elastic Kubernetes Service (Amazon EKS)</a>. This capability helps you detect and fix container issues faster by providing detailed performance metrics and logs.</p> \n<p>Expanding this capability, today we’re launching enhanced observability for your container workloads running on <a href=\"https://aws.amazon.com/ecs/\">Amazon Elastic Container Service (Amazon ECS)</a>. This new capability will help reduce your mean time to detect (MTTD) and mean time to repair (MTTR) for your overall applications, helping prevent issues that could negatively impact your user experience.</p> \n<p>Here’s a quick look at Container Insights with enhanced observability for Amazon ECS.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-2-1.png\" width=\"3840\" height=\"1935\"></p> \n<p>Container Insights with enhanced observability addresses a critical gap in container monitoring. Previously, correlating metrics with logs and events was a time-consuming process, often requiring manual searches and expertise in application architecture. Now, with this capability, CloudWatch and Amazon ECS automatically collect granular performance metrics such as CPU utilization at both the task and container levels while providing visual drill downs enabling easy root-cause analysis.</p> \n<p>This new capability enables the following use cases:</p> \n<ul> \n <li>Quickly identify root causes by viewing granular resource usage patterns and correlating telemetry data.</li> \n <li>Proactively manage your ECS resources using curated dashboards based on AWS best practices.</li> \n <li>Track your recent deployments and root causes of your deployment failures with the matching infrastructure anomalies enabling faster issue detection and quicker rollbacks when necessary.</li> \n <li>Effortlessly monitor resources across multiple accounts without manual setup. Built-in cross-account support reduces operational overhead with single pane of glass observability.</li> \n <li>Integration with other CloudWatch services such as Application Signals and CloudWatch Logs provides a seamless experience to correlate infrastructure with the services running and identify the impacted services.</li> \n</ul> \n<p><span><strong>Using container insights with enhanced observability for Amazon ECS<br></strong></span>There are two ways to enable Container Insights with enhanced observability:</p> \n<ol> \n <li><strong>Cluster-level onboarding</strong> – You can enable it for specific clusters individually.</li> \n <li><strong>Account-level onboarding</strong> – You can also enable it at the account level, which automatically enables observability for all new clusters created in your account. This approach saves time and effort by eliminating the need to manually enable it for each new cluster.</li> \n</ol> \n<p>To enable this feature at the account level, I navigate to the Amazon ECS console and select&nbsp;<strong>Account settings</strong>. Under the&nbsp;<strong>CloudWatch Container Insights observability</strong>&nbsp;section, I can see it’s currently disabled. I choose&nbsp;<strong>Update</strong>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-1.png\" width=\"3840\" height=\"1935\"></p> \n<p>On this page, I find a new option called&nbsp;<strong>Container Insights with enhanced observability</strong>. I select this option and then choose&nbsp;<strong>Save changes</strong>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-2-copy.png\" width=\"3840\" height=\"1935\"></p> \n<p>If I need to enable this capability at the cluster level, I can do so when creating a new cluster.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-3-1.png\" width=\"3134\" height=\"2616\"></p> \n<p>I can also enable this capability for my existing clusters. To do so, I select&nbsp;<strong>Update cluster</strong>, and then choose the option.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-4.png\" width=\"3840\" height=\"1934\"></p> \n<p>Once enabled, I can see task-level metrics by navigating to the&nbsp;<strong>Metrics</strong>&nbsp;tab in my cluster overview console. To access health and performance metrics across my clusters, I can select&nbsp;<strong>View Container Insights</strong>,&nbsp;which will redirect me to the Container Insights page.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-5-1.png\" width=\"3840\" height=\"1869\"></p> \n<p>To get a big picture of all my workloads across different clusters, I can navigate to Amazon CloudWatch and then to <strong>Container Insights</strong>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/25/news-2024-eci-ecs-19.png\" width=\"3840\" height=\"1935\"></p> \n<p>This view addresses the challenge of effectively monitoring clusters, services, tasks, and containers by providing a honeycomb visualization that offers an intuitive, high-level summary of cluster health. The dashboard employs a dual-state monitoring approach:</p> \n<ol> \n <li><strong>Alarm state (red or green)</strong> – Reflects customer-defined thresholds and alerts, allowing teams to configure monitoring based on their specific requirements</li> \n <li><strong>Utilization state (dark blue or light blue)</strong> – Uses CloudWatch built-in best practices to monitor resource usage patterns across containers. The darker blue indicates clusters operating under higher utilization, enabling teams to proactively identify potential resource constraints before they impact performance</li> \n</ol> \n<p>Let’s say there’s an issue in one of my clusters. I can hover over the cluster to display all the alarms created under that cluster at different layers, from the cluster layer down to the container layer.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-8.png\" width=\"3840\" height=\"1935\"></p> \n<p>I also have the option to view all clusters in a list format. The list format is essential for cross-account observability, displaying account IDs and labels for cluster ownership. This helps DevOps engineers quickly identify and collaborate with account owners to resolve potential application issues.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/28/news-2024-eci-ecs-r20-1.png\" width=\"2342\" height=\"1235\"></p> \n<p>Now, I’d like to explore further. I select my cluster link, which redirects me to the Container Insights detailed dashboard view. Here, I can see a spike in memory utilization for this cluster.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-10.png\" width=\"3840\" height=\"1935\"></p> \n<p>I can dive deeper into container-level details, which help me quickly identify which services are causing this issue.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-11.png\" width=\"3840\" height=\"1934\"></p> \n<p>Another useful feature I found is the&nbsp;<strong>Filters</strong>&nbsp;option, which helps me conduct more thorough investigations across containers, services, or tasks in this cluster.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-12.png\" width=\"3840\" height=\"1935\"></p> \n<p>If I need to delve deeper into the application logs to understand the root cause of this issue, I can select the task, choose Actions, and choose which logs I would like to view.</p> \n<p>On top of using <a href=\"https://aws.amazon.com/x-ray/\">AWS X-Ray</a> traces, I can investigate another two types of logs here. First, I can use performance logs—structured logs containing metric data—to drill down and identify container-level root causes. Second, I examine collected application or container logs . These logs give me detailed insights into application behavior within the container, helping me trace the sequence of events that led to any issues.</p> \n<p>In this case, I use application logs.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-17.png\" width=\"3840\" height=\"1935\"></p> \n<p>This streamlines my journey to troubleshoot my application. In this case, the issue is on the downstream calls to third-party applications, which return timeouts.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-18.png\" width=\"3114\" height=\"2587\"></p> \n<p>This enhanced capability also works with <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Monitoring-Sections.html\">Amazon CloudWatch Application Signals</a> to automatically instrument my application. I can monitor current application health and track long-term application performance against <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-ServiceLevelObjectives.html\">service-level objectives</a>.</p> \n<p>I select the <strong>Application Signals</strong> tab.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-15.png\" width=\"3840\" height=\"1934\"></p> \n<p>This integration with Amazon CloudWatch Application Signals provides me with end-to-end visibility, helping me correlate container performance with end-user experience.</p> \n<p>When I select datapoints in the graphs, I can see associated traces, which show me all correlated services and their impact. I can also access relevant logs to understand root causes.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/news-2024-eci-ecs-13.png\" width=\"3840\" height=\"1935\"></p> \n<p><span><strong>Additional things to know</strong></span> <br>Here are a couple of important points to note:</p> \n<ul> \n <li><strong>Availability</strong> – Container Insights with enhanced observability for ECS is now available in all AWS Regions including the China Regions.</li> \n <li><strong>Pricing</strong> – Container Insights with enhanced observability for ECS comes with a flat metric pricing, visit the <a href=\"https://aws.amazon.com/cloudwatch/pricing/\">Amazon CloudWatch Pricing</a> page.</li> \n</ul> \n<p>Get started today and experience improved observability for your container workloads. Learn more on the <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights.html\">Amazon CloudWatch documentation</a> page.</p> \n<p>Happy monitoring, <br>— <a href=\"https://linkedin.com/in/donnieprakoso\">Donnie Prakoso</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/container-insights-with-enhanced-observability-now-available-in-amazon-ecs/",
            "title": "Container Insights with enhanced observability now available in Amazon ECS",
            "date_modified": "2024-12-02T03:28:56.000Z"
        },
        {
            "content_html": "<p>When I speak to customers who are planning to migrate massive amounts of on-premises data to AWS, they tell me that they want to simplify their storage management, reduce their costs, and to make the data more accessible so that it can be used for analytics, machine learning training, genomics, and other use cases. Customers are already using Network Attached Storage (NAS) on-premises, and are looking for a cloud-based upgrade that offers similar capabilities including point-in-time snapshots, data clones, and user management.</p> \n<p>AWS customers such as <a href=\"https://aws.amazon.com/partners/success/amdocs/\">Amdocs</a>, <a href=\"https://aws.amazon.com/blogs/gametech/vela-games-cuts-game-build-times-by-60-using-infrastructure-on-aws/\">Vela Games</a>, and <a href=\"https://aws.amazon.com/solutions/case-studies/astera-labs/\">Astera Labs</a> have been running their mission-critical and performance-intensive NAS workloads like databases, game development and streaming, and semiconductor chip design on <a href=\"https://aws.amazon.com/fsx/openzfs/\">Amazon FSx for OpenZFS</a>. They’ve been using the existing SSD storage class on FSx to provide the predictable, high performance these workloads need. However, many other customers have large data sets that are stored on HDD-based or hybrid SSD/HDD-based NAS storage on prem that find it cost-prohibitive to move their data sets to all-SSD storage. Additionally, these customers are finding it increasingly challenging and expensive to manage provisioned storage on prem for unpredictable data sets and avoid running out of space. And they are keeping their NAS data around for longer because it could have future value for building their next model, investment strategy, or product, but that means they need to spend more time and effort monitoring access patterns and moving data around between hot and cold storage media to optimize costs.</p> \n<p><span><strong>FSx Intelligent-Tiering</strong></span><br> <img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/13/fsx_int_1.png\" width=\"420\" height=\"165\">Taking all of this into account, I am happy to be able to tell you about the new Amazon FSx Intelligent-Tiering storage class, available today for use with <a href=\"https://aws.amazon.com/fsx/openzfs/\">Amazon FSx for OpenZFS</a> file systems. The new storage class is priced 85% lower than the existing SSD storage class and 20% lower than traditional HDD-based deployments on premises, and brings full elasticity and intelligent tiering to NAS data sets.</p> \n<p>Your data moves between three storage tiers (Frequent Access, Infrequent Access, and Archive) with no effort on your part, so you get automatic cost savings with no upfront costs or commitments. Here’s how the tiers work:</p> \n<p><strong>Frequent Access</strong> – Data that has been accessed within the last 30 days is stored in this tier.</p> \n<p><strong>Infrequent Access</strong> – Data that has been not been accessed for 30 to 90 days is stored in tier, at a 44% cost reduction from Frequent Access.</p> \n<p><strong>Archive</strong> – Data that has not been accessed for 90 or more days is stored in this tier, at a 65% cost reduction from Infrequent Access.</p> \n<p>Regardless of the storage tier, your data is stored across multiple AWS Availability Zones (AZs) for redundancy and availability, and can be retrieved instantly in milliseconds.</p> \n<p>There’s no need to manage or pre-provision storage, making this storage class a great fit for uses case such as genomics, financial data analytics, seismic imagery analysis, and machine learning where storage requirements can change dramatically over the course of days or weeks.</p> \n<p>Along with the potential for cost savings, you get high performance: up to 400K IOPS and 20 GB/second of throughput for each OpenZFS file system, with a time-to-first-byte of tens of milliseconds for all data, regardless of storage class. You can also configure an SSD-based read cache (64 GiB to 512 TiB) to reduce the time-to-first-byte by 10x to 100x for cached data.</p> \n<p><span><strong>Creating a File System</strong></span><br> I can create a file system using the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>, CLI, API, or a <a href=\"https://aws.amazon.com/cloudformation/\">AWS CloudFormation</a>. From the Console I click <strong>Create file system</strong> to get started:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/fsx_filesystems_1.png\" width=\"893\" height=\"257\"></p> \n<p>I choose <strong>Amazon FSx for OpenZFS</strong> and click <strong>Next</strong>:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/fsx_pick_zfs_1.png\" width=\"894\" height=\"629\"></p> \n<p>Then I enter a name (<strong>jeff_fsx_openzfs_1</strong>) for my file system and select the <strong>Intelligent-Tiering</strong> storage class. I choose the desired <strong>Throughput capacity</strong>, and I select one of the three <strong>sizing mode</strong> options for the read cache, click <strong>Next</strong>, and confirm my choices in order to create my file system:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/fsx_create_zfs_1.png\" width=\"894\" height=\"909\"></p> \n<p>It is ready within minutes, and I can NFS mount it to my EC2 instance:</p> \n<div> \n <pre><code>$ sudo mkdir /fsx_zfs\n$ sudo mount -t nfs -o noatime,nfsvers=4.2,sync,nconnect=16,rsize=1048576,wsize=1048576 \\\n  fs-00fc74f020d1e6f4e.fsx.us-east-2.aws.internal:/fsx/ /fsx_zfs/</code></pre> \n</div> \n<p>After I run a representative workload for a while I can look at the metrics and review the performance of my file system:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/fsx_metrics_1.png\" width=\"894\" height=\"517\"></p> \n<p>It appears that I have plenty of throughput, but my read cache may be larger than needed. I created it in Automatically Provisioned mode, which allocated 3200 GiB of cache. I can change that (and save some money) with a couple of clicks:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/fsx_edit_cache_1.png\" width=\"496\" height=\"408\"></p> \n<p>I can also change the throughput capacity as needed:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/fsx_edit_tpc_1.png\" width=\"506\" height=\"340\"></p> \n<p><strong><span>Amazon FSx NAS</span> Features and Attributes</strong><br> Let’s take a quick look at some of the features which make FSx for OpenZFS and the FSx Intelligent-Tiering storage class a great for for your NAS-level storage needs:</p> \n<p><strong>Built-in Backups</strong> – Amazon FSx automatically makes a daily backup of each file system during a specified backup window and retains them for a specified retention period. The backups are file-system consistent, highly durable, and incremental. You can also create backups on your own and retain them for as long as needed.</p> \n<p><strong>Point-In-Time Snapshots</strong> -You can create a read-only image of an OpenZFS volume at any time. The snapshots are stored within the file system and consume storage; they can be used to restore a volume, restore individual files and folders, or to create a new volume as either a clone or a full-copy.</p> \n<p><strong>Replication</strong> – You can replicate a point-in-time view of an OpenZFS volume to another volume across file systems, AWS Regions, and AWS accounts. FSx uses <a href=\"https://openzfs.github.io/openzfs-docs/man/master/8/zfs-send.8.html\">ZFS send/receive</a> technology behind the scenes to perform this replication and automatically establishes and maintains network connectivity between file systems to handle interruptions and resume data transfer as needed.</p> \n<p><strong>Data Compression</strong> – You can enable ZSTD or LZ4 compression on your OpenZFS volumes to reduce storage cost and speed up data transfer.</p> \n<p><strong>User and Volume Quotas</strong> – You can limit the amount of storage consumed by an individual volume or user.</p> \n<p><span><strong>Things to Know</strong></span><br> Here are a couple of things to keep in mind before we wrap up:</p> \n<p><strong>Regions</strong> – This new storage class is available in the US East (Ohio, N. Virginia), US West (Oregon), Asia Pacific (Mumbai, Singapore, Sydney, Tokyo), Canada (Central), and Europe (Frankfurt, Ireland) AWS Regions.</p> \n<p><strong>Pricing</strong> – Pricing is based on the amount of primary storage consumed (GB/Month) and read cache provisioned (GB/Month). See the <a href=\"https://aws.amazon.com/fsx/openzfs/pricing/\">Amazon FSx for OpenZFS Pricing</a> page for more information.</p> \n<p></p>\n<p>— <a href=\"https://twitter.com/jeffbarr\">Jeff</a>;</p>\n<p></p>",
            "url": "https://aws.amazon.com/blogs/aws/announcing-amazon-fsx-intelligent-tiering-a-new-storage-class-for-fsx-for-openzfs/",
            "title": "Announcing Amazon FSx Intelligent-Tiering, a new storage class for FSx for OpenZFS",
            "date_modified": "2024-12-02T03:22:42.000Z"
        },
        {
            "content_html": "<p><strong>Update on December 2, 2024</strong>: Updated SDKs with default integrity protections will be available in the coming weeks.</p> \n<hr> \n<p>At <a href=\"https://aws.amazon.com/\">Amazon Web Services (AWS)</a>, the vast majority of new capabilities are driven by your direct feedback. <a href=\"https://aws.amazon.com/blogs/aws/new-additional-checksum-algorithms-for-amazon-s3/\">Two years ago, Jeff announced</a> additional checksum algorithms and the optional client-side computation of checksums to make sure the objects stored on Amazon S3 are exactly what you sent. You told us you love this extra verification because it gives you confidence the object stored is the one you sent. You also told us you would prefer to have this extra verification enabled automatically, freeing you from developing additional code.</p> \n<p>Starting today, we’re updating the <a href=\"https://aws.amazon.com/s3/\">Amazon Simple Storage Service (Amazon S3)</a> default behavior when you upload objects. To build upon its existing durability posture, Amazon S3 now automatically verifies that your data is correctly transmitted over the network from your applications to your S3 bucket.</p> \n<p>Amazon S3 is designed for 99.999999999% data durability (<a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/DataDurability.html\">that’s 11 nines</a>). Amazon S3 has always verified the integrity of object uploads by calculating checksums when objects reach our servers, before they are written to multiple storage devices. Once your data is stored in Amazon S3, it continually monitors data durability over time with periodic integrity checks of data at rest. Amazon S3 also actively monitors the redundancy of your data to help verify that your objects can tolerate the concurrent failure of multiple storage devices.</p> \n<p>But data can still face integrity risks as it traverses the public internet before reaching our servers. Issues such as faulty hardware on networks we don’t manage or client software bugs could potentially corrupt or drop data before Amazon S3 has a chance to validate it. Previously, you could extend the integrity protection by providing your own precomputed checksums with your <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html\">PutObject</a> or <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html\">UploadPart</a> requests. However, this requires configuring tools and applications to generate and track checksums, which can be complex to implement consistently across all your client applications uploading objects to Amazon S3.</p> \n<p>The new default behavior builds upon existing data integrity protections without requiring any changes to your applications. Additionally, the new checksums are stored in the object’s metadata, making them accessible for integrity verification at any time.</p> \n<p><span><strong>Automatic client-side integrity protection<br> </strong></span>Amazon S3 now extends data integrity protection all the way to client-side applications by default. The latest versions of our <a href=\"https://aws.amazon.com/tools/\">AWS SDKs</a> automatically calculate a <a href=\"https://en.wikipedia.org/wiki/Cyclic_redundancy_check\">cyclic redundancy check (CRC)-based checksum</a> for each upload and send it to Amazon S3. Amazon S3 independently calculates a checksum on the server side and validates it against the provided value before durably storing the object and its checksum in the object’s metadata.</p> \n<p>When your client application doesn’t send a CRC checksum (maybe it uses an old version of our SDK or you haven’t updated your application custom code yet), Amazon S3 computes a CRC-based checksum anyway and stores it in the object metadata for future reference. You can compare at a later stage the stored CRC with a CRC computed on your side and verify the network transmission was correct.</p> \n<p>This new capability provides you with an automatic checksum calculation and validation for new uploads from the latest versions of the AWS SDKs, the <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a>, and the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>. You can also verify the checksum stored in the object’s metadata at any time. The new default data integrity protections use the existing <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/API/API_Checksum.html\">CRC32 and CRC32C algorithms or the new CRC64NVME algorithm</a>. Amazon S3 also provides developers with consistent full-object checksums across single-part and multipart uploads.</p> \n<p>When uploading files in multiple parts, the SDKs calculate checksums for each part. Amazon S3 uses these checksums to verify the integrity of each part through the <code>UploadPart</code> API. Additionally, S3 validates the entire file’s size and checksum when you call the <code>CompleteMultipartUpload</code> API.</p> \n<p>The <code>CreateMultiPartUpload</code> API introduces a new HTTP header, <code>x-amz-checksum-type</code>, which lets you specify the type of checksum to use. You can choose either a full object checksum (calculated by combining the checksums of all individual parts) or a composite checksum.</p> \n<p>The full object checksum is stored with the object metadata for future reference. This new protection works seamlessly with <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html\">server-side encryption</a>. The consistent behavior across uploads, multipart uploads, downloads, and encryption modes simplifies client-side integrity checks. The ability to use full-object checksums to validate integrity and store them for use later can help you streamline your applications.</p> \n<p><span><strong>Let’s see it in action</strong></span><br> To start using this additional integrity protection, update to the latest version of the AWS SDK or AWS CLI. No code changes are required to enable the new integrity protections.</p> \n<p><strong>Case 1: Amazon S3 now attaches a checksum to objects on the server side when objects are uploaded without a checksum</strong></p> \n<p>I wrote a simple Python script to upload and download content to and from an S3 bucket. I enabled maximum logging verbosity to see the actual HTTP headers sent to and from Amazon S3.</p> \n<pre><code>import boto3\nimport logging\n\nBUCKET_NAME=\"aws-news-blog-20241111\"\nCONTENT='Hello World!'\nOBJECT_NAME='test.txt'\n\n# Enable debug logging for boto3 and botocore to stdout (this is verbose !!!)\nlogging.basicConfig(level=logging.DEBUG)\n\n# create a s3 client\nclient = boto3.client('s3')\n\n# put an object\nclient.put_object(Bucket=BUCKET_NAME, Key=OBJECT_NAME, Body=CONTENT)\n\n# get the object \nresponse = client.get_object(Bucket=BUCKET_NAME, Key=OBJECT_NAME)\nprint(response['Body'].read().decode('utf-8'))</code></pre> \n<p>In the first step of this demo, I use an old AWS SDK for Python that doesn’t compute the CRC checksum on the client side. Despite this, I can observe that Amazon S3 now responds with a checksum it computed upon receiving the object.</p> \n<pre><code>S3 RESPONSE:\n{\n    ...\n    \"x-amz-checksum-crc64nvme\": \"AuUcyF784aU=\",\n    \"x-amz-checksum-type\": \"FULL_OBJECT\",\n    ...\n}</code></pre> \n<p><strong>Case 2: Upload with manually pre-computed CRC64NVME checksum, a new checksum type</strong></p> \n<p>When I don’t have the option to use the latest version of the AWS SDK, or when I use my own code to upload objects to S3 buckets, I can compute the checksum and send it in the <code>PutObject</code> API request. Here is how I compute the checksum on my content before sending it to Amazon S3. To keep this code short, I use the <code>checksums</code> package available in the new AWS SDK for Python.</p> \n<pre><code>from awscrt import checksums\nimport base64\n\nchecksum = checksums.crc64nvme(\"Hello World!\")\nchecksum_bytes = checksum.to_bytes(8, byteorder='big')  # CRC64 is 8 bytes\nchecksum_base64 = base64.b64encode(checksum_bytes)\nprint(checksum_base64)</code></pre> \n<p>And when I run it, I see the CRC64NVME checksum is the same as the one returned by Amazon S3 in the previous step.</p> \n<pre><code>$ python crc.py\nb'AuUcyF784aU='</code></pre> \n<p>I can provide this checksum as part of the <code>PutObject</code> API call.</p> \n<pre><code>response = s3.put_object(\n    Bucket=BUCKET_NAME,\n    Key=OBJECT_NAME,\n    Body=b'Hello World!',\n    ChecksumAlgorithm='CRC64NVME', \n    ChecksumCRC64NVME=checksum_base64\n)</code></pre> \n<p><strong>Case 3: The new SDKs compute the checksum on the client-side</strong></p> \n<p>Now, I run the upload and download script again. This time, I use the latest version of the AWS SDK for Python. I observe that the SDK now sends the CRC headers in the request. The response also contains the checksum. I can easily compare the versions in the request and in the response to make sure the object received is the one I sent.</p> \n<pre><code>REQUEST:\n{\n    ...\n    \"x-amz-checksum-crc64nvme\": \"AuUcyF784aU=\",\n    \"x-amz-checksum-type\": \"FULL_OBJECT\",\n    ... \n}</code></pre> \n<p>At any time, I can request the object checksum to verify the integrity of my local copy using the <code>HeadObject</code> or <code>GetObject</code> APIs.</p> \n<pre><code> get_response = s3.get_object(\n        Bucket=BUCKET_NAME,\n        Key=OBJECT_NAME,\n        ChecksumMode='ENABLED'\n    )</code></pre> \n<p>The response object contains the checksum in the <code>HTTPHeaders</code> field.</p> \n<pre><code>{\n...\n    \"x-amz-checksum-crc64nvme\": \"AuUcyF784aU=\",\n    \"x-amz-checksum-type\": \"FULL_OBJECT\",\n...\n}</code></pre> \n<p><strong>Case 4: Multi-part uploads with new CRC-based whole-object checksum</strong></p> \n<p>When uploading large objects using the <code>CreateMultipartUpload</code>, <code>UploadPart</code>, and <code>CompleteMultipartUpload</code> APIs, the latest version of the SDK will automatically compute the checksums for you.</p> \n<p>If you want to validate the integrity of your data by using a known content checksum, you can pre-compute the CRC-based whole-object checksum for multi-part uploads to simplify your client side tooling. When using full object checksums for multi-part uploads, you no longer have to keep track of part level checksums as you upload objects.</p> \n<pre><code>\n# precomputed CRC64NVME checksum for the full object\nfull_object_crc64_nvme_checksum = 'Naz0uXkYBPM='\n\n# start multipart upload\ncreate_response = s3.create_multipart_upload(\n            Bucket=BUCKET_NAME,\n            Key=OBJECT_NAME,\n            ChecksumAlgorithm='CRC64NVME',\n            ChecksumType='FULL_OBJECT'\n        )\nupload_id = create_response['UploadId']\n\n# Upload parts\nuploaded_parts = []\n\n# part 1\ndata_part_1 = b'0' * (5 * 1024 * 1024) # minimum part size\nupload_part_response_1 = s3.upload_part(\n    Body=data_part_1,\n    Bucket=BUCKET_NAME,\n    Key=OBJECT_NAME,\n    PartNumber=1,\n    UploadId=upload_id,\n    ChecksumAlgorithm='CRC64NVME'\n)\nuploaded_parts.append({'PartNumber': 1, 'ETag': upload_part_response_1['ETag']})\n\n# part 2\ndata_part_2 = b'0' * (5 * 1024 * 1024)\nupload_part_response_2 = s3.upload_part(\n    Body=data_part_2,\n    Bucket=BUCKET_NAME,\n    Key=OBJECT_NAME,\n    PartNumber=2,\n    UploadId=upload_id,\n    ChecksumAlgorithm='CRC64NVME'\n)\nuploaded_parts.append({'PartNumber': 2, 'ETag': upload_part_response_2['ETag']})\n\n# Complete the multipart upload with the FULL_OBJECT CRC64NVME checksum to validate the integrity of your entire object. \ncomplete_response = s3.complete_multipart_upload(\n            Bucket=BUCKET_NAME,\n            Key=OBJECT_NAME,\n            UploadId=upload_id,\n            ChecksumCRC64NVME=full_object_crc64_nvme_checksum,\n            ChecksumType='FULL_OBJECT',\n            MultipartUpload={'Parts': uploaded_parts}\n        )\nprint(complete_response)</code></pre> \n<p><span><strong>Things to know<br> </strong></span>For your existing objects, the checksum will be added when you copy them. We updated the <code>CopyObject</code> API so you can choose the desired checksum algorithm for the destination object.</p> \n<p>This new client-side checksum calculation is implemented in the latest version of the AWS SDKs. When you use an old SDK or custom code that doesn’t pre-compute checksums, Amazon S3 computes the checksum on all new objects it receives and stores it in the object’s metadata, even for multipart uploads.</p> \n<p><span><strong>Pricing and availability<br> </strong></span>This extended checksum computation and storage is available in all <a href=\"https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#region\">AWS Regions</a> at no additional cost.</p> \n<p>Update your AWS SDK and AWS CLI today to automatically benefit from this additional integrity protection for data in transit.</p> \n<p data-pm-slice=\"1 1 []\">To learn more about data integrity protection on Amazon S3, visit <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html\">Checking object integrity</a> in the Amazon S3 User Guide.</p> \n<p><a href=\"https://twitter.com/sebsto\">-- seb</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-default-data-integrity-protections-for-new-objects-in-amazon-s3/",
            "title": "Introducing default data integrity protections for new objects in Amazon S3",
            "date_modified": "2024-12-01T20:46:36.000Z"
        },
        {
            "content_html": "<p><a href=\"https://aws.amazon.com/verified-access/\">AWS Verified Access</a> provides secure access to your corporate applications and resources without a virtual private network (VPN). <a href=\"https://aws.amazon.com/blogs/aws/aws-verified-access-preview-vpn-less-secure-network-access-to-corporate-applications/\">We launched <span title=\"AWS Verified Access\">Verified Access</span></a> in preview at re:Invent 2 years ago as a way to provide secure, VPN-less access to corporate applications, enabling organizations to manage network access based on identity and device security instead of IP addresses, which increases control and security over application access.</p> \n<p>Today, <span title=\"AWS Verified Access\">Verified Access</span> is launching a preview of its secure, VPN-less access capabilities to non-HTTP(S) applications and resources, enabling <a href=\"https://aws.amazon.com/security/zero-trust/\">zero trust</a> access to corporate resources over protocols such as Secure Shell (SSH) and Remote Desktop Protocol (RDP).</p> \n<p>Organizations increasingly require secure, remote access to internal resources such as databases, remote desktops, and <a href=\"https://aws.amazon.com/ec2/\">Amazon Elastic Compute Cloud (Amazon EC2)</a> instances. Traditional VPN solutions, although effective for network access, often grant broad privileges and don’t support granular access controls, which can expose infrastructure with sensitive data. Although some organizations use bastion hosts to mediate access, this approach can create complexity and policy inconsistencies across HTTP(S) and non-HTTP(S) applications. With the rise of zero trust architectures, these gaps highlight the need for a secure access solution that extends consistent access policies across all applications and resources.</p> \n<p><span title=\"AWS Verified Access\">Verified Access</span> addresses these needs by providing zero trust access controls for your corporate applications and resources. By supporting protocols such as SSH, RDP, or Java Database Connectivity (JDBC) or Open Database Connectivity (ODBC), <span title=\"AWS Verified Access\">Verified Access</span> simplifies your security operations. Now, you can establish uniform, context-aware access policies across your corporate applications and resources. <span title=\"AWS Verified Access\">Verified Access</span> evaluates each access request in real time, making sure access is granted only to users who meet specific identity and device security requirements. Additionally, it eliminates the need for separate VPNs or bastion hosts, streamlining operations and reducing the risk of over-privileged access.</p> \n<p>One of my favorite capabilities is onboarding a group of resources by specifying their IP <a href=\"https://aws.amazon.com/what-is/cidr/?nc1=h_ls\">Classless Inter-Domain Routing (CIDR)</a> and ports, rather than onboarding one resource at a time. <span title=\"AWS Verified Access\">Verified Access</span> automatically creates DNS records for each active resource within the specified CIDR range. This eliminates the need for manual DNS configuration and users can therefore connect to new resources instantly.</p> \n<p><span><strong>Using Verified Access for non-HTTPS access<br> </strong></span>Configuring <span title=\"AWS Verified Access\">Verified Access</span> for non-HTTPS access isn’t very different from what exists today. You can read <a href=\"https://aws.amazon.com/blogs/aws/aws-verified-access-preview-vpn-less-secure-network-access-to-corporate-applications/\">the blog post I wrote for the launch of the preview 2 years ago</a> or the <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/getting-started.html\">Get started with Verified Access</a> tutorial to learn how to get started.</p> \n<p><span title=\"AWS Verified Access\">Verified Access</span> proposes two new types of endpoint targets: a target for one single resource and a target for multiple resources.</p> \n<p>With the <strong>network interface, load balancer, or RDS endpoint target</strong> you can provide access to an individual resource such as an <a href=\"https://aws.amazon.com/rds/\">Amazon Relational Database Service (Amazon RDS)</a> instance or an arbitrary TCP application fronted by a <a href=\"https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html\">Network Load Balancer</a> or an <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html\">elastic network interface</a>. This type of target endpoint is defined by a combination of a target type (such as a load balancer or a network interface) and a range of TCP ports. <span title=\"AWS Verified Access\">Verified Access</span> will provide a DNS name for each endpoint upon its creation. A <span title=\"AWS Verified Access\">Verified Access</span> DNS name is assigned for each target. This is the name end users will use to securely access the resource.</p> \n<p>With <strong>network CIDR endpoint target</strong>, the resources are defined using an IP CIDR&nbsp;and port range. Through this type of endpoint target, you can easily provision secure access to ephemeral resources such as EC2 instances over protocols such as SSH and RDP. This is done without having to perform any actions such as creating or deleting endpoint targets each time a resource is added or removed. As long as these resources are assigned an IP address from the defined CIDR, <span title=\"AWS Verified Access\">Verified Access</span> provides a unique public DNS record for each active IP detected in the defined CIDR.</p> \n<p>Here is a diagram of the setup for this demo.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/28/illustration-ava-20241128-01.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/28/illustration-ava-20241128-01.png\" alt=\"AWS Verified Access Demo Setup\" width=\"800\" height=\"450\"></a></p> \n<p><strong>Part 1: As a Verified Access administrator</strong></p> \n<p>As a <span title=\"AWS Verified Access\">Verified Access</span> administrator, I create the Verified Access <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/verified-access-instances.html\">instance</a>, <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/trust-providers.html\">trust provider</a>, <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/verified-access-groups.html\">access group</a>, <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/verified-access-endpoints.html\">endpoint</a>, and <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/auth-policies.html\">access policies</a>, allowing access by the end user to the SSH server.</p> \n<p>For this demo, I configure a <span title=\"AWS Verified Access\">Verified Access</span> network CIDR endpoint target. I select <strong>TCP</strong> as <strong>Protocol</strong> and <strong>Network CIDR</strong> as <strong>Endpoint type</strong>. I make sure the <strong>CIDR</strong> range is within the one of the <strong>VPC </strong>where my target resources are. I select the TCP <strong>Port ranges</strong> and the <strong>Subnets </strong>within the VPC.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/19/2024-11-18_14-00-39.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/19/2024-11-18_14-00-39.png\" alt=\"AVA : Create endpoint\" width=\"800\" height=\"717\"></a></p> \n<p>This is a good moment to stretch your legs and refill your cup of coffee, it takes a few minutes to create the endpoint.</p> \n<p>Once, the status is <img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2705.png\" alt=\"✅\"> <strong><span>Active</span></strong>, I launch an EC2 instance in a private <a href=\"https://aws.amazon.com/vpc/\">Amazon Virtual Private Cloud (Amazon VPC)</a>. I enable SSH and configure the instance’s security group to only access requests coming from the VPC. A few minutes later, I can see the instance IP has been detected and assigned a DNS name to connect to from the <span title=\"AWS Verified Access\">Verified Access</span> client application.</p> \n<p>I also have the option during the configuration to delegate my own DNS subdomain, such as <code>secure.mycompany.com</code>, and <span title=\"AWS Verified Access\">Verified Access</span> will assign DNS names for the resources within that subdomain.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-20_16-07-09.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-20_16-07-09.png\" alt=\"AVA : DNS names\" width=\"800\" height=\"350\"></a></p> \n<p><strong>Create an access policy</strong></p> \n<p>At this stage, there is no policy defined on the Verified Access endpoint. It will deny every request by default.</p> \n<p>On the <strong>Verified Access groups</strong> page, I select the <strong>Policy</strong> tab. Then I select the <strong>Modify Verified Access endpoint policy</strong> button to create an access policy.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2022/11/24/2022-11-24_18-08-14.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2022/11/24/2022-11-24_18-08-14.png\" alt=\"Verified Access - group policy tab\" width=\"810\" height=\"458\"></a></p> \n<p>I enter a policy allowing anybody who is authenticated and has an email address ending with <code>@amazon.com</code>. This is the email address I used for the user defined in <a href=\"https://aws.amazon.com/iam/identity-center/\">AWS IAM Identity Center</a>. Note that the name after <code>context</code> is the name I entered as <strong>Policy reference name</strong> when I created the <strong>Verified Access trust provider</strong>. The <a href=\"https://docs.aws.amazon.com/verified-access/latest/ug/what-is-verified-access.html\">documentation page</a> has the details of the policy syntax, the attributes, and the operators I can use.</p> \n<pre><code>permit(principal, action, resource)\nwhen {\n    context.awsnewsblog.user.email.address like \"*@amazon.com\"\n};</code></pre> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2022/11/24/2022-11-24_18-12-59.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2022/11/24/2022-11-24_18-12-59.png\" alt=\"Verified Access - group define policy\" width=\"810\" height=\"538\"></a></p> \n<p>After a few minutes, Verified Access updates the policy and becomes <strong><span>Active</span></strong> again.</p> \n<p><strong><span><span>Distribute the configuration to clients</span></span></strong></p> \n<p>The last task as a <span title=\"AWS Verified Access\">Verified Access</span> administrator is to extract the JSON configuration file of the client applications.</p> \n<p>I retrieve the client application configuration file with the <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a>. As a system administrator, I’ll distribute this configuration to each client machine.</p> \n<pre><code>aws ec2 export-verified-access-instance-client-configuration \\\n     --verified-access-instance-id \"vai-0dbf2c4c011083069\"\n\n{\n    \"Version\": \"1.0\",\n    \"VerifiedAccessInstanceId\": \"vai-0dbf2c4c011083069\",\n    \"Region\": \"us-east-1\",\n    \"DeviceTrustProviders\": [],\n    \"UserTrustProvider\": {\n        \"Type\": \"iam-identity-center\",\n        \"Scopes\": \"verified_access:application:connect\",\n        \"Issuer\": \"https://identitycenter.amazonaws.com/ssoins-xxxx\",\n        \"PkceEnabled\": true\n    },\n    \"OpenVpnConfigurations\": [\n        {\n            \"Config\": \"Y2...bWU=\",\n            \"Routes\": [\n                {\n                    \"Cidr\": \"2600:1f10:4a02:8700::/57\"\n                }\n            ]\n        }\n    ]\n}</code></pre> \n<p>Now that I have a resource to connect to and the <span title=\"AWS Verified Access\">Verified Access</span> infrastructure in place, let me show you the end user experience to access a network endpoint.</p> \n<p><strong>Part 2: As an end user</strong></p> \n<p>As the end user, I receive a link to <a href=\"https://aws.amazon.com/verified-access/connectivity-client-download/\">download and install the <span title=\"AWS Verified Access\">Verified Access</span> Connectivity Client application</a>. We support Windows and macOS clients at the time of this writing.</p> \n<p><span>I install the configuration file I received from my administrator. I use <code>ClientConfig1.json</code> as the file name and I copy the file to <code>C:\\ProgramData\\Connectivity Client</code> on Windows or <code>/Library/Application\\ Support/Connectivity\\ Client</code> on macOS.</span></p> \n<p><span>This is the same configuration file for all users, and the system administrator might push the file to all client machines using an endpoint management tool.</span></p> \n<p>I start the Connectivity Client application. I choose <strong>Sign in</strong> to start the authentication sequence.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-20_16-20-33.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-20_16-20-33.png\" alt=\"AVA Client : Sign in\" width=\"450\" height=\"275\"></a>The authentication opens my web browser on the authentication page of my identity provider. The exact screen and login sequence varies from one provider to the other. After I’m authenticated, the Connectivity Client creates the secure tunnel to access my resource, an EC2 instance for this demo.</p> \n<table> \n <tbody> \n  <tr> \n   <td><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-19_22-08-40.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-19_22-08-40.png\" alt=\"AVA Client : Connecting\" width=\"350\" height=\"214\"></a></td> \n   <td><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-19_22-10-04.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/2024-11-19_22-10-04.png\" alt=\"AVA Client : Connected\" width=\"350\" height=\"214\"></a></td> \n  </tr> \n </tbody> \n</table> \n<p>Once the status is <strong>Connected,</strong> I can securely connect to the resource, using the DNS name provided by <span title=\"AWS Verified Access\">Verified Access</span>. In a terminal application, I type the <code>ssh</code> command to start the connection.</p> \n<p>For this demo, I configured a delegated DNS domain <code>secure.mycompany.com</code> for Verified Access. The DNS address I received for the EC2 instance is <code>10-0-1-199.awsnews.secure.mycompany.com</code>.</p> \n<pre><code>$ ssh -i mykey.pem ec2-user@10-0-1-199.awsnews.secure.mycompany.com\n\n   ,     #_\n   ~\\_  ####_        Amazon Linux 2023\n  ~~  \\_#####\\\n  ~~     \\###|\n  ~~       \\#/ ___   https://aws.amazon.com/linux/amazon-linux-2023\n   ~~       V~' '-&gt;\n    ~~~         /\n      ~~._.   _/\n         _/ _/\n       _/m/'\nLast login: Sat Nov 17 20:17:46 2024 from 1.2.3.4\n\n$</code></pre> \n<p><strong><span>Availability and pricing</span><br> </strong><span title=\"AWS Verified Access\">Verified Access</span> is available as a public preview in 18 <a href=\"https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#region\">AWS Regions</a>: US East (Ohio, N. Virginia), US West (N. California, Oregon), Asia Pacific (Jakarta, Mumbai, Seoul, Singapore, Sydney, Tokyo), Canada (Central), Europe (Frankfurt, Ireland, London, Milan, Stockholm), Israel (Tel Aviv), and South America (São Paulo).</p> \n<p><span>You’re charged for each hour</span> that your non-HTTP(S) Verified Access endpoint remains active and per connection. The first 100 connections per month on each Verified Access endpoint are free. For more information, refer to <a href=\"https://aws.amazon.com/verified-access/pricing\" target=\"_blank\" rel=\"noopener noreferrer\">AWS Verified Access Pricing</a>.</p> \n<p>With <span title=\"AWS Verified Access\">Verified Access</span> for HTTP(S) and non-HTTP(S) applications you can unify the access controls to your private applications and systems and apply zero trust policies uniformly to all applications, and SSH, RDP, and HTTP(S) resources. It reduces the complexity of your network infrastructure and helps you to implement zero-trust access to your applications and resources. Finally, it adapts to your growing infrastructure, automating DNS setup and supporting large-scale deployments without resource-specific registration.</p> \n<p>Go, try <span title=\"AWS Verified Access\">Verified Access</span> today, and share your feedback with the team!</p> \n<p><a href=\"https://twitter.com/sebsto\">-- seb</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/aws-verified-access-now-supports-secure-access-to-resources-over-non-https-protocols/",
            "title": "AWS Verified Access now supports secure access to resources over non-HTTP(S) protocols (in preview)",
            "date_modified": "2024-12-01T20:40:48.000Z"
        },
        {
            "content_html": "<p>Today, we’re announcing the general availability of <a href=\"https://aws.amazon.com/eks/auto-mode?trk=ea66575f-9c47-4d58-9bbe-a262d8aa74fd&amp;sc_channel=el\">Amazon Elastic Kubernetes Service (Amazon EKS) Auto Mode</a>, a new capability to streamline Kubernetes cluster management for compute, storage, and networking, from provisioning to on-going maintenance with a single click. You can achieve higher agility, performance, and cost-efficiency by eliminating the operational overhead of managing the cluster infrastructure required to run production-grade Kubernetes applications at scale on <a href=\"https://aws.amazon.com/\">Amazon Web Services (AWS)</a>.</p> \n<p>Customers choose Amazon EKS because they can use the open standards and portability of Kubernetes with the security, scalability, and availability of AWS cloud. While Kubernetes gives advanced customers deep controls over application operations, other customers find managing the components required for production-grade Kubernetes applications to be complex and labor-intensive.</p> \n<p>With the EKS Auto Mode, you can automate cluster management without deep Kubernetes expertise, because it selects optimal compute instances, dynamically scales resources, continuously optimizes costs, manages core add-ons, patches operating systems, and integrates with AWS security services. AWS expands its operational responsibility in EKS Auto Mode compared to customer-managed infrastructure in your EKS clusters. In addition to the EKS control plane, AWS will configure, manage, and secure the AWS infrastructure in EKS clusters that your applications need to run.</p> \n<p>You can now get started quickly, improve performance, and reduce overhead, enabling you to focus on building applications that drive innovation instead of on cluster management tasks. EKS Auto Mode also reduces the work required to acquire and run cost-efficient GPU-accelerated instances so that your <a href=\"https://aws.amazon.com/generative-ai/\">generative AI</a> workloads have the capacity they need when they need it.</p> \n<p><strong><u>Get started with Amazon EKS Auto Mode</u></strong><br> To get started, go to the <a href=\"https://console.aws.amazon.com/eks/home#/cluster-create\">Amazon EKS console</a> and start to create your EKS cluster. You’ll have two options, <strong>Quick configuration (with EKS Auto Mode)</strong> and <strong>Custom configuration</strong>.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/22/2024-eks-automode-1-create-cluster.png\" width=\"1960\" height=\"2196\"></p> \n<p>After you choose quick configuration, enter your cluster name and Kubernetes version, IAM roles, VPC subnets. You can view configuration default values in EKS Auto Mode whether you can edit after the cluster is created.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/22/2024-eks-automode-2-cluster-detail.png\" width=\"1860\" height=\"2170\"></p> \n<p>EKS Auto Mode enables the following Kubernetes capabilities in your EKS cluster:</p> \n<ul> \n <li>Compute auto scaling and management</li> \n <li>Application load balancing management</li> \n <li>Pod and service networking and network policies</li> \n <li>Cluster DNS and GPU support</li> \n <li>Block storage volume support</li> \n</ul> \n<p>When you choose <strong>Create</strong>, your EKS cluster with Auto Mode will be deployed in minutes with a single click.</p> \n<p>If you choose the custom configuration option, you can customize other aspects of your cluster. You can use EKS Auto Mode in this option too.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/22/2024-eks-automode-3-configure-cluster.png\" width=\"2516\" height=\"1360\"></p> \n<p>You can also create an EKS Auto Mode cluster using <a href=\"https://aws.amazon.com/cli\">AWS Command Line Interface (AWS CLI)</a>, <a href=\"https://eksctl.io/\">eksctl</a>, and <a href=\"https://aws.amazon.com/cloudformation/\">AWS CloudFormation</a>. Run the following <code>eksctl</code> command to create a new EKS Auto Mode cluster with:</p> \n<pre><code>$ eksctl create cluster --name=&lt;cluster-name&gt; --enable-auto-mode</code></pre> \n<p>To learn more, visit <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/create-auto.html\">Create cluster with EKS Auto Mode</a> in the Amazon EKS User Guide.</p> \n<p>If you want to enable EKS Auto Mode for an existing EKS cluster, choose <strong>Manage</strong> in the <strong>EKS Auto Mode</strong> section of the <strong>Overview </strong>tab in the EKS cluster detail page.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/22/2024-eks-automode-4-cluster-details.png\" width=\"2412\" height=\"1626\"></p> \n<p>Select the box next to <strong>Use EKS Auto Mode</strong> to enable the EKS Auto Mode. You can unselect the EKS Auto Mode that will be configured in the cluster. The default is to create both a system and a default node pool and a node class.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/22/2024-eks-automode-5-manage-eks-automode.png\" width=\"2424\" height=\"1586\"></p> \n<p>You can also migrate from <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/auto-migrate-karpenter.html\">Karpenter</a>, <a href=\"https://alpha.www.docs.aws.a2z.com/eks/latest/userguide/auto-migrate-mng.html\">EKS Managed Node Groups</a>, and EKS Fargate to EKS Auto Mode. To learn more, visit <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/migrate-auto.html\">Enable EKS Auto Mode on existing EKS clusters</a> in the Amazon EKS User Guide.</p> \n<p>To meet your workload requirements, you can configure specific aspects of your EKS Auto Mode clusters. While EKS Auto Mode manages most infrastructure components automatically, you can customize <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/create-node-class.html\">node networking settings</a>, <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/create-node-pool.html\">node compute resources</a>, <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/create-storage-class.html\">storage class settings</a>, and <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/auto-configure-alb.html\">application load balancing behaviors</a> while maintaining the benefits of automated infrastructure management. To learn more, visit <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/settings-auto.html\">Change EKS Auto cluster settings</a> in the Amazon EKS User Guide.</p> \n<p>Now, you can deploy different types of workloads to Amazon EKS clusters running in EKS Auto Mode. We provide key workload patterns including <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/automode-workload.html\">sample applications</a>, <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/auto-elb-example.html\">load-balanced web applications</a>, <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/sample-storage-workload.html\">stateful workloads using persistent storage</a>, and <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/associate-workload.html\">workloads with specific node placement requirements</a>. Each example includes complete manifests and step-by-step deployment instructions that you can use as templates for your own applications. To learn more, visit <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/auto-workloads.html\">Run workloads in EKS Auto Mode clusters</a> in the Amazon EKS User Guide.</p> \n<div> \n <p><strong><u>Now available</u></strong><br> <a href=\"https://aws.amazon.com/eks/auto-mode?trk=ea66575f-9c47-4d58-9bbe-a262d8aa74fd&amp;sc_channel=el\">Amazon EKS Auto Mode</a> is now available in all commercial AWS Regions except China Regions where Amazon EKS is available. You can enable EKS Auto Mode in any EKS cluster running Kubernetes 1.29 and above with no upfront fees or commitments—you pay for the management of the compute resources provisioned, in addition to your regular EC2 costs. To learn more, visit&nbsp;<a href=\"https://aws.amazon.com/eks/pricing/\">Amazon EKS pricing page.</a></p> \n <p>Please register for the online webinar: <a href=\"https://aws-experience.com/emea/smb/events/series/simplifying-kubernetes-operations-with-amazon-eks-auto-mode\">Simplifying Kubernetes operations with Amazon EKS Auto Mode</a> on December 12, 2024 to learn more about how EKS Auto Mode can accelerate your time to deploy workloads to production and reduce the operational overheads of Kubernetes. To learn more, visit <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/automode.html\"><u>Automate cluster infrastructure with EKS Auto Mode</u></a> in the Amazon EKS User Guide.</p> \n <p>Give EKS Auto Mode a try in the <a href=\"https://console.aws.amazon.com/eks/home#/cluster-create\">Amazon EKS console</a> and send feedback to <a href=\"https://repost.aws/tags/TA4IvCeWI1TE66q4jEj4Z9zg/amazon-elastic-kubernetes-service\">AWS re:Post for EKS</a> or through your usual AWS Support contacts.</p> \n <p>— <a href=\"https://twitter.com/channyun\">Channy</a></p> \n</div>",
            "url": "https://aws.amazon.com/blogs/aws/streamline-kubernetes-cluster-management-with-new-amazon-eks-auto-mode/",
            "title": "Streamline Kubernetes cluster management with new Amazon EKS Auto Mode",
            "date_modified": "2024-12-01T20:35:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42289087\">Comments</a>",
            "url": "https://avi.im/blag/2024/s3-log/",
            "title": "Building a distributed log using S3 (under 150 lines of Go)",
            "date_modified": "2024-12-01T16:10:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42288956\">Comments</a>",
            "url": "https://bilbof.com/posts/kubernetes-on-hetzner",
            "title": "Kubernetes on Hetzner: cutting my infra bill by 75%",
            "date_modified": "2024-12-01T15:43:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42287526\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=42287526",
            "title": "Ask HN: What's the one feature you'd want in a GitHub productivity tool?",
            "date_modified": "2024-12-01T10:21:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42285310\">Comments</a>",
            "url": "https://pablosblog.dev/posts/3/",
            "title": "Unlocking the Power of DynamoDB: A Developer's Journey",
            "date_modified": "2024-12-01T01:05:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42275609\">Comments</a>",
            "url": "https://lucvandonkersgoed.com/2024/11/16/making-aws-news-stupid-fast-with-smart-caching/",
            "title": "Making AWS News stupid fast with smart caching",
            "date_modified": "2024-11-29T17:52:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/gmb1vg/quick_takes_on_latest_cloudflare_public\">Comments</a></p>",
            "url": "https://surfingcomplexity.blog/2024/11/28/quick-takes-on-the-latest-cloudflare-public-incident-write-up/",
            "title": "Quick takes on the latest Cloudflare public incident write-up",
            "date_modified": "2024-11-29T01:05:55.000Z"
        },
        {
            "content_html": "<p>Today, we are announcing support for <a href=\"https://aws.amazon.com/hpc/efa/\">Elastic Fabric Adapter (EFA)</a> and <a href=\"https://developer.nvidia.com/gpudirect-storage\">NVIDIA GPUDirect Storage (GDS)</a> on <a href=\"https://aws.amazon.com/fsx/lustre/\">Amazon FSx for Lustre</a>. EFA is a network interface for Amazon EC2 instances that makes it possible to run applications requiring high levels of inter-node communications at scale. GDS is a technology that creates a direct data path between local or remote storage and GPU memory. With these enhancements, Amazon FSx for Lustre with EFA/GDS support provides up to 12 times higher (up to 1200 Gbps) per-client throughput compared to the previous FSx for Lustre version.</p> \n<p>You can use FSx for Lustre to build and run the most performance demanding applications, such as deep learning training, drug discovery, financial modeling, and autonomous vehicle development. As datasets grow and new technologies emerge, you can adopt increasingly powerful GPU and HPC instances such as Amazon EC2 <a href=\"https://aws.amazon.com/ec2/instance-types/p5/\">P5</a>, <a href=\"https://aws.amazon.com/ec2/instance-types/trn1/\">Trn1</a>, and <a href=\"https://aws.amazon.com/ec2/instance-types/hpc7a/\">Hpc7a</a>. Until now, when accessing FSx for Lustre file systems, the use of traditional TCP networking limited throughput to 100 Gbps for individual client instances. This adoption is driving the need for FSx for Lustre file systems to provide the performance necessary to optimally utilize the increasing network bandwidth of these cutting-edge EC2 instances when accessing large datasets.</p> \n<p>With EFA and GDS support in FSx for Lustre, you can now achieve up to 1,200 Gbps throughput per client instance (twelve times more throughput than previously) when using P5 GPU instances and <a href=\"https://developer.nvidia.com/cuda-toolkit\">NVIDIA CUDA</a> in your applications.</p> \n<p>With this new capability, you can fully utilize the network bandwidth of the most powerful compute instances and accelerate your <a href=\"https://aws.amazon.com/ai/machine-learning/\">machine learning (ML)</a> and <a href=\"https://aws.amazon.com/hpc/\">HPC</a>&nbsp;workloads. EFA enhances performance by bypassing the operating system and using the <a href=\"https://ieeexplore.ieee.org/document/9167399\">AWS Scalable Reliable Datagram (SRD) protocol</a> to optimize data transfer. GDS further improves performance by enabling direct data transfer between the file system and GPU memory, bypassing the CPU and eliminating redundant memory copies.</p> \n<p>Let’s see how this works in practice.</p> \n<p><span><strong>Creating an Amazon FSx for Lustre file system with EFA enabled<br> </strong></span>To get started, in the <a href=\"https://us-east-2.console.aws.amazon.com/fsx/home#landing\">Amazon FSx console</a>, I choose <strong>Create file system</strong> and then <strong>Amazon FSx for Lustre</strong>.</p> \n<p>I enter a name for the file system. In the <strong>Deployment and storage type</strong> section, I select <strong>Persistent, SSD</strong> and the new <strong>with EFA enabled</strong> option. I select <strong>1000 MB/s/TiB</strong> in the <strong>Throughput per unit of storage</strong> section. With these settings, I enter 4.8 TiB for <strong>Storage capacity</strong>, which is the minimum supported with these settings.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/fsx-lustre-efa.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/fsx-lustre-efa.png\" alt=\"Console screenshot.\" width=\"1195\" height=\"1039\"></a></p> \n<p>For networking, I use the <a href=\"https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html\">default virtual private cloud (VPC)</a> and an <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa-start.html#efa-start-security\">EFA-enabled security group</a>. I leave all other options to their default values.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/fsx-lustre-efa-security-group.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/fsx-lustre-efa-security-group.png\" alt=\"Console screenshot.\" width=\"1195\" height=\"423\"></a></p> \n<p>I review all the options and proceed to create the file system. After a few minutes, the file system is ready to be used.</p> \n<p><span><strong>Mounting an Amazon FSx for Lustre file system with EFA enabled from an Amazon EC2 instance<br> </strong></span>In the <a href=\"https://console.aws.amazon.com/ec2\">Amazon EC2 console</a>, I choose <strong>Launch instance</strong>, enter a name for the instance, and select the Ubuntu Amazon Machine Image (AMI). For <strong>Instance type</strong>, I select&nbsp;<strong>trn1.32xlarge</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/ec2-launch-instance-type.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/ec2-launch-instance-type.png\" alt=\"Console screenshot.\" width=\"967\" height=\"275\"></a></p> \n<p>In <strong>Network settings</strong>, I edit the default settings and select the same subnet used by the FSx Lustre file system. In <strong>Firewall (security groups)</strong>, I select three existing security groups: the EFA-enabled security group used by the FSx for Lustre file system, the default security group, and a security group that provides Secure Shell (SSH) access.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/ec2-launch-networking.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/ec2-launch-networking.png\" alt=\"Console screenshot.\" width=\"967\" height=\"689\"></a></p> \n<p>In <strong>Advanced network configuration</strong>, I select <strong>ENA and EFA</strong> as <strong>Interface type</strong>. Without this setting, the instance would use traditional TCP networking and the connection with the FSx for Lustre file system would still be limited to 100 Gbps in throughput.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/ec2-launch-networking-advanced.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/ec2-launch-networking-advanced.png\" alt=\"Console screenshot.\" width=\"967\" height=\"777\"></a></p> \n<p>To have more throughput, I can add more EFA network interfaces, depending on the instance type.</p> \n<p>I launch the instance and, when the instance is ready, I connect using <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html\">EC2 Instance Connect</a>&nbsp;and follow the instructions for <a href=\"https://docs.aws.amazon.com/fsx/latest/LustreGuide/install-lustre-client.html\">installing the Lustre client in the FSx for Lustre User Guide</a>&nbsp;and <a href=\"https://docs.aws.amazon.com/fsx/latest/LustreGuide/configure-efa-clients.html\">configuring EFA clients</a>.</p> \n<p>Then, I follow the instructions for <a href=\"https://docs.aws.amazon.com/fsx/latest/LustreGuide/mounting-ec2-instance.html\">mounting an FSx for Lustre file system from an EC2 instance</a>.</p> \n<p>I create a folder to use as mount point:</p> \n<div> \n <pre><code>sudo mkdir -p /fsx</code></pre> \n</div> \n<p>I select the file system in the FSx console and lookup the <strong>DNS name</strong> and <strong>Mount name</strong>. Using these values, I mount the file system:</p> \n<div> \n <pre><code>sudo mount -t lustre -o relatime,flock file_system_dns_name@tcp:/mountname /fsx</code></pre> \n</div> \n<p>EFA is automatically used when you access an EFA-enabled file system from client instances that support EFA and are using Lustre version 2.15 or higher.</p> \n<p><span><strong>Things to know<br> </strong></span>EFA and GDS support is available today with no additional cost on new <a href=\"https://aws.amazon.com/fsx/lustre/\">Amazon FSx for Lustre</a> file systems in all <a href=\"https://aws.amazon.com/about-aws/global-infrastructure/regions_az/\">AWS Regions</a> where persistent 2 is offered. FSx for Lustre automatically uses EFA when customers access an EFA-enabled file system from client instances that support EFA, without requiring any additional configuration. For a list of EC2 client instances that support EFA, see <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html#efa-instance-types\">supported instance types</a> in the&nbsp;Amazon EC2 User Guide. This <a href=\"https://docs.aws.amazon.com/ec2/latest/instancetypes/ac.html#ac_network\">network specifications table</a> describes network bandwidths and EFA support for instance types in the accelerated computing category.</p> \n<p>To use EFA-enabled instances with FSx for Lustre file systems, you must use Lustre 2.15 clients on Ubuntu 22.04 with kernel 6.8 or higher.</p> \n<p>Note that your client instances and your file systems must be located in the same subnet within your <a href=\"https://aws.amazon.com/vpc/\">Amazon Virtual Private Cloud (Amazon VPC) connection</a>.</p> \n<p>GDS is automatically supported on EFA-enabled file systems. To use GDS with your FSx for Lustre file systems, you need the <a href=\"https://developer.nvidia.com/cuda-toolkit\">NVIDIA Compute Unified Device Architecture (CUDA) package</a>, the <a href=\"https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#nvidia-open-gpu-kernel-modules\">open source NVIDIA driver</a>, and the <a href=\"https://github.com/NVIDIA/gds-nvidia-fs\">NVIDIA GPUDirect Storage Driver</a> installed on your client instance. These packages come preinstalled on the <a href=\"https://aws.amazon.com/ai/machine-learning/amis/\">AWS Deep Learning AMI</a>. You can then use your CUDA-enabled application to use GPUDirect storage for data transfer between your file system and GPUs.</p> \n<p>When planning your deployment, note that EFA-enabled file systems have larger minimum storage capacity increments than file systems that are not EFA-enabled. For instance, if you choose the 1,000 MB/s/TiB throughput tier, the minimum storage capacity for EFA-enabled file systems starts at 4.8 TiB as compared to 1.2TB for FSx for Lustre file systems not enabling EFA. If you’re looking to migrate your existing workloads, you can use <a href=\"https://aws.amazon.com/datasync/\">AWS DataSync</a> to move your data from an existing file system to a new one that supports EFA and GDS.</p> \n<p>For maximum flexibility, FSx for Lustre maintains compatibility with both EFA and non-EFA workloads. When accessing an EFA-enabled file system, traffic from non-EFA client instances automatically flows over traditional TCP/IP networking using <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html\">Elastic Network Adapter (ENA)</a>, allowing seamless access for all workloads without any additional configuration.</p> \n<p>To learn more about EFA and GDS support on FSx for Lustre, including detailed setup instructions and best practices, visit the <a href=\"https://docs.aws.amazon.com/fsx/latest/LustreGuide/what-is.html\">Amazon FSx for Lustre documentation</a>. Get started today and experience the fastest storage performance available for your GPU instances in the cloud.</p> \n<p>— <a href=\"https://twitter.com/danilop\">Danilo</a></p> \n<p><em>Update 11/27: post updated to reflect 12x throughput</em></p>",
            "url": "https://aws.amazon.com/blogs/aws/amazon-fsx-for-lustre-unlocks-full-network-bandwidth-and-gpu-performance/",
            "title": "Amazon FSx for Lustre increases throughput to GPU instances by up to 12x",
            "date_modified": "2024-11-27T22:13:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42260027\">Comments</a>",
            "url": "https://www.bbc.co.uk/news/articles/cje050wz22qo",
            "title": "London's 850-year-old food markets to close",
            "date_modified": "2024-11-27T21:36:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42256771\">Comments</a>",
            "url": "https://kerkour.com/aws-s3-vs-cloudflare-r2-price-performance-user-experience",
            "title": "Comparing AWS S3 with Cloudflare R2: Price, Performance and User Experience",
            "date_modified": "2024-11-27T15:26:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42254870\">Comments</a>",
            "url": "https://github.com/vordenken/AutoPiP",
            "title": "Show HN: AutoPiP – Safari extension for automatic Picture-in-Picture mode",
            "date_modified": "2024-11-27T10:36:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42252336\">Comments</a>",
            "url": "https://benhouston3d.com/blog/why-i-left-kubernetes-for-google-cloud-run",
            "title": "I Didn't Need Kubernetes, and You Probably Don't Either",
            "date_modified": "2024-11-27T02:26:19.000Z"
        },
        {
            "content_html": "<p>You can now specify a desired completion duration (15 minutes to 48 hours) when you copy an <a href=\"https://aws.amazon.com/ebs/\">Amazon Elastic Block Store (Amazon EBS)</a> snapshot within or between AWS Regions and/or accounts. This will help you to meet time-based compliance and business requirements for critical workloads. For example:</p> \n<p><strong>Testing</strong> – Distribute fresh data on a timely basis as part of your Test Data Management (TDM) plan.</p> \n<p><strong>Development</strong> – Provide your developers with updated snapshot data on a regular and frequent basis.</p> \n<p><strong>Disaster Recovery</strong> – Ensure that critical snapshots are copied in order to meet a Recovery Point Objective (RPO).</p> \n<p>Regardless of your use case, this new feature gives you consistent and predictable copies. This does not affect the performance or reliability of standard copies—you can choose the option and timing that works best for each situation.</p> \n<p><span><strong>Creating a Time-Based Snapshot Copy</strong></span><br> I can create time-based snapshot copies from the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>, CLI (<code><a href=\"https://docs.aws.amazon.com/cli/latest/reference/ec2/copy-snapshot.html\">copy-snapshot</a></code>), or API (<code><a href=\"https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopySnapshot.html\">CopySnapshot</a></code>). While working on this post I created two EBS volumes (100 GiB and 1 TiB), filled each one with files, and created snapshots:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/02/eg_snaps_1.jpg\" width=\"894\" height=\"222\"></p> \n<p>To create a time-based snapshot, I select the source as usual and choose <strong>Copy snapshot</strong> from the <strong>Action</strong> menu. I enter a description for the copy, choose the <strong>us-east-1</strong> AWS Region as the destination, select <strong>Enable time-based copy</strong>, and (because this is a time-critical snapshot), enter a 15 minute <strong>Completion duration</strong>:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/21/eg_copy_4.png\" width=\"893\" height=\"772\"></p> \n<p>When I click <strong>Copy snapshot</strong>, the request will be accepted (and the copy will become <em>Pending</em>) only if my account’s throughput quotas are not already exceeded due to the throughput consumed by other active copies that I am making to the destination region. If the account level throughput quota is already exceeded, the console will display an error.</p> \n<p>I can click <strong>Launch copy duration calculator</strong> to get a better idea of the minimum achievable copy duration for the snapshot. I open the calculator, enter my account’s throughput limit, and choose an evaluation period:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/eg_calc_start_1.png\" width=\"855\" height=\"665\"></p> \n<p>The calculator then uses historical data collected over the course of previous snapshot copies to tell me the minimum achievable completion duration. In this example I copied 1,800,000 MiB in the last 24 hours; with time-based copy and my current account throughput quota of 2000 MiB/second I can copy this much data in 15 minutes.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/14/eg_calc_out_1.png\" width=\"871\" height=\"183\"></p> \n<p>While the copy is in progress, I can monitor progress using the console or by calling <code><a href=\"https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSnapshots.html\">DescribeSnapshots</a> </code>and examining the <code>progress</code> field of the result. I can also use the following <a href=\"https://aws.amazon.com/eventbridge\">Amazon EventBridge</a> events to take actions (if the copy operation crosses regions, the event is sent in the destination region):</p> \n<p><strong><code>copySnapshot</code> </strong>– Sent after the copy operation completes.</p> \n<p><code><strong>copyMissedCompletionDuration</strong></code> – Sent if the copy is still pending when the deadline has passed.</p> \n<p><span><strong>Things to Know</strong></span><br> And that’s just about all there is to it! Here’s what you need to know about time-based snapshot copies:</p> \n<p><strong>CloudWatch Metrics</strong> – The <strong>SnapshotCopyBytesTransferred</strong> metric is emitted in the destination region, and reflect the amount of data transferred between the source and destination region in bytes.</p> \n<p><strong>Duration</strong> – The duration can range from 15 minutes to 48 hours in 15 minute increments, and is specified on a per-copy basis.</p> \n<p><strong>Concurrency</strong> – If a snapshot is being copied and I initiate a second copy of the same snapshot to the same destination, the duration for the second one starts when the first one is completed.</p> \n<p><strong>Throughput</strong> – There is a default per-account limit of 2000 MiB/second between each source and destination pair. If you need additional throughput in order to meet your RPO you can request an increase via the <a href=\"https://console.aws.amazon.com/support/home#/\">AWS Support Center</a>. Maximum per-snapshot throughput is 500 MiB/second and cannot be increased.</p> \n<p><strong>Pricing</strong> – Refer to the <a href=\"https://aws.amazon.com/ebs/pricing/\">Amazon EBS Pricing</a> page for complete pricing information.</p> \n<p><strong>Regions</strong> – Time-based snapshot copies are available in all AWS Regions.</p> \n<p>— <a href=\"https://twitter.com/jeffbarr\">Jeff</a>;</p>",
            "url": "https://aws.amazon.com/blogs/aws/time-based-snapshot-copy-for-amazon-ebs/",
            "title": "Time-based snapshot copy for Amazon EBS",
            "date_modified": "2024-11-26T22:31:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42247583\">Comments</a>",
            "url": "https://www.warp.dev/blog/lifting-login-requirement",
            "title": "Warp terminal – no more login required",
            "date_modified": "2024-11-26T17:14:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42241851\">Comments</a>",
            "url": "https://status.flyio.net",
            "title": "Fly.io outage – resolved",
            "date_modified": "2024-11-26T01:47:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42240678\">Comments</a>",
            "url": "https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-functionality-conditional-writes/",
            "title": "Amazon S3 Adds Put-If-Match (Compare-and-Swap)",
            "date_modified": "2024-11-25T22:11:31.000Z"
        },
        {
            "content_html": "<div><div src=\"https://www.youtube.com/embed/LH1J1EbqCaI?start=0&amp;wmode=transparent\"></div><div>\n    <div></div>\n    <div>\n      Ben Schwartz voices the titular character in <em>Sonic the Hedgehog 3</em>.\n\n          </div>\n  </div>\n</div>\n<p>Some lucky folks got a heads-up last week that a trailer for <a href=\"https://en.wikipedia.org/wiki/Sonic_the_Hedgehog_3_(film)\"><em>Sonic the Hedgehog 3</em></a> was about to drop when <a href=\"https://www.ign.com/articles/paramount-made-a-sega-genesis-cartridge-just-to-tease-a-sonic-the-hedgehog-3-trailer-next-week\">Paramount Pictures released</a> a playable Sega Genesis gaming cartridge to select individuals. Hidden among a mini-game and character posters and accessible by entering a cheat code was the trailer release date. True to its word, Paramount just dropped the final trailer for the third film in the successful franchise. All our favorite characters are back, as well as a couple of new ones, most notably a new villain familiar to fans of the games: Shadow, voiced by Keanu Reeves.</p>\n<p><strong>(Spoilers for the first two films below.)</strong></p>\n<p>As <a href=\"https://arstechnica.com/gaming/2022/03/our-fave-speedy-blue-hedgehog-proves-his-worthiness-in-sonic-2-final-trailer/\">previously reported</a>, in the first film, Sonic (Ben Schwartz) teamed up with local town sheriff Tom Wachowski (James Marsden) to stop the sinister mad scientist <a title=\"Doctor Eggman\" href=\"https://en.wikipedia.org/wiki/Doctor_Eggman\">Dr. Robotnik</a> (Jim Carrey). Robotnik wanted to catch and experiment on the hedgehog, and if he could also frame Tom as a domestic terrorist, even better.</p><p><a href=\"https://arstechnica.com/culture/2024/11/keanu-reeves-voices-arch-villain-shadow-in-sonic-3-trailer/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/culture/2024/11/keanu-reeves-voices-arch-villain-shadow-in-sonic-3-trailer/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/culture/2024/11/keanu-reeves-voices-arch-villain-shadow-in-sonic-3-trailer/",
            "title": "Keanu Reeves voices archvillain Shadow in Sonic 3 trailer",
            "date_modified": "2024-11-25T17:36:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42237424\">Comments</a>",
            "url": "https://www.anthropic.com/news/model-context-protocol",
            "title": "Model Context Protocol",
            "date_modified": "2024-11-25T16:14:22.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/pb40pd/clickhouse_fast_deduplicated_reads\">Comments</a></p>",
            "url": "https://medium.com/@jgrodman/clickhouse-fast-deduplicated-reads-de2026a4e0ad",
            "title": "ClickHouse — fast, deduplicated reads",
            "date_modified": "2024-11-25T12:04:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42234415\">Comments</a>",
            "url": "https://github.com/github/roadmap/discussions/1014",
            "title": "GitHub removes 42 \"basic functionality\" features from their roadmap",
            "date_modified": "2024-11-25T08:48:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42234415\">Comments</a>",
            "url": "https://github.com/github/roadmap/discussions/1014",
            "title": "Deprecating outdated issues on the GitHub public roadmap",
            "date_modified": "2024-11-25T08:48:26.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/bpd8fk/zero_disk_architecture\">Comments</a></p>",
            "url": "https://avi.im/blag/2024/zero-disk-architecture/",
            "title": "Zero Disk Architecture",
            "date_modified": "2024-11-24T15:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42219009\">Comments</a>",
            "url": "https://tailscale.com/blog/infra-team-stays-small",
            "title": "How Tailscale's infra team stays small",
            "date_modified": "2024-11-23T03:28:46.000Z"
        },
        {
            "content_html": "<p>Today, I’m excited to introduce a new and improved version of <a href=\"https://aws.amazon.com/systems-manager/\">AWS Systems Manager</a> that brings a highly requested cross-account, and cross-Region experience for managing nodes at scale.</p> \n<p>The new System Manager experience provides centralized visibility of all your managed nodes which include various infrastructure types, such as <a href=\"https://aws.amazon.com/ec2/getting-started/\">Amazon Elastic Compute Cloud (EC2)</a> instances, containers, virtual machines on other cloud providers, on-premise servers, and edge <a href=\"https://aws.amazon.com/iot/\">Internet of Things (IoT)</a> devices. They are referred to as “managed nodes” when they have the <a href=\"https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html\">Systems Manager Agent (SSM Agent)</a> installed and are connected to Systems Manager.</p> \n<p>If an SSM Agent stops working on a node for whatever reason, then Systems Manager loses connection to it and that node is then referred to as an “unmanaged node.” With the new update, Systems Manager can also help you to easily discover and troubleshoot unmanaged nodes. You can run and even schedule an automated diagnosis that provides you with recommended runbooks that you can execute to fix any issues and reestablish connection so they become managed nodes again.</p> \n<p>Systems Manager is also now integrated with <a href=\"https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/what-is.html\">Amazon Q Developer</a>, the most capable generative AI–powered assistant for software development. You can ask questions about your managed nodes to Amazon Q Developer using natural language and it will provide you with rapid insights plus links straight to Systems Manager where you can perform actions or continue to explore further.</p> \n<p>With this release, you can also use <a href=\"https://aws.amazon.com/organizations/\">AWS Organizations</a>, to allow a delegated administrator to centrally manage nodes across the organization thanks to the new integration with Systems Manager.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/21/StoryLane-700width-12fps.gif\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/21/StoryLane-700width-12fps.gif\" alt=\"the new systems manager experience\" width=\"700\" height=\"610\"></a></p> \n<p>Let’s examine a quick example that helps to demonstrate some of these new capabilities.</p> \n<p>Imagine a scenario where you are a cloud platform engineer leading a migration plan aiming to replace all nodes running Windows Server 2016 Datacenter in the organization. Let’s use the new Systems Manager experience to quickly gather information about all the nodes that needs to be included in our plan.</p> \n<p><strong><span>Step 1 – Asking Amazon Q Developer<br> </span></strong>The easiest starting point is using Amazon Q Developer to ask what you want to find using natural language. Using the AWS Console, I open the Amazon Q chatbot and type <code>Find all of my managed nodes running Microsoft Windows Server 2016 Datacenter in my organization</code>.</p> \n<p>Amazon Q quickly comes back with an answer: it tells us that there are ten nodes that fit the criteria and provides a list with an overview of each one.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/combined-image.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/20/combined-image.png\" width=\"448\" height=\"836\"></a></p> \n<p>There is also a link that redirects to the new <strong>Explore nodes</strong> page in System Manager where we can learn more information. Let’s follow it.</p> \n<p><span><strong>Step 2 – Reviewing our infrastructure<br> </strong></span>The&nbsp;<strong>Explore nodes</strong>&nbsp;page provides a comprehensive overview of all managed nodes across your organization, with options to group and filter results for quick access. In this case, we can see that the results are already filtered by&nbsp;<strong>Operating system name</strong>&nbsp;providing us with a list of all the nodes that are running&nbsp;<strong>Microsoft Windows Server 2016 Datacenter</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/21/marked-2-explore-nodes-deeplink.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/21/marked-2-explore-nodes-deeplink.png\" width=\"1329\" height=\"797\"></a></p> \n<p>This is a great start! We could just finish here by downloading the report and add those nodes to our migration plan, however, this page only shows you information about your managed nodes. Could it be that there are unmanaged nodes that need to included in our plan? Let’s find out.</p> \n<p><span><strong>Step 3 – Handling unmanaged nodes</strong></span><br> Open the menu, and navigate to the <strong>Review node insights</strong> page. Here you can see a dashboard with widgets that provide insightful interactive charts that you can use to drill down and discover more information about your nodes or even take actions. For example, the&nbsp;<strong>Managed node types</strong> pie chart shows the types of managed nodes we have whereas the <strong>SSM Agent versions</strong> graph provides us with an overview of all the different versions of SSM Agent running on them. You can also customize this view by adding and replacing widgets.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-dashboard-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-dashboard-2.png\" width=\"1417\" height=\"1161\"></a>We want to investigate any unmanaged nodes to make sure we don’t miss any that may need to be added to our migration plan. The <strong>Node summary</strong> widget clearly shows that there are two unmanaged nodes. This could mean that these nodes don’t have the SSM Agent installed in which case we will need to investigate them manually. However, it could also just mean there are issues with the SSM agent permissions or network connectivity preventing Systems Manager from managing these nodes and treating them like any other managed node. The new Systems Manager experience allows you easily troubleshoot and remediate SSM Agents issues so let’s attempt to do this now.</p> \n<p>Start by selecting the piece of the chart displaying our unmanaged nodes. This pops up an option to initiate a comprehensive diagnosis of all our unmanaged nodes with only one click. Let’s run this.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-dashboard-unmanaged-1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-dashboard-unmanaged-1.png\" width=\"1417\" height=\"1158\"></a></p> \n<p>The diagnosis reviews key configurations such as missing <a href=\"https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html\">virtual private cloud (VPC)</a> endpoints, misconfigured VPC DNS settings, and misconfigured instance security groups that may be preventing the SSM Agent from connecting to Systems Manager. After the scanning is complete, we can see that it displays two <strong>Misconfigured VPC endpoint </strong>findings. It also gives you a link that you can use to open a side panel containing a recommended runbook that you can execute to solve the issues as well as links to relevant documentation.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-diagnose-remediate-results-recommendation-copy.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-diagnose-remediate-results-recommendation-copy.png\" width=\"1433\" height=\"491\"></a></p> \n<p>Choosing to execute the recommended runbook presents you with a detailed preview of the changes which include a thorough overview of the actions it’s going to take in addition to the input parameters used, a link to view a breakdown of the steps involved, and the target nodes for this execution.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-diagnose-remediate-results-runbook-preview-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-diagnose-remediate-results-runbook-preview-2.png\" width=\"1431\" height=\"857\"></a></p> \n<p>Let’s choose to go ahead and select Execute. Keep in mind that this may incur costs, so make sure to review them before executing. You can keep an eye on progress on this page as it goes through the steps to attempt to fix the issues on each node.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-diagnose-automation-execution.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-diagnose-automation-execution.png\" width=\"1459\" height=\"1243\"></a></p> \n<p>Aha! After the remediation is complete, we can see that Systems Manager has found and corrected issues with the SSM Agent with two nodes. This means that Systems Manager is able to connect with the SSM Agent running in those nodes successfully making them “managed nodes.” We can verify this by returning to the <strong>Explore nodes</strong> page and noticing that the count of “unmanaged nodes” has been reduced to zero now.</p> \n<p>Now that all of our nodes are managed, we’re ready to get a full list of all of those that need to be added to our migration plan.</p> \n<p><span><strong>Step 4 – Downloading a report<br> </strong></span>Back on the <strong>Explore nodes</strong> page we can see that the count for nodes running Microsoft Windows Server 2016 Datacenter has gone up from ten to twelve! That means that those previously unmanaged nodes that we fixed through the automated diagnosis are indeed running our target operating system.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-explore-nodes-12-windows2016-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-explore-nodes-12-windows2016-2.png\" width=\"1432\" height=\"1139\"></a></p> \n<p>This is exactly what we need so we choose to download a <strong>Report</strong>. You give it a file name, and then choose from a few options such as which columns to include. In this case, we choose to download a CSV file with a row containing the column names.</p> \n<p><span><strong><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-export-report.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/18/marked-export-report.png\" width=\"647\" height=\"538\"></a></strong></span></p> \n<p>That’s it! We have our CSV with detailed information about the nodes that need upgrading across our entire infrastructure. And the best part? You can also use Systems Manager to automate the upgrade once you’re ready to go ahead with the migration.</p> \n<p><span><strong>Conclusion</strong></span><br> Systems Manager is a critical tool for gaining visibility and control over your compute infrastructure and performing operational actions at scale. The new experience offers a centralized cross-account, cross-Region view of all your nodes in your AWS accounts, on-premises, and multicloud environments through a centralized dashboard, offering integration with Amazon Q Developer for natural language queries, and one-click SSM Agent troubleshooting. You can enable the new experience at no extra cost by navigating to the Systems Manager console and following the straightforward instructions.</p> \n<p>To learn more, see the <a href=\"https://console.aws.amazon.com/systems-manager/\">documentation</a> for more detail about the new Systems Manager experience.</p> \n<p>Check out this interactive demo for a <a href=\"https://aws.storylane.io/demo/e4yn5yuxsezf?embed=inline\">full visual tour of this experience</a>.</p>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-a-new-experience-for-aws-system-manager/",
            "title": "Introducing a new experience for AWS Systems Manager",
            "date_modified": "2024-11-22T22:36:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42215030\">Comments</a>",
            "url": "https://beeb.li/blog/aws-lambda-rust-docker",
            "title": "Rust for AWS Lambda, the Docker Way",
            "date_modified": "2024-11-22T16:16:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42211280\">Comments</a>",
            "url": "https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-express-one-zone-append-data-object/",
            "title": "Amazon S3 now supports the ability to append data to an object",
            "date_modified": "2024-11-22T04:46:39.000Z"
        },
        {
            "content_html": "<p>In November 2023, <a href=\"https://aws.amazon.com/blogs/aws/amazon-cloudwatch-application-signals-for-automatic-instrumentation-of-your-applications-preview/\">we announced Amazon CloudWatch Application Signals</a>, an AWS built-in application performance monitoring (APM) solution, to solve the complexity associated with monitoring performance of distributed systems for applications hosted on <a href=\"https://aws.amazon.com/pm/eks/\">Amazon EKS</a>, <a href=\"https://aws.amazon.com/ecs/\">Amazon ECS</a>, and <a href=\"https://aws.amazon.com/pm/ec2/\">Amazon EC2</a>. Application Signals automatically correlates telemetry across metrics, traces, and logs, to speed up troubleshooting and reduce application disruption. By providing an integrated experience for analyzing performance in the context of your applications, Application Signals gives you improved productivity focusing on the applications that support your most critical business functions.</p> \n<p>Today we’re announcing the availability of Application Signals for AWS Lambda to eliminate the complexities of manual setup and performance issues required to assess application health for Lambda functions. With <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable-Lambda.html\">CloudWatch Application Signals for Lambda</a>, you can now collect application golden metrics (the incoming and outgoing volume of requests, latency, faults, and errors).</p> \n<p>AWS Lambda abstracts away the complexity of the underlying infrastructure, enabling you to focus on building your application without having to monitor server health. This allows you to shift your focus toward monitoring the performance and health of your applications, which is necessary to operate your applications at peak performance and availability. This requires deep visibility into performance insights such as volume of transactions, latency spikes, availability drops, and errors for your critical business operations and application programming interfaces (APIs).</p> \n<p>Previously, you had to spend signiﬁcant time correlating disjointed logs, metrics, and traces across multiple tools to establish the root cause of anomalies, increasing mean time to recovery (MTTR) and operational costs. Additionally, building your own APM solutions with custom code or manual instrumentation using open source (OSS) libraries was time-consuming, complex, operationally expensive, and often resulted in increased cold start times and deployment challenges when managing large ﬂeets of Lambda functions. Now, you can use Application Signals to seamlessly monitor and troubleshoot health and performance issues in serverless applications, without requiring any manual instrumentation or code changes from your application developers.</p> \n<p><span><strong>How it works</strong></span><br> Using the pre-built, standardized dashboards of Application Signals, you can identify the root cause of performance anomalies in just a few clicks by drilling down into performance metrics for critical business operations and APIs. This helps you visualize application topology which shows interactions between the function and its dependencies. In addition, you can define <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-ServiceLevelObjectives.html\">Service Level Objectives (SLOs)</a> on your applications to monitor specific operations that matter most to you. An example of an SLO could be to set a goal that a webpage should render within 2000 ms 99.9 percent of the time in a rolling 28-day interval.</p> \n<p>Application Signals auto-instruments your Lambda function using enhanced AWS Distro for OpenTelemetry (ADOT) libraries. This delivers better performance such as lower cold start latency,<br> memory consumption, and function invocation duration, so you can quickly monitor your applications.</p> \n<p>I have an existing Lambda function <code>appsignals1</code> and I will configure Application Signals in the Lambda Console to collect various telemetry on this application.</p> \n<p>In the Configuration tab of the function I select <strong>Monitoring and operations tools</strong> to enable both the <strong>Application signals</strong> and the <strong>Lambda service traces</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen1_1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen1_1-1024x439.png\" width=\"1024\" height=\"439\"></a></p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen2-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen2-2-1024x394.png\" width=\"1024\" height=\"394\"></a></p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen1-4.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen1-4-1024x441.png\" width=\"1024\" height=\"441\"></a></p> \n<p>I have an application <code>myAppSignalsApp</code> that has this Lambda function <a href=\"https://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/myApp-getting-started.html\">attached as a resource</a>. I’ve defined an SLO for my application to monitor specific operations that matter most to me. I’ve defined a goal that states that the application executes within 10 ms 99.9 percent of the time in a rolling 1-day interval.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/screen6.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/screen6-1024x365.png\" width=\"1024\" height=\"365\"></a></p> \n<p>It can take 5-10 minutes for Application Signals to discover the function after it’s been invoked. As a result you’ll need to refresh the <strong>Services</strong> page before you can see the service.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen3_1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/15/screen3_1.png\" width=\"989\" height=\"310\"></a></p> \n<p>Now I’m in the <strong>Services</strong> page and I can see a list of all my Lambda functions that have been discovered by Application Signals. Any telemetry that is emitted will be displayed here.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/screen5.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/screen5-1024x459.png\" width=\"1024\" height=\"459\"></a></p> \n<p>I can then visualize the complete application topology from the <strong>Service Map</strong> and quickly spot anomalies across my service’s individual operations and dependencies, using the newly collected metrics of volume of requests, latency, faults, and errors. To troubleshoot, I can click into any point in time for any application metric graph to discover correlated traces and logs related to that metric, to quickly identify if issues impacting end users are isolated to an individual task or deployment.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/screen4-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/07/screen4-2-1024x350.png\" width=\"1024\" height=\"350\"></a></p> \n<p><span><strong>Available now</strong></span><br> Amazon CloudWatch Application Signals for Lambda is now generally available and you can start using it today in all AWS Regions where Lambda and Application Signals are available. Today, Application Signals is available for Lambda functions that use Python and Node.js managed runtimes. We’ll continue to add support for other Lambda runtimes in near future.</p> \n<p>To learn more, visit the&nbsp;<a href=\"https://docs.aws.amazon.com/lambda/latest/dg/monitoring-application-signals.html\">AWS Lambda developer guide</a> and <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable-Lambda.html\">Application Signals developer guide</a>. You can submit your questions to <a href=\"https://repost.aws/tags/articles/TArz36ydKETWeIc1aO7QuRFw\">AWS re:Post for Amazon CloudWatch</a>, or through your usual AWS Support contacts.</p> \n<p>– <a href=\"https://www.linkedin.com/in/veliswa-boya/\">Veliswa</a>.</p>",
            "url": "https://aws.amazon.com/blogs/aws/track-performance-of-serverless-applications-built-using-aws-lambda-with-application-signals/",
            "title": "Track performance of serverless applications built using AWS Lambda with Application Signals",
            "date_modified": "2024-11-21T20:50:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42195004\">Comments</a>",
            "url": "https://github.com/janderland/fql",
            "title": "FQL: A KV Query Language",
            "date_modified": "2024-11-20T15:50:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42187761\">Comments</a>",
            "url": "https://underjord.io/how-i-use-erlang-hot-code-updates.html",
            "title": "Using Erlang hot code updates",
            "date_modified": "2024-11-19T20:29:03.000Z"
        },
        {
            "content_html": "<div><img src=\"https://cdn.macstories.net/predicting-motion-state-from-frequencies-clean-2-1731936257254.png\" alt=\"Source: Transit\"><p>Source: Transit</p></div>\n<p id=\"p2\">Earlier this month, Transit, <a href=\"https://www.macstories.net/reviews/transit-is-still-the-best-designed-transit-app-on-the-iphone-in-2024/\" rel=\"noopener noreferrer\">one of my favorite apps</a> of all time, gained an impressive new feature: the app is now able to track your train and warn you when you are about to reach your destination even when your train is underground. Previously, Transit had to rely on GPS and cellular service to precisely locate your train on its route, which meant it couldn’t reliably function as soon as you entered a subway tunnel.</p>\n<p id=\"p3\">The way they have been able to achieve this is fascinating. Transit now utilizes the iPhone’s built-in accelerometer and analyzes its patterns to identify when the vehicle you boarded is in motion, and every time it reaches a station. The company’s account of the whole process is nothing short of impressive. The team spent a week riding buses and trains to collect data and proceeded to create an entirely new prediction model that is able to count down the underground stations that you will need to ride through to reach your destination. Transit says the model works completely offline and on-device.</p>\n<p id=\"p4\">I know I’m going to give this new feature a try as soon as I get a chance to ride the Paris Métro next week.</p>\n<p>→ Source: <a href=\"https://blog.transitapp.com/go-underground/\" target=\"_blank\">blog.transitapp.com</a></p>",
            "url": "https://www.macstories.net/linked/transit-can-now-track-underground-trains-without-gps/",
            "title": "Transit Can Now Track Underground Trains without GPS",
            "date_modified": "2024-11-18T13:31:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42164058\">Comments</a>",
            "url": "https://blog.mbrt.dev/posts/transactional-object-storage/",
            "title": "Transactional Object Storage?",
            "date_modified": "2024-11-17T13:20:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42154583\">Comments</a>",
            "url": "https://github.com/hengyoush/kyanos",
            "title": "Kyanos: eBPF-based network issue analysis tool",
            "date_modified": "2024-11-16T05:34:57.000Z"
        },
        {
            "content_html": "<p><a href=\"https://aws.amazon.com/iam/\">AWS Identity and Access Management (IAM)</a> is launching a new capability allowing security teams to <strong>centrally manage root access</strong>&nbsp;for member accounts in <a href=\"https://aws.amazon.com/organizations/\">AWS Organizations</a>. You can now easily manage root credentials and perform highly privileged actions.</p> \n<p><span><strong>Managing root user credentials at scale<br> </strong></span>For a long time, <a href=\"https://aws.amazon.com/\">Amazon Web Services (AWS)</a> accounts were provisioned with highly privileged root user credentials, which had unrestricted access to the account. This root access, while powerful, also posed significant security risks. Each AWS account’s root user had to be secured by adding layers of protection like multi-factor authentication (MFA). Security teams were required to manage and secure these root credentials manually. The process involved rotating credentials periodically, storing them securely, and making sure that the credentials complied with security policies.</p> \n<p>As our customers expanded their AWS environments, this manual approach became cumbersome and prone to error. For example, large enterprises operating hundreds or thousands of member accounts struggled to secure root access consistently across all accounts. The manual intervention not only added operational overhead but also created a lag in account provisioning, preventing full automation and increasing security risks. Root access, if not properly secured, could lead to account takeovers and unauthorized access to sensitive resources.</p> \n<p>Furthermore, whenever specific root actions such as unlocking an <a href=\"https://aws.amazon.com/s3/\">Amazon Simple Storage Service (Amazon S3)</a> <a href=\"https://aws.amazon.com/premiumsupport/knowledge-center/change-vpc-endpoint-s3-bucket-policy/\">bucket policy</a> or an <a href=\"https://aws.amazon.com/sqs/\">Amazon Simple Queue Service (Amazon SQS)</a> <a href=\"https://aws.amazon.com/premiumsupport/knowledge-center/sqs-queue-access-issues-deny-policy\">resource policy</a> were required, security teams had to retrieve and use root credentials, which only increased the attack surface. Even with rigorous monitoring and strong security policies, maintaining long-term root credentials opened doors to potential mismanagement, compliance risks, and manual errors.</p> \n<p>Security teams began seeking a more automated, scalable solution. They needed a way to not only centralize the management of root credentials but also programmatically manage root access without needing long-term credentials in the first place.</p> \n<p><span><strong>Centrally manage root access<br> </strong></span>With the new ability to centrally manage root access, we address the longstanding challenge of managing root credentials across multiple accounts. This new capability introduces two essential capabilities: the <strong>central management of root credentials</strong>&nbsp;and <strong>root sessions</strong>. Together, they offer security teams a secure, scalable, and compliant way to manage root access across AWS Organizations member accounts.</p> \n<p>Let’s first discuss the <strong>central</strong> <strong>management of root credentials</strong>. With this capability, you can now centrally manage and secure privileged root credentials across all accounts in AWS Organizations. Root credentials management allows you to:</p> \n<ul> \n <li><strong>Remove long-term root credentials </strong>– Security teams can now programmatically remove root user credentials from member accounts, confirming that no long-term privileged credentials are left vulnerable to misuse.</li> \n <li><strong>Prevent credential recovery </strong>– It not only removes the credentials but also prevents their recovery, safeguarding against any unintended or unauthorized root access in the future.</li> \n <li><strong>Provision secure-by-default accounts </strong>– Because you can now create member accounts without root credentials from the start, you no longer need to apply additional security measures like MFA after account provisioning. Accounts are secure by default, which drastically reduces security risks associated with long-term root access and helps simplify the entire provisioning process.</li> \n <li><strong>Help to stay compliant </strong>– Root credentials management allows security teams to demonstrate compliance by centrally discovering and monitoring the status of root credentials across all member accounts. This automated visibility confirms that no long-term root credentials exist, making it easier to meet security policies and regulatory requirements.</li> \n</ul> \n<p>But how can we make sure it remains possible to perform selected root actions on the accounts? This is the second capability we launch today: <strong>root sessions</strong>. It offers a secure alternative to maintaining long-term root access. Instead of manually accessing root credentials whenever privileged actions are required, security teams can now gain short-term, task-scoped root access to member accounts. This capability makes sure that actions such as unlocking S3 bucket policies or SQS queue policies can be performed securely without the need for long-term root credentials.</p> \n<p>Root sessions key benefits include:</p> \n<ul> \n <li><strong>Task-scoped root access </strong>– AWS enables short-term root access for specific actions, adhering to the best practices of least privilege. This limits the scope of what can be done and minimizes the duration of access, reducing potential risks.</li> \n <li><strong>Centralized management </strong>– You can now perform privileged root actions from a central account without needing to log in to each member account individually. This streamlines the process and reduces the operational burden on security teams, allowing them to focus on higher-level tasks.</li> \n <li><strong>Alignment with AWS best practices </strong>– By using short-term credentials, organizations align themselves with AWS security best practices, which emphasize the principle of least privilege and the use of short-term, temporary access where possible.</li> \n</ul> \n<p>This new capability does not grant full root access. It provides temporary credentials for performing one of these five specific actions. The first three actions are possible with central management of root user credentials. The last two come when enabling root sessions.</p> \n<ul> \n <li><strong>Auditing root user credentials –</strong> Read-only access to review root user information</li> \n <li><strong>Re-enabling account recovery –</strong> Reactivating account recovery without root credentials</li> \n <li><strong>Deleting root user credentials –</strong> Removing console passwords, access keys, signing certificates, and MFA devices</li> \n <li><strong>Unlocking an S3 bucket policy –</strong> Editing or deleting an S3 bucket policy that denies all principals</li> \n <li><strong>Unlocking an SQS queue policy –</strong> Editing or deleting an Amazon SQS resource policy that denies all principals</li> \n</ul> \n<p><span><strong>How to obtain root credentials on a member account<br> </strong></span>In this demo, I show you how to prepare your management account, create a member account without root credentials, and obtain temporary root credentials to make one of the five authorized API call on the member account. I assume you have an organization already created.</p> \n<p>First, I create a member account.</p> \n<pre><code>aws organizations create-account    \\\n     --email stormacq+rootaccountdemo@amazon.com \\\n     --account-name 'Root User Demo account'\n{\n    \"CreateAccountStatus\": {\n        \"Id\": \"car-695abd4ee1ca4b85a34e5dcdcd1b944f\",\n        \"AccountName\": \"Root User Demo account\",\n        \"State\": \"IN_PROGRESS\",\n        \"RequestedTimestamp\": \"2024-09-04T20:04:09.960000+00:00\"\n    }\n}</code></pre> \n<p>Then, I enable the two new capabilities on my management account. Don’t worry, these commands don’t alter the behavior of the accounts in any way other than enabling use of the new capability.</p> \n<pre><code>➜  aws organizations enable-aws-service-access \\\n        --service-principal iam.amazonaws.com\n\n➜  aws iam enable-organizations-root-credentials-management\n{\n    \"OrganizationId\": \"o-rlrup7z3ao\",\n    \"EnabledFeatures\": [\n        \"RootCredentialsManagement\"\n    ]\n}\n\n➜  aws iam enable-organizations-root-sessions\n{\n    \"OrganizationId\": \"o-rlrup7z3ao\",\n    \"EnabledFeatures\": [\n        \"RootSessions\",\n        \"RootCredentialsManagement\"\n    ]\n}</code></pre> \n<p>Alternatively, I can also use the console on the management account. On the<strong> IAM page</strong>, under <strong>Access management</strong>, I select <strong>Account settings</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/12/Screenshot-2024-11-11-at-11.31.09.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/11/12/Screenshot-2024-11-11-at-11.31.09.png\" alt=\"Root Access Management\" width=\"800\" height=\"195\"></a></p> \n<p>Now, I’m ready to make requests to obtain temporary root credentials. I have to pass one of the five managed IAM policies to scope down the credentials to a specific action.</p> \n<pre><code>➜  aws sts assume-root    \\\n       --region us-east-1 \\\n       --target-principal &lt;my member account id&gt; \\\n       --task-policy-arn arn=arn:aws:iam::aws:policy/root-task/S3UnlockBucketPolicy \n\n{\n    \"Credentials\": {\n        \"AccessKeyId\": \"AS....XIG\",\n        \"SecretAccessKey\": \"ao...QxG\",\n        \"SessionToken\": \"IQ...SS\",\n        \"Expiration\": \"2024-09-23T17:44:50+00:00\"\n    }\n}</code></pre> \n<p>Once I obtain the access key ID, the secret access key, and the session token, I use them as usual with the <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a> or an <a href=\"https://aws.amazon.com/tools/\">AWS SDKs</a>.</p> \n<p>For example, I can pass these three values as environment variables.</p> \n<pre><code>$ export AWS_ACCESS_KEY_ID=ASIA356SJWJITG32xxx\n$ export AWS_SECRET_ACCESS_KEY=JFZzOAWWLocoq2of5Exxx\n$ export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEMb//////////wEaCXVxxxx\n</code></pre> \n<p>Now that I received the temporary credentials, I can make a restricted API call as root on the member account. First, I verify I now have root credentials. The <code>Arn</code> field confirms I’m working with the root user.</p> \n<pre><code>\n# Call get Caller Identity and observe I'm root in the member account\n$ aws sts get-caller-identity\n{\n   \"UserId\": \"012345678901\",\n   \"Account\": \"012345678901\",\n   \"Arn\": \"arn:aws:iam::012345678901:root\"\n}\n</code></pre> \n<p>Then, I use the <code>delete-bucket-policy</code> from S3 to remove an incorrect policy that has been applied to a bucket. The invalid policy removed all bucket access for everybody. Removing such policy requires root credentials.</p> \n<p><code>aws s3api delete-bucket-policy --bucket my_bucket_with_incorrect_policy</code></p> \n<p>When there is no output, it means the operation is successful. I can now apply a correct access policy to this bucket.</p> \n<p>Credentials are valid only for 15 minutes. <a href=\"https://gist.github.com/sebsto/6f7c9eaf500ac11756a86babde75ffc0\">I wrote a short shell script to automate the process</a> of getting the credentials as JSON, exporting the correct environment variables, and issuing the command I want to run as root.</p> \n<p><span><strong>Availability</strong><br> </span>Central management of root access is available at no additional cost in all <a href=\"https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#region\">AWS Regions</a> except AWS GovCloud (US) and AWS China Regions, where there is no root user. Root sessions are available everywhere.</p> \n<p>You can start using it through the IAM console, AWS CLI or AWS SDK. For more information, visit <a href=\"https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user-access-management\">AWS account root user</a> in our documentation and follow best practices for securing your AWS accounts.</p> \n<p><a href=\"https://twitter.com/sebsto\">-- seb</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/centrally-managing-root-access-for-customers-using-aws-organizations/",
            "title": "Centrally managing root access for customers using AWS Organizations",
            "date_modified": "2024-11-15T16:56:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42148248\">Comments</a>",
            "url": "https://www.allthingsdistributed.com/2024/11/aws-lambda-turns-10-a-rare-look-at-the-doc-that-started-it.html",
            "title": "AWS Lambda PR/FAQ After 10 Years",
            "date_modified": "2024-11-15T16:16:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42146782\">Comments</a>",
            "url": "https://cmtops.dev/posts/building-observability-with-clickhouse/",
            "title": "Building Observability with ClickHouse",
            "date_modified": "2024-11-15T13:35:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42139044\">Comments</a>",
            "url": "https://www.redhat.com/en/blog/red-hat-contribute-comprehensive-container-tools-collection-cloud-native-computing-foundation",
            "title": "Red Hat to contribute container tech (Podman, bootc, ComposeFS...) to CNCF",
            "date_modified": "2024-11-14T17:59:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42129097\">Comments</a>",
            "url": "https://netflixtechblog.com/netflixs-distributed-counter-abstraction-8d0c45eb66b2",
            "title": "Netflix's Distributed Counter Abstraction",
            "date_modified": "2024-11-13T19:31:11.000Z"
        },
        {
            "content_html": "<div><img src=\"https://cdn.macstories.net/kino-with-camera-control-1731518037181.jpg\" alt=\"Source: Lux Camera.\"><p>Source: Lux Camera.</p></div>\n<p id=\"p2\">Lux Camera’s video camera app <a href=\"http://www.shotwithkino.com/download\" rel=\"noopener noreferrer\">Kino</a> has been updated to version 1.2, bringing a variety of new features and a redesigned icon. I covered <a href=\"https://www.macstories.net/stories/kino-first-impressions-an-iphone-video-camera-app-from-the-makers-of-halide/\" rel=\"noopener noreferrer\">the debut of Kino</a> back in May and have been using it a lot lately because its design makes taking great-looking video so easy.</p>\n<p id=\"p3\">At the heart of Kino’s 1.2 update is support for the latest iPhones. Kino now works with the iPhone 16 and 16 Pro’s Camera Control for making adjustments that previously were only possible by touching your iPhone’s screen.</p>\n<div><img src=\"https://cdn.macstories.net/kino-with-instant-grade-1731518136143.jpg\" alt=\"Kino Instant Grades. Source: Lux Camera.\"><p>Kino Instant Grades. Source: Lux Camera.</p></div>\n<p id=\"p5\">On the 16 Pro, the app also supports 4K video at 120fps with its Instant Grade feature enabled. That’s the feature that lets you pick a color grading preset created by video experts, including&nbsp;<a href=\"https://prolost.com/about\" rel=\"noopener noreferrer\">Stu Maschwitz</a>,&nbsp;<a href=\"https://sandwich.co/\" rel=\"noopener noreferrer\">Sandwich Video</a>,&nbsp;<a href=\"https://www.schneidervisuals.com/\" rel=\"noopener noreferrer\">Evan Schneider</a>,&nbsp;<a href=\"https://www.youtube.com/channel/UC6OICk-ceplUJf4sCN3DMnQ\" rel=\"noopener noreferrer\">Tyler Stalman</a>, and&nbsp;<a href=\"https://www.kevouthere.online/\" rel=\"noopener noreferrer\">Kevin Ong</a>. Version 1.2 of Kino lets you reorder those grades in its settings to make your favorites easier to access. Finally, Kino has added support for the following languages: Chinese, Dutch, French, German, Italian, Japanese, Korean, Portuguese, Russian, and Spanish.</p>\n<p id=\"p6\">If you haven’t tried Kino before, <a href=\"http://www.shotwithkino.com/download\" rel=\"noopener noreferrer\">it’s available on the App Store</a> for $9.99.</p>\n<hr><h3>Access Extra Content and Perks</h3><p></p><p>Founded in 2015, <a href=\"https://club.macstories.net/plans?utm_source=ms&amp;utm_medium=web-inline\" rel=\"noopener noreferrer\">Club MacStories</a> has delivered exclusive content every week for nearly a decade.</p>\n<p>What started with weekly and monthly email newsletters has blossomed into <a href=\"https://club.macstories.net/plans?utm_source=ms&amp;utm_medium=web-inline\" rel=\"noopener noreferrer\">a family of memberships</a> designed every MacStories fan.</p>\n<p><strong><a href=\"https://club.macstories.net/plans/club\" rel=\"noopener noreferrer\">Club MacStories</a></strong>: Weekly and monthly newsletters via email and the web that are brimming with apps, tips, automation workflows, longform writing, early access to the <a href=\"https://www.macstories.net/unwind/\" rel=\"noopener noreferrer\">MacStories Unwind podcast</a>, periodic giveaways, and more;</p>\n<p><strong><a href=\"https://club.macstories.net/plans/plus\" rel=\"noopener noreferrer\">Club MacStories+</a></strong>: Everything that Club MacStories offers, plus an active Discord community, advanced search and custom RSS features for exploring the Club’s entire back catalog, bonus columns, and dozens of app discounts;</p>\n<p><strong><a href=\"https://club.macstories.net/plans/premier\" rel=\"noopener noreferrer\">Club Premier</a></strong>: All of the above <em>and</em> AppStories+, an extended version of our flagship podcast that’s delivered early, ad-free, and in high-bitrate audio.</p>\n<p>Learn more <a href=\"https://club.macstories.net/plans?utm_source=ms&amp;utm_medium=web-inline\" rel=\"noopener noreferrer\">here</a> and from our <a href=\"https://club.macstories.net/faq\" rel=\"noopener noreferrer\">Club FAQs</a>.</p>\n<p></p><a href=\"https://club.macstories.net/?utm_source=ms&amp;utm_medium=rss\">Join Now</a>",
            "url": "https://www.macstories.net/news/kino-1-2-adds-camera-control-support-and-higher-resolution-and-frame-rate-recording/",
            "title": "Kino 1.2 Adds Camera Control Support and Higher Resolution and Frame Rate Recording",
            "date_modified": "2024-11-13T17:22:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42105797\">Comments</a>",
            "url": "https://sst.dev/blog/container-support/",
            "title": "SST: Container Support",
            "date_modified": "2024-11-11T09:51:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42093756\">Comments</a>",
            "url": "https://mergiraf.org/",
            "title": "Mergiraf: a syntax-aware merge driver for Git",
            "date_modified": "2024-11-09T11:06:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42084588\">Comments</a>",
            "url": "https://jhftss.github.io/A-New-Era-of-macOS-Sandbox-Escapes/",
            "title": "Multiple new macOS sandbox escape vulnerabilities",
            "date_modified": "2024-11-08T06:10:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42084571\">Comments</a>",
            "url": "https://julien.danjou.info/p/theres-almost-no-gitlab",
            "title": "There's Almost No Gitlab",
            "date_modified": "2024-11-08T06:04:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42079275\">Comments</a>",
            "url": "https://bunny.net/blog/introducing-bunny-edge-scripting-a-better-way-to-build-and-deploy-applications-at-the-edge/",
            "title": "Edge Scripting: Build and run applications at the edge",
            "date_modified": "2024-11-07T18:17:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42074592\">Comments</a>",
            "url": "https://developer.apple.com/documentation/virtualization/accelerating_the_performance_of_rosetta",
            "title": "Accelerating the Performance of Rosetta in Linux VMs on Apple Silicon",
            "date_modified": "2024-11-07T08:02:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42053180\">Comments</a>",
            "url": "https://github.com/dstackai/dstack",
            "title": "Dstack: An alternative to K8 for AI/ML tasks",
            "date_modified": "2024-11-05T16:56:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42041917\">Comments</a>",
            "url": "https://www.gitpod.io/blog/we-are-leaving-kubernetes",
            "title": "We're Leaving Kubernetes",
            "date_modified": "2024-11-04T14:41:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42030625\">Comments</a>",
            "url": "https://slawlor.github.io/ractor/quickstart/",
            "title": "Ractor – a Rust Actor Framework",
            "date_modified": "2024-11-03T01:47:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=42017163\">Comments</a>",
            "url": "https://blog.howardjohn.info/posts/go-build-times/",
            "title": "Analyzing Go Build Times (2023)",
            "date_modified": "2024-11-01T14:22:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41983394\">Comments</a>",
            "url": "https://holos.run/docs/guides/helm/",
            "title": "Show HN: Holos – Configure Helm and Kustomize Holistically with Cue",
            "date_modified": "2024-10-29T12:58:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41953155\">Comments</a>",
            "url": "https://linderud.dev/blog/nixos-is-not-reproducible/",
            "title": "NixOS Is Not Reproducible",
            "date_modified": "2024-10-26T07:19:07.000Z"
        },
        {
            "content_html": "<p>It took a crew of more than 100 talented modders and another hundred voice actors nearly five years to make <a href=\"https://fallout4london.com/\"><em>Fallout: London</em></a>. Just as they planned to release it, Bethesda <a href=\"https://arstechnica.com/gaming/2024/07/fallout-london-devs-will-downgrade-fallout-4-to-save-their-massive-mod/\">came out with a \"next-gen upgrade\" of the mod's base game</a>, <em>Fallout 4</em>, forcing the team to scramble and ultimately find a way to downgrade the game. When they finally released <em>London</em>, they then dealt with game-stopping bugs and quality issues that their small QA team could not have caught. It's been a long, maybe even post-apocalyptic road for these modders.</p>\n<p>A few updates later, <em>Fallout: London</em> is in much better shape. I've been able to put about 12 hours into it, and that, in itself, is essentially my review: it is worth that kind of time and more. If you can still enjoy <em>Fallout 4</em>, of course.</p>\n<p>Any Fallout fan waits a long time between official releases, so it can be tempting to go easy on any new offering, however spit-and-bailing-wire it may seem. But <em>Fallout: London </em>is a game in its own right, with a distinct look, vision, and stories to tell. You can find evidence of its unofficial mod-ness if you look around, but you're better off doing the <em>Fallout</em> thing: wandering, wondering, fighting, and occasionally talking to some messed-up weirdo.</p><p><a href=\"https://arstechnica.com/gaming/2024/10/fallout-london-is-a-huge-fallout-4-mod-that-is-now-playable-and-worth-playing/\">Read full article</a></p>\n<p><a href=\"https://arstechnica.com/gaming/2024/10/fallout-london-is-a-huge-fallout-4-mod-that-is-now-playable-and-worth-playing/#comments\">Comments</a></p>",
            "url": "https://arstechnica.com/gaming/2024/10/fallout-london-is-a-huge-fallout-4-mod-that-is-now-playable-and-worth-playing/",
            "title": "Fallout: London is a huge Fallout 4 mod that is now playable—and worth playing",
            "date_modified": "2024-10-25T11:30:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41941493\">Comments</a>",
            "url": "https://github.com/open-feature",
            "title": "OpenFeature – a vendor-agnostic, community-driven API for feature flagging",
            "date_modified": "2024-10-25T01:26:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41941414\">Comments</a>",
            "url": "https://rsbuild.dev/",
            "title": "Rsbuild – A Better Vite?",
            "date_modified": "2024-10-25T01:12:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41941308\">Comments</a>",
            "url": "https://github.com/resola-ai/rust-aws-tui",
            "title": "Show HN: Rust based AWS Lambda Logs Viewer (TUI)",
            "date_modified": "2024-10-25T00:53:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41937815\">Comments</a>",
            "url": "https://github.com/E-xyza/zigler",
            "title": "Zigler: Zig NIFs in Elixir",
            "date_modified": "2024-10-24T17:53:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41935741\">Comments</a>",
            "url": "https://danielquinn.org/blog/developing-with-docker/",
            "title": "Developing with Docker",
            "date_modified": "2024-10-24T14:17:23.000Z"
        },
        {
            "content_html": "<p>Workflows, Cloudflare’s durable execution engine that allows you to build reliable, repeatable multi-step applications that scale for you, is now in open beta. Any developer with a free or paid <a href=\"https://workers.cloudflare.com/\"><u>Workers</u></a> plan can build and deploy a Workflow right now: no waitlist, no sign-up form, no fake line around-the-block.</p><p>If you learn by doing, you can create your first Workflow via a single command (or <a href=\"https://developers.cloudflare.com/workflows/get-started/guide/\"><u>visit the docs for the full guide)</u></a>:</p>\n            <pre><code>npm create cloudflare@latest workflows-starter -- \\\n  --template \"cloudflare/workflows-starter\"</code></pre>\n            <p>Open the <code>src/index.ts</code> file, poke around, start extending it, and deploy it with a quick <code>wrangler deploy</code>.</p><p>If you want to learn more about how Workflows works, how you can use it to build applications, and how we built it, read on.</p>\n          <div>\n            <h2>Workflows? Durable Execution?</h2>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#workflows-durable-execution\">\n              \n            </a>\n          </div>\n          <p>Workflows—which we <a href=\"https://blog.cloudflare.com/data-anywhere-events-pipelines-durable-execution-workflows/#durable-execution\"><u>announced back during Developer Week</u></a> earlier this year—is our take on the concept of “Durable Execution”: the ability to build and execute applications that are <i>durable</i> in the face of errors, network issues, upstream API outages, rate limits, and (most importantly) infrastructure failure.</p><p>As <a href=\"https://cloudflare.tv/event/xvm4qdgm?startTime=8m5s\"><u>over 2.4 million developers</u></a> continue to build applications on top of Cloudflare Workers, R2, and Workers AI, we’ve noticed more developers building multi-step applications and workflows that process user data, transform unstructured data into structured, export metrics, persist state as they progress, and automatically retry &amp; restart. But writing any non-trivial application and making it <i>durable</i> in the face of failure is hard: this is where Workflows comes in. Workflows manages the retries, emitting the metrics, and durably storing the state (without you having to stand up your own database) as the Workflow progresses.</p><p>What makes Workflows different from other takes on “Durable Execution” is that we manage the underlying compute and storage infrastructure for you. You’re not left managing a compute cluster and hoping it scales both up (on a Monday morning) and down (during quieter periods) to manage costs, or ensuring that you have compute running in the right locations. Workflows is built on Cloudflare Workers — our job is to run your code and operate the infrastructure for you.</p><p>As an example of how Workflows can help you build durable applications, assume you want to post-process file uploads from your users that were uploaded to an R2 bucket directly via <a href=\"https://developers.cloudflare.com/r2/api/s3/presigned-urls/\"><u>a pre-signed URL</u></a>. That post-processing could involve multiple actions: text extraction via a <a href=\"https://developers.cloudflare.com/workers-ai/models/\"><u>Workers AI model</u></a>, calls to a third-party API to validate data, updating or querying rows in a database once the file has been processed… the list goes on.</p><p>But what each of these actions has in common is that it could <i>fail</i>. Maybe that upstream API is unavailable, maybe you get rate-limited, maybe your database is down. Having to write extensive retry logic around each action, manage backoffs, and (importantly) ensure your application doesn’t have to start from scratch when a later <i>step</i> fails is more boilerplate to write and more code to test and debug.</p><p>What’s a <i>step, </i>you ask? The core building block of every Workflow is the step: an individually retriable component of your application that can optionally emit state. That state is then persisted, even if subsequent steps were to fail. This means that your application doesn’t have to restart, allowing it to not only recover more quickly from failure scenarios, but it can also avoid doing redundant work. You don’t want your application hammering an expensive third-party API (or getting you rate limited) because it’s naively retrying an API call that you don’t have to.</p>\n            <pre><code>export class MyWorkflow extends WorkflowEntrypoint&lt;Env, Params&gt; {\n\tasync run(event: WorkflowEvent&lt;Params&gt;, step: WorkflowStep) {\n\t\tconst files = await step.do('my first step', async () =&gt; {\n\t\t\treturn {\n\t\t\t\tinputParams: event,\n\t\t\t\tfiles: [\n\t\t\t\t\t'doc_7392_rev3.pdf',\n\t\t\t\t\t'report_x29_final.pdf',\n\t\t\t\t\t'memo_2024_05_12.pdf',\n\t\t\t\t\t'file_089_update.pdf',\n\t\t\t\t\t'proj_alpha_v2.pdf',\n\t\t\t\t\t'data_analysis_q2.pdf',\n\t\t\t\t\t'notes_meeting_52.pdf',\n\t\t\t\t\t'summary_fy24_draft.pdf',\n\t\t\t\t],\n\t\t\t};\n\t\t});\n\n\t\t// Other steps...\n\t}\n}\n</code></pre>\n            <p>Notably, a Workflow can have hundreds of steps: one of the <a href=\"https://developers.cloudflare.com/workflows/build/rules-of-workflows/\"><u>Rules of Workflows</u></a> is to encapsulate every API call or stateful action within your application into its own step. Each step can also define its own retry strategy, automatically backing off, adding a delay and/or (eventually) giving up after a set number of attempts.</p>\n            <pre><code>await step.do(\n\t'make a call to write that could maybe, just might, fail',\n\t// Define a retry strategy\n\t{\n\t\tretries: {\n\t\t\tlimit: 5,\n\t\t\tdelay: '5 seconds',\n\t\t\tbackoff: 'exponential',\n\t\t},\n\t\ttimeout: '15 minutes',\n\t},\n\tasync () =&gt; {\n\t\t// Do stuff here, with access to the state from our previous steps\n\t\tif (Math.random() &gt; 0.5) {\n\t\t\tthrow new Error('API call to $STORAGE_SYSTEM failed');\n\t\t}\n\t},\n);\n</code></pre>\n            <p>To illustrate this further, imagine you have an application that reads text files from an R2 storage bucket, pre-processes the text into chunks, generates text embeddings <a href=\"https://developers.cloudflare.com/workers-ai/models/bge-large-en-v1.5/\"><u>using Workers AI</u></a>, and then inserts those into a vector database (like <a href=\"https://developers.cloudflare.com/vectorize/\"><u>Vectorize</u></a>) for semantic search.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7b9m0rPDlGvIiTnhguyvzI/3f27678b141ce600f1f54eb999e9d671/WORKFLOWS.png\">\n          </figure><p>In the Workflows programming model, each of those is a discrete step, and each can emit state. For example, each of the four actions below can be a discrete <code>step.do</code> call in a Workflow:</p><ol><li><p>Reading the files from storage and emitting the list of filenames</p></li><li><p>Chunking the text and emitting the results</p></li><li><p>Generating text embeddings</p></li><li><p>Upserting them into Vectorize and capturing the result of a test query</p></li></ol><p>You can also start to imagine that some steps, such as chunking text or generating text embeddings, can be broken down into even more steps — a step per file that we chunk, or a step per API call to our text embedding model, so that our application is even more resilient to failure.</p><p>Steps can be created programmatically or conditionally based on input, allowing you to dynamically create steps based on the number of inputs your application needs to process. You do not need to define all steps ahead of time, and each instance of a Workflow may choose to conditionally create steps on the fly.</p>\n          <div>\n            <h2>Building Cloudflare on Cloudflare</h2>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#building-cloudflare-on-cloudflare\">\n              \n            </a>\n          </div>\n          <p>As the Cloudflare Developer platform <a href=\"https://www.cloudflare.com/birthday-week/\"><u>continues to grow</u></a>, almost all of our own products are built on top of it. Workflows is yet another example of how we built a new product from scratch using nothing but Workers and its vast catalog of features and APIs. This section of the blog has two goals: to explain how we built it, and to demonstrate that anyone can create a complex application or platform with demanding requirements and multiple architectural layers on our stack, too.</p><p>If you’re wondering how Workflows manages to make durable execution easy, how it persists state, and how it automatically scales: it’s because we built it on Cloudflare Workers, including the brand-new <a href=\"https://blog.cloudflare.com/sqlite-in-durable-objects/\"><u>zero-latency SQLite storage we recently introduced to Durable Objects</u></a>.\n</p><p>To understand how Workflows uses Workers &amp; Durable Objects, here’s the high-level overview of our architecture:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7pknYk0Sshxka3iPbxBCRj/bb8b75986601e38b6b69fe8d849c0cbe/image9.png\">\n          </figure><p>There are three main blocks in this diagram:</p><p>The user-facing APIs are where the user interacts with the platform, creating and deploying new workflows or instances, controlling them, and accessing their state and activity logs. These operations can be executed through our public <a href=\"https://developers.cloudflare.com/api/\"><u>API gateway</u></a> using REST calls, a Worker script using bindings, <a href=\"https://blog.cloudflare.com/wrangler3\"><u>Wrangler</u></a> (Cloudflare's developer platform command line tool), or via the <a href=\"https://dash.cloudflare.com/\"><u>Dashboard</u></a> user interface.</p><p>The managed platform holds the internal configuration APIs running on a Worker implementing a catalog of REST endpoints, the binding shim, which is supported by another dedicated Worker, every account controller, and their correspondent workflow engines, all powered by SQLite-backed Durable Objects. This is where all the magic happens and what we are sharing more details about in this technical blog.</p><p>Finally, there are the workflow instances, essentially independent clones of the workflow application. Instances are user account-owned and have a one-to-one relationship with a managed engine that powers them. You can run as many instances and engines as you want concurrently.</p><p>Let's get into more detail…</p>\n          <div>\n            <h3>Configuration API and Binding Shim</h3>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#configuration-api-and-binding-shim\">\n              \n            </a>\n          </div>\n        \n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2qEGr9M8KwgPS66Ju8mELL/189db9764392c00ae34dd3a44eeb1ed7/image6.png\">\n          </figure><p>The Configuration API and the Binding Shim are two stateless Workers; one receives REST API calls from clients calling our <a href=\"https://developers.cloudflare.com/api/\"><u>API Gateway</u></a> directly, using <a href=\"https://developers.cloudflare.com/workers/wrangler/\"><u>Wrangler</u></a>, or navigating the <a href=\"https://dash.cloudflare.com/\"><u>Dashboard</u></a> UI, and the other is the endpoint for the Workflows <a href=\"https://developers.cloudflare.com/workers/runtime-apis/bindings/\"><u>binding</u></a>, an efficient and authenticated interface to interact with the Cloudflare Developer Platform resources from a Workers script.</p><p>The configuration API worker uses <a href=\"https://hono.dev/docs/getting-started/cloudflare-workers\"><u>HonoJS</u></a> and <a href=\"https://hono.dev/examples/zod-openapi\"><u>Zod</u></a> to implement the REST endpoints, which are declared in an <a href=\"https://swagger.io/specification/\"><u>OpenAPI</u></a> schema and exported to our API Gateway, thus adding our methods to the Cloudflare API <a href=\"https://developers.cloudflare.com/api/\"><u>catalog</u></a>.</p>\n            <pre><code>import { swaggerUI } from '@hono/swagger-ui';\nimport { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';\nimport { Hono } from 'hono';\n\n...\n\n​​api.openapi(\n  createRoute({\n    method: 'get',\n    path: '/',\n    request: {\n      query: PaginationParams,\n    },\n    responses: {\n      200: {\n        content: {\n          'application/json': {\n             schema: APISchemaSuccess(z.array(WorkflowWithInstancesCountSchema)),\n          },\n        },\n        description: 'List of all Workflows belonging to a account.',\n      },\n    },\n  }),\n  async (ctx) =&gt; {\n    ...\n  },\n);\n\n...\n\napi.route('/:workflow_name', routes.workflows);\napi.route('/:workflow_name/instances', routes.instances);\napi.route('/:workflow_name/versions', routes.versions);</code></pre>\n            <p>These Workers perform two different functions, but they share a large portion of their code and implement similar logic; once the request is authenticated and ready to travel to the next stage, they use the account ID to delegate the operation to a Durable Object called Account Controller.</p>\n            <pre><code>// env.ACCOUNTS is the Account Controllers Durable Objects namespace\nconst accountStubId = c.env.ACCOUNTS.idFromName(accountId.toString());\nconst accountStub = c.env.ACCOUNTS.get(accountStubId);</code></pre>\n            <p>As you can see, every account has its own Account Controller Durable Object.</p>\n          <div>\n            <h3>Account Controllers</h3>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#account-controllers\">\n              \n            </a>\n          </div>\n        <p>The Account Controller is a dedicated persisted database that stores the list of all the account’s workflows, versions, and instances. We scale to millions of account controllers, one per every Cloudflare account using Workflows, by leveraging the power of <a href=\"https://developers.cloudflare.com/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend\"><u>Durable Objects with SQLite backend</u></a>.</p><p><a href=\"https://developers.cloudflare.com/durable-objects/\"><u>Durable Objects</u></a> (DOs) are single-threaded singletons that run in our data centers and are bound to a stateful storage API, in this case, SQLite. They are also Workers, just a special kind, and have access to all of our other APIs. This makes it easy to build consistent, highly available distributed applications with them.</p><p>Here’s what we get for free by using one Durable Object per Workflows account:</p><ul><li><p>Sharding based on account boundaries aligns perfectly with the way we manage resources at Cloudflare internally. Also, due to the nature of DOs, there are other things that this model gets us for free: Not that we expect them, but eventual bugs or state inconsistencies during beta are confined to the affected account, and don’t impact everyone.</p></li><li><p>DO instances run close to the end user; Alice is in London and will call the config API through our <a href=\"https://www.cloudflare.com/en-gb/network/\"><u>LHR data center</u></a>, while Bob is in Lisbon and will connect to LIS.</p></li><li><p>Because every account is a Worker, we can gradually upgrade them to new versions, starting with the internal users, thus derisking real customers.</p></li></ul><p>Before SQLite, our only option was to use the Durable Object's <a href=\"https://developers.cloudflare.com/durable-objects/api/storage-api/#get\"><u>key-value</u></a> storage API, but having a relational database at our fingertips and being able to create tables and do complex queries is a significant enabler. For example, take a look at how we implement the internal method getWorkflow():</p>\n            <pre><code>async function getWorkflow(accountId: number, workflowName: string) {\n  try {\n    const res = this.ctx.storage.transactionSync(() =&gt; {\n      const cursor = Array.from(\n        this.ctx.storage.sql.exec(\n          `\n                    SELECT *,\n                    (SELECT class_name\n                        FROM   versions\n                        WHERE  workflow_id = w.id\n                        ORDER  BY created_on DESC\n                        LIMIT  1) AS class_name\n                    FROM   workflows w\n                    WHERE  w.name = ? \n                    `,\n          workflowName\n        )\n      )[0] as Workflow;\n\n      return cursor;\n    });\n\n    this.sendAnalytics(accountId, begin, \"getWorkflow\");\n    return res as Workflow | undefined;\n  } catch (err) {\n    this.sendErrorAnalytics(accountId, begin, \"getWorkflow\");\n    throw err;\n  }\n}\n</code></pre>\n            <p>The other thing we take advantage of in Workflows is using the recently <a href=\"https://blog.cloudflare.com/javascript-native-rpc/\"><u>announced</u></a> JavaScript-native RPC feature when communicating between components.</p><p>Before <a href=\"https://developers.cloudflare.com/workers/runtime-apis/rpc/\"><u>RPC</u></a>, we had to <code>fetch()</code> between components, make HTTP requests, and serialize and deserialize the parameters and the payload. Now, we can async call the remote object's method as if it was local. Not only does this feel more natural and simplify our logic, but it's also more efficient, and we can take advantage of TypeScript type-checking when writing code.</p><p>This is how the Configuration API would call the Account Controller’s <code>countWorkflows()</code> method before:</p>\n            <pre><code>const resp = await accountStub.fetch(\n      \"https://controller/count-workflows\",\n      {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json; charset=utf-8\",\n        },\n        body: JSON.stringify({ accountId }),\n      },\n    );\n\nif (!resp.ok) {\n  return new Response(\"Internal Server Error\", { status: 500 });\n}\n\nconst result = await resp.json();\nconst total_count = result.total_count;</code></pre>\n            <p>This is how we do it using RPC:</p>\n            <pre><code>const total_count = await accountStub.countWorkflows(accountId);</code></pre>\n            <p>The other powerful feature of our RPC system is that it supports passing not only <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types\"><u>Structured Cloneable</u></a> objects back and forth but also entire classes. More on this later.</p><p>Let’s move on to Engine.</p>\n          <div>\n            <h3>Engine and instance</h3>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#engine-and-instance\">\n              \n            </a>\n          </div>\n        <p>Every instance of a workflow runs alongside an Engine instance. The Engine is responsible for starting up the user’s workflow entry point, executing the steps on behalf of the user, handling their results, and tracking the workflow state until completion.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6yrKsuF501oRCDujckr3yM/bde40097ec5bedda07793375e53e99b9/image1.png\">\n          </figure><p>When we started thinking about the Engine, we thought about modeling it after a <a href=\"https://en.wikipedia.org/wiki/Finite-state_machine\"><u>state machine</u></a>, and that was what our initial prototypes looked like. However, state machines require an ahead-of-time understanding of the userland code, which implies having a build step before running them. This is costly at scale and introduces additional complexity.</p><p>A few iterations later, we had another idea. What if we could model the engine as a game loop?</p><p>Unlike other computer programs, games operate regardless of a user's input. The game loop is essentially a sequence of tasks that implement the game's logic and update the display, typically one loop per video frame. Here’s an example of a game loop in pseudo-code:</p>\n            <pre><code>while (game in running)\n    check for user input\n    move graphics\n    play sounds\nend while</code></pre>\n            <p>Well, an oversimplified version of our Workflow engine would look like this:</p>\n            <pre><code>while (last step not completed)\n    iterate every step\n       use memoized cache as response if the step has run already\n       continue running step or timer if it hasn't finished yet\nend while</code></pre>\n            <p>A workflow is indeed a loop that keeps on going, performing the same sequence of logical tasks until the last step completes.</p><p>The Engine and the instance run hand-in-hand in a one-to-one relationship. The first is managed, and part of the platform. It uses SQLite and other platform APIs internally, and we can constantly add new features, fix bugs, and deploy new versions, while keeping everything transparent to the end user. The second is the actual account-owned Worker script that declares the Workflow steps.</p><p>For example, when someone passes a callback into <code>step.do()</code>:</p>\n            <pre><code>export class MyWorkflow extends WorkflowEntrypoint&lt;Env, Params&gt; {\n  async run(event: WorkflowEvent&lt;Params&gt;, step: WorkflowStep) {\n    step.do('step1', () =&gt; { ... });\n  }\n}</code></pre>\n            <p>We switch execution over to the Engine. Again, this is possible because of the power of JS RPC. Besides passing Structured Cloneable objects back and forth, JS RPC allows us to <a href=\"https://developers.cloudflare.com/workers/runtime-apis/rpc/#send-functions-as-parameters-of-rpc-methods\"><u>create and pass entire application-defined classes</u></a> that extend the built-in RpcTarget. So this is what happens behind the scenes when your Instance calls <code>step.do()</code> (simplified):</p>\n            <pre><code>export class Context extends RpcTarget {\n\n  async do&lt;T&gt;(name: string, callback: () =&gt; Promise&lt;T&gt;): Promise&lt;T&gt; {\n\n    // First we check we have a cache of this step.do() already\n    const maybeResult = await this.#state.storage.get(name);\n\n    // We return the cache if it exists\n    if (maybeValue) { return maybeValue; }\n\n    // Else we run the user callback\n    return doWrapper(callback);\n  }\n\n}\n</code></pre>\n            <p>Here’s a more complete diagram of the Engine’s <code>step.do()</code> lifecycle:</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4MymVGS7BxwityCRlWcBOX/136d4dcf0affce04164f87b6bbe8b12a/image5.png\">\n          </figure><p>Again, this diagram only partially represents everything we do in the Engine; things like logging for observability or handling exceptions are missing, and we don't get into the details of how queuing is implemented. However, it gives you a good idea of how the Engine abstracts and handles all the complexities of completing a step under the hood, allowing us to expose a simple-to-use API to end users.</p><p>Also, it's worth reiterating that every workflow instance is an Engine behind the scenes, and every Engine is an SQLite-backed Durable Object. This ensures that every instance runtime and state are isolated and independent of each other and that we can effortlessly scale to run billions of workflow instances, a solved problem for Durable Objects.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4uEoEAtsjNquPCD3F50S9d/006556baf2a0478d1de10e4514843baa/image3.png\">\n          </figure>\n          <div>\n            <h3>Durability</h3>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#durability\">\n              \n            </a>\n          </div>\n        <p>Durable Execution is all the rage now when we talk about workflow engines, and ours is no exception. Workflows are typically long-lived processes that run multiple functions in sequence where anything can happen. Those functions can time out or fail because of a remote server error or a network issue and need to be retried. A workflow engine ensures that your application runs smoothly and completes regardless of the problems it encounters.</p><p>Durability means that if and when a workflow fails, the Engine can re-run it, resume from the last recorded step, and deterministically re-calculate the state from all the successful steps' cached responses. This is possible because steps are stateful and idempotent; they produce the same result no matter how many times we run them, thus not causing unintended duplicate effects like sending the same invoice to a customer multiple times.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1R5UfQfNMKI7hB6QXJfCUr/242e85f2b5287394871e916844359bd4/image7.png\">\n          </figure><p>We ensure durability and handle failures and retries by sharing the same technique we use for a <code>step.sleep()</code> that requires sleeping for days or months: a combination of using <code>scheduler.wait()</code>, a method of the <a href=\"https://github.com/WICG/scheduling-apis\"><u>upcoming WICG Scheduling API</u></a> that we already <a href=\"https://developers.cloudflare.com/workers/platform/changelog/historical-changelog/#2021-12-10\"><u>support</u></a>, and <a href=\"https://developers.cloudflare.com/durable-objects/api/alarms/\"><u>Durable Objects alarms</u></a>, which allow you to schedule the Durable Object to be woken up at a time in the future.</p><p>These two APIs allow us to overcome the lack of guarantees that a Durable Object runs forever, giving us complete control of its lifecycle. Since every state transition through userland code persists in the Engine’s strongly consistent SQLite, we track timestamps when a step begins execution, its attempts (if it needs retries), and its completion.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6FSCXRt9fO4EaaBP7hLV8x/a59de27dfbe18f39addd4eb8240b9df9/image10.png\">\n          </figure><p>This means that steps pending if a Durable Object is <a href=\"https://developers.cloudflare.com/durable-objects/reference/in-memory-state/\"><u>evicted</u></a> — perhaps due to a two-month-long timer — get rerun on the next lifetime of the Engine (with its cache from the previous lifetime hydrated) that is triggered by an alarm set with the timestamp of the next expected state transition.&nbsp;</p>\n          <div>\n            <h2>Real-life workflow, step by step</h2>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#real-life-workflow-step-by-step\">\n              \n            </a>\n          </div>\n          <p>Let's walk through an example of a real-life application. You run an e-commerce website and would like to send email reminders to your customers for forgotten carts that haven't been checked out in a few days.</p><p>What would typically have to be a combination of a queue, a cron job, and querying a database table periodically can now simply be a Workflow that we start on every new cart:</p>\n            <pre><code>import {\n  WorkflowEntrypoint,\n  WorkflowEvent,\n  WorkflowStep,\n} from \"cloudflare:workers\";\nimport { sendEmail } from \"./legacy-email-provider\";\n\ntype Params = {\n  cartId: string;\n};\n\ntype Env = {\n  DB: D1Database;\n};\n\nexport class Purchase extends WorkflowEntrypoint&lt;Env, Params&gt; {\n  async run(\n    event: WorkflowEvent&lt;Params&gt;,\n    step: WorkflowStep\n  ): Promise&lt;unknown&gt; {\n    await step.sleep(\"wait for three days\", \"3 days\");\n\n    // Retrieve cart from D1\n    const cart = await step.do(\"retrieve cart from database\", async () =&gt; {\n      const { results } = await this.env.DB.prepare(`SELECT * FROM cart WHERE id = ?`)\n        .bind(event.payload.cartId)\n        .all();\n      return results[0];\n    });\n\n    if (!cart.checkedOut) {\n      await step.do(\"send an email\", async () =&gt; {\n        await sendEmail(\"reminder\", cart);\n      });\n    }\n  }\n}\n</code></pre>\n            <p>This works great. However, sometimes the <code>sendEmail</code> function fails due to an upstream provider erroring out. While <code>step.do</code> automatically retries with a reasonable default configuration, we can define our settings:</p>\n            <pre><code>if (cart.isComplete) {\n  await step.do(\n    \"send an email\",\n    {\n      retries: {\n        limit: 5,\n        delay: \"1 min\",\n        backoff: \"exponential\",\n      },\n    },\n    async () =&gt; {\n      await sendEmail(\"reminder\", cart);\n    }\n  );\n}\n</code></pre>\n            \n          <div>\n            <h3>Managing Workflows</h3>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#managing-workflows\">\n              \n            </a>\n          </div>\n        <p>Workflows allows us to create and manage workflows using four different interfaces:</p><ul><li><p>Using our REST HTTP API available on <a href=\"https://developers.cloudflare.com/api/\"><u>Cloudflare’s API catalog</u></a></p></li><li><p>Using <a href=\"https://developers.cloudflare.com/workers/wrangler/\"><u>Wrangler</u></a>, Cloudflare's developer platform command-line tool</p></li><li><p>Programmatically inside a Worker using <a href=\"https://developers.cloudflare.com/workers/runtime-apis/bindings/\"><u>bindings</u></a></p></li><li><p>Using our Web UI in the <a href=\"https://dash.cloudflare.com/\"><u>dashboard</u></a></p></li></ul><p>The HTTP API makes it easy to trigger new instances of workflows from any system, even if it isn’t on Cloudflare, or from the command line. For example:</p>\n            <pre><code>curl --request POST \\\n  --url https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workflows/purchase-workflow/instances/$CART_INSTANCE_ID \\\n  --header 'Authorization: Bearer $ACCOUNT_TOKEN \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n\t\"id\": \"$CART_INSTANCE_ID\",\n\t\"params\": {\n\t\t\"cartId\": \"f3bcc11b-2833-41fb-847f-1b19469139d1\"\n\t}\n  }'</code></pre>\n            <p>Wrangler goes one step further and gives us a friendlier set of commands to interact with workflows with fancy formatted outputs without needing to authenticate with tokens. Type <code>npx wrangler workflows</code> for help, or:</p>\n            <pre><code>npx wrangler workflows trigger purchase-workflow '{ \"cartId\": \"f3bcc11b-2833-41fb-847f-1b19469139d1\" }'</code></pre>\n            <p>Furthermore, Workflows has first-party support in wrangler, and you can test your instances locally. A Workflow is similar to a regular<a href=\"https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/\"><u> WorkerEntrypoint</u></a> in your Worker, which means that <code>wrangler dev</code> just naturally works.</p>\n            <pre><code>❯ npx wrangler dev\n\n ⛅️ wrangler 3.82.0\n----------------------------\n\nYour worker has access to the following bindings:\n- Workflows:\n  - CART_WORKFLOW: EcommerceCartWorkflow\n⎔ Starting local server...\n[wrangler:inf] Ready on http://localhost:8787\n╭───────────────────────────────────────────────╮\n│  [b] open a browser, [d] open devtools        │\n╰───────────────────────────────────────────────╯\n</code></pre>\n            <p>Workflow APIs are also available as a Worker binding. You can interact with the platform programmatically from another Worker script in the same account without worrying about permissions or authentication. You can even have workflows that call and interact with other workflows.</p>\n            <pre><code>import { WorkerEntrypoint } from \"cloudflare:workers\";\n\ntype Env = { DEMO_WORKFLOW: Workflow };\nexport default class extends WorkerEntrypoint&lt;Env&gt; {\n  async fetch() {\n    // Pass in a user defined name for this instance\n    // In this case, we use the same as the cartId\n    const instance = await this.env.DEMO_WORKFLOW.create({\n      id: \"f3bcc11b-2833-41fb-847f-1b19469139d1\",\n      params: {\n          cartId: \"f3bcc11b-2833-41fb-847f-1b19469139d1\",\n      }\n    });\n  }\n  async scheduled() {\n    // Restart errored out instances in a cron\n    const instance = await this.env.DEMO_WORKFLOW.get(\n      \"f3bcc11b-2833-41fb-847f-1b19469139d1\"\n    );\n    const status = await instance.status();\n    if (status.error) {\n      await instance.restart();\n    }\n  }\n}</code></pre>\n            \n          <div>\n            <h3>Observability&nbsp;</h3>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#observability\">\n              \n            </a>\n          </div>\n        <p>Having good observability and data on often long-lived asynchronous tasks is crucial to understanding how we're doing under normal operation and, more importantly, when things go south, and we need to troubleshoot problems or when we are iterating on code changes.</p><p>We designed Workflows around the philosophy that there is no such thing as too much logging. You can get all the SQLite data for your workflow and its instances by calling the REST APIs. Here is the output of an instance:</p>\n            <pre><code>{\n  \"success\": true,\n  \"errors\": [],\n  \"messages\": [],\n  \"result\": {\n    \"status\": \"running\",\n    \"params\": {},\n    \"trigger\": { \"source\": \"api\" },\n    \"versionId\": \"ae042999-39ff-4d27-bbcd-22e03c7c4d02\",\n    \"queued\": \"2024-10-21 17:15:09.350\",\n    \"start\": \"2024-10-21 17:15:09.350\",\n    \"end\": null,\n    \"success\": null,\n    \"steps\": [\n      {\n        \"name\": \"send email\",\n        \"start\": \"2024-10-21 17:15:09.411\",\n        \"end\": \"2024-10-21 17:15:09.678\",\n        \"attempts\": [\n          {\n            \"start\": \"2024-10-21 17:15:09.411\",\n            \"end\": \"2024-10-21 17:15:09.678\",\n            \"success\": true,\n            \"error\": null\n          }\n        ],\n        \"config\": {\n          \"retries\": { \"limit\": 5, \"delay\": 1000, \"backoff\": \"constant\" },\n          \"timeout\": \"15 minutes\"\n        },\n        \"output\": \"celso@example.com\",\n        \"success\": true,\n        \"type\": \"step\"\n      },\n      {\n        \"name\": \"sleep-1\",\n        \"start\": \"2024-10-21 17:15:09.763\",\n        \"end\": \"2024-10-21 17:17:09.763\",\n        \"finished\": false,\n        \"type\": \"sleep\",\n        \"error\": null\n      }\n    ],\n    \"error\": null,\n    \"output\": null\n  }\n}</code></pre>\n            <p>As you can see, this is essentially a dump of the instance engine SQLite in JSON. You have the <b>errors</b>, <b>messages</b>, current <b>status</b>, and what happened with <b>every step</b>, all time stamped to the millisecond.</p><p>It's one thing to get data about a specific workflow instance, but it's another to zoom out and look at aggregated statistics of all your workflows and instances over time. Workflows data is available through our <a href=\"https://developers.cloudflare.com/analytics/graphql-api/\"><u>GraphQL Analytics API</u></a>, so you can query it in aggregate and generate valuable insights and reports. In this example we ask for aggregated analytics about the wall time of all the instances of the “e-commerce-carts” workflow:</p>\n            <pre><code>{\n  viewer {\n    accounts(filter: { accountTag: \"febf0b1a15b0ec222a614a1f9ac0f0123\" }) {\n      wallTime: workflowsAdaptiveGroups(\n        limit: 10000\n        filter: {\n          datetimeHour_geq: \"2024-10-20T12:00:00.000Z\"\n          datetimeHour_leq: \"2024-10-21T12:00:00.000Z\"\n          workflowName: \"e-commerce-carts\"\n        }\n        orderBy: [count_DESC]\n      ) {\n        count\n        sum {\n          wallTime\n        }\n        dimensions {\n          date: datetimeHour\n        }\n      }\n    }\n  }\n}\n</code></pre>\n            <p>For convenience, you can evidently also use Wrangler to describe a workflow or an instance and get an instant and beautifully formatted response:</p>\n            <pre><code>sid ~ npx wrangler workflows instances describe purchase-workflow latest\n\n ⛅️ wrangler 3.80.4\n\nWorkflow Name:         purchase-workflow\nInstance Id:           d4280218-7756-41d2-bccd-8d647b82d7ce\nVersion Id:            0c07dbc4-aaf3-44a9-9fd0-29437ed11ff6\nStatus:                ✅ Completed\nTrigger:               🌎 API\nQueued:                14/10/2024, 16:25:17\nSuccess:               ✅ Yes\nStart:                 14/10/2024, 16:25:17\nEnd:                   14/10/2024, 16:26:17\nDuration:              1 minute\nLast Successful Step:  wait for three days\nOutput:                false\nSteps:\n\n  Name:      wait for three days\n  Type:      💤 Sleeping\n  Start:     14/10/2024, 16:25:17\n  End:       17/10/2024, 16:25:17\n  Duration:  3 day</code></pre>\n            <p>And finally, we worked really hard to get you the best dashboard UI experience when navigating Workflows data.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/64XUtBwldkSXUTJ5xEJBgo/2aa861583c8c56c19194cb0869a15a2a/image8.png\">\n          </figure>\n          <div>\n            <h2>So, how much does it cost?</h2>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#so-how-much-does-it-cost\">\n              \n            </a>\n          </div>\n          <p>It’d be painful if we introduced a powerful new way to build Workers applications but made it cost prohibitive.</p><p>Workflows is <a href=\"https://developers.cloudflare.com/workers/platform/pricing/#workers\"><u>priced</u></a> just like Cloudflare Workers, where we <a href=\"https://blog.cloudflare.com/workers-pricing-scale-to-zero/\"><u>introduced CPU-based pricing</u></a>: only on active CPU time and requests, not duration (aka: wall time).</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/11WroT4xt0zPj6bsou4u3X/8f2775569f280107345322cb97603b3e/image4.png\">\n          </figure><p><sup><i>Workers Standard pricing model</i></sup></p><p>This is especially advantageous when building the long-running, multi-step applications that Workflows enables: if you had to pay while your Workflow was sleeping, waiting on an event, or making a network call to an API, writing the “right” code would be at odds with writing affordable code.</p><p>There’s also no need to keep a Kubernetes cluster or a group of virtual machines running (and burning a hole in your wallet): we manage the infrastructure, and you only pay for the compute your Workflows consume.&nbsp;&nbsp;&nbsp;</p>\n          <div>\n            <h2>What’s next?</h2>\n            <a href=\"https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/#whats-next\">\n              \n            </a>\n          </div>\n          <p>Today, after months of developing the platform, we are announcing the open beta program, and we couldn't be more excited to see how you will be using Workflows. Looking forward, we want to do things like triggering instances from queue messages and have other ideas, but at the same time, we are certain that your feedback will help us shape the roadmap ahead.</p><p>We hope that this blog post gets you thinking about how to use Workflows for your next application, but also that it inspires you on what you can build on top of Workers. Workflows as a platform is entirely built on top of Workers, its resources, and APIs. Anyone can do it, too.</p><p>To chat with the team and other developers building on Workflows, join the #workflows-beta channel on the<a href=\"https://discord.cloudflare.com/\"> <u>Cloudflare Developer Discord</u></a>, and keep an eye on the<a href=\"https://developers.cloudflare.com/workflows/reference/changelog/\"> <u>Workflows changelog</u></a> during the beta. Otherwise,<a href=\"https://developers.cloudflare.com/workflows/get-started/guide/\"> visit the Workflows tutorial</a> to get started.</p><p>If you're an engineer, <a href=\"https://www.cloudflare.com/en-gb/careers/jobs/\"><u>look for opportunities</u></a> to work with us and help us improve Workflows or build other products.</p>",
            "url": "https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/",
            "title": "Build durable applications on Cloudflare Workers: you write the Workflows, we take care of the rest",
            "date_modified": "2024-10-24T13:00:00.000Z"
        },
        {
            "content_html": "<p>With the rapid advancements occurring in the AI space, developers face significant challenges in keeping up with the ever-changing landscape. New models and providers are continuously emerging, and understandably, developers want to experiment and test these options to find the best fit for their use cases. This creates the need for a streamlined approach to managing multiple models and providers, as well as a centralized platform to efficiently monitor usage, implement controls, and gather data for optimization.</p><p><a href=\"https://developers.cloudflare.com/ai-gateway/\"><u>AI Gateway</u></a> is specifically designed to address these pain points. Since its launch in <a href=\"https://blog.cloudflare.com/announcing-ai-gateway\"><u>September 2023</u></a>, AI Gateway has empowered developers and organizations by successfully proxying over 2 billion requests in just one year, as we <a href=\"https://blog.cloudflare.com/workers-ai-bigger-better-faster/#optimizing-ai-workflows-with-ai-gateway\"><u>highlighted during September’s Birthday Week</u></a>. With AI Gateway, developers can easily store, analyze, and optimize their AI <a href=\"https://www.cloudflare.com/learning/ai/inference-vs-training/\"><u>inference</u></a> requests and responses in real time.</p><p>With our initial architecture, AI Gateway faced a significant challenge: the logs, those critical trails of data interactions between applications and AI models, could only be retained for 30 minutes. This limitation was not just a minor inconvenience; it posed a substantial barrier for developers and businesses needing to analyze long-term patterns, ensure compliance, or simply debug over more extended periods.</p><p>In this post, we'll explore the technical challenges and strategic decisions behind extending our log storage capabilities from 30 minutes to being able to store billions of logs indefinitely. We'll discuss the challenges of scale, the intricacies of data management, and how we've engineered a system that not only meets the demands of today, but is also scalable for the future of AI development.</p>\n          <div>\n            <h2>Background</h2>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#background\">\n              \n            </a>\n          </div>\n          <p>AI Gateway is built on <a href=\"https://workers.cloudflare.com\"><u>Cloudflare Workers</u></a>, a serverless platform that runs on the Cloudflare network, allowing developers to write small JavaScript functions that can execute at the point of need, near the user, on Cloudflare's vast network of data centers, without worrying about platform scalability.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6jV3iKCN771ixU21Hixfpz/18086a52cfe05cd20f1c94bbba21e293/_BLOG-2593_2.png\">\n          </figure><p>Our customers use multiple providers and models and are always looking to optimize the way they do inference. And, of course, in order to evaluate their prompts, performance, cost, and to troubleshoot what’s going on, AI Gateway’s customers need to store requests and responses. New requests show up within 15 seconds and customers can check a request’s cost, duration, number of tokens, and provide their feedback (thumbs up or down).</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/RBqZXnLJNCaQPbtbzjQmj/70aa2598f9b9294b67db8cd5712a6345/_BLOG-2593_3.png\">\n          </figure><p>This scales in a way where an account can have multiple gateways and each gateway has its own settings. In our first implementation, a backend worker was responsible for storing Real Time Logs and other background tasks. However, in the rapidly evolving domain of artificial intelligence, where real-time data is as precious as the insights it provides, managing log data efficiently becomes paramount. We recognized that to truly empower our users, we needed to offer a solution where logs weren't just transient records but could be stored permanently. Permanent log storage means developers can now track the performance, security, and operational insights of their AI applications over time, enabling not only immediate troubleshooting but also longitudinal studies of AI behavior, usage trends, and system health.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1TcC1ZdyNzT0xwFwme2oBt/a9202691a0a983fa3eafdf6c0ee92f2c/_BLOG-2593_4.png\">\n          </figure><p>The diagram above describes our old architecture, which could only store 30 minutes of data.</p><p>Tracing the path of a request through the AI Gateway, as depicted in the sequence above:</p><ol><li><p>A developer sends a new inference request, which is first received by our Gateway Worker.</p></li><li><p>The Gateway Worker then performs several checks: it looks for cached results, enforces rate limits, and verifies any other configurations set by the user for their gateway. Provided all conditions are met, it forwards the request to the selected inference provider (in this diagram, OpenAI).</p></li><li><p>The inference provider processes the request and sends back the response.</p></li><li><p>Simultaneously, as the response is relayed back to the developer, the request and response details are also dispatched to our Backend Worker. This worker's role is to manage and store the log of this transaction.</p></li></ol>\n          <div>\n            <h2>The challenge: Store two billion logs</h2>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#the-challenge-store-two-billion-logs\">\n              \n            </a>\n          </div>\n          \n          <div>\n            <h3>First step: real-time logs</h3>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#first-step-real-time-logs\">\n              \n            </a>\n          </div>\n        <p>Initially, the AI Gateway project stored both request metadata and the actual request bodies in a <a href=\"https://developers.cloudflare.com/d1/\"><u>D1 database</u></a>. This approach facilitated rapid development in the project's infancy. However, as customer engagement grew, the D1 database began to fill at an accelerating rate, eventually retaining logs for only 30 minutes at a time.</p><p>To mitigate this, we first optimized the database schema, which extended the log retention to one hour. However, we soon encountered diminishing returns due to the sheer volume of byte data from the request bodies. Post-launch, it became clear that a more scalable solution was necessary. We decided to migrate the request bodies to R2 storage, significantly alleviating the data load on D1. This adjustment allowed us to incrementally extend log retention to 24 hours.</p><p>Consequently, D1 functioned primarily as a log index, enabling users to search and filter logs efficiently. When users needed to view details or download a log, these actions were seamlessly proxied through to R2.</p><p>This dual-system approach provided us with the breathing room to contemplate and develop more sophisticated storage solutions for the future.</p>\n          <div>\n            <h3>Second step: persistent logs and Durable Object transactional storage</h3>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#second-step-persistent-logs-and-durable-object-transactional-storage\">\n              \n            </a>\n          </div>\n        <p>As our traffic surged, we encountered a growing number of requests from customers wanting to access and compare older logs.</p><p>Upon learning that the Durable Objects team was seeking beta testers for their new <a href=\"https://blog.cloudflare.com/sqlite-in-durable-objects/\"><u>Durable Objects with SQLite</u></a>, we eagerly signed up.</p><p>Originally, we considered Durable Objects as the ideal solution for expanding our log storage capacity, which required us to shard the logs by a unique string. Initially, this string was the account ID, but during a mid-development load test, we hit a cap at 10 million logs per Durable Object. This limitation meant that each account could only support up to this number of logs.</p><p>Given our commitment to the DO migration, we saw an opportunity rather than a constraint. To overcome the 10 million log limit per account, we refined our approach to shard by both account ID and gateway name. This adjustment effectively raised the storage ceiling from 10 million logs per account to 10 million per gateway. With the default setting allowing each account up to 10 gateways, the potential storage for each account skyrocketed to 100 million logs.</p><p>This strategic pivot not only enabled us to store a significantly larger number of logs. But also enhanced our flexibility in gateway management. Now, when a gateway is deleted, we can simply remove the corresponding Durable Object.</p><p>Additionally, this sharding method isolates high-volume request scenarios. If one customer's heavy usage slows down log insertion, it only impacts their specific Durable Object, thereby preserving performance for other customers.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Q6degDA3V02dZFVugW2LO/ae121890a3d4493e5c01459c477f32d9/_BLOG-2593_5.png\">\n          </figure><p>Taking a glance at the revised architecture diagram, we replaced the Backend Worker with our newly integrated Durable Object. The rest of the request flow remains unchanged, including the concurrent response to the user and the interaction with the Durable Object, which occurs in the fourth step.</p><p>Leveraging Cloudflare’s network, our Gateway Worker operates near the user's location, which in turn positions the user's Durable Object close by. This proximity significantly enhances the speed of log insertion and query operations.</p>\n          <div>\n            <h3>Third step: managing thousands of Durable Objects</h3>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#third-step-managing-thousands-of-durable-objects\">\n              \n            </a>\n          </div>\n        <p>As the number of users and requests on AI Gateway grows, managing each unique Durable Object (DO) becomes increasingly complex. New customers join continuously, and we needed an efficient method to track each DO, ensure users stay within their 10 gateway limit, and manage the storage capacity for free users.</p><p>To address these challenges, we introduced another layer of control with a new Durable Object we've named the Account Manager. The primary function of the Account Manager is straightforward yet crucial: it keeps user activities in check.</p><p>Here's how it works: before any Gateway commits a new log to permanent storage, it consults the Account Manager. This check determines whether the gateway is allowed to insert the log based on the user's current usage and entitlements. The Account Manager uses its own SQLite database to verify the total number of rows a user has and their service level. If all checks pass, it signals the Gateway that the log can be inserted. It was paramount to guarantee that this entire validation process occurred in the background, ensuring that the user experience remains seamless and uninterrupted.</p><p>The Account Manager stays updated by periodically receiving data from each Gateway’s Durable Object. Specifically, after every 1000 inference requests, the Gateway sends an update on its total rows to the Account Manager, which then updates its local records. This system ensures that the Account Manager has the most current data when making its decisions.</p><p>Additionally, the Account Manager is responsible for monitoring customer entitlements. It tracks whether an account is on a free or paid plan, how many gateways a user is permitted to create, and the log storage capacity allocated to each gateway.&nbsp;</p><p>Through these mechanisms, the Account Manager not only helps in maintaining system integrity but also ensures fair usage across all users of AI Gateway.</p>\n          <div>\n            <h2>AI evaluations and Durable Objects sharding</h2>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#ai-evaluations-and-durable-objects-sharding\">\n              \n            </a>\n          </div>\n          <p>As we continue to develop evaluations to fully automatic and, in the future, use Large Language Models (LLMs),&nbsp; we are now taking the first step towards this goal and launching the open beta phase of comprehensive <a href=\"https://blog.cloudflare.com/workers-ai-bigger-better-faster/#optimizing-ai-workflows-with-ai-gateway\"><u>AI evaluations</u></a>, centered on Human-in-the-Loop feedback.</p><p>This feature empowers users to create bespoke datasets from their application logs, thereby enabling them to score and evaluate the performance, speed, and cost-effectiveness of their models, with a primary focus on LLMs and automated scoring, analyzing the performance of LLMs, providing developers with objective, data-driven insights to refine their models.</p><p>To do this, developers require a reliable logging mechanism that persists logs from multiple gateways, storing up to 100 million logs in total (10 million logs per gateway, across 10 gateways). This represents a significant volume of data, as each request made through the AI Gateway generates a log entry, with some log entries potentially exceeding 50 MB in size.</p><p>This necessity leads us to work on the expansion of log storage capabilities. Since log storage is limited to 10 million logs per gateway, in future iterations, we aim to scale this capacity by implementing sharded Durable Objects (DO), allowing multiple Durable Objects per gateway to handle and store logs. This scaling strategy will enable us to store significantly larger volumes of logs, providing richer data for evaluations (using LLMs as a judge or from user input), all through AI Gateway.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7FLy2JEfvGFo8P7PCVBZYT/a4d6367341e9fc224dedaad3aa0f02e2/_BLOG-2593_6.png\">\n          </figure>\n          <div>\n            <h2>Coming Soon</h2>\n            <a href=\"https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/#coming-soon\">\n              \n            </a>\n          </div>\n          <p>We are working on improving our existing <a href=\"https://developers.cloudflare.com/ai-gateway/providers/universal/\"><u>Universal Endpoint</u></a>, the next step on an enhanced solution that builds on existing fallback mechanisms to offer greater resilience, flexibility, and intelligence in request management.</p><p>Currently, when a provider encounters an error or is unavailable, our system <a href=\"https://developers.cloudflare.com/ai-gateway/configuration/fallbacks/\"><u>falls back</u></a> to an alternative provider to ensure continuity. The improved Universal Endpoint takes this a step further by introducing automatic retry capabilities, allowing failed requests to be reattempted before fallback is triggered. This significantly improves reliability by handling transient errors and increasing the likelihood of successful request fulfillment. It will look something like this:</p>\n            <pre><code>curl --location 'https://aig.example.com/' \\\n--header 'CF-AIG-TOKEN: Bearer XXXX' \\\n--header 'Content-Type: application/json' \\\n--data-raw '[\n    {\n        \"id\": \"0001\",\n        \"provider\": \"openai\",\n        \"endpoint\": \"chat/completions\",\n        \"headers\": {\n            \"Authorization\": \"Bearer XXXX\",\n            \"Content-Type\": \"application/json\"\n        },\n        \"query\": {\n            \"model\": \"gpt-3.5-turbo\",\n            \"messages\": [\n                {\n                    \"role\": \"user\",\n                    \"content\": \"generate a prompt to create cloudflare random images\"\n                }\n            ]\n        },\n        \"option\": {\n            \"retry\": 2,\n            \"delay\": 200,\n            \"onComplete\": {\n                \"provider\": \"workers-ai\",\n                \"endpoint\": \"@cf/stabilityai/stable-diffusion-xl-base-1.0\",\n                \"headers\": {\n                    \"Authorization\": \"Bearer A5UFQkHewHF1-sA3hTVQFaPxRuu5wmS0eJcCS_MC\",\n                    \"Content-Type\": \"application/json\"\n                },\n                \"query\": {\n                    \"messages\": [\n                        {\n                            \"role\": \"user\",\n                            \"content\": \"&lt;prompt-response id='\\''0001'\\'' /&gt;\"\n                        }\n                    ]\n                }\n            }\n        }\n    },\n    {\n        \"provider\": \"workers-ai\",\n        \"endpoint\": \"@cf/stabilityai/stable-diffusion-xl-base-1.0\",\n        \"headers\": {\n            \"Authorization\": \"Bearer XXXXXX\",\n            \"Content-Type\": \"application/json\"\n        },\n        \"query\": {\n            \"messages\": [\n                {\n                    \"role\": \"user\",\n                    \"content\": \"create a image of a missing cat\"\n                }\n            ]\n        }\n    }\n]'\n</code></pre>\n            <p>The request to the improved Universal Endpoint system demonstrates how it handles multiple providers with integrated retry mechanisms and fallback logic. In this example, the first request is sent to a provider like OpenAI, asking it to generate a text-to-image prompt. The “retry” option ensures that transient issues don’t result in immediate failure.</p><p>The system’s ability to seamlessly switch between providers while applying retry strategies ensures higher reliability and robustness in managing requests. By leveraging fallback logic, the Improved Universal Endpoint can dynamically adapt to provider failures, ensuring that tasks are completed successfully even in complex, multi-step workflows.</p><p>In addition to retry logic, we will have the ability to inspect requests and responses and make dynamic decisions based on the content of the result. This enables developers to create conditional workflows where the system can adapt its behavior depending on the nature of the response, creating a highly flexible and intelligent decision-making process.</p><p>If you haven’t yet used AI Gateway, check out our <a href=\"https://developers.cloudflare.com/ai-gateway/\"><u>developer documentation</u></a> on how to get started. If you have any questions, reach out on our <a href=\"http://discord.cloudflare.com/\"><u>Discord channel</u></a>.</p>",
            "url": "https://blog.cloudflare.com/billions-and-billions-of-logs-scaling-ai-gateway-with-the-cloudflare/",
            "title": "Billions and billions (of logs): scaling AI Gateway with the Cloudflare Developer Platform",
            "date_modified": "2024-10-24T13:00:00.000Z"
        },
        {
            "content_html": "<p></p><p><a href=\"https://www.cloudflare.com/developer-platform/products/cloudflare-queues/\"><u>Cloudflare Queues</u></a> let a developer decouple their Workers into event-driven services. Producer Workers write events to a Queue, and consumer Workers are invoked to take actions on the events. For example, you can use a Queue to decouple an e-commerce website from a service which sends purchase confirmation emails to users. During 2024’s Birthday Week, we <a href=\"https://blog.cloudflare.com/builder-day-2024-announcements?_gl=1*18s1fwl*_gcl_au*MTgyNDA5NjE5OC4xNzI0MjgzMTQ0*_ga*OTgwZmE0YWUtZWJjMS00NmYxLTllM2QtM2RmY2I4ZjAwNzZk*_ga_SQCRB0TXZW*MTcyODkyOTU2OS4xNi4xLjE3Mjg5Mjk1NzcuNTIuMC4w/#queues-is-ga\"><u>announced that Cloudflare Queues is now Generally Available</u></a>, with significant performance improvements that enable larger workloads. To accomplish this, we switched to a new architecture for Queues that enabled the following improvements:</p><ul><li><p>Median latency for sending messages has dropped from ~200ms to ~60ms</p></li><li><p>Maximum throughput for each Queue has increased over 10x, from 400 to 5000 messages per second</p></li><li><p>Maximum Consumer concurrency for each Queue has increased from 20 to 250 concurrent invocations</p></li></ul>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5PvIsHfLwwIkp2LXVUDhmG/99f286f2f89d10b2a7e359d8d66f6dba/image5.png\">\n          </figure><p><sup><i>Median latency drops from ~200ms to ~60ms as Queues are migrated to the new architecture</i></sup></p><p>In this blog post, we'll share details about how we built Queues using Durable Objects and the Cloudflare Developer Platform, and how we migrated from an initial Beta architecture to a geographically-distributed, horizontally-scalable architecture for General Availability.</p>\n          <div>\n            <h3>v1 Beta architecture</h3>\n            <a href=\"https://blog.cloudflare.com/how-we-built-cloudflare-queues#v1-beta-architecture\">\n              \n            </a>\n          </div>\n        <p>When initially designing Cloudflare Queues, we decided to build something simple that we could get into users' hands quickly. First, we considered leveraging an off-the-shelf messaging system such as Kafka or Pulsar. However, we decided that it would be too challenging to operate these systems at scale with the large number of isolated tenants that we wanted to support.</p><p>Instead of investing in new infrastructure, we decided to build on top of one of Cloudflare's existing developer platform building blocks: <b>Durable Objects.</b> <a href=\"https://www.cloudflare.com/developer-platform/durable-objects/\"><u>Durable Objects</u></a> are a simple, yet powerful building block for coordination and storage in a distributed system. In our initial <i>v1 </i>architecture, each Queue was implemented using a single Durable Object. As shown below, clients would send messages to a Worker running in their region, which would be forwarded to the single Durable Object hosted in the WNAM (Western North America) region. We used a single Durable Object for simplicity, and hosted it in WNAM for proximity to our centralized configuration API service.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/yxj5Gut3usYa0mFbRddXU/881f5905f789bc2f910ee1b2dcadac92/image1.png\">\n          </figure><p>One of a Queue's main responsibilities is to accept and store incoming messages. Sending a message to a <i>v1</i> Queue used the following flow:</p><ul><li><p>A client sends a POST request containing the message body to the Queues API at <code>/accounts/:accountID/queues/:queueID/messages</code></p></li><li><p>The request is handled by an instance of the <b>Queue Broker Worker</b> in a Cloudflare data center running near the client.</p></li><li><p>The Worker performs authentication, and then uses Durable Objects <code>idFromName</code> API to route the request to the <b>Queue Durable Object</b> for the given <code>queueID</code></p></li><li><p>The Queue Durable Object persists the message to storage before returning a <i>success </i>back to the client.</p></li></ul><p>Durable Objects handled most of the heavy-lifting here: we did not need to set up any new servers, storage, or service discovery infrastructure. To route requests, we simply provided a <code>queueID</code> and the platform handled the rest. To store messages, we used the Durable Object storage API to <code>put</code> each message, and the platform handled reliably storing the data redundantly.</p>\n          <div>\n            <h3>Consuming messages</h3>\n            <a href=\"https://blog.cloudflare.com/how-we-built-cloudflare-queues#consuming-messages\">\n              \n            </a>\n          </div>\n        <p>The other main responsibility of a Queue is to deliver messages to a Consumer. Delivering messages in a v1 Queue used the following process:</p><ul><li><p>Each Queue Durable Object maintained an <b>alarm </b>that was always set when there were undelivered messages in storage. The alarm guaranteed that the Durable Object would reliably wake up to deliver any messages in storage, even in the presence of failures. The alarm time was configured to fire after the user's selected <i>max wait time</i><b><i>, </i></b>if only a partial batch of messages was available. Whenever one or more full batches were available in storage, the alarm was scheduled to fire immediately.</p></li><li><p>The alarm would wake the Durable Object, which continually looked for batches of messages in storage to deliver.</p></li><li><p>Each batch of messages was sent to a \"Dispatcher Worker\" that used <a href=\"https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/\"><u>Workers for Platforms</u></a> <a href=\"https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker\"><i><u>dynamic dispatch</u></i></a> to pass the messages to the <code>queue()</code> function defined in a user's Consumer Worker</p></li></ul>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4vAM17x3nN49JBMGNTblPp/6af391950df5f0fbc14faeccb351e38c/image4.png\">\n          </figure><p>This v1 architecture let us flesh out the initial version of the Queues Beta product and onboard users quickly. Using Durable Objects allowed us to focus on building application logic, instead of complex low-level systems challenges such as global routing and guaranteed durability for storage. Using a separate Durable Object for each Queue allowed us to host an essentially unlimited number of Queues, and provided isolation between them.</p><p>However, using <i>only</i> one Durable Object per queue had some significant limitations:</p><ul><li><p><b>Latency: </b>we created all of our v1 Queue Durable Objects in Western North America. Messages sent from distant regions incurred significant latency when traversing the globe.</p></li><li><p><b>Throughput: </b>A single Durable Object is not scalable: it is single-threaded and has a fixed capacity for how many requests per second it can process. This is where the previous 400 messages per second limit came from.</p></li><li><p><b>Consumer Concurrency: </b>Due to <a href=\"https://developers.cloudflare.com/workers/platform/limits/#simultaneous-open-connections\"><u>concurrent subrequest limits</u></a>, a single Durable Object was limited in how many concurrent subrequests it could make to our Dispatcher Worker. This limited the number of <code>queue()</code> handler invocations that it could run simultaneously.</p></li></ul><p>To solve these issues, we created a new v2 architecture that horizontally scales across <b>multiple</b> Durable Objects to implement each single high-performance Queue.</p>\n          <div>\n            <h3>v2 Architecture</h3>\n            <a href=\"https://blog.cloudflare.com/how-we-built-cloudflare-queues#v2-architecture\">\n              \n            </a>\n          </div>\n        <p>In the new v2 architecture for Queues, each Queue is implemented using multiple Durable Objects, instead of just one. Instead of a single region, we place <i>Storage Shard </i>Durable Objects in <a href=\"https://developers.cloudflare.com/durable-objects/reference/data-location/#supported-locations-1\"><u>all available regions</u></a> to enable lower latency. Within each region, we create multiple Storage Shards and load balance incoming requests amongst them. Just like that, we’ve multiplied message throughput.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2SJTb2UO8tKGrh26ixwLrA/7272e4eaf6f7e85f086a5ae08670387e/image2.png\">\n          </figure><p>Sending a message to a v2 Queue uses the following flow:</p><ul><li><p>A client sends a POST request containing the message body to the Queues API at <code>/accounts/:accountID/queues/:queueID/messages</code></p></li><li><p>The request is handled by an instance of the <b>Queue Broker Worker</b> running in a Cloudflare data center near the client.</p></li><li><p>The Worker:</p><ul><li><p>Performs authentication</p></li><li><p>Reads from Workers KV to obtain a <i>Shard Map</i> that lists available storage shards for the given <code>region</code> and <code>queueID</code></p></li><li><p>Picks one of the region's Storage Shards at random, and uses Durable Objects <code>idFromName</code> API to route the request to the chosen shard</p></li></ul></li><li><p>The Storage Shard persists the message to storage before returning a <i>success </i>back to the client.</p></li></ul><p>In this v2 architecture, messages are stored in the closest available Durable Object storage cluster near the user, greatly reducing latency since messages don't need to be shipped all the way to WNAM. Using multiple shards within each region removes the bottleneck of a single Durable Object, and allows us to scale each Queue horizontally to accept even more messages per second. <a href=\"https://blog.cloudflare.com/tag/cloudflare-workers-kv/\"><u>Workers KV</u></a> acts as a fast metadata store: our Worker can quickly look up the shard map to perform load balancing across shards.</p><p>To improve the <i>Consumer</i> side of v2 Queues, we used a similar \"scale out\" approach. A single Durable Object can only perform a limited number of concurrent subrequests. In v1 Queues, this limited the number of concurrent subrequests we could make to our Dispatcher Worker. To work around this, we created a new <i>Consumer Shard</i> Durable Object class that we can scale horizontally, enabling us to execute many more concurrent instances of our users' <code>queue()</code> handlers.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ujodUVBegDcWXi6DYJR41/5f31ba4da387df82613a496ff311f65f/image3.png\">\n          </figure><p>Consumer Durable Objects in v2 Queues use the following approach:</p><ul><li><p>Each Consumer maintains an alarm that guarantees it will wake up to process any pending messages. <i>v2 </i>Consumers are notified by the Queue's <i>Coordinator </i>(introduced below) when there are messages ready for consumption. Upon notification, the Consumer sets an alarm to go off immediately.</p></li><li><p>The Consumer looks at the shard map, which contains information about the storage shards that exist for the Queue, including the number of available messages on each shard.</p></li><li><p>The Consumer picks a random storage shard with available messages, and asks for a batch.</p></li><li><p>The Consumer sends the batch to the Dispatcher Worker, just like for v1 Queues.</p></li><li><p>After processing the messages, the Consumer sends another request to the Storage Shard to either \"acknowledge\" or \"retry\" the messages.</p></li></ul><p>This scale-out approach enabled us to work around the subrequest limits of a single Durable Object, and increase the maximum supported concurrency level of a Queue from 20 to 250.&nbsp;</p>\n          <div>\n            <h3>The Coordinator and “Control Plane”</h3>\n            <a href=\"https://blog.cloudflare.com/how-we-built-cloudflare-queues#the-coordinator-and-control-plane\">\n              \n            </a>\n          </div>\n        <p>So far, we have primarily discussed the \"Data Plane\" of a v2 Queue: how messages are load balanced amongst Storage Shards, and how Consumer Shards read and deliver messages. The other main piece of a v2 Queue is the \"Control Plane\", which handles creating and managing all the individual Durable Objects in the system. In our v2 architecture, each Queue has a single <i>Coordinator</i> Durable Object that acts as the brain of the Queue. Requests to create a Queue, or change its settings, are sent to the Queue's Coordinator.</p>\n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7lYJs23oJ8ibtGgbuOk9JN/7ffa8073a4391602b67a0c6e134975bc/image7.png\">\n          </figure><p>The Coordinator maintains a <i>Shard Map</i> for the Queue, which includes metadata about all the Durable Objects in the Queue (including their region, number of available messages, current estimated load, etc.). The Coordinator periodically writes a fresh copy of the Shard Map into Workers KV, as pictured in step 1 of the diagram. Placing the shard map into Workers KV ensures that it is globally cached and available for our Worker to read quickly, so that it can pick a shard to accept the message.</p><p>Every shard in the system periodically sends a heartbeat to the Coordinator as shown in steps 2 and 3 of the diagram. Both Storage Shards and Consumer Shards send heartbeats, including information like the number of messages stored locally, and the current load (requests per second) that the shard is handling. The Coordinator uses this information to perform <b><i>autoscaling. </i></b>When it detects that the shards in a particular region are overloaded, it creates additional shards in the region, and adds them to the shard map in Workers KV. Our Worker sees the updated shard map and naturally load balances messages across the freshly added shards. Similarly, the Coordinator looks at the backlog of available messages in the Queue, and decides to add more Consumer shards to increase Consumer throughput when the backlog is growing. Consumer Shards pull messages from Storage Shards for processing as shown in step 4 of the diagram.</p><p>Switching to a new scalable architecture allowed us to meet our performance goals and take Queues to GA. As a recap, this new architecture delivered these significant improvements:</p><ul><li><p>P50 latency for writing to a Queue has dropped from ~200ms to ~60ms.</p></li><li><p>Maximum throughput for a Queue has increased from 400 to 5000 messages per second.</p></li><li><p>Maximum consumer concurrency has increased from 20 to 250 invocations.\t</p></li></ul>\n          <div>\n            <h3>What's next for Queues</h3>\n            <a href=\"https://blog.cloudflare.com/how-we-built-cloudflare-queues#whats-next-for-queues\">\n              \n            </a>\n          </div>\n        <ul><li><p>We plan on leveraging the performance improvements in the new <a href=\"https://developers.cloudflare.com/durable-objects/\"><u>beta version of Durable Objects</u></a> which use SQLite to continue to improve throughput/latency in Queues.</p></li><li><p>We will soon be adding message management features to Queues so that you can take actions to purge messages in a queue, pause consumption of messages, or “redrive”/move messages from one queue to another (for example messages that have been sent to a Dead Letter Queue could be “redriven” or moved back to the original queue).</p></li><li><p>Work to make Queues the \"event hub\" for the Cloudflare Developer Platform:</p><ul><li><p>Create a low-friction way for events emitted from other Cloudflare services with event schemas to be sent to Queues.</p></li><li><p>Build multi-Consumer support for Queues so that Queues are no longer limited to one Consumer per queue.</p></li></ul></li></ul><p>To start using Queues, head over to our <a href=\"https://developers.cloudflare.com/queues/get-started/\"><u>Getting Started</u></a> guide.&nbsp;</p><p>Do distributed systems like Cloudflare Queues and Durable Objects interest you? Would you like to help build them at Cloudflare? <a href=\"https://boards.greenhouse.io/embed/job_app?token=5390243&amp;gh_src=b2e516a81us\"><u>We're Hiring!</u></a></p>",
            "url": "https://blog.cloudflare.com/how-we-built-cloudflare-queues",
            "title": "Durable Objects aren't just durable, they're fast: a 10x speedup for Cloudflare Queues",
            "date_modified": "2024-10-24T13:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41931572\">Comments</a>",
            "url": "https://benjdd.com/aws/",
            "title": "AWS data center latencies, visualized",
            "date_modified": "2024-10-24T03:18:23.000Z"
        },
        {
            "content_html": "<p>I’m thrilled to announce <a href=\"https://en.wikipedia.org/wiki/MacOS\">macOS</a> support in <a href=\"https://aws.amazon.com/image-builder/\">EC2 Image Builder</a>. This new capability allows you to create and manage machine images for your macOS workloads in addition to the existing support for Windows and Linux.</p> \n<p>A golden image is a bootable disk image, also called an <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html\">Amazon Machine Image (AMI)</a>, pre-installed with the operating system and all the tools required for your workloads. In the context of a continuous integration and continuous deployment (CI/CD) pipeline, your golden image most probably contains the specific version of your operating system (macOS) and all required development tools and libraries to build and test your applications (<a href=\"https://developer.apple.com/xcode/\">Xcode</a>, <a href=\"https://fastlane.tools/\">Fastlane</a>, and so on.)</p> \n<p>Developing and manually managing pipelines to build macOS golden images is time-consuming and diverts talented resources from other tasks. And when you have existing pipelines to build Linux or Windows images, you need to use different tools for creating macOS images, leading to a disjointed workflow.</p> \n<p>For these reasons, many of you have been asking for the ability to manage your macOS images using EC2 Image Builder. You want to consolidate your image pipelines across operating systems and take advantage of the automation and cloud-centered integrations that EC2 Image Builder provides.</p> \n<p>By adding macOS support to EC2 Image Builder, you can now streamline your image management processes and reduce the operational overhead of maintaining macOS images. EC2 Image Builder takes care of testing, versioning, and validating the base images at scale, saving you the costs associated with maintaining your preferred macOS versions.</p> \n<p><span><strong>Let’s see it in action<br> </strong></span>Let’s create a pipeline to create a macOS AMI with Xcode 16. You can follow a similar process to install Fastlane on your AMIs.</p> \n<p>At a high level, there are four main steps.</p> \n<ol> \n <li>I define a component for each tool I want to install. A component is a <a href=\"https://docs.aws.amazon.com/imagebuilder/latest/userguide/toe-use-documents.html\">YAML document</a> that tells EC2 Image Builder what application to install and how. In this example, I create a custom component to install Xcode. If you want to install Fastlane, you create a second component. I use the <code>ExecuteBash</code> action to enter the shell commands required to install Xcode.</li> \n <li>I define a <a href=\"https://docs.aws.amazon.com/imagebuilder/latest/userguide/manage-recipes.html\">recipe</a>. A recipe starts from a base image and lists the components I want to install on it.</li> \n <li>I define the <a href=\"https://docs.aws.amazon.com/imagebuilder/latest/userguide/manage-infra-config.html\">infrastructure configuration</a> I want to use to build my image. This defines the pool of <a href=\"https://aws.amazon.com/ec2/\">Amazon Elastic Compute Cloud (Amazon EC2)</a> instances to build the image. In my case, I allocate an EC2 Mac Dedicated Host in my account and reference it in the infrastructure configuration.</li> \n <li>I create a pipeline and a schedule to run on the infrastructure with the given recipes and an <a href=\"https://docs.aws.amazon.com/imagebuilder/latest/userguide/manage-image-workflows.html\">image workflow</a>. I test the output AMI and deliver it at the chosen destination (my account or another account)</li> \n</ol> \n<p>It’s much easier than it sounds. I’ll show you the steps in the <a href=\"https://console.aws.amazon.com\">AWS Management Console</a>. I can also configure EC2 Image Builder with the <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface (AWS CLI)</a> or write code using one of our <a href=\"https://aws.amazon.com/tools/\">AWS SDKs</a>.</p> \n<p><span><strong>Step 1: Create a component<br> </strong></span>I open the console and select <strong>EC2 Image Builder</strong>, then <strong>Components</strong>, and finally <strong>Create component</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_08-51-09.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_08-51-09.png\" alt=\"Image Builder - Create component\" width=\"800\" height=\"554\"></a></p> \n<p>I select a base <strong>Image operating system </strong>and&nbsp;the <strong>Compatible OS Versions</strong>. Then, I enter a <strong>Component name</strong> and <strong>Component version</strong>. I select <strong>Define document content</strong> and enter this YAML as <strong>Content</strong>.</p> \n<pre><code>name: InstallXCodeDocument\ndescription: This downloads and installs Xcode. Be sure to run `xcodeinstall authenticate -s us-east-1` from your laptop first.\nschemaVersion: 1.0\n\nphases:\n  - name: build\n    steps:\n      - name: InstallXcode\n        action: ExecuteBash\n        inputs:\n          commands:\n             - sudo -u ec2-user /opt/homebrew/bin/brew tap sebsto/macos\n             - sudo -u ec2-user /opt/homebrew/bin/brew install xcodeinstall\n             - sudo -u ec2-user /opt/homebrew/bin/xcodeinstall download -s us-east-1 --name \"Xcode 16.xip\"\n             - sudo -u ec2-user /opt/homebrew/bin/xcodeinstall install --name \"Xcode 16.xip\"\n  \n  - name: validate\n    steps:\n      - name: TestXcode\n        action: ExecuteBash\n        inputs:\n          commands:\n            -  xcodebuild -version &amp;&amp; xcode-select -p   </code></pre> \n<p>I use a tool I wrote to download and install Xcode from the command line. <a href=\"https://github.com/sebsto/xcodeinstall\">xcodeinstall</a> integrates with <a href=\"https://aws.amazon.com/secrets-manager/\">AWS Secrets Manager</a> to securely store authentication web tokens. Before running the pipeline, I authenticate <strong>from my laptop</strong> with the command <code>xcodeinstall authenticate -s us-east-1</code>. This command starts a session with Apple server’s and stores the session token in Secrets Manager. xcodeinstall uses this token during the image creation pipeline to download Xcode.</p> \n<p>When you use xcodeinstall with Secrets Manager, you must give permission to your pipeline to access the secrets. Here is the policy document I added to the role attached to the EC2 instance used by EC2 Image Builder (in the following infrastructure configuration).</p> \n<pre><code>{\n\t\"Sid\": \"xcodeinstall\",\n\t\"Effect\": \"Allow\",\n\t\"Action\": [\n           &nbsp;\"secretsmanager:GetSecretValue\"\n            \"secretsmanager:PutSecretValue\"\n        ],\n\t\"Resource\": \"arn:aws:secretsmanager:us-east-1:&lt;YOUR ACCOUNT ID&gt;:secret:xcodeinstall*\"\n}</code></pre> \n<p>To test and debug these components locally, without having to wait for long cycle to start and recycle the EC2 Mac instance, you can use the <a href=\"https://docs.aws.amazon.com/imagebuilder/latest/userguide/toe-get-started.html\">AWS Task Orchestrator and Executor (AWSTOE) command</a>.</p> \n<p><span><strong>Step 2: Create a recipe<br> </strong></span>The next step is to create a recipe. On the console, I select <strong>Image recipes</strong> and <strong>Create image recipe</strong>.</p> \n<p>I select macOS as the base <strong>Image Operating System</strong>. I choose macOS Sonoma ARM64 as <strong>Image name</strong>.</p> \n<p>In the <strong>Build components</strong> section, I select the Xcode 16 component I just created during step 1.</p> \n<p>Finally, I make sure the volume is large enough to store the operating system, Xcode, and my builds. I usually select a 500 Gb <code>gp3</code> volume.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-36-20.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-36-20.png\" alt=\"Image Builder - Create a recipe\" width=\"800\" height=\"2883\"></a></p> \n<p><span><strong>Steps 3 and 4: Create the pipeline (and the infrastructure configuration)<br> </strong></span>On the <strong>EC2 Image Builder</strong> page, I select <strong>Image pipelines</strong> and <strong>Create image pipeline</strong>. I give my pipeline a name and select a <strong>Build schedule</strong>. For this demo, I select a manual trigger.<a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-40-32-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-40-32-2.png\" alt=\"Image Builder - Create Pipeline 1\" width=\"1287\" height=\"1040\"></a></p> \n<p>Then, I select the recipe I just created (Sonoma-Xcode).</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-41-15.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-41-15.png\" alt=\"Image Builder - Create Pipeline 2\" width=\"1329\" height=\"1187\"></a></p> \n<p>I chose <strong>Default workflows</strong> for <strong>Define image creation process</strong> (not shown for brevity).</p> \n<p>I create or select an existing <strong>infrastructure configuration</strong>. In the context of building macOS images, you have to allocate <a href=\"https://aws.amazon.com/ec2/dedicated-hosts/getting-started/\">Amazon EC2 Dedicated Hosts</a> first. This is where I choose the instance type that EC2 Image Builder will use to create the AMI. I may also optionally select my virtual private cloud (VPC), security group, <a href=\"https://aws.amazon.com/iam/\">AWS Identity and Access Management (IAM)</a> roles with permissions required during the preparation of the image, key pair, and all the parameters I usually select when I start an EC2 instance.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-51-49.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-51-49.png\" alt=\"Image Builder - Create Pipeline 4\" width=\"1319\" height=\"988\"></a></p> \n<p>Finally, I select where I want to distribute the output AMI. By default, it stays on my account. But I can also share or copy it to other accounts.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-52-08.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-52-08.png\" alt=\"Image Builder - Create Pipeline 5\" width=\"1337\" height=\"776\"></a></p> \n<p><span><strong>Run the pipeline<br> </strong></span>Now I’m ready to run the pipeline. I select <strong>Image pipelines</strong>, then I select the pipeline I just created (<strong>Sonoma-Xcode</strong>). From the <strong>Actions</strong> menu, I select <strong>Run pipeline</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-55-22.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/01/2024-10-01_10-55-22.png\" alt=\"Image Builder - launch pipeline\" width=\"800\" height=\"392\"></a></p> \n<p>I can observe the progress and the detailed logs from <a href=\"https://aws.amazon.com/cloudwatch/\">Amazon CloudWatch</a>.</p> \n<p>After a while, the AMI is created and ready to use.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/02/2024-10-02_08-36-51.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/02/2024-10-02_08-36-51.png\" alt=\"Image Builder - AMI build succeeded\" width=\"800\" height=\"409\"></a></p> \n<p><span><strong>Testing my AMI</strong></span><br> To finish the demo, I start an EC2 Mac instance with the AMI I just created (remember to allocate a Dedicated Host first or to reuse the one you used for EC2 Image Builder).</p> \n<p>Once the instance is started, I connect to it using secure shell (SSH) and verify that Xcode is correctly installed.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/02/2024-10-02_10-50-39.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/10/02/2024-10-02_10-50-39.png\" alt=\"Image Builder - Connect to new AMI\" width=\"1674\" height=\"692\"></a></p> \n<p><span><strong>Pricing and availability<br> </strong></span>EC2 Image Builder for macOS is now available in all AWS Regions <a href=\"https://docs.aws.amazon.com/ec2/latest/instancetypes/ec2-instance-regions.html\">where EC2 Mac instances are available</a>: US East (Ohio, N. Virginia), US West (Oregon), Asia Pacific (Mumbai, Seoul, Singapore, Sydney, Tokyo), and Europe (Frankfurt, Ireland, London, Stockholm) (not all Mac instance types are available in all Regions).</p> \n<p>It comes at no additional cost, and you’re only charged for the resources in use during the pipeline execution, namely the time your EC2 Mac Dedicated Host is allocated, with a minimum of 24 hours.</p> \n<p>The preview of macOS support in EC2 Image Builder allows you to consolidate your image pipelines, automate your golden image creation processes, and use the benefits of cloud-focused integrations on AWS. As the EC2 Mac platform continues to expand with more instance types, this new capability positions EC2 Image Builder as a comprehensive solution for image management across Windows, Linux, and macOS.</p> \n<p><strong><a href=\"https://console.aws.amazon.com/imagebuilder/home\">Create your first pipeline today!&nbsp;</a></strong></p> \n<p><a href=\"https://twitter.com/sebsto\">-- seb</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/ec2-image-builder-now-supports-building-and-testing-macos-images/",
            "title": "EC2 Image Builder now supports building and testing macOS images",
            "date_modified": "2024-10-23T17:52:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41927286\">Comments</a>",
            "url": "https://determinate.systems/posts/flakehub-cache-and-private-flakes/",
            "title": "Nix at work: FlakeHub Cache and private flakes",
            "date_modified": "2024-10-23T17:28:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41925622\">Comments</a>",
            "url": "https://blogsystem5.substack.com/p/bazelcon-2024-recap",
            "title": "BazelCon 2024 Recap",
            "date_modified": "2024-10-23T14:50:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41923429\">Comments</a>",
            "url": "https://blog.cloudflare.com/intro-access-for-infrastructure-ssh/",
            "title": "Fearless SSH: Short-lived certificates bring Zero Trust to infrastructure",
            "date_modified": "2024-10-23T09:44:36.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/v0bwa0/bazelcon_2024_recap\">Comments</a></p>",
            "url": "https://blogsystem5.substack.com/p/bazelcon-2024-recap",
            "title": "BazelCon 2024 recap",
            "date_modified": "2024-10-22T21:55:22.000Z"
        },
        {
            "content_html": "<div><p><span>At the end of the day, developers build, test, deploy and maintain software. But like with lots of things, </span><span>it’s about the journey, not the destination.</span></p>\n<p><span>Among platform engineers, we sometimes refer to that journey as </span><span>the developer experience (DX), which encompasses how developers feel and interact with the tools and services they use throughout the software build, test, deployment and maintenance process.</span></p>\n<p><span>Prioritizing DX is essential: Frustrated developers lead to inefficiency and talent loss as well as to shadow IT. Conversely, a positive DX drives innovation, community, and productivity. And if you want to provide a&nbsp; positive DX, you need to start measuring how you’re doing.</span></p>\n<p><span>At </span><a href=\"https://platformcon.com/\" rel=\"noopener\" target=\"_blank\"><span>PlatformCon 2024</span></a><span>, I gave a talk entitled \"Improving your developers' platform experience by applying Google frameworks and methods” where I spoke about </span><a href=\"https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36299.pdf\" rel=\"noopener\" target=\"_blank\"><span>Google’s HEART Framework</span></a><span>, which provides a holistic view of your organization's developers’ experience through actionable data.</span></p>\n<p><span>In this article, I will share ideas on how you can apply the HEART framework to your Platform Engineering practice, to gain a more comprehensive view of your organization’s developer experience. But before I do that, let me explain what the HEART Framework is.</span></p></div>\n<div><dl>\n    <dt>aside_block</dt>\n    <dd>&lt;ListValue: [StructValue([('title', 'Try Google Cloud for free'), ('body', &lt;wagtail.rich_text.RichText object at 0x3e05a44c79a0&gt;), ('btn_text', 'Get started for free'), ('href', 'https://console.cloud.google.com/freetrial?redirectPath=/welcome'), ('image', None)])]&gt;</dd>\n</dl></div>\n<div><h3><strong>The HEART Framework: an introduction</strong></h3>\n<p><span>In a nutshell, HEART measures developer behaviors and attitudes from their experience of your platform and provides you with insights into what’s going on behind the numbers, by defining specific metrics to track progress towards goals. This is beneficial because continuous improvements through feedback are vital components of a platform engineering journey, helping both platform and application product teams make decisions that are data-driven and user-centered.</span></p>\n<p><span>However, HEART is not a data collection tool in and of itself; rather, it’s a user-sentiment framework for selecting the right metrics to focus on based on product or platform objectives. It balances quantitative or empirical data, e.g., number of active portal users, with qualitative or subjective insights such as \"My users feel the portal navigation is confusing.\" In other words, </span><span>consider HEART as a framework or methodology for assessing user experience, rather than a specific tool or assessment. </span><span>It helps you decide </span><span>what</span><span> to measure, not </span><span>how</span><span> to measure it.</span></p></div>\n<div>\n\n\n\n\n\n\n  \n    <div>\n      <div>\n  \n\n    <figure>\n\n      \n      \n        \n        <img src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/image2_3Y8Y7hV.max-1000x1000.jpg\" alt=\"image2\">\n        \n        \n      \n    </figure>\n\n  \n      </div>\n    </div>\n  \n\n\n\n\n</div>\n<div><p><span>Let’s take a look at each of these in more detail.</span></p>\n<h3><strong>Happiness: Do users actually enjoy using your product?</strong></h3>\n<p><strong>Highlight:</strong><span> </span><span>Gathering and analyzing developer feedback</span></p>\n<p><strong>Subjective metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Surveys: Conduct regular surveys to gather feedback about overall satisfaction, ease of use, and pain points. Toil negatively affects developer satisfaction and morale. Repetitive, manual work can lead to frustration burnout and decreased happiness with the platform.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Feedback mechanisms: Establish easy ways for developers to provide direct feedback on specific features or areas of the platform like Net Promoter Score (NPS) or Customer Satisfaction surveys (CSAT).</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Collect open-ended feedback from developers through interviews and user groups.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Sentiment analysis: Analyze developer sentiment expressed in feedback channels, support tickets and online communities.</span></p>\n</li>\n</ul>\n<p><strong>System metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Feature requests: Track the number and types of feature requests submitted by developers. This provides insights into their needs and desires and can help you prioritize improvements that will enhance happiness.</span></p>\n</li>\n</ul>\n<p><strong>Watch out for:</strong><span> While platforms can boost developer productivity, they might not necessarily contribute to developer job satisfaction. This warrants further investigation, especially if your research suggests that your developers are unhappy.</span></p>\n<h3><strong>Engagement: What is the developer breadth and quality of platform experience?</strong></h3>\n<p><strong>Highlight:</strong><span> Frequency of interaction between platform engineers with developers and quality of interaction — intensity and quality of interaction with the platform, participation on chat channels, training, dual ownership of golden paths, joint troubleshooting, engaging in architectural design discussions, and the breadth of interaction by everyone from new hires through to senior developers.</span></p>\n<p><strong>Subjective metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Survey for quality of interaction — focus on depth and type of interaction whether through chat channel, trainings, dual ownership of golden paths, joint troubleshooting, or architectural design discussions</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>High toil can reduce developer engagement with the platform. When developers spend excessive amounts of time on tedious tasks, they are less likely to explore new features, experiment, and contribute to the platform's evolution.</span></p>\n</li>\n</ul>\n<p><strong>System metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Active users: Track daily, weekly, and monthly active developers and how long they spend on tasks.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Usage patterns: Analyze the most used platform features, tools, and portal resources.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Frequency of interaction between platform engineers with developers.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Breadth of user engagement: Track onboarding time for new hires to reach proficiency, measure the percentage of senior developers actively contributing to golden paths or portal functionality.</span></p>\n</li>\n</ul>\n<p><strong>Watch out for:</strong><span> Don’t confuse </span><span>engagement</span><span> with </span><span>satisfaction</span><span>. Developers may rate the platform highly in surveys, but usage data might reveal low frequency of interaction with core features or a limited subset of teams actively using the platform. Ask them “How has the platform changed your daily workflow?” rather than \"Are you satisfied with the platform?”</span></p>\n<h3><strong>Adoption: What is the platform growth rate and developer feature adoption?</strong></h3>\n<p><strong>Highlight:</strong><span> Overall acceptance and integration of the platform into the development workflow.</span></p>\n<p><strong>System metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>New user registrations: Monitor the growth rate of new developers using the platform.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Track time between registration and time to use the platform i.e., executing golden paths, tooling and portal functionality.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Number of active users per week / month / quarter / half-year / year who authenticate via the portal and/or use golden paths, tooling and portal functionality</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Feature adoption: Track how quickly and widely new features or updates are used.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Percentage of developers using CI/CD through the platform</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Number of deployments per user / team / day / week / month — basically of your choosing</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Training: Evaluate changes in adoption, after delivering training.</span></p>\n</li>\n</ul>\n<p><strong>Watch out for:</strong><span> Overlooking the \"long tail\" of adoption. A platform might see a burst of early adoption, but then plateau or even decline if it fails to continuously evolve and meet changing developer needs. Don't just measure initial adoption, monitor how usage evolves over weeks, months, and years.</span></p>\n<h3><strong>Retention: Are developers loyal to the platform?</strong></h3>\n<p><strong>Highlight:</strong><span> Long-term engagement and reducing churn.</span></p>\n<p><strong>Subjective metrics:</strong></p>\n<ul>\n<li role=\"presentation\"><span>Use an exit survey if a user is dormant for 12 or more months.</span></li>\n</ul>\n<p><strong>System metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Churn rate: Track the percentage of developers who stop logging into the portal and are not using it.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Dormant users: Identify developers who become inactive after 6 months and investigate why.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Track services that are less frequently used.</span></p>\n</li>\n</ul>\n<p><strong>Watch out for:</strong><span> Misinterpreting the reasons for churn. When developers stop using your platform (churn), it's crucial to understand why. Incorrectly identifying the cause can lead to wasted effort and missed opportunities for improvement. Consider factors outside the platform — churn could be caused by changes in project requirements, team structures or industry trends.</span></p>\n<h3><strong>Task success: Can developers complete specific tasks?</strong></h3>\n<p><strong>Highlight:</strong><span> Efficiency and effectiveness of the platform in supporting specific developer activities.</span></p>\n<p><strong>Subjective metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Survey to assess the ongoing presence of toil and its inimical influence on developer productivity, ultimately hindering efficiency and leading to increased task completion times.</span></p>\n</li>\n</ul>\n<p><strong>System metrics:</strong></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Completion rates: Measure the percentage of golden paths and tools successfully run on the platform without errors.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Time to complete tasks using golden paths, portal, or tooling.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Error rates: Track common errors and failures developers encounter from log files or monitoring dashboards from golden paths, portal or tooling.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Mean Time to Resolution (MTTR): When errors do occur, how long does it take to resolve them? A lower MTTR indicates a more resilient platform and faster recovery from failures.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><span>Developer platform and portal uptime: Measure the percentage of time that the developer platform and portal is available and operational. Higher uptime ensures developers can consistently access the platform and complete their tasks.</span></p>\n</li>\n</ul>\n<p><strong>Watch out for:</strong><span> Don't confuse </span><span>task success</span><span> with </span><span>task completion</span><span>. Simply measuring whether developers can complete tasks on the platform doesn't necessarily indicate true success. Developers might find workarounds or complete tasks inefficiently, even if they technically achieve the end goal. It may be worth manually observing developer workflows in their natural environment to identify pain points and areas of friction in their workflows.</span></p>\n<p><span>Also, be careful with misaligning task success with business goals. Task completion might overlook the broader impact on business objectives. A platform might enable developers to complete tasks efficiently, but if those tasks don't contribute to overall business goals, the platform's true value is questionable.</span></p>\n<h3><strong>Applying the HEART framework to platform engineering</strong></h3>\n<p><span>It’s not necessary to use all of the categories each time. </span><span>The number of categories to consider really depends on the specific goals and context of the assessment; you can include everything or trim it down to better match your objective. Here are some examples:</span></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>Improving onboarding for new developers:</strong><span> Focus on adoption, task success and happiness.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>Launching a new feature:</strong><span> Concentrate on adoption and happiness.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>Increasing platform usage:</strong><span> Track engagement, retention and task success.</span></p>\n</li>\n</ul>\n<p><span>Keep in mind that relying on just one category will likely provide an incomplete picture.</span></p>\n<h3><strong>When should you use the framework?</strong></h3>\n<p><span>In a perfect world, you would use the HEART framework to establish a baseline assessment a few months after launching your platform, which will provide you with a valuable insight into early developer experience. As your platform evolves, this initial data becomes a benchmark for measuring progress and identifying trends. Early measurement allows you to proactively address UX issues, guide design decisions with data, and iterate quickly for optimal functionality and developer satisfaction. If you're starting with an MVP, conduct the baseline assessment once the core functionality is in place and you have a small group of early users to provide feedback.</span></p>\n<p><span>After 12 or more months of usage, you can also add metrics to embody a new or more mature platform. This can help you gather deeper insights into your developers’ experience by understanding how they are using the platform, measure the impact of changes you’ve made to the platform, or identify areas for improvement and prioritize future development efforts. If you've added new golden paths, tooling, or enhanced functionality, then you'll need to track metrics that measure their success and impact on developer behavior.</span></p>\n<p><span>The frequency with which you assess HEART metrics depends on several factors, including:</span></p>\n<ul>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>The maturity of your platform: </strong><span>Newer platforms benefit from more frequent reviews (e.g. monthly or quarterly) to track progress and address early issues. As the platform matures, you can reduce the frequency of your HEART assessments (e.g., bi-annually or annually).</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>The rate of change:</strong><span> To ensure updates and changes have a positive impact, apply the HEART framework more frequently when your platform is undergoing a period of rapid evolution such as major platform updates, new portal features or new golden paths, or some change in user behavior. This allows you to closely monitor the effects of each change on key metrics.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>The size and complexity of your platform:</strong><span> Larger and more complex platforms may require more frequent assessments to capture nuances and potential issues.</span></p>\n</li>\n<li aria-level=\"1\">\n<p role=\"presentation\"><strong>Your team's capacity:</strong><span> Running HEART assessments requires time and resources. Consider your team's bandwidth and adjust the frequency accordingly.</span></p>\n</li>\n</ul>\n<p><span>Schedule periodic deep dives (e.g. quarterly or bi-annually) using the HEART framework to gain a more in-depth understanding of your platform's performance and identify areas for improvement.</span></p>\n<h3><strong>Taking more steps towards platform engineering</strong></h3>\n<p><span>In this </span><span>blog post, we’ve shown how the HEART framework can be applied to platform engineering to measure and improve the developer experience. We’ve explored the five key aspects of the framework — happiness, engagement, adoption, retention, and task success — and provided specific metrics for each and guidance on when to apply them.</span><span>By applying these insights, platform engineering teams can create a more positive and productive environment for their developers, leading to greater success in their software development efforts.</span><span>To learn more about platform engineering, check out some of our other articles: </span><a href=\"https://cloud.google.com/blog/products/application-development/common-myths-about-platform-engineering\"><span>&nbsp;5 myths about platform engineering: what it is and what it isn’t</span></a><span>, </span><a href=\"https://cloud.google.com/blog/products/application-development/another-five-myths-about-platform-engineering\"><span>Another five myths about platform engineering</span></a><span>, and </span><a href=\"https://cloud.google.com/blog/products/application-development/how-to-become-a-platform-engineer\"><span>Laying the foundation for a career in platform engineering</span></a><span>.</span></p>\n<p><span>And finally, check out the </span><a href=\"https://dora.dev/dora-report-2024\" rel=\"noopener\" target=\"_blank\"><span>DORA Report 2024</span></a><span>, which now has a section on Platform Engineering.</span></p></div>",
            "url": "https://cloud.google.com/blog/products/application-development/how-platform-engineers-can-improve-their-developers-experience/",
            "title": "Measuring developer experience with the HEART Framework: A guide for platform engineers",
            "date_modified": "2024-10-22T17:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41915724\">Comments</a>",
            "url": "https://planetscale.com/blog/announcing-planetscale-vectors-public-beta",
            "title": "The PlanetScale vectors public beta",
            "date_modified": "2024-10-22T16:06:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41914845\">Comments</a>",
            "url": "https://clickhouse.com/blog/a-new-powerful-json-data-type-for-clickhouse",
            "title": "A new JSON data type for ClickHouse",
            "date_modified": "2024-10-22T14:47:00.000Z"
        },
        {
            "content_html": "<p>Today, I am excited to announce the general availability of <a href=\"https://aws.amazon.com/rds/aurora/zero-etl/\">Amazon Aurora PostgreSQL-Compatible Edition</a> and <a href=\"https://aws.amazon.com/dynamodb/integrations/\">Amazon DynamoDB</a> zero-ETL integrations with <a href=\"https://aws.amazon.com/redshift/\">Amazon Redshift</a>. Zero-ETL integration seamlessly makes transactional or operational data available in Amazon Redshift, removing the need to build and manage complex data pipelines that perform extract, transform, and load (ETL) operations. It automates the replication of source data to Amazon Redshift, simultaneously updating source data for you to use in Amazon Redshift for analytics and machine learning (ML) capabilities to derive timely insights and respond effectively to critical, time-sensitive events.</p> \n<p>Using these new zero-ETL integrations, you can run unified analytics on your data from different applications without having to build and manage different data pipelines to write data from multiple relational and non-relational data sources into a single data warehouse. In this post, I provide two step-by-step walkthroughs on how to get started with both Amazon Aurora PostgreSQL and Amazon DynamoDB zero-ETL integrations with Amazon Redshift.</p> \n<p>To create a zero-ETL integration, you specify a source and Amazon Redshift as the target. The integration replicates data from the source to the target data warehouse, making it available in Amazon Redshift seamlessly, and monitors the pipeline’s health.</p> \n<p>Let’s explore how these new integrations work. In this post, you will learn how to create zero-ETL integrations to replicate data from different source databases (Aurora PostgreSQL and DynamoDB) to the same Amazon Redshift cluster. You will also learn how to select multiple tables or databases from Aurora PostgreSQL source databases to replicate data to the same Amazon Redshift cluster. You will observe how zero-ETL integrations provide flexibility without the operational burden of building and managing multiple ETL pipelines.</p> \n<p><span><strong>Getting started with Aurora PostgreSQL zero-ETL integration with Amazon Redshift</strong></span><br> Before creating a database, I create a custom cluster parameter group because Aurora PostgreSQL zero-ETL integration with Amazon Redshift requires specific values for the <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/zero-etl.setting-up.html#zero-etl.parameters\">Aurora DB cluster parameters</a>. In the <a href=\"https://console.aws.amazon.com/rds\">Amazon RDS console</a>, I go to <strong>Parameter groups</strong> in the navigation pane. I choose <strong>Create parameter group</strong>.</p> \n<p>I enter <code>custom-pg-aurora-postgres-zero-etl</code> for <strong>Parameter group name</strong> and <strong>Description</strong>. I choose <strong>Aurora PostgreSQL</strong> for <strong>Engine type</strong> and <strong>aurora-postgresql16 </strong>for <strong>Parameter group family</strong> (zero-ETL integration works with PostgreSQL 16.4 or above versions). Finally, I choose <strong>DB Cluster Parameter Group</strong> for <strong>Type </strong>and choose <strong>Create</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/01a-create-custom-pg-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/01a-create-custom-pg-LaunchMarketingIntake-1191.png\" width=\"1696\" height=\"1482\"></a></p> \n<p>Next, I edit the newly created cluster parameter group by choosing it on the <strong>Parameter groups</strong> page. I choose <strong>Actions</strong>&nbsp;and then choose <strong>Edit</strong>. I set the following cluster parameter settings:</p> \n<ul> \n <li><code>rds.logical_replication=1</code></li> \n <li><code>aurora.enhanced_logical_replication=1</code></li> \n <li><code>aurora.logical_replication_backup=0</code></li> \n <li><code>aurora.logical_replication_globaldb=0</code></li> \n</ul> \n<p>I choose <strong>Save Changes</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/02-edit-custom-pg-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/02-edit-custom-pg-LaunchMarketingIntake-1191.png\" width=\"2222\" height=\"1042\"></a></p> \n<p>Next, I create an <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_GettingStartedAurora.CreatingConnecting.AuroraPostgreSQL.html\">Aurora PostgreSQL database</a>. When creating the database, you can set the configurations according to your needs. Remember to choose <strong>Aurora PostgreSQL (compatible with PostgreSQL 16.4 or above) </strong>from <strong>Available versions </strong>and the custom cluster parameter group (<code>custom-pg-aurora-postgres-zero-etl</code> in this case) for <strong>DB cluster parameter group</strong> in the <strong>Additional configuration</strong> section.</p> \n<p>After the database becomes available, I connect to the Aurora PostgreSQL cluster, create a database named <strong>books,</strong> create a table named <strong>book_catalog</strong> in the default schema for this database and insert sample data to use with zero-ETL integration.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/03e-postgresdb-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/03e-postgresdb-LaunchMarketingIntake-1191.png\" width=\"2506\" height=\"290\"></a></p> \n<p>To get started with zero-ETL integration, I use an existing Amazon Redshift data warehouse. To create and manage Amazon Redshift resources, visit the <a href=\"https://docs.aws.amazon.com/redshift/latest/gsg/new-user-serverless.html\">Amazon Redshift Getting Started Guide</a>.</p> \n<p>In the Amazon RDS console, I go to the <strong>Zero-ETL integrations</strong> tab in the navigation pane and choose <strong>Create zero-ETL integration</strong>. I enter <code>postgres-redshift-zero-etl</code> for <strong>Integration identifier&nbsp;</strong>and <code>Amazon Aurora zero-ETL integration with Amazon Redshift</code> for <strong>Integration description</strong>. I choose <strong>Next</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/04-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/04-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"2740\" height=\"1332\"></a></p> \n<p>On the next page, I choose <strong>Browse RDS databases</strong> to select the source database. For the <strong>Data filtering options</strong>, I use <code>database.schema.table</code> pattern. I include my table called <strong>book_catalog</strong> in Aurora PostgreSQL <strong>books</strong> database. The <code>*</code> in filters will replicate all <strong>book_catalog</strong> tables in all schemas within <strong>books</strong> database. I choose <strong>Include</strong> as filter type and enter <code>books.*.book_catalog</code> into the <strong>Filter expression</strong> field. I choose <strong>Next</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/05b-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/05b-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"2032\" height=\"1540\"></a></p> \n<p>On the next page, I choose <strong>Browse Redshift data warehouses</strong> and select the existing Amazon Redshift data warehouse as the target. I must specify authorized principals and integration source on the target to enable Amazon Aurora to replicate into the data warehouse and enable case sensitivity. Amazon RDS can complete these steps for me during setup, or I can configure them manually in Amazon Redshift. For this demo, I choose <strong>Fix it for me</strong>&nbsp;and choose <strong>Next</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/06-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/06-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"1124\" height=\"867\"></a></p> \n<p>After the case sensitivity parameter and the resource policy for data warehouse are fixed, I choose <strong>Next</strong> on the next <strong>Add tags and encryption</strong> page. After I review the configuration, I choose <strong>Create zero-ETL integration</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/07-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/07-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"1845\" height=\"882\"></a></p> \n<p>After the integration succeeded, I choose the integration name to check the details.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/08e-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/08e-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"2882\" height=\"1582\"></a></p> \n<p>Now, I need to create a database from integration to finish setting up. I go to the <a href=\"https://console.aws.amazon.com/redshiftv2/home\">Amazon Redshift console</a>, choose <strong>Zero-ETL integrations</strong> in the navigation pane and select the Aurora PostgreSQL integration I just created. I choose <strong>Create database from integration</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/28/08ab-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/28/08ab-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"2838\" height=\"1558\"></a></p> \n<p>I choose <strong>books</strong> as <strong>Source named database</strong> and I enter <code>zeroetl_aurorapg</code>&nbsp;as the <strong>Destination database name</strong>. I&nbsp;choose <strong>Create database</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/09f-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/09f-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"1206\" height=\"1098\"></a></p> \n<p>After the database is created, I return to the Aurora PostgreSQL integration page. On this page, I choose <strong>Query data</strong> to connect to the Amazon Redshift data warehouse to observe if the data is replicated. When I run a select query in the <strong>zeroetl_aurorapg</strong> database, I see that the data in <strong>book_catalog</strong> table is replicated to Amazon Redshift successfully.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/10a-postgres-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/10a-postgres-zetl-LaunchMarketingIntake-1191.png\" width=\"3032\" height=\"968\"></a></p> \n<p>As I said in the beginning, you can select multiple tables or databases from the Aurora PostgreSQL source database to replicate the data to the same Amazon Redshift cluster. To add another database to the same zero-ETL integration, all I have to do is to add another filter to the <strong>Data filtering options</strong> in the form of <code>database.schema.table</code>, replacing the database part with the database name I want to replicate. For this demo, I will select multiple tables to be replicated to the same data warehouse. I create another table named <strong>publisher </strong>in the Aurora PostgreSQL cluster and insert sample data to it.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/11-postgresdb-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/11-postgresdb-LaunchMarketingIntake-1191.png\" width=\"1054\" height=\"280\"></a></p> \n<p>I edit the <strong>Data filtering options&nbsp;</strong>to include publisher table for replication. To do this, I go to the <strong>postgres-redshift-zero-etl </strong>details page and choose <strong>Modify</strong>. I append <code>books.*.publisher</code> using comma in the <strong>Filter expression</strong> field. I choose <strong>Continue</strong>. I review the changes and choose <strong>Save changes</strong>. I observe that the <strong>Filtered data tables</strong> section on the integration details page has now 2 tables included for replication.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/13-postgresdb-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/13-postgresdb-LaunchMarketingIntake-1191.png\" width=\"2758\" height=\"866\"></a></p> \n<p>When I switch to the Amazon Redshift Query editor and refresh the tables, I can see that the new <strong>publisher</strong> table and its records are replicated to the data warehouse.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/14-postgresdb-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/22/14-postgresdb-LaunchMarketingIntake-1191.png\" width=\"3032\" height=\"1010\"></a></p> \n<p>Now that I completed the Aurora PostgreSQL zero-ETL integration with Amazon Redshift, let’s create a DynamoDB zero-ETL integration with the same data warehouse.</p> \n<p><span><strong>Getting started with DynamoDB zero-ETL integration with Amazon Redshift<br> </strong></span>In this part, I proceed to create an Amazon DynamoDB zero-ETL integration using an existing Amazon DynamoDB table named <strong>Book_Catalog</strong>. The table has 2 items in it:</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/19/20a-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/19/20a-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"2356\" height=\"942\"></a></p> \n<p>I go to the <a href=\"https://console.aws.amazon.com/redshiftv2/home\">Amazon Redshift console</a> and choose <strong>Zero-ETL integrations</strong> in the navigation pane. Then, I choose the arrow next to the <strong>Create zero-ETL integration </strong>and choose <strong>Create DynamoDB integration</strong>. I enter <code>dynamodb-redshift-zero-etl</code> for <strong>Integration name</strong> and <code>Amazon DynamoDB zero-ETL integration with Amazon Redshift</code> for <strong>Description</strong>. I choose <strong>Next</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/20-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/17/20-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"1385\" height=\"587\"></a></p> \n<p>On the next page, I choose <strong>Browse DynamoDB tables</strong> and select the <strong>Book_Catalog</strong> table. I must specify a resource policy with authorized principals and integration sources, and enable point-in-time recovery (PITR) on the source table before I create an integration. Amazon DynamoDB can do it for me, or I can change the configuration manually. I choose <strong>Fix it for me</strong> to automatically apply the required resource policies for the integration and enable PITR on the DynamoDB table. I choose <strong>Next</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/18/21a-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/18/21a-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"1633\" height=\"629\"></a></p> \n<p>Then, I choose my existing <a href=\"https://aws.amazon.com/redshift/redshift-serverless/\">Amazon Redshift Serverless</a> data warehouse as the target and choose <strong>Next</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/18/22a-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/18/22a-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"1638\" height=\"759\"></a></p> \n<p>I choose <strong>Next</strong> again in the <strong>Add tags and encryption</strong> page and choose <strong>Create DynamoDB integration</strong> in the <strong>Review and create page</strong>.</p> \n<p>Now, I need to create a database from integration to finish setting up just like I did with Aurora PostgreSQL zero-ETL integration. In the Amazon Redshift console, I choose the DynamoDB integration and I choose <strong>Create database from integration</strong>. In the popup screen, I enter <code>zeroetl_dynamodb</code> as the <strong>Destination database name</strong> and choose <strong>Create database</strong>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/18/24-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/18/24-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"661\" height=\"478\"></a></p> \n<p>After the database is created, I go to the Amazon Redshift <strong>Zero-ETL integrations</strong> page and choose the DynamoDB integration I created. On this page, I choose <strong>Query data</strong> to connect to the Amazon Redshift data warehouse to observe if the data from DynamoDB <strong>Book_Catalog</strong> table is replicated. When I run a select query in the <strong>zeroetl_dynamodb</strong> database, I see that the data is replicated to Amazon Redshift successfully. Note that the data from DynamoDB is replicated in <a href=\"https://docs.aws.amazon.com/redshift/latest/dg/r_SUPER_type.html\">SUPER datatype</a> column and can be accessed using <a href=\"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html\">PartiQL sql</a>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/19/26a-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/19/26a-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"1337\" height=\"523\"></a></p> \n<p>I insert another entry to the DynamoDB <strong>Book_Catalog</strong> table.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/19/27-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/19/27-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"2346\" height=\"1026\"></a></p> \n<p>When I switch to the Amazon Redshift Query editor and refresh the select query, I can see that the new record is replicated to the data warehouse.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/20/28-dynamodb-zetl-LaunchMarketingIntake-1191.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2024/09/20/28-dynamodb-zetl-LaunchMarketingIntake-1191.png\" width=\"3012\" height=\"988\"></a></p> \n<p>Zero-ETL integrations between Aurora PostgreSQL and DynamoDB with Amazon Redshift help you unify data from multiple database clusters and unlock insights in your data warehouse. Amazon Redshift allows cross-database queries and materialized views based off the multiple tables, giving you the opportunity to consolidate and simplify your analytics assets, improve operational efficiency, and optimize cost. You no longer have to worry about setting up and managing complex ETL pipelines.</p> \n<p><span><strong>Now available<br> </strong></span>Aurora PostgreSQL zero-ETL integration with Amazon Redshift is now available in US East (N. Virginia), US East (Ohio), US West (Oregon), Asia Pacific (Hong Kong), Asia Pacific (Mumbai), Asia Pacific (Singapore), Asia Pacific (Sydney), Asia Pacific (Tokyo), Europe (Frankfurt), Europe (Ireland), and Europe (Stockholm) AWS Regions.</p> \n<p>Amazon DynamoDB zero-ETL integration with Amazon Redshift is now available in all commercial, China and GovCloud AWS Regions.</p> \n<p>For pricing information, visit the&nbsp;<a href=\"https://aws.amazon.com/rds/aurora/pricing/#Zero-ETL_integration_costs\">Amazon Aurora</a> and <a href=\"https://aws.amazon.com/dynamodb/pricing/\">Amazon DynamoDB</a> pricing pages.</p> \n<p>To get started with this feature, visit&nbsp;<a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/zero-etl.html\">Working with Aurora zero-ETL integrations with Amazon Redshift</a>&nbsp;and <a href=\"https://docs.aws.amazon.com/redshift/latest/mgmt/zero-etl-using.html\">Amazon Redshift Zero-ETL integrations</a> documentation.</p> \n<p><a href=\"https://www.linkedin.com/in/esrakayabali/\">— Esra</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/amazon-aurora-postgresql-and-amazon-dynamodb-zero-etl-integrations-with-amazon-redshift-now-generally-available/",
            "title": "Amazon Aurora PostgreSQL and Amazon DynamoDB zero-ETL integrations with Amazon Redshift now generally available",
            "date_modified": "2024-10-15T19:59:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41848851\">Comments</a>",
            "url": "https://www.honeycomb.io/blog/engineers-checklist-logging-best-practices",
            "title": "Logging Best Practices: An Engineer's Checklist",
            "date_modified": "2024-10-15T14:15:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41832547\">Comments</a>",
            "url": "https://simonwillison.net/2024/Oct/13/zero-latency-sqlite-storage-in-every-durable-object/",
            "title": "Zero-latency SQLite storage in every Durable Object",
            "date_modified": "2024-10-13T23:07:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41826032\">Comments</a>",
            "url": "https://longform.asmartbear.com/icp-ideal-customer-persona/",
            "title": "Why targeting an ICP brings 10x more customers than you expected",
            "date_modified": "2024-10-13T07:52:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ipfrdt/road_ociv2_images_what_s_wrong_with_tar\">Comments</a></p>",
            "url": "https://www.cyphar.com/blog/post/20190121-ociv2-images-i-tar",
            "title": "The Road to OCIv2 Images: What's Wrong with Tar? (2019)",
            "date_modified": "2024-10-12T17:25:53.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/q1j6ng/packaging_elixir_phoenix_application\">Comments</a></p>",
            "url": "https://curiosum.com/blog/packaging-elixir-application-with-nix",
            "title": "Packaging an Elixir/Phoenix application with Nix",
            "date_modified": "2024-10-11T19:54:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41812336\">Comments</a>",
            "url": "https://peq42.com/blog/gleam-a-basic-introduction/",
            "title": "Gleam: A Basic Introduction",
            "date_modified": "2024-10-11T18:57:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41797041\">Comments</a>",
            "url": "https://nixiesearch.substack.com/p/nixiesearch-running-lucene-over-s3",
            "title": "Nixiesearch: Running Lucene over S3, and why we're building a new search engine",
            "date_modified": "2024-10-10T09:11:26.000Z"
        },
        {
            "content_html": "<p>Failure is an expected state in production systems, and no predictable failure of either software or hardware components should result in a negative experience for users. The exact failure mode may vary, but certain remediation steps must be taken after detection. A common example is when an error occurs on a server, rendering it unfit for production workloads, and requiring action to recover.</p><p>When operating at Cloudflare’s scale, it is important to ensure that our platform is able to recover from faults seamlessly. It can be tempting to rely on the expertise of world-class engineers to remediate these faults, but this would be manual, repetitive, unlikely to produce enduring value, and not scaling. In one word: toil; not a viable solution at our scale and rate of growth.</p><p>In this post we discuss how we built the foundations to enable a more scalable future, and what problems it has immediately allowed us to solve.</p>\n          <div>\n            <h2>Growing pains</h2>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#growing-pains\">\n              \n            </a>\n          </div>\n          <p>The Cloudflare <a href=\"https://en.wikipedia.org/wiki/Site_reliability_engineering\"><u>Site Reliability Engineering (SRE)</u></a> team builds and manages the platform that helps product teams deliver our extensive suite of offerings to customers. One important component of this platform is the collection of servers that power critical products such as Durable Objects, Workers, and DDoS mitigation. We also build and maintain foundational software services that power our product offerings, such as configuration management, provisioning, and IP address allocation systems.</p><p>As part of tactical operations work, we are often required to respond to failures in any of these components to minimize impact to users. Impact can vary from lack of access to a specific product feature, to total unavailability. The level of response required is determined by the priority, which is usually a reflection of the severity of impact on users. Lower-priority failures are more common — a server may run too hot, or experience an unrecoverable hardware error. Higher-priority failures are rare and are typically resolved via a well-defined incident response process, requiring collaboration with multiple other teams.</p><p>The commonality of lower-priority failures makes it obvious when the response required, as defined in runbooks, is “toilsome”. To reduce this toil, we had previously implemented a plethora of solutions to automate runbook actions such as manually-invoked shell scripts, cron jobs, and ad-hoc software services. These had grown organically over time and provided solutions on a case-by-case basis, which led to duplication of work, tight coupling, and lack of context awareness across the solutions.</p><p>We also care about how long it takes to resolve any potential impact on users. A resolution process which involves the manual invocation of a script relies on human action, increasing the Mean-Time-To-Resolve (MTTR) and leaving room for human error. This risks increasing the amount of errors we serve to users and degrading trust.</p><p>These problems proved that we needed a way to automatically heal these platform components. This especially applies to our servers, for which failure can cause impact across multiple product offerings. While we have <a href=\"https://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer\"><u>mechanisms to automatically steer traffic away</u></a> from these degraded servers, in some rare cases the breakage is sudden enough to be visible.</p>\n          <div>\n            <h2>Solving the problem</h2>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#solving-the-problem\">\n              \n            </a>\n          </div>\n          <p>To provide a more reliable platform, we needed a new component that provides a common ground for remediation efforts. This would remove duplication of work, provide unified context-awareness and increase development speed, which ultimately saves hours of engineering time and effort.</p><p>A good solution would not allow only the SRE team to auto-remediate, it would empower the entire company. The key to adding self-healing capability was a generic interface for all teams to self-service and quickly remediate failures at various levels: machine, service, network, or dependencies.</p><p>A good way to think about auto-remediation is in terms of workflows. A workflow is a sequence of steps to get to a desired outcome. This is not dissimilar to a manual shell script which executes what a human would otherwise do via runbook instructions. Because of this logical fit with workflows, we decided to adopt <a href=\"https://temporal.io/\"><u>Temporal</u></a>.&nbsp;</p><p>Temporal is a durable execution platform which is useful to gracefully manage infrastructure failures such as network outages and transient failures in external service endpoints. This capability meant we only needed to build a way to schedule “workflow” tasks and have Temporal provide reliability guarantees. This allowed us to focus on building out the orchestration system to support the control and flow of workflow execution in our data centers.&nbsp;</p><p><a href=\"https://learn.temporal.io/getting_started/go/first_program_in_go/\"><u>Temporal’s documentation</u></a> provides a good introduction to writing Temporal workflows.</p>\n          <div>\n            <h2>Building an Automatic Remediation System</h2>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#building-an-automatic-remediation-system\">\n              \n            </a>\n          </div>\n          <p>Below, we describe how our automatic remediation system works. It is essentially a way to schedule tasks across our global network with built-in reliability guarantees. With this system, teams can serve their customers more reliably. An unexpected failure mode can be recognized and immediately mitigated, while the root cause can be determined later via a more detailed analysis.</p>\n          <div>\n            <h3>Step one: we need a coordinator</h3>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#step-one-we-need-a-coordinator\">\n              \n            </a>\n          </div>\n        \n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/FOpkEE13QcgwHJ9vhcIZj/5b0c5328ee5326794329a4a07c5db065/Building_on_Temporal_process.png\">\n          </figure><p>After our initial testing of Temporal, it was now possible to write workflows. But we needed a way to schedule workflow tasks from other internal services. The coordinator was built to serve this purpose, and became the primary mechanism for the authorisation and scheduling of workflows.&nbsp;</p><p>The most important roles of the coordinator are authorisation, workflow task routing, and safety constraints enforcement. Each consumer is authorized via <a href=\"https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/\"><u>mTLS authentication</u></a>, and the coordinator uses an ACL to determine whether to permit the execution of a workflow. An ACL configuration looks like the following example.</p>\n            <pre><code>server_config {\n    enable_tls = true\n    [...]\n    route_rule {\n      name  = \"global_get\"\n      method = \"GET\"\n      route_patterns = [\"/*\"]\n      uris = [\"spiffe://example.com/worker-admin\"]\n    }\n    route_rule {\n      name = \"global_post\"\n      method = \"POST\"\n      route_patterns = [\"/*\"]\n      uris = [\"spiffe://example.com/worker-admin\"]\n      allow_public = true\n    }\n    route_rule {\n      name = \"public_access\"\n      method = \"GET\"\n      route_patterns = [\"/metrics\"]\n      uris = []\n      allow_public = true\n      skip_log_match = true\n    }\n}\n</code></pre>\n            <p>Each workflow specifies two key characteristics: where to run the tasks and the safety constraints, using an <a href=\"https://github.com/hashicorp/hcl\"><u>HCL</u></a> configuration file. Example constraints could be whether to run on only a specific node type (such as a database), or if multiple parallel executions are allowed: if a task has been triggered too many times, that is a sign of a wider problem that might require human intervention. The coordinator uses the Temporal <a href=\"https://docs.temporal.io/visibility\"><u>Visibility API</u></a> to determine the current state of the executions in the Temporal cluster.</p><p>An example of a configuration file is shown below:</p>\n            <pre><code>task_queue_target = \"&lt;target&gt;\"\n\n# The following entries will ensure that\n# 1. This workflow is not run at the same time in a 15m window.\n# 2. This workflow will not run more than once an hour.\n# 3. This workflow will not run more than 3 times in one day.\n#\nconstraint {\n    kind = \"concurency\"\n    value = \"1\"\n    period = \"15m\"\n}\n\nconstraint {\n    kind = \"maxExecution\"\n    value = \"1\"\n    period = \"1h\"\n}\n\nconstraint {\n    kind = \"maxExecution\"\n    value = \"3\"\n    period = \"24h\"\n    is_global = true\n}\n</code></pre>\n            \n          <div>\n            <h3>Step two: Task Routing is amazing</h3>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#step-two-task-routing-is-amazing\">\n              \n            </a>\n          </div>\n        \n          <figure>\n          <img src=\"https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4wOIwKfkKzp7k46Z6tPuhW/f35667a65872cf7a90fc9c03f38a48d5/Task_Routing_is_amazing_process.png\">\n          </figure><p>An unforeseen benefit of using a central Temporal cluster was the discovery of Task Routing. This feature allows us to schedule a Workflow/Activity on any server that has a running Temporal Worker, and further segment by the type of server, its location, etc. For this reason, we have three primary task queues — the general queue in which tasks can be executed by any worker in the datacenter, the node type queue in which tasks can only be executed by a specific node type in the datacenter, and the individual node queue where we target a specific node for task execution.</p><p>We rely on this heavily to ensure the speed and efficiency of automated remediation. Certain tasks can be run in datacenters with known low latency to an external resource, or a node type with better performance than others (due to differences in the underlying hardware). This reduces the amount of failure and latency we see overall in task executions. Sometimes we are also constrained by certain types of tasks that can only run on a certain node type, such as a database.</p><p>Task Routing also means that we can configure certain task queues to have a higher priority for execution, although this is not a feature we have needed so far. A drawback of task routing is that every Workflow/Activity needs to be registered to the target task queue, which is a common gotcha. Thankfully, it is possible to catch this failure condition with proper testing.</p>\n          <div>\n            <h3>Step three: when/how to self-heal?</h3>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#step-three-when-how-to-self-heal\">\n              \n            </a>\n          </div>\n        <p>None of this would be relevant if we didn’t put it to good use. A primary design goal for the platform was to ensure we had easy, quick ways to trigger workflows on the most important failure conditions. The next step was to determine what the best sources to trigger the actions were. The answer to this was simple: we could trigger workflows from anywhere as long as they are properly authorized and detect the failure conditions accurately.</p><p>Example triggers are an alerting system, a log tailer, a health check daemon, or an authorized engineer via a chatbot. Such flexibility allows a high level of reuse, and permits to invest more in workflow quality and reliability.</p><p>As part of the solution, we built a daemon that is able to poll a signal source for any unwanted condition and trigger a configured workflow. We have initially found <a href=\"https://blog.cloudflare.com/how-cloudflare-runs-prometheus-at-scale\"><u>Prometheus</u></a> useful as a source because it contains both service-level and hardware/system-level metrics. We are also exploring more event-based trigger mechanisms, which could eliminate the need to use precious system resources to poll for metrics.</p><p>We already had internal services that are able to detect widespread failure conditions for our customers, but were only able to page a human. With the adoption of auto-remediation, these systems are now able to react automatically. This ability to create an automatic feedback loop with our customers is the cornerstone of these self-healing capabilities and we continue to work on stronger signals, faster reaction times, and better prevention of future occurrences.</p><p>The most exciting part, however, is the future possibility. Every customer cares about any negative impact from Cloudflare. With this platform we can onboard several services (especially those that are foundational for the critical path) and ensure we react quickly to any failure conditions, even before there is any visible impact.</p>\n          <div>\n            <h3>Step four: packaging and deployment</h3>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#step-four-packaging-and-deployment\">\n              \n            </a>\n          </div>\n        <p>The whole system is written in <a href=\"https://go.dev/\"><u>golang</u></a>, and a single binary can implement each role. We distribute it as an apt package or a container for maximum ease of deployment.</p><p>We deploy a Temporal-based worker to every server we intend to run tasks on, and a daemon in datacenters where we intend to automatically trigger workflows based on the local conditions. The coordinator is more nuanced since we rely on task routing and can trigger from a central coordinator, but we have also found value in running coordinators locally in the datacenters. This is especially useful in datacenters with less capacity or degraded performance, removing the need for a round-trip to schedule the workflows.</p>\n          <div>\n            <h3>Step five: test, test, test</h3>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#step-five-test-test-test\">\n              \n            </a>\n          </div>\n        <p>Temporal provides native mechanisms to test an entire workflow, via a <a href=\"https://docs.temporal.io/develop/go/testing-suite\"><u>comprehensive test suite</u></a> that supports end-to-end, integration, and unit testing, which we used extensively to prevent regressions while developing. We also ensured proper test coverage for all the critical platform components, especially the coordinator.</p><p>Despite the ease of written tests, we quickly discovered that they were not enough. After writing workflows, engineers need an environment as close as possible to the target conditions. This is why we configured our staging environments to support quick and efficient testing. These environments receive the latest changes and point to a different (staging) Temporal cluster, which enables experimentation and easy validation of changes.</p><p>After a workflow is validated in the staging environment, we can then do a full release to production. It seems obvious, but catching simple configuration errors before releasing has saved us many hours in development/change-related-task time.</p>\n          <div>\n            <h2>Deploying to production</h2>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#deploying-to-production\">\n              \n            </a>\n          </div>\n          <p>As you can guess from the title of this post, we put this in production to automatically react to server-specific errors and unrecoverable failures. To this end, we have a set of services that are able to detect single-server failure conditions based on analyzed traffic data. After deployment, we have successfully mitigated potential impact by taking any errant single sources of failure out of production.</p><p>We have also created a set of workflows to reduce internal toil and improve efficiency. These workflows can automatically test pull requests on target machines, wipe and reset servers after experiments are concluded, and take away manual processes that cost many hours in toil.</p><p>Building a system that is maintained by several SRE teams has allowed us to iterate faster, and rapidly tackle long-standing problems. We have set ambitious goals regarding toil elimination and are on course to achieve them, which will allow us to scale faster by eliminating the human bottleneck.</p>\n          <div>\n            <h2>Looking to the future</h2>\n            <a href=\"https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare#looking-to-the-future\">\n              \n            </a>\n          </div>\n          <p>Our immediate plans are to leverage this system to provide a more reliable platform for our customers and drastically reduce operational toil, freeing up engineering resources to tackle larger-scale problems. We also intend to leverage more Temporal features such as <a href=\"https://docs.temporal.io/develop/go/versioning\"><u>Workflow Versioning</u></a>, which will simplify the process of making changes to workflows by ensuring that triggered workflows run expected versions.&nbsp;</p><p>&nbsp;We are also interested in how others are solving problems using durable execution platforms such as Temporal, and general strategies to eliminate toil. If you would like to discuss this further, feel free to reach out on the <a href=\"https://community.cloudflare.com\"><u>Cloudflare Community</u></a> and start a conversation!</p><p>If you’re interested in contributing to projects that help build a better Internet, <a href=\"https://www.cloudflare.com/en-gb/careers/jobs/?department=Engineering&amp;location=default\"><u>our engineering teams are hiring</u></a>.</p>",
            "url": "https://blog.cloudflare.com/improving-platform-resilience-at-cloudflare",
            "title": "Improving platform resilience at Cloudflare through automation",
            "date_modified": "2024-10-09T06:07:17.805Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41767852\">Comments</a>",
            "url": "https://www.signalfire.com/blog/devrel-for-startups",
            "title": "Unlocking the 'aha' moment: Developer relations for startups",
            "date_modified": "2024-10-07T16:40:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41758915\">Comments</a>",
            "url": "https://blog.drewolson.org/gleam-is-pragmatic/",
            "title": "Gleam Is Pragmatic",
            "date_modified": "2024-10-06T18:15:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41755805\">Comments</a>",
            "url": "https://medium.com/yandex/good-retry-bad-retry-an-incident-story-648072d3cee6",
            "title": "Good Retry, Bad Retry: An Incident Story",
            "date_modified": "2024-10-06T08:56:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41735019\">Comments</a>",
            "url": "https://flawless.dev/docs/",
            "title": "Flawless is now in public beta",
            "date_modified": "2024-10-03T21:09:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41723569\">Comments</a>",
            "url": "https://github.com/tqwewe/kameo",
            "title": "Show HN: Kameo – a Rust library for building fault-tolerant, async actors",
            "date_modified": "2024-10-02T18:22:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41723569\">Comments</a>",
            "url": "https://github.com/tqwewe/kameo",
            "title": "Show HN: Kameo – Fault-tolerant async actors built on Tokio",
            "date_modified": "2024-10-02T18:22:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41720846\">Comments</a>",
            "url": "https://threedots.tech/post/distributed-transactions-in-go",
            "title": "Distributed Transactions in Go: Read Before You Try",
            "date_modified": "2024-10-02T14:10:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41720846\">Comments</a>",
            "url": "https://threedots.tech/post/distributed-transactions-in-go",
            "title": "Distributed transactions in Go: Read before you try",
            "date_modified": "2024-10-02T14:10:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41720084\">Comments</a>",
            "url": "https://www.highlight.io/blog/alert-evaluations-incremental-merges-in-clickhouse",
            "title": "Alert Evaluations: Incremental Merges in ClickHouse",
            "date_modified": "2024-10-02T12:55:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41718485\">Comments</a>",
            "url": "https://automattic.com/2024/10/01/wpe-terms/",
            "title": "Automattic–WP Engine Term Sheet",
            "date_modified": "2024-10-02T08:41:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41694044\">Comments</a>",
            "url": "https://walzr.com/bop-spotter",
            "title": "Bop Spotter",
            "date_modified": "2024-09-30T06:09:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41692922\">Comments</a>",
            "url": "https://int10h.org/oldschool-pc-fonts/",
            "title": "The Ultimate Oldschool PC Font Pack",
            "date_modified": "2024-09-30T02:26:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41690667\">Comments</a>",
            "url": "https://ma.tt/2024/09/t3/",
            "title": "On With Theo / T3.gg",
            "date_modified": "2024-09-29T21:15:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41687707\">Comments</a>",
            "url": "https://jvns.ca/blog/2024/09/27/some-go-web-dev-notes/",
            "title": "Some Go web dev notes",
            "date_modified": "2024-09-29T14:36:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41683293\">Comments</a>",
            "url": "https://discord.com/blog/how-discord-stores-trillions-of-messages",
            "title": "How Discord stores trillions of messages",
            "date_modified": "2024-09-28T22:07:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41675671\">Comments</a>",
            "url": "https://wordpress.org/news/2024/09/wp-engine-reprieve/",
            "title": "WP Engine Reprieve",
            "date_modified": "2024-09-27T21:23:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41669964\">Comments</a>",
            "url": "https://devstarterpack.io/",
            "title": "Dev Starter Pack: The essential startup starter kit",
            "date_modified": "2024-09-27T13:06:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41669961\">Comments</a>",
            "url": "https://blog.cloudflare.com/container-platform-preview/",
            "title": "Our container platform is in production. It has GPUs. Here's an early look",
            "date_modified": "2024-09-27T13:05:53.000Z"
        },
        {
            "content_html": "<p>I spent a lot of time in the past couple of weeks working on a website in Go\nthat may or may not ever see the light of day, but I learned a couple of things\nalong the way I wanted to write down. Here they are:</p>\n<h3 id=\"go-1-22-now-has-better-routing\">go 1.22 now has better routing</h3>\n<p>I’ve never felt motivated to learn any of the Go routing libraries\n(gorilla/mux, chi, etc), so I’ve been doing all my routing by hand, like this.</p>\n<pre><code>\t// DELETE /records:\n\tcase r.Method == \"DELETE\" &amp;&amp; n == 1 &amp;&amp; p[0] == \"records\":\n\t\tif !requireLogin(username, r.URL.Path, r, w) {\n\t\t\treturn\n\t\t}\n\t\tdeleteAllRecords(ctx, username, rs, w, r)\n\t// POST /records/&lt;ID&gt;\n\tcase r.Method == \"POST\" &amp;&amp; n == 2 &amp;&amp; p[0] == \"records\" &amp;&amp; len(p[1]) &gt; 0:\n\t\tif !requireLogin(username, r.URL.Path, r, w) {\n\t\t\treturn\n\t\t}\n\t\tupdateRecord(ctx, username, p[1], rs, w, r)\n\n</code></pre>\n<p>But apparently <a href=\"https://go.dev/blog/routing-enhancements\">as of Go 1.22</a>, Go\nnow has better support for routing in the standard library, so that code can be\nrewritten something like this:</p>\n<pre><code>\tmux.HandleFunc(\"DELETE /records/\", app.deleteAllRecords)\n\tmux.HandleFunc(\"POST /records/{record_id}\", app.updateRecord)\n</code></pre>\n<p>Though it would also need a login middleware, so maybe something more like\nthis, with a <code>requireLogin</code> middleware.</p>\n<pre><code>\tmux.Handle(\"DELETE /records/\", requireLogin(http.HandlerFunc(app.deleteAllRecords)))\n</code></pre>\n<h3 id=\"a-gotcha-with-the-built-in-router-redirects-with-trailing-slashes\">a gotcha with the built-in router: redirects with trailing slashes</h3>\n<p>One annoying gotcha I ran into was: if I make a route for <code>/records/</code>, then a\nrequest for <code>/records</code> <a href=\"https://pkg.go.dev/net/http#hdr-Trailing_slash_redirection-ServeMux\">will be redirected</a> to <code>/records/</code>.</p>\n<p>I ran into an issue with this where sending a POST request to <code>/records</code>\nredirected to a GET request for <code>/records/</code>, which broke the POST request\nbecause it removed the request body. Thankfully <a href=\"https://xeiaso.net/blog/go-servemux-slash-2021-11-04/\">Xe Iaso wrote a blog post about the exact same issue</a> which made it\neasier to debug.</p>\n<p>I think the solution to this is just to use API endpoints like <code>POST /records</code>\ninstead of <code>POST /records/</code>, which seems like a more normal design anyway.</p>\n<h3 id=\"sqlc-automatically-generates-code-for-my-db-queries\">sqlc automatically generates code for my db queries</h3>\n<p>I got a little bit tired of writing so much boilerplate for my SQL queries, but\nI didn’t really feel like learning an ORM, because I know what SQL queries I\nwant to write, and I didn’t feel like learning the ORM’s conventions for\ntranslating things into SQL queries.</p>\n<p>But then I found <a href=\"https://sqlc.dev/\">sqlc</a>, which will compile a query like this:</p>\n<pre><code>\n-- name: GetVariant :one\nSELECT *\nFROM variants\nWHERE id = ?;\n\n</code></pre>\n<p>into Go code like this:</p>\n<pre><code>const getVariant = `-- name: GetVariant :one\nSELECT id, created_at, updated_at, disabled, product_name, variant_name\nFROM variants\nWHERE id = ?\n`\n\nfunc (q *Queries) GetVariant(ctx context.Context, id int64) (Variant, error) {\n\trow := q.db.QueryRowContext(ctx, getVariant, id)\n\tvar i Variant\n\terr := row.Scan(\n\t\t&amp;i.ID,\n\t\t&amp;i.CreatedAt,\n\t\t&amp;i.UpdatedAt,\n\t\t&amp;i.Disabled,\n\t\t&amp;i.ProductName,\n\t\t&amp;i.VariantName,\n\t)\n\treturn i, err\n}\n</code></pre>\n<p>What I like about this is that if I’m ever unsure about what Go code to write\nfor a given SQL query, I can just write the query I want, read the generated\nfunction and it’ll tell me exactly what to do to call it. It feels much easier\nto me than trying to dig through the ORM’s documentation to figure out how to\nconstruct the SQL query I want.</p>\n<p>Reading <a href=\"https://brandur.org/fragments/sqlc-2024\">Brandur’s sqlc notes from 2024</a> also gave me some confidence\nthat this is a workable path for my tiny programs. That post gives a really\nhelpful example of how to conditionally update fields in a table using CASE\nstatements (for example if you have a table with 20 columns and you only want\nto update 3 of them).</p>\n<h3 id=\"sqlite-tips\">sqlite tips</h3>\n<p>Someone on Mastodon linked me to this post called <a href=\"https://kerkour.com/sqlite-for-servers\">Optimizing sqlite for servers</a>. My projects are small and I’m\nnot so concerned about performance, but my main takeaways were:</p>\n<ul>\n<li>have a dedicated object for <strong>writing</strong> to the database, and run\n<code>db.SetMaxOpenConns(1)</code> on it. I learned the hard way that if I don’t do this\nthen I’ll get <code>SQLITE_BUSY</code> errors from two threads trying to write to the db\nat the same time.</li>\n<li>if I want to make reads faster, I could have 2 separate db objects, one for writing and one for reading</li>\n</ul>\n<p>There are a more tips in that post that seem useful (like “COUNT queries are\nslow” and “Use STRICT tables”), but I haven’t done those yet.</p>\n<p>Also sometimes if I have two tables where I know I’ll never need to do a <code>JOIN</code>\nbeteween them, I’ll just put them in separate databases so that I can connect\nto them independently.</p>\n<h3 id=\"go-1-19-introduced-a-way-to-set-a-gc-memory-limit\">Go 1.19 introduced a way to set a GC memory limit</h3>\n<p>I run all of my Go projects in VMs with relatively little memory, like 256MB or\n512MB. I ran into an issue where my application kept getting OOM killed and it\nwas confusing – did I have a memory leak? What?</p>\n<p>After some Googling, I realized that maybe I didn’t have a memory leak, maybe I\njust needed to reconfigure the garbage collector! It turns out that by default (according to <a href=\"https://tip.golang.org/doc/gc-guide\">A Guide to the Go Garbage Collector</a>), Go’s garbage collector will\nlet the application allocate memory up to <strong>2x</strong> the current heap size.</p>\n<p><a href=\"https://messwithdns.net\">Mess With DNS</a>’s base heap size is around 170MB and\nthe amount of memory free on the VM is around 160MB right now, so if its memory\ndoubled, it’ll get OOM killed.</p>\n<p>In Go 1.19, they added a way to tell Go “hey, if the application starts using\nthis much memory, run a GC”. So I set the GC memory limit to 250MB and it seems\nto have resulted in the application getting OOM killed less often:</p>\n<pre><code>export GOMEMLIMIT=250MiB\n</code></pre>\n<h3 id=\"some-reasons-i-like-making-websites-in-go\">some reasons I like making websites in Go</h3>\n<p>I’ve been making tiny websites (like the <a href=\"https://nginx-playground.wizardzines.com/\">nginx playground</a>) in Go on and off for the last 4 years or so and it’s really been working for me. I think I like it because:</p>\n<ul>\n<li>there’s just 1 static binary, all I need to do to deploy it is copy the binary. If there are static files I can just embed them in the binary with <a href=\"https://pkg.go.dev/embed\">embed</a>.</li>\n<li>there’s a built-in webserver that’s okay to use in production, so I don’t need to configure WSGI or whatever to get it to work. I can just put it behind <a href=\"https://caddyserver.com/\">Caddy</a> or run it on fly.io or whatever.</li>\n<li>Go’s toolchain is very easy to install, I can just do <code>apt-get install golang-go</code> or whatever and then a <code>go build</code> will build my project</li>\n<li>it feels like there’s very little to remember to start sending HTTP responses\n– basically all there is are functions like <code>Serve(w http.ResponseWriter, r *http.Request)</code> which read the request and send a response. If I need to\nremember some detail of how exactly that’s accomplished, I just have to read\nthe function!</li>\n<li>also <code>net/http</code> is in the standard library, so you can start making websites\nwithout installing any libraries at all. I really appreciate this one.</li>\n<li>Go is a pretty systems-y language, so if I need to run an <code>ioctl</code> or\nsomething that’s easy to do</li>\n</ul>\n<p>In general everything about it feels like it makes projects easy to work on for\n5 days, abandon for 2 years, and then get back into writing code without a lot\nof problems.</p>\n<p>For contrast, I’ve tried to learn Rails a couple of times and I really <em>want</em>\nto love Rails – I’ve made a couple of toy websites in Rails and it’s always\nfelt like a really magical experience. But ultimately when I come back to those\nprojects I can’t remember how anything works and I just end up giving up. It\nfeels easier to me to come back to my Go projects that are full of a lot of\nrepetitive boilerplate, because at least I can read the code and figure out how\nit works.</p>\n<h3 id=\"things-i-haven-t-figured-out-yet\">things I haven’t figured out yet</h3>\n<p>some things I haven’t done much of yet in Go:</p>\n<ul>\n<li>rendering HTML templates: usually my Go servers are just APIs and I make the\nfrontend a single-page app with Vue. I’ve used <code>html/template</code> a lot in Hugo (which I’ve used for this blog for the last 8 years)\nbut I’m still not sure how I feel about it.</li>\n<li>I’ve never made a real login system, usually my servers don’t have users at all.</li>\n<li>I’ve never tried to implement CSRF</li>\n</ul>\n<p>In general I’m not sure how to implement security-sensitive features so I don’t\nstart projects which need login/CSRF/etc. I imagine this is where a framework\nwould help.</p>\n<h3 id=\"it-s-cool-to-see-the-new-features-go-has-been-adding\">it’s cool to see the new features Go has been adding</h3>\n<p>Both of the Go features I mentioned in this post (<code>GOMEMLIMIT</code> and the routing)\nare new in the last couple of years and I didn’t notice when they came out. It\nmakes me think I should pay closer attention to the release notes for new Go\nversions.</p>",
            "url": "https://jvns.ca/blog/2024/09/27/some-go-web-dev-notes/",
            "title": "Some Go web dev notes",
            "date_modified": "2024-09-27T11:16:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41655382\">Comments</a>",
            "url": "https://blog.trailofbits.com/2024/09/24/notes-on-aws-nitro-enclaves-attack-surface/",
            "title": "AWS Nitro Enclaves: Attack Surface",
            "date_modified": "2024-09-26T07:12:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41642114\">Comments</a>",
            "url": "https://www.artie.com/blogs/postgres-write-ahead-logs",
            "title": "Deep Dive into Postgres Write-Ahead Logs",
            "date_modified": "2024-09-24T23:32:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41635871\">Comments</a>",
            "url": "https://blog.oodle.ai/building-a-high-performance-low-cost-metrics-observability-system/",
            "title": "Show HN: Oodle – serverless, fully-managed, drop-in replacement for Prometheus",
            "date_modified": "2024-09-24T12:39:39.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/xjxac8/make_your_next_js_docker_images\">Comments</a></p>",
            "url": "https://xeiaso.net/notes/2024/small-nextjs-images/",
            "title": "Make your Next.JS Docker images microscopic",
            "date_modified": "2024-09-23T10:45:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41618505\">Comments</a>",
            "url": "https://laike9m.com/blog/no-one-builds-in-public,160/",
            "title": "Does “building in public” work?",
            "date_modified": "2024-09-22T17:30:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41616491\">Comments</a>",
            "url": "https://github.com/xataio/pgroll",
            "title": "pgroll: PostgreSQL zero-downtime migrations made easy",
            "date_modified": "2024-09-22T12:09:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41613628\">Comments</a>",
            "url": "https://wordpress.org/news/2024/09/wp-engine/",
            "title": "WP Engine is not WordPress",
            "date_modified": "2024-09-22T00:16:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41608648\">Comments</a>",
            "url": "https://www.rijksmuseum.nl/en/stories/operation-night-watch/story/ultra-high-resolution-image-of-the-night-watch",
            "title": "Ultra high-resolution image of The Night Watch",
            "date_modified": "2024-09-21T09:08:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41604267\">Comments</a>",
            "url": "https://discord.com/blog/how-discord-reduced-websocket-traffic-by-40-percent",
            "title": "Discord Reduced WebSocket Traffic by 40%",
            "date_modified": "2024-09-20T18:09:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41604042\">Comments</a>",
            "url": "https://www.inngest.com/",
            "title": "Show HN: Inngest 1.0 – Open-source durable workflows on every platform",
            "date_modified": "2024-09-20T17:33:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41598119\">Comments</a>",
            "url": "https://www.anthropic.com/news/contextual-retrieval",
            "title": "Anthropic – Introducing Contextual Retrieval",
            "date_modified": "2024-09-20T01:57:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41598006\">Comments</a>",
            "url": "http://blog.rongarret.info/2024/09/yes-you-can-have-exactly-once-delivery.html",
            "title": "Yes, you can have exactly-once delivery",
            "date_modified": "2024-09-20T01:33:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41597929\">Comments</a>",
            "url": "https://travel.stackexchange.com/questions/26780/is-there-a-flight-search-engine-that-combines-flights-from-different-airlines",
            "title": "Is there a flight search engine that combines flights from different airlines?",
            "date_modified": "2024-09-20T01:20:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41585156\">Comments</a>",
            "url": "https://dtinth.github.io/comic-mono-font/",
            "title": "Comic Mono",
            "date_modified": "2024-09-18T20:36:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41571606\">Comments</a>",
            "url": "https://www.swift.org/blog/announcing-swift-6/",
            "title": "Swift 6",
            "date_modified": "2024-09-17T19:20:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41570163\">Comments</a>",
            "url": "https://jeremymorrell.dev/blog/minimal-js-tracing/",
            "title": "OpenTelemetry Tracing in < 200 lines of code",
            "date_modified": "2024-09-17T17:21:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41567555\">Comments</a>",
            "url": "https://newsroom.ibm.com/blog-ibm-acquires-kubecost-to-broaden-hybrid-cloud-cost-management-capabilities",
            "title": "IBM acquires Kubecost",
            "date_modified": "2024-09-17T13:47:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41553940\">Comments</a>",
            "url": "https://jfmengels.net/constraints-and-guarantees/",
            "title": "Constraints and Guarantees",
            "date_modified": "2024-09-16T08:26:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41551547\">Comments</a>",
            "url": "https://twitter.com/NickADobos/status/1835242067255238862",
            "title": "Mr Beast YouTube Playbook Leaked",
            "date_modified": "2024-09-16T00:06:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41550211\">Comments</a>",
            "url": "https://www.brailleinstitute.org/freefont/",
            "title": "Atkinson Hyperlegible Font",
            "date_modified": "2024-09-15T20:42:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41531033\">Comments</a>",
            "url": "https://delightyourusers.com/psychology",
            "title": "Product Psychology",
            "date_modified": "2024-09-13T13:22:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41527564\">Comments</a>",
            "url": "https://www.hadijaveed.me/2024/09/08/does-your-startup-really-need-complex-cloud-infrastructure/",
            "title": "Does your startup need complex cloud infrastructure?",
            "date_modified": "2024-09-13T02:29:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41520261\">Comments</a>",
            "url": "https://dashbit.co/blog/remix-concurrent-submissions-flawed",
            "title": "Remix's concurrent submissions are fundamentally flawed",
            "date_modified": "2024-09-12T12:47:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41500506\">Comments</a>",
            "url": "https://briefer.cloud/blog/posts/open-source-strategy/",
            "title": "Going open-source as a VC-Backed company",
            "date_modified": "2024-09-10T13:16:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41490161\">Comments</a>",
            "url": "https://blog.gitbutler.com/why-github-actually-won/",
            "title": "Why GitHub Won",
            "date_modified": "2024-09-09T16:27:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41490161\">Comments</a>",
            "url": "https://blog.gitbutler.com/why-github-actually-won/",
            "title": "Why GitHub won",
            "date_modified": "2024-09-09T16:27:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41479487\">Comments</a>",
            "url": "https://github.com/keisku/gmon",
            "title": "Show HN: Goroutine Monitor Powered by eBPF",
            "date_modified": "2024-09-08T10:19:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41458240\">Comments</a>",
            "url": "https://github.com/cloudflare/serverless-registry",
            "title": "Serverless-registry: A Docker registry backed by Workers and R2",
            "date_modified": "2024-09-05T16:34:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41456815\">Comments</a>",
            "url": "https://ferd.ca/a-pipeline-made-of-airbags.html",
            "title": "A Pipeline Made of Airbags",
            "date_modified": "2024-09-05T14:11:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41446898\">Comments</a>",
            "url": "https://pentacent.com/blog/leaving-the-cloud-broken-paas-promise/",
            "title": "Cloud of Disillusion: The Broken Promise of PaaS",
            "date_modified": "2024-09-04T15:26:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41445564\">Comments</a>",
            "url": "https://hynek.me/articles/docker-uv/",
            "title": "Production-ready Docker Containers with uv",
            "date_modified": "2024-09-04T13:27:46.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/kfpzks/departure_mono\">Comments</a></p>",
            "url": "https://departuremono.com/",
            "title": "Departure Mono",
            "date_modified": "2024-09-02T17:41:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41427185\">Comments</a>",
            "url": "https://www.somethingsimilar.com/2013/01/14/notes-on-distributed-systems-for-young-bloods/",
            "title": "Notes on Distributed Systems for Young Bloods",
            "date_modified": "2024-09-02T17:23:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41424954\">Comments</a>",
            "url": "https://github.com/Glimesh/broadcast-box",
            "title": "Show HN: OBS Live-streaming with 120ms latency",
            "date_modified": "2024-09-02T12:46:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41421846\">Comments</a>",
            "url": "https://orbstack.dev/",
            "title": "OrbStack: The fast, light, and easy way to run Docker containers and Linux",
            "date_modified": "2024-09-02T01:35:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41420597\">Comments</a>",
            "url": "https://kittenlabs.de/blog/2024/09/01/extreme-pi-boot-optimization/",
            "title": "Extreme Pi Boot Optimization",
            "date_modified": "2024-09-01T21:36:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41407407\">Comments</a>",
            "url": "https://rockyj-blogs.web.app/2024/08/31/shine-with-gleam.html",
            "title": "Shine with Gleam",
            "date_modified": "2024-08-31T08:42:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41385205\">Comments</a>",
            "url": "https://github.com/newsnowlabs/runcvm",
            "title": "RunCVM: An open-source Docker runtime for launching container images in VMs",
            "date_modified": "2024-08-28T22:38:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41370020\">Comments</a>",
            "url": "https://owickstrom.github.io/the-monospace-web/",
            "title": "The Monospace Web",
            "date_modified": "2024-08-27T17:09:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41368583\">Comments</a>",
            "url": "https://www.bly.com/Pages/documents/STIKFS.html",
            "title": "Marketing to Engineers (2001)",
            "date_modified": "2024-08-27T15:23:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41357123\">Comments</a>",
            "url": "https://www.morling.dev/blog/leader-election-with-s3-conditional-writes/",
            "title": "Leader Election with S3 Conditional Writes",
            "date_modified": "2024-08-26T13:54:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41356279\">Comments</a>",
            "url": "https://blog.colinbreck.com/predicting-the-future-of-distributed-systems/",
            "title": "Predicting the future of distributed systems",
            "date_modified": "2024-08-26T11:56:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41356239\">Comments</a>",
            "url": "https://blog.api-fiddle.com/posts/coolify-revolution",
            "title": "Coolify’s rise to fame, and why it could be a big deal",
            "date_modified": "2024-08-26T11:50:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41355890\">Comments</a>",
            "url": "https://www.ubicloud.com/blog/building-infrastructure-control-planes-in-ruby",
            "title": "13 Years of Building Infrastructure Control Planes in Ruby",
            "date_modified": "2024-08-26T10:48:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41337469\">Comments</a>",
            "url": "https://github.com/moxie0/knockknock",
            "title": "Knockknock: Simple, secure, and stealthy port knocking implementation",
            "date_modified": "2024-08-24T11:49:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41327735\">Comments</a>",
            "url": "https://github.com/beac0n/ruroco",
            "title": "Show HN: Ruroco – like port knocking, but better",
            "date_modified": "2024-08-23T10:19:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41305770\">Comments</a>",
            "url": "https://playinzoi.com/en",
            "title": "PUBG developer Krafton make a life simulation game inzoi",
            "date_modified": "2024-08-21T01:10:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41305736\">Comments</a>",
            "url": "https://www.ycombinator.com/launches/Ler-ares-industries-building-low-cost-cruise-missiles",
            "title": "Launch YC: Ares Industries – Building low-cost cruise missiles",
            "date_modified": "2024-08-21T01:04:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41303974\">Comments</a>",
            "url": "https://www.zen-browser.app/",
            "title": "Zen, a Arc-like open-source browser based on the Firefox engine",
            "date_modified": "2024-08-20T20:57:04.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/z240zu/systems_distributed_24\">Comments</a></p>",
            "url": "https://www.youtube.com/playlist?list=PL9eL-xg48OM3aoODepNLa2Qqq67CDsSKf",
            "title": "Systems Distributed '24",
            "date_modified": "2024-08-17T08:33:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41260951\">Comments</a>",
            "url": "https://blackmesa.com/",
            "title": "Black Mesa",
            "date_modified": "2024-08-15T22:07:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41246181\">Comments</a>",
            "url": "https://github.com/project-oak/oak",
            "title": "Project Oak: Meaningful control of data in distributed systems",
            "date_modified": "2024-08-14T14:00:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41230794\">Comments</a>",
            "url": "https://www.blacksmith.sh/blog/faster-docker-builds-using-a-remote-buildkit-instance",
            "title": "Faster Docker builds using a remote BuildKit instance",
            "date_modified": "2024-08-13T00:11:04.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/snjs8q/building_static_binaries_nix\">Comments</a></p>",
            "url": "https://kokada.capivaras.dev/blog/building-static-binaries-in-nix/",
            "title": "Building static binaries in Nix",
            "date_modified": "2024-08-11T19:35:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41217136\">Comments</a>",
            "url": "https://servermono.com/",
            "title": "Server Mono: A Typeface Inspired by Typewriters, Apple's SF Mono, and CLIs",
            "date_modified": "2024-08-11T16:04:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41200019\">Comments</a>",
            "url": "https://www.helpnetsecurity.com/2024/08/07/github-bitbucket-gitlab-jira-incidents/",
            "title": "Number of incidents affecting GitHub, Bitbucket, Gitlab and Jira is rising",
            "date_modified": "2024-08-09T08:48:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41198151\">Comments</a>",
            "url": "https://slimsaas.com/blog/saas-copy-writing-saas-marketing-framework-works",
            "title": "SaaS Copywriting: Marketing SaaS Framework",
            "date_modified": "2024-08-09T01:49:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41195805\">Comments</a>",
            "url": "https://www.enterpriseintegrationpatterns.com/ramblings/queues_flow_control.html",
            "title": "Queues invert control flow but require flow control",
            "date_modified": "2024-08-08T20:22:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41193512\">Comments</a>",
            "url": "https://blog.cloudflare.com/introducing-automatic-ssl-tls-securing-and-simplifying-origin-connectivity",
            "title": "Cloudflare Introduces Automatic SSL/TLS",
            "date_modified": "2024-08-08T16:44:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41191720\">Comments</a>",
            "url": "https://jepsen.io/analyses/jetcd-0.8.2",
            "title": "Jepsen: Jetcd 0.8.2",
            "date_modified": "2024-08-08T14:09:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41182055\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=41182055",
            "title": "Launch HN: Release (YC W20) – Orchestrate AI Infrastructure and Applications",
            "date_modified": "2024-08-07T14:50:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41180492\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=41180492",
            "title": "Ask HN: How to Price a Product",
            "date_modified": "2024-08-07T12:02:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41178959\">Comments</a>",
            "url": "https://news.alvaroduran.com/p/cringey-but-true-how-uber-tests-payments",
            "title": "Cringey, but True: How Uber Tests Payments in Production",
            "date_modified": "2024-08-07T07:16:02.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/3boto7/first_impressions_gleam_lots_joys_some\">Comments</a></p>",
            "url": "https://ntietz.com/blog/first-impressions-of-gleam",
            "title": "First impressions of Gleam: lots of joys and some rough edges",
            "date_modified": "2024-08-07T00:00:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41144685\">Comments</a>",
            "url": "https://lsd.gnunet.org/lsd0004/draft-schanzen-r5n.html",
            "title": "R5N - Obfuscated mesh routing on hostile networks.",
            "date_modified": "2024-08-03T04:30:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41139924\">Comments</a>",
            "url": "https://techcrunch.com/2024/07/30/real-time-database-startup-clickhouse-acquires-peerdb-to-expand-its-postgres-support/",
            "title": "ClickHouse acquires PeerDB to expand its Postgres support",
            "date_modified": "2024-08-02T15:57:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41114839\">Comments</a>",
            "url": "https://blog.trailofbits.com/2024/07/30/our-audit-of-homebrew/",
            "title": "Our Audit of Homebrew",
            "date_modified": "2024-07-30T22:39:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41112889\">Comments</a>",
            "url": "https://trayce.dev",
            "title": "Show HN: Trayce – Network tab for your local Docker containers",
            "date_modified": "2024-07-30T18:58:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41104293\">Comments</a>",
            "url": "https://blog.healthchecks.io/2024/07/running-one-man-saas-9-years-in/",
            "title": "Running One-man SaaS for 9 Years",
            "date_modified": "2024-07-29T22:15:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41104293\">Comments</a>",
            "url": "https://blog.healthchecks.io/2024/07/running-one-man-saas-9-years-in/",
            "title": "One-man SaaS, 9 Years In",
            "date_modified": "2024-07-29T22:15:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41102981\">Comments</a>",
            "url": "https://trayce.dev",
            "title": "Show HN: Trayce – Network tab for Docker containers",
            "date_modified": "2024-07-29T19:23:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41100958\">Comments</a>",
            "url": "http://jpetazzo.github.io/2024/07/26/cloudflare-images-overcharge-billing/",
            "title": "Is Cloudflare overcharging us for their images service?",
            "date_modified": "2024-07-29T14:55:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41083119\">Comments</a>",
            "url": "https://blog.val.town/blog/node-spawn-performance/",
            "title": "Why is spawning a new process in Node so slow?",
            "date_modified": "2024-07-26T23:07:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41070870\">Comments</a>",
            "url": "https://blog.thinkst.com/2024/07/unfashionably-secure-why-we-use-isolated-vms.html",
            "title": "Unfashionably secure: why we use isolated VMs",
            "date_modified": "2024-07-25T17:00:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41068475\">Comments</a>",
            "url": "https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-scare-you/",
            "title": "Don't Let Architecture Astronauts Scare You",
            "date_modified": "2024-07-25T13:30:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41066169\">Comments</a>",
            "url": "https://blog.replay.io/a-new-direction",
            "title": "Replay.io is discontinuing Replay Test Suites",
            "date_modified": "2024-07-25T08:19:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41060631\">Comments</a>",
            "url": "https://thereader.mitpress.mit.edu/the-rich-history-of-ham-radio-culture/",
            "title": "The Rich History of Ham Radio Culture",
            "date_modified": "2024-07-24T19:05:11.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/qqyfjh/enhancing_your_elixir_codebase_with\">Comments</a></p>",
            "url": "https://blog.appsignal.com/2024/07/23/enhancing-your-elixir-codebase-with-gleam.html",
            "title": "Enhancing Your Elixir Codebase with Gleam",
            "date_modified": "2024-07-23T23:27:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=41023138\">Comments</a>",
            "url": "https://syscall.sh/",
            "title": "Syscall.sh",
            "date_modified": "2024-07-21T07:13:39.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/lyqe4o/google_now_defaults_not_indexing_your\">Comments</a></p>",
            "url": "https://www.vincentschmalbach.com/google-now-defaults-to-not-indexing-your-content/",
            "title": "Google Now Defaults to Not Indexing Your Content",
            "date_modified": "2024-07-17T20:04:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40983486\">Comments</a>",
            "url": "https://www.reuters.com/markets/deals/google-backed-software-developer-gitlab-explores-sale-sources-say-2024-07-17/",
            "title": "Gitlab Explores Sale",
            "date_modified": "2024-07-17T07:57:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40983328\">Comments</a>",
            "url": "https://pippy.dev/",
            "title": "Show HN: Pippy – Pipelines for GitHub Actions",
            "date_modified": "2024-07-17T07:17:23.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yf2rwh/introducing_multi_version_schema\">Comments</a></p>",
            "url": "https://xata.io/blog/multi-version-schema-migrations",
            "title": "Introducing multi-version schema migrations for Postgres",
            "date_modified": "2024-07-16T17:01:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40970032\">Comments</a>",
            "url": "https://blog.izissise.net/posts/uki/",
            "title": "Create Unified Kernel Image from Scratch",
            "date_modified": "2024-07-15T17:59:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40966414\">Comments</a>",
            "url": "https://lwn.net/Articles/978738/",
            "title": "Rust for Filesystems",
            "date_modified": "2024-07-15T09:39:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40944712\">Comments</a>",
            "url": "https://www.checklyhq.com/blog/300ms-from-every-pod-startup-with-a-single-grafana-query/",
            "title": "We saved $5k a month with a single Grafana query",
            "date_modified": "2024-07-12T11:57:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40942732\">Comments</a>",
            "url": "https://ochagavia.nl/blog/using-s3-as-a-container-registry/",
            "title": "Using S3 as a Container Registry",
            "date_modified": "2024-07-12T04:26:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40941412\">Comments</a>",
            "url": "https://github.com/aws/aws-secretsmanager-agent",
            "title": "AWS Secrets Manager Agent",
            "date_modified": "2024-07-11T23:09:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40938923\">Comments</a>",
            "url": "https://xata.io/blog/postgres-free-tier",
            "title": "The economics of a Postgres free tier",
            "date_modified": "2024-07-11T17:43:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40938810\">Comments</a>",
            "url": "https://github.com/gojue/ecapture",
            "title": "Capturing Linux SSL/TLS plaintext without a CA certificate using eBPF",
            "date_modified": "2024-07-11T17:31:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40937973\">Comments</a>",
            "url": "https://mydiplomaticlife.com/how-to-survive-3-years-in-north-korea-as-a-foreigner/",
            "title": "How to Survive 3 Years in North Korea as a Foreigner",
            "date_modified": "2024-07-11T15:52:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40934924\">Comments</a>",
            "url": "https://typesetinthefuture.com/2018/12/04/walle/",
            "title": "The Typeset of Wall·E",
            "date_modified": "2024-07-11T09:28:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40931486\">Comments</a>",
            "url": "https://support.stripe.com/questions/july-2024-changes-to-stripe-billing",
            "title": "Changes to Stripe Billing",
            "date_modified": "2024-07-10T21:07:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40916786\">Comments</a>",
            "url": "https://turbopuffer.com/blog/turbopuffer",
            "title": "Turbopuffer: Fast search on object storage",
            "date_modified": "2024-07-09T14:48:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40915970\">Comments</a>",
            "url": "https://gleam.run/news/auto-imports-and-tolerant-expressions/",
            "title": "Gleam v1.3.0 – Auto-imports and tolerant expressions",
            "date_modified": "2024-07-09T13:35:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40911637\">Comments</a>",
            "url": "https://rye.astral.sh/",
            "title": "Rye: A Hassle-Free Python Experience",
            "date_modified": "2024-07-09T01:18:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40909554\">Comments</a>",
            "url": "https://trigger.dev/blog/event-loop-lag",
            "title": "How we tamed Node.js event loop lag: a deepdive",
            "date_modified": "2024-07-08T20:52:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40898844\">Comments</a>",
            "url": "https://wippler.dev/posts/synchronization-is-bad-for-scale",
            "title": "Synchronization Is Bad for Scale",
            "date_modified": "2024-07-07T17:03:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40888501\">Comments</a>",
            "url": "https://thenewstack.io/devops-isnt-dead-but-its-not-in-great-health-either/",
            "title": "DevOps Isn't Dead, but It's Not in Great Health Either",
            "date_modified": "2024-07-06T06:22:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40864489\">Comments</a>",
            "url": "https://code.visualstudio.com/docs/devcontainers/containers",
            "title": "Developing Inside a Container",
            "date_modified": "2024-07-03T10:09:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40862436\">Comments</a>",
            "url": "https://nextword.substack.com/p/why-ai-infrastructure-startups-are",
            "title": "Why AI Infrastructure Startups Are Insanely Hard to Build",
            "date_modified": "2024-07-03T03:15:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40828610\">Comments</a>",
            "url": "https://www.propelauth.com/post/nextjs-challenges",
            "title": "It's not just you, Next.js is getting harder to use",
            "date_modified": "2024-06-29T08:00:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40826989\">Comments</a>",
            "url": "https://logical.li/blog/devops/",
            "title": "DevOps: The Funeral",
            "date_modified": "2024-06-29T00:54:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40826236\">Comments</a>",
            "url": "https://matduggan.com/a-eulogy-for-devops/",
            "title": "A Eulogy for DevOps",
            "date_modified": "2024-06-28T22:59:05.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yh1qls/adding_parallel_evaluation_nix\">Comments</a></p>",
            "url": "https://determinate.systems/posts/parallel-nix-eval",
            "title": "Adding parallel evaluation to Nix",
            "date_modified": "2024-06-27T15:19:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40798740\">Comments</a>",
            "url": "https://kmcd.dev/posts/grpc-the-bad-parts/",
            "title": "gRPC: The Bad Parts",
            "date_modified": "2024-06-26T11:30:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40777275\">Comments</a>",
            "url": "https://multi.app/blog/multi-is-joining-openai",
            "title": "OpenAI Acquires Multi",
            "date_modified": "2024-06-24T15:32:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40751468\">Comments</a>",
            "url": "https://criu.org/Main_Page",
            "title": "CRIU, a project to implement checkpoint/restore functionality for Linux",
            "date_modified": "2024-06-21T16:59:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40741702\">Comments</a>",
            "url": "https://www.cloudflarestatus.com/incidents/p7l6rrbhysck",
            "title": "Cloudflare Connectivity Issues in Eastern US and Central Europe",
            "date_modified": "2024-06-20T18:31:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40709181\">Comments</a>",
            "url": "https://www.dbos.dev/blog/dbos-vs-aws-step-functions-benchmark",
            "title": "Making Serverless Orchestration 25x Faster",
            "date_modified": "2024-06-17T18:24:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40699462\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40699462",
            "title": "8 days downtime: Cloudflare r2 subscription bug ruins my business",
            "date_modified": "2024-06-16T19:30:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40676937\">Comments</a>",
            "url": "https://discourse.nixos.org/t/brew-nix-a-flake-automatically-packaging-all-homebrew-casks/44880",
            "title": "Brew-Nix: a flake automatically packaging all homebrew casks",
            "date_modified": "2024-06-14T02:16:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40659160\">Comments</a>",
            "url": "https://restate.dev/",
            "title": "Show HN: Restate – Low-latency durable workflows for JavaScript/Java, in Rust",
            "date_modified": "2024-06-12T15:25:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40656747\">Comments</a>",
            "url": "https://elixir-lang.org/blog/2024/06/12/elixir-v1-17-0-released/",
            "title": "Elixir 1.17 released: set-theoretic types in patterns, durations, OTP 27",
            "date_modified": "2024-06-12T11:13:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40655635\">Comments</a>",
            "url": "https://londonist.substack.com/p/a-new-map-of-medieval-london",
            "title": "A new map of medieval London",
            "date_modified": "2024-06-12T07:54:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40643167\">Comments</a>",
            "url": "https://christopher.engineering/en/blog/gleam-overview/",
            "title": "Exploring Gleam, a type-safe language on the BEAM",
            "date_modified": "2024-06-11T06:29:49.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/q84ufv/microvm_infrastructure_using_low\">Comments</a></p>",
            "url": "https://codesandbox.io/blog/how-we-scale-our-microvm-infrastructure-using-low-latency-memory-decompression",
            "title": "microVM infrastructure using low-latency memory decompression",
            "date_modified": "2024-06-08T00:09:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40596687\">Comments</a>",
            "url": "https://github.com/ory/kratos/releases/tag/v1.2.0",
            "title": "Show HN: OSS Auth0 Alternative Ory Kratos Now with Full PassKey Support",
            "date_modified": "2024-06-06T12:38:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40590570\">Comments</a>",
            "url": "https://www.dropbox.com/scl/fi/6i1800ml23i6x4wn6jp0q/Enron-Summer.pdf?rlkey=i6eyhsa658w4la0vzkdhfy7uj&e=1&st=2yjulw6q&dl=0",
            "title": "Memories of an Enron Summer",
            "date_modified": "2024-06-05T21:01:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40588508\">Comments</a>",
            "url": "https://pigweed.dev/docs/blog/02-bazel-feature-flags.html",
            "title": "Feature flags in Bazel builds",
            "date_modified": "2024-06-05T18:26:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40586757\">Comments</a>",
            "url": "https://coder.com/blog/delivering-5x-faster-throughput-in-coder-2-12-0",
            "title": "We Improved the Performance of a Userspace TCP Stack in Go by 5X",
            "date_modified": "2024-06-05T16:13:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40586757\">Comments</a>",
            "url": "https://coder.com/blog/delivering-5x-faster-throughput-in-coder-2-12-0",
            "title": "We improved the performance of a userspace TCP stack in Go",
            "date_modified": "2024-06-05T16:13:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40582029\">Comments</a>",
            "url": "https://chrisdown.name/2018/01/02/in-defence-of-swap.html",
            "title": "In defence of swap: common misconceptions",
            "date_modified": "2024-06-05T06:42:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40581545\">Comments</a>",
            "url": "https://github.blog/2024-06-03-arm64-on-github-actions-powering-faster-more-efficient-build-systems/",
            "title": "GitHub now provides Arm-based runners",
            "date_modified": "2024-06-05T05:02:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40581545\">Comments</a>",
            "url": "https://github.blog/2024-06-03-arm64-on-github-actions-powering-faster-more-efficient-build-systems/",
            "title": "Arm64 on GitHub Actions",
            "date_modified": "2024-06-05T05:02:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40573852\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40573852",
            "title": "Ask HN: How to bring traffic to my product",
            "date_modified": "2024-06-04T12:34:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40563984\">Comments</a>",
            "url": "https://brioche.dev/blog/announcing-brioche/",
            "title": "Show HN: Brioche – A new Nix-like package manager",
            "date_modified": "2024-06-03T16:00:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40562342\">Comments</a>",
            "url": "https://duckdb.org/2024/06/03/announcing-duckdb-100.html",
            "title": "DuckDB 1.0.0",
            "date_modified": "2024-06-03T13:18:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40552257\">Comments</a>",
            "url": "https://github.com/andyk/ht",
            "title": "ht: Headless Terminal",
            "date_modified": "2024-06-02T07:53:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40545122\">Comments</a>",
            "url": "https://bbozzay.com/posts/dev/multi-tenant-auth-zenstack",
            "title": "Multi-Tenant Authorization with Zenstack",
            "date_modified": "2024-06-01T12:19:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40532515\">Comments</a>",
            "url": "https://www.lux.camera/orion-from-idea-to-launch-in-45-days/",
            "title": "Orion – From idea to launch in 45 days",
            "date_modified": "2024-05-31T08:03:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40519976\">Comments</a>",
            "url": "https://andrewkelley.me/post/zig-new-cli-progress-bar-explained.html",
            "title": "Zig's new CLI progress bar explained",
            "date_modified": "2024-05-30T04:07:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40517576\">Comments</a>",
            "url": "https://github.com/caddyserver/caddy/releases/tag/v2.8.0",
            "title": "Caddy 2.8",
            "date_modified": "2024-05-29T22:04:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40514282\">Comments</a>",
            "url": "https://www.lux.camera/introducing-kino-pro-video-camera/",
            "title": "Kino: Pro Video Camera",
            "date_modified": "2024-05-29T17:06:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40510627\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40510627",
            "title": "Ask HN: Can a website kill my internet connection? (WebRTC)",
            "date_modified": "2024-05-29T11:09:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40509824\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=8zj7ei5Egk8",
            "title": "Cloudflare has become a pig butchering scam but legal [video]",
            "date_modified": "2024-05-29T08:45:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40502201\">Comments</a>",
            "url": "https://cedardb.com/blog/ode_to_postgres/",
            "title": "CedarDB: German-Powered, PostgreSQL-Compatiable Freak of Nature Database System",
            "date_modified": "2024-05-28T16:07:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40491377\">Comments</a>",
            "url": "https://www.ubicloud.com/blog/how-we-enabled-arm64-vms",
            "title": "How we enabled ARM64 VMs",
            "date_modified": "2024-05-27T15:02:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40477166\">Comments</a>",
            "url": "https://www.cmtops.dev/posts/rootless-docker-in-multiuser-environment/",
            "title": "Rootless Docker in a multi-user environment",
            "date_modified": "2024-05-25T19:11:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40456959\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40456959",
            "title": "Show HN: Porter Cloud – PaaS with an eject button",
            "date_modified": "2024-05-23T16:47:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40455208\">Comments</a>",
            "url": "https://depot.dev/blog/faster-ec2-boot-time",
            "title": "Making EC2 boot time faster",
            "date_modified": "2024-05-23T14:31:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40450128\">Comments</a>",
            "url": "https://magicui.design/",
            "title": "Magic UI: UI Library for Design Engineers",
            "date_modified": "2024-05-23T03:23:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40444267\">Comments</a>",
            "url": "https://materializedview.io/p/s3-is-showing-its-age",
            "title": "S3 is showing its age",
            "date_modified": "2024-05-22T18:23:17.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/pkmqif/stripe_s_monorepo_developer_environment\">Comments</a></p>",
            "url": "https://blog.nelhage.com/post/stripe-dev-environment/",
            "title": "Stripe's monorepo developer environment",
            "date_modified": "2024-05-21T19:42:33.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/balg4y/state_sandboxing_linux\">Comments</a></p>",
            "url": "https://git.sr.ht/~alip/syd/tree/main/item/doc/toctou-or-gtfo.md",
            "title": "State of Sandboxing in Linux",
            "date_modified": "2024-05-21T16:16:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40430381\">Comments</a>",
            "url": "https://www.backblaze.com/blog/how-backblaze-scales-our-storage-cloud/",
            "title": "Backblaze Scales Storage Cloud",
            "date_modified": "2024-05-21T16:14:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40418885\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40418885",
            "title": "Ask HN: Why do you all think that Htmx is such a recent development?",
            "date_modified": "2024-05-20T19:00:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40408515\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40408515",
            "title": "Ask HN: Video streaming is expensive yet YouTube \"seems\" to do it for free. How?",
            "date_modified": "2024-05-19T17:51:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40376857\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40376857",
            "title": "Ask HN: SaaS Subscription or Usage-Based Pricing?",
            "date_modified": "2024-05-16T10:35:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40369467\">Comments</a>",
            "url": "https://jepsen.io/analyses/datomic-pro-1.0.7075",
            "title": "Jepsen: Datomic Pro 1.0.7075",
            "date_modified": "2024-05-15T16:57:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40368729\">Comments</a>",
            "url": "https://www.threads.net/@mikeyk/post/C6_feeSr0Q4",
            "title": "Mike Krieger: I've joined AnthropicAI as their Chief Product Officer",
            "date_modified": "2024-05-15T16:03:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40367090\">Comments</a>",
            "url": "https://github.com/quarylabs/quary",
            "title": "Show HN: Open-source BI and analytics for engineers",
            "date_modified": "2024-05-15T14:02:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40358588\">Comments</a>",
            "url": "https://blog.jak-linux.org/2024/05/14/solver3/",
            "title": "The new APT 3.0 solver",
            "date_modified": "2024-05-14T18:40:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40355875\">Comments</a>",
            "url": "https://www.highlight.io/blog/lw5-clickhouse-performance-optimization",
            "title": "Optimizing ClickHouse: Tactics that worked for us",
            "date_modified": "2024-05-14T14:57:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40353963\">Comments</a>",
            "url": "https://dl.acm.org/doi/10.1145/3132747.3132763",
            "title": "My VM is lighter (and safer) than your container",
            "date_modified": "2024-05-14T11:24:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40352656\">Comments</a>",
            "url": "https://marc.info/?l=openbsd-tech&m=171562561424289",
            "title": "Add sysctl to disable Nagle's algorithm (RFC 896 – Congestion Control)",
            "date_modified": "2024-05-14T07:51:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40352426\">Comments</a>",
            "url": "https://qmacro.org/blog/posts/2024/05/13/using-arg-in-a-dockerfile-beware-the-gotcha/",
            "title": "Using ARG in a Dockerfile – beware the gotcha",
            "date_modified": "2024-05-14T07:05:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40326815\">Comments</a>",
            "url": "https://www.cultured.systems/2024/04/24/Soft-delete/",
            "title": "Avoiding the soft delete anti-pattern",
            "date_modified": "2024-05-11T08:26:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40326398\">Comments</a>",
            "url": "https://www.flatcar.org/blog/2024/04/os-innovation-with-systemd-sysext/",
            "title": "Flatcar: OS Innovation with Systemd-Sysext",
            "date_modified": "2024-05-11T06:27:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40318542\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40318542",
            "title": "Show HN: A web debugger an ex-Cloudflare team has been working on for 4 years",
            "date_modified": "2024-05-10T13:08:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40312895\">Comments</a>",
            "url": "https://buf.build/blog/protobuf-editions-are-here",
            "title": "Protobuf Editions are here: don't panic",
            "date_modified": "2024-05-09T20:58:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40310896\">Comments</a>",
            "url": "https://brooker.co.za/blog/2024/05/09/nagle.html",
            "title": "It's always TCP_NODELAY",
            "date_modified": "2024-05-09T17:54:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40309719\">Comments</a>",
            "url": "https://www.ellipsis.dev/",
            "title": "Show HN: Ellipsis – Automated PR reviews and bug fixes",
            "date_modified": "2024-05-09T16:14:47.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/c3hbh6/it_s_not_about_flakes_vs_channels\">Comments</a></p>",
            "url": "https://samuel.dionne-riel.com/blog/2024/05/07/its-not-flakes-vs-channels.html",
            "title": "It's not about “Flakes vs. Channels”",
            "date_modified": "2024-05-08T23:20:34.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/imyxxn/modern_sqlite_generated_columns\">Comments</a></p>",
            "url": "https://antonz.org/sqlite-generated-columns/",
            "title": "Modern SQLite: Generated columns",
            "date_modified": "2024-05-08T13:53:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40297927\">Comments</a>",
            "url": "https://encore.dev/blog/encore-for-typescript",
            "title": "Encore: Distributed systems runtime for TypeScript, written in Rust",
            "date_modified": "2024-05-08T13:32:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/4b528k/inclusive_sans_text_font_designed_for\">Comments</a></p>",
            "url": "https://www.oliviaking.com/inclusive-sans",
            "title": "Inclusive Sans, a text font designed for accessibility and readability",
            "date_modified": "2024-05-08T12:54:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40294957\">Comments</a>",
            "url": "https://fly.io/infra-log/",
            "title": "Fly.io Infra log: week-by-week record of what the team does",
            "date_modified": "2024-05-08T06:27:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40276768\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40276768",
            "title": "Show HN: Peerdb Streams – Simple, native Postgres change data capture",
            "date_modified": "2024-05-06T17:00:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40274662\">Comments</a>",
            "url": "https://keygen.sh/blog/your-14-day-free-trial-aint-gonna-cut-it/",
            "title": "A fourteen-day free trial ain’t gonna cut it",
            "date_modified": "2024-05-06T13:50:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40271242\">Comments</a>",
            "url": "https://lix.systems/",
            "title": "Lix is a modern, delicious implementation of the Nix package manager",
            "date_modified": "2024-05-06T04:55:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40264042\">Comments</a>",
            "url": "https://j6b72.de/article/why-you-should-take-a-look-at-traefik/",
            "title": "Take a look at Traefik, even if you don't use containers",
            "date_modified": "2024-05-05T11:31:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40262680\">Comments</a>",
            "url": "https://depot.dev/blog/building-container-layers-from-scratch",
            "title": "Building Containers from Scratch: Layers",
            "date_modified": "2024-05-05T06:20:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40259100\">Comments</a>",
            "url": "https://chatgpt.com",
            "title": "OpenAI Bought Chatgpt.com",
            "date_modified": "2024-05-04T17:36:21.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/gdxcsk/deno_kv_internals_building_database_for\">Comments</a></p>",
            "url": "https://deno.com/blog/building-deno-kv",
            "title": "Deno KV internals: building a database for the modern web",
            "date_modified": "2024-05-04T17:01:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40253079\">Comments</a>",
            "url": "https://old.reddit.com/r/wallstreetbets/comments/1cirikp/snowflake_isnt_worth_50_billion_dollars/",
            "title": "Snowflake's tech is not worth $50B dollars",
            "date_modified": "2024-05-03T22:30:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40231790\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40231790",
            "title": "Ask HN: How do people create those sleek looking demos for startups?",
            "date_modified": "2024-05-02T01:31:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40227208\">Comments</a>",
            "url": "https://www.terminal.shop/",
            "title": "New startup sells coffee through SSH",
            "date_modified": "2024-05-01T18:26:33.000Z"
        },
        {
            "content_html": "<p>The Kubernetes <a href=\"https://kubernetes.io/docs/concepts/architecture/cri/\">Container Runtime Interface (CRI)</a>\nacts as the main connection between the <a href=\"https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/\">kubelet</a>\nand the <a href=\"https://kubernetes.io/docs/setup/production-environment/container-runtimes/\">Container Runtime</a>.\nThose runtimes have to provide a <a href=\"https://grpc.io\">gRPC</a> server which has to\nfulfill a Kubernetes defined <a href=\"https://protobuf.dev\">Protocol Buffer</a> interface.\n<a href=\"https://github.com/kubernetes/cri-api/blob/63929b3/pkg/apis/runtime/v1/api.proto\">This API definition</a>\nevolves over time, for example when contributors add new features or fields are\ngoing to become deprecated.</p>\n<p>In this blog post, I'd like to dive into the functionality and history of three\nextraordinary Remote Procedure Calls (RPCs), which are truly outstanding in\nterms of how they work: <code>Exec</code>, <code>Attach</code> and <code>PortForward</code>.</p>\n<p><strong>Exec</strong> can be used to run dedicated commands within the container and stream\nthe output to a client like <a href=\"https://kubernetes.io/docs/reference/kubectl/\">kubectl</a> or\n<a href=\"https://kubernetes.io/docs/tasks/debug/debug-cluster/crictl/\">crictl</a>. It also allows interaction with\nthat process using standard input (stdin), for example if users want to run a\nnew shell instance within an existing workload.</p>\n<p><strong>Attach</strong> streams the output of the currently running process via <a href=\"https://en.wikipedia.org/wiki/Standard_streams\">standard I/O</a>\nfrom the container to the client and also allows interaction with them. This is\nparticularly useful if users want to see what is going on in the container and\nbe able to interact with the process.</p>\n<p><strong>PortForward</strong> can be utilized to forward a port from the host to the container\nto be able to interact with it using third party network tools. This allows it\nto bypass <a href=\"https://kubernetes.io/docs/concepts/services-networking/service/\">Kubernetes services</a>\nfor a certain workload and interact with its network interface.</p>\n<h2 id=\"what-is-so-special-about-them\">What is so special about them?</h2>\n<p>All RPCs of the CRI either use the <a href=\"https://grpc.io/docs/what-is-grpc/core-concepts/#unary-rpc\">gRPC unary calls</a>\nfor communication or the <a href=\"https://grpc.io/docs/what-is-grpc/core-concepts/#server-streaming-rpc\">server side streaming</a>\nfeature (only <code>GetContainerEvents</code> right now). This means that mainly all RPCs\nretrieve a single client request and have to return a single server response.\nThe same applies to <code>Exec</code>, <code>Attach</code>, and <code>PortForward</code>, where their <a href=\"https://github.com/kubernetes/cri-api/blob/63929b3/pkg/apis/runtime/v1/api.proto#L94-L99\">protocol definition</a>\nlooks like this:</p>\n<div><pre tabindex=\"0\"><code data-lang=\"protobuf\"><span><span><span>// Exec prepares a streaming endpoint to execute a command in the container.\n</span></span></span><span><span><span></span><span>rpc</span> Exec(ExecRequest) <span>returns</span> (ExecResponse) {}<span>\n</span></span></span></code></pre></div><div><pre tabindex=\"0\"><code data-lang=\"protobuf\"><span><span><span>// Attach prepares a streaming endpoint to attach to a running container.\n</span></span></span><span><span><span></span><span>rpc</span> Attach(AttachRequest) <span>returns</span> (AttachResponse) {}<span>\n</span></span></span></code></pre></div><div><pre tabindex=\"0\"><code data-lang=\"protobuf\"><span><span><span>// PortForward prepares a streaming endpoint to forward ports from a PodSandbox.\n</span></span></span><span><span><span></span><span>rpc</span> PortForward(PortForwardRequest) <span>returns</span> (PortForwardResponse) {}<span>\n</span></span></span></code></pre></div><p>The requests carry everything required to allow the server to do the work,\nfor example, the <code>ContainerId</code> or command (<code>Cmd</code>) to be run in case of <code>Exec</code>.\nMore interestingly, all of their responses only contain a <code>url</code>:</p>\n<div><pre tabindex=\"0\"><code data-lang=\"protobuf\"><span><span><span>message</span> <span>ExecResponse</span> {<span>\n</span></span></span><span><span><span></span> <span>// Fully qualified URL of the exec streaming server.\n</span></span></span><span><span><span></span> <span>string</span> url <span>=</span> <span>1</span>;<span>\n</span></span></span><span><span><span></span>}<span>\n</span></span></span></code></pre></div><div><pre tabindex=\"0\"><code data-lang=\"protobuf\"><span><span><span>message</span> <span>AttachResponse</span> {<span>\n</span></span></span><span><span><span></span> <span>// Fully qualified URL of the attach streaming server.\n</span></span></span><span><span><span></span> <span>string</span> url <span>=</span> <span>1</span>;<span>\n</span></span></span><span><span><span></span>}<span>\n</span></span></span></code></pre></div><div><pre tabindex=\"0\"><code data-lang=\"protobuf\"><span><span><span>message</span> <span>PortForwardResponse</span> {<span>\n</span></span></span><span><span><span></span> <span>// Fully qualified URL of the port-forward streaming server.\n</span></span></span><span><span><span></span> <span>string</span> url <span>=</span> <span>1</span>;<span>\n</span></span></span><span><span><span></span>}<span>\n</span></span></span></code></pre></div><p>Why is it implemented like that? Well, <a href=\"https://docs.google.com/document/d/1MreuHzNvkBW6q7o_zehm1CBOBof3shbtMTGtUpjpRmY\">the original design document</a>\nfor those RPCs even predates <a href=\"https://github.com/kubernetes/enhancements\">Kubernetes Enhancements Proposals (KEPs)</a>\nand was originally outlined back in 2016. The kubelet had a native\nimplementation for <code>Exec</code>, <code>Attach</code>, and <code>PortForward</code> before the\ninitiative to bring the functionality to the CRI started. Before that,\neverything was bound to <a href=\"https://www.docker.com\">Docker</a> or the later abandoned\ncontainer runtime <a href=\"https://github.com/rkt/rkt\">rkt</a>.</p>\n<p>The CRI related design document also elaborates on the option to use native RPC\nstreaming for exec, attach, and port forward. The downsides outweighed this\napproach: the kubelet would still create a network bottleneck and future\nruntimes would not be free in choosing the server implementation details. Also,\nanother option that the Kubelet implements a portable, runtime-agnostic solution\nhas been abandoned over the final one, because this would mean another project\nto maintain which nevertheless would be runtime dependent.</p>\n<p>This means, that the basic flow for <code>Exec</code>, <code>Attach</code> and <code>PortForward</code>\nwas proposed to look like this:</p>\n<figure>\n<img src=\"https://kubernetes.io/blog/2024/05/01/cri-streaming-explained/flow.svg\" alt=\"CRI Streaming flow\">\n</figure>\n<p>Clients like crictl or the kubelet (via kubectl) request a new exec, attach or\nport forward session from the runtime using the gRPC interface. The runtime\nimplements a streaming server that also manages the active sessions. This\nstreaming server provides an HTTP endpoint for the client to connect to. The\nclient upgrades the connection to use the <a href=\"https://en.wikipedia.org/wiki/SPDY\">SPDY</a>\nstreaming protocol or (in the future) to a <a href=\"https://en.wikipedia.org/wiki/WebSocket\">WebSocket</a>\nconnection and starts to stream the data back and forth.</p>\n<p>This implementation allows runtimes to have the flexibility to implement\n<code>Exec</code>, <code>Attach</code> and <code>PortForward</code> the way they want, and also allows a\nsimple test path. Runtimes can change the underlying implementation to support\nany kind of feature without having a need to modify the CRI at all.</p>\n<p>Many smaller enhancements to this overall approach have been merged into\nKubernetes in the past years, but the general pattern has always stayed the\nsame. The kubelet source code transformed into <a href=\"https://github.com/kubernetes/kubernetes/blob/db9fcfe/staging/src/k8s.io/kubelet/pkg/cri/streaming\">a reusable library</a>,\nwhich is nowadays usable from container runtimes to implement the basic\nstreaming capability.</p>\n<h2 id=\"how-does-the-streaming-actually-work\">How does the streaming actually work?</h2>\n<p>At a first glance, it looks like all three RPCs work the same way, but that's\nnot the case. It's possible to group the functionality of <strong>Exec</strong> and\n<strong>Attach</strong>, while <strong>PortForward</strong> follows a distinct internal protocol\ndefinition.</p>\n<h3 id=\"exec-and-attach\">Exec and Attach</h3>\n<p>Kubernetes defines <strong>Exec</strong> and <strong>Attach</strong> as <em>remote commands</em>, where its\nprotocol definition exists in <a href=\"https://github.com/kubernetes/kubernetes/blob/9791f0d/staging/src/k8s.io/apimachinery/pkg/util/remotecommand/constants.go#L28-L52\">five different versions</a>:</p>\n<table>\n<thead>\n<tr>\n<th>#</th>\n<th>Version</th>\n<th>Note</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td><code>channel.k8s.io</code></td>\n<td>Initial (unversioned) SPDY sub protocol (<a href=\"https://issues.k8s.io/13394\">#13394</a>, <a href=\"https://issues.k8s.io/13395\">#13395</a>)</td>\n</tr>\n<tr>\n<td>2</td>\n<td><code>v2.channel.k8s.io</code></td>\n<td>Resolves the issues present in the first version (<a href=\"https://github.com/kubernetes/kubernetes/pull/15961\">#15961</a>)</td>\n</tr>\n<tr>\n<td>3</td>\n<td><code>v3.channel.k8s.io</code></td>\n<td>Adds support for resizing container terminals (<a href=\"https://github.com/kubernetes/kubernetes/pull/25273\">#25273</a>)</td>\n</tr>\n<tr>\n<td>4</td>\n<td><code>v4.channel.k8s.io</code></td>\n<td>Adds support for exit codes using JSON errors (<a href=\"https://github.com/kubernetes/kubernetes/pull/26541\">#26541</a>)</td>\n</tr>\n<tr>\n<td>5</td>\n<td><code>v5.channel.k8s.io</code></td>\n<td>Adds support for a CLOSE signal (<a href=\"https://github.com/kubernetes/kubernetes/pull/119157\">#119157</a>)</td>\n</tr>\n</tbody>\n</table>\n<p>On top of that, there is an overall effort to replace the SPDY transport\nprotocol using WebSockets as part <a href=\"https://github.com/kubernetes/enhancements/issues/4006\">KEP #4006</a>.\nRuntimes have to satisfy those protocols over their life cycle to stay up to\ndate with the Kubernetes implementation.</p>\n<p>Let's assume that a client uses the latest (<code>v5</code>) version of the protocol as\nwell as communicating over WebSockets. In that case, the general flow would be:</p>\n<ol>\n<li>\n<p>The client requests an URL endpoint for <strong>Exec</strong> or <strong>Attach</strong> using the CRI.</p>\n<ul>\n<li>The server (runtime) validates the request, inserts it into a connection\ntracking cache, and provides the HTTP endpoint URL for that request.</li>\n</ul>\n</li>\n<li>\n<p>The client connects to that URL, upgrades the connection to establish\na WebSocket, and starts to stream data.</p>\n<ul>\n<li>In the case of <strong>Attach</strong>, the server has to stream the main container process\ndata to the client.</li>\n<li>In the case of <strong>Exec</strong>, the server has to create the subprocess command within\nthe container and then streams the output to the client.</li>\n</ul>\n<p>If stdin is required, then the server needs to listen for that as well and\nredirect it to the corresponding process.</p>\n</li>\n</ol>\n<p>Interpreting data for the defined protocol is fairly simple: The first\nbyte of every input and output packet <a href=\"https://github.com/kubernetes/kubernetes/blob/9791f0d/staging/src/k8s.io/apimachinery/pkg/util/remotecommand/constants.go#L57-L64\">defines</a>\nthe actual stream:</p>\n<table>\n<thead>\n<tr>\n<th>First Byte</th>\n<th>Type</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>0</code></td>\n<td>standard input</td>\n<td>Data streamed from stdin</td>\n</tr>\n<tr>\n<td><code>1</code></td>\n<td>standard output</td>\n<td>Data streamed to stdout</td>\n</tr>\n<tr>\n<td><code>2</code></td>\n<td>standard error</td>\n<td>Data streamed to stderr</td>\n</tr>\n<tr>\n<td><code>3</code></td>\n<td>stream error</td>\n<td>A streaming error occurred</td>\n</tr>\n<tr>\n<td><code>4</code></td>\n<td>stream resize</td>\n<td>A terminal resize event</td>\n</tr>\n<tr>\n<td><code>255</code></td>\n<td>stream close</td>\n<td>Stream should be closed (for WebSockets)</td>\n</tr>\n</tbody>\n</table>\n<p>How should runtimes now implement the streaming server methods for <strong>Exec</strong> and\n<strong>Attach</strong> by using the provided kubelet library? The key is that the streaming\nserver implementation in the kubelet <a href=\"https://github.com/kubernetes/kubernetes/blob/db9fcfe/staging/src/k8s.io/kubelet/pkg/cri/streaming/server.go#L63-L68\">outlines an interface</a>\ncalled <code>Runtime</code> which has to be fulfilled by the actual container runtime if it\nwants to use that library:</p>\n<div><pre tabindex=\"0\"><code data-lang=\"go\"><span><span><span>// Runtime is the interface to execute the commands and provide the streams.\n</span></span></span><span><span><span></span><span>type</span> Runtime <span>interface</span> {\n</span></span><span><span> <span>Exec</span>(ctx context.Context, containerID <span>string</span>, cmd []<span>string</span>, in io.Reader, out, err io.WriteCloser, tty <span>bool</span>, resize <span>&lt;-</span><span>chan</span> remotecommand.TerminalSize) <span>error</span>\n</span></span><span><span> <span>Attach</span>(ctx context.Context, containerID <span>string</span>, in io.Reader, out, err io.WriteCloser, tty <span>bool</span>, resize <span>&lt;-</span><span>chan</span> remotecommand.TerminalSize) <span>error</span>\n</span></span><span><span> <span>PortForward</span>(ctx context.Context, podSandboxID <span>string</span>, port <span>int32</span>, stream io.ReadWriteCloser) <span>error</span>\n</span></span><span><span>}\n</span></span></code></pre></div><p>Everything related to the protocol interpretation is\nalready in place and runtimes only have to implement the actual <code>Exec</code> and\n<code>Attach</code> logic. For example, the container runtime <a href=\"https://github.com/cri-o/cri-o\">CRI-O</a>\ndoes it <a href=\"https://github.com/cri-o/cri-o/blob/2a0867/server/container_exec.go#L27-L46\">like this pseudo code</a>:</p>\n<div><pre tabindex=\"0\"><code data-lang=\"go\"><span><span><span>func</span> (s StreamService) <span>Exec</span>(\n</span></span><span><span> ctx context.Context,\n</span></span><span><span> containerID <span>string</span>,\n</span></span><span><span> cmd []<span>string</span>,\n</span></span><span><span> stdin io.Reader, stdout, stderr io.WriteCloser,\n</span></span><span><span> tty <span>bool</span>,\n</span></span><span><span> resizeChan <span>&lt;-</span><span>chan</span> remotecommand.TerminalSize,\n</span></span><span><span>) <span>error</span> {\n</span></span><span><span> <span>// Retrieve the container by the provided containerID\n</span></span></span><span><span><span></span> <span>// …\n</span></span></span><span><span><span></span>\n</span></span><span><span> <span>// Update the container status and verify that the workload is running\n</span></span></span><span><span><span></span> <span>// …\n</span></span></span><span><span><span></span>\n</span></span><span><span> <span>// Execute the command and stream the data\n</span></span></span><span><span><span></span> <span>return</span> s.runtimeServer.<span>Runtime</span>().<span>ExecContainer</span>(\n</span></span><span><span> s.ctx, c, cmd, stdin, stdout, stderr, tty, resizeChan,\n</span></span><span><span> )\n</span></span><span><span>}\n</span></span></code></pre></div><h3 id=\"portforward\">PortForward</h3>\n<p>Forwarding ports to a container works a bit differently when comparing it to\nstreaming IO data from a workload. The server still has to provide a URL\nendpoint for the client to connect to, but then the container runtime has to\nenter the network namespace of the container, allocate the port as well as\nstream the data back and forth. There is no simple protocol definition available\nlike for <strong>Exec</strong> or <strong>Attach</strong>. This means that the client will stream the\nplain SPDY frames (with or without an additional WebSocket connection) which can\nbe interpreted using libraries like <a href=\"https://github.com/moby/spdystream\">moby/spdystream</a>.</p>\n<p>Luckily, the kubelet library already provides the <code>PortForward</code> interface method\nwhich has to be implemented by the runtime. <a>CRI-O does that</a> by (simplified):</p>\n<div><pre tabindex=\"0\"><code data-lang=\"go\"><span><span><span>func</span> (s StreamService) <span>PortForward</span>(\n</span></span><span><span> ctx context.Context,\n</span></span><span><span> podSandboxID <span>string</span>,\n</span></span><span><span> port <span>int32</span>,\n</span></span><span><span> stream io.ReadWriteCloser,\n</span></span><span><span>) <span>error</span> {\n</span></span><span><span> <span>// Retrieve the pod sandbox by the provided podSandboxID\n</span></span></span><span><span><span></span> sandboxID, err <span>:=</span> s.runtimeServer.<span>PodIDIndex</span>().<span>Get</span>(podSandboxID)\n</span></span><span><span> sb <span>:=</span> s.runtimeServer.<span>GetSandbox</span>(sandboxID)\n</span></span><span><span> <span>// …\n</span></span></span><span><span><span></span>\n</span></span><span><span> <span>// Get the network namespace path on disk for that sandbox\n</span></span></span><span><span><span></span> netNsPath <span>:=</span> sb.<span>NetNsPath</span>()\n</span></span><span><span> <span>// …\n</span></span></span><span><span><span></span>\n</span></span><span><span> <span>// Enter the network namespace and stream the data\n</span></span></span><span><span><span></span> <span>return</span> s.runtimeServer.<span>Runtime</span>().<span>PortForwardContainer</span>(\n</span></span><span><span> ctx, sb.<span>InfraContainer</span>(), netNsPath, port, stream,\n</span></span><span><span> )\n</span></span><span><span>}\n</span></span></code></pre></div><h2 id=\"future-work\">Future work</h2>\n<p>The flexibility Kubernetes provides for the RPCs <code>Exec</code>, <code>Attach</code> and\n<code>PortForward</code> is truly outstanding compared to other methods. Nevertheless,\ncontainer runtimes have to keep up with the latest and greatest implementations\nto support those features in a meaningful way. The general effort to support\nWebSockets is not only a plain Kubernetes thing, it also has to be supported by\ncontainer runtimes as well as clients like <code>crictl</code>.</p>\n<p>For example, <code>crictl</code> v1.30 features a new <code>--transport</code> flag for the\nsubcommands <code>exec</code>, <code>attach</code> and <code>port-forward</code>\n(<a href=\"https://github.com/kubernetes-sigs/cri-tools/pull/1383\">#1383</a>,\n<a href=\"https://github.com/kubernetes-sigs/cri-tools/pull/1385\">#1385</a>)\nto allow choosing between <code>websocket</code> and <code>spdy</code>.</p>\n<p>CRI-O is going an experimental path by moving the streaming server\nimplementation into <a href=\"https://github.com/containers/conmon-rs\">conmon-rs</a>\n(a substitute for the container monitor <a href=\"https://github.com/containers/conmon\">conmon</a>). conmon-rs is\na <a href=\"https://www.rust-lang.org\">Rust</a> implementation of the original container\nmonitor and allows streaming WebSockets directly using supported libraries\n(<a href=\"https://github.com/containers/conmon-rs/pull/2070\">#2070</a>). The major benefit\nof this approach is that CRI-O does not even have to be running while conmon-rs\ncan keep active <strong>Exec</strong>, <strong>Attach</strong> and <strong>PortForward</strong> sessions open. The\nsimplified flow when using crictl directly will then look like this:</p>\n<figure>\n<div>\nsequenceDiagram\nautonumber\nparticipant crictl\nparticipant runtime as Container Runtime\nparticipant conmon-rs\nNote over crictl,runtime: Container Runtime Interface (CRI)\ncrictl-&gt;&gt;runtime: Exec, Attach, PortForward\nNote over runtime,conmon-rs: Cap’n Proto\nruntime-&gt;&gt;conmon-rs: Serve Exec, Attach, PortForward\nconmon-rs-&gt;&gt;runtime: HTTP endpoint (URL)\nruntime-&gt;&gt;crictl: Response URL\ncrictl--&gt;&gt;conmon-rs: Connection upgrade to WebSocket\nconmon-rs-)crictl: Stream data\n</div>\n</figure>\n\n<p>All of those enhancements require iterative design decisions, while the original\nwell-conceived implementation acts as the foundation for those. I really hope\nyou've enjoyed this compact journey through the history of CRI RPCs. Feel free\nto reach out to me anytime for suggestions or feedback using the\n<a href=\"https://kubernetes.slack.com/team/U53SUDBD4\">official Kubernetes Slack</a>.</p>",
            "url": "https://kubernetes.io/blog/2024/05/01/cri-streaming-explained/",
            "title": "Container Runtime Interface streaming explained",
            "date_modified": "2024-05-01T00:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40211891\">Comments</a>",
            "url": "https://github.com/borgo-lang/borgo",
            "title": "Borgo is a statically typed language that compiles to Go",
            "date_modified": "2024-04-30T15:13:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40206063\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=ddTV12hErTc",
            "title": "Rabbit R1: Barely Reviewable [video]",
            "date_modified": "2024-04-30T00:44:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40203126\">Comments</a>",
            "url": "https://medium.com/@maciej.pocwierz/how-an-empty-s3-bucket-can-make-your-aws-bill-explode-934a383cb8b1",
            "title": "How an empty S3 bucket can make your AWS bill explode",
            "date_modified": "2024-04-29T19:42:23.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/qnmzre/aux_computer_alternative_nix_ecosystem\">Comments</a></p>",
            "url": "https://aux.computer/",
            "title": "aux.computer: an alternative to the Nix ecosystem",
            "date_modified": "2024-04-29T19:20:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40200081\">Comments</a>",
            "url": "https://github.blog/2024-04-29-github-copilot-workspace/",
            "title": "GitHub Copilot Workspace: Copilot-native developer environment",
            "date_modified": "2024-04-29T16:03:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40199118\">Comments</a>",
            "url": "https://www.warpstream.com/blog/tiered-storage-wont-fix-kafka",
            "title": "Tiered storage won't fix Kafka",
            "date_modified": "2024-04-29T14:54:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40188282\">Comments</a>",
            "url": "https://github.com/remorses/docker-phobia",
            "title": "Show HN: Docker-phobia: Analyze Docker image size with a treemap",
            "date_modified": "2024-04-28T13:01:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40186752\">Comments</a>",
            "url": "https://postgres.ai/blog/20220525-common-db-schema-change-mistakes",
            "title": "Common DB schema change mistakes in Postgres",
            "date_modified": "2024-04-28T07:30:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40176908\">Comments</a>",
            "url": "https://www.devicu.com/blog/software-supply-chan-security",
            "title": "Software Supply Chain Security",
            "date_modified": "2024-04-27T03:31:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40171383\">Comments</a>",
            "url": "https://railsdesigner.com/hidden-gems-tailwind/",
            "title": "Gems of Tailwind CSS",
            "date_modified": "2024-04-26T16:36:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40168406\">Comments</a>",
            "url": "https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/optimizing-your-programs-for-arm-platforms",
            "title": "Optimizing your programs for Arm platforms",
            "date_modified": "2024-04-26T12:10:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40163104\">Comments</a>",
            "url": "https://www.osohq.com/post/logic-language-distributed-sql-queries",
            "title": "A Logic Language for Distributed SQL Queries",
            "date_modified": "2024-04-25T21:13:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40146505\">Comments</a>",
            "url": "https://www.stainlessapi.com/blog/announcing-the-stainless-sdk-generator",
            "title": "The Stainless SDK Generator",
            "date_modified": "2024-04-24T16:34:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/olccof/double_edged_sword_docker_balancing\">Comments</a></p>",
            "url": "https://it-notes.dragas.net/2024/04/22/the-doubled-edge-sword-of-docker/",
            "title": "Double Edged Sword of Docker: Balancing Benefits and Risks",
            "date_modified": "2024-04-23T15:19:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40117558\">Comments</a>",
            "url": "https://blacksmith.sh/blog/going-through-yc-winter-24",
            "title": "What it was like going through YC W24",
            "date_modified": "2024-04-22T18:55:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40092773\">Comments</a>",
            "url": "https://community.fly.io/t/some-volumes-were-slow-and-we-figured-out-why/19394",
            "title": "Some Volumes Were Slow and We Figured Out Why",
            "date_modified": "2024-04-19T22:47:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40070138\">Comments</a>",
            "url": "https://github.com/1buran/rHttp",
            "title": "RHttp: REPL for HTTP",
            "date_modified": "2024-04-17T21:17:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40062263\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=40062263",
            "title": "Ask HN: What software sparks joy when using?",
            "date_modified": "2024-04-17T09:26:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40060728\">Comments</a>",
            "url": "https://csvbase.com/blog/8",
            "title": "Caching secrets of the HTTP elders, part 1",
            "date_modified": "2024-04-17T05:19:55.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/dk4fcc/effect_3_0\">Comments</a></p>",
            "url": "https://effect.website/blog/effect-3.0",
            "title": "Effect 3.0",
            "date_modified": "2024-04-16T15:44:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40052729\">Comments</a>",
            "url": "https://www.osohq.com/post/distributed-authorization",
            "title": "Launching Distributed Authorization",
            "date_modified": "2024-04-16T14:51:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40052729\">Comments</a>",
            "url": "https://www.osohq.com/post/distributed-authorization",
            "title": "Distributed Authorization",
            "date_modified": "2024-04-16T14:51:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40051818\">Comments</a>",
            "url": "https://pmf.firstround.com/levels",
            "title": "Product-Market Fit Isn't a Black Box – A New Framework to Help B2B Founders",
            "date_modified": "2024-04-16T13:48:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40049670\">Comments</a>",
            "url": "https://ssg.dev/ipv6-for-the-remotely-interested-af214dd06aa7?gi=79f1ddf22e27",
            "title": "IPv6 for the Remotely Interested",
            "date_modified": "2024-04-16T08:39:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40033497\">Comments</a>",
            "url": "https://www.ralfj.de/blog/2024/04/14/bubblebox.html",
            "title": "Sandboxing All the Things with Flatpak and BubbleBox",
            "date_modified": "2024-04-14T19:04:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40026064\">Comments</a>",
            "url": "https://www.theguardian.com/world/live/2024/apr/13/iran-launches-drone-attack-against-israel",
            "title": "Iran launches drone attack against Israel",
            "date_modified": "2024-04-13T20:59:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40020601\">Comments</a>",
            "url": "https://www.sequoiacap.com/article/pmf-framework/",
            "title": "The Arc Product-Market Fit Framework",
            "date_modified": "2024-04-13T04:55:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40015185\">Comments</a>",
            "url": "https://kenkantzer.com/lessons-after-a-half-billion-gpt-tokens/",
            "title": "Lessons after a Half-billion GPT Tokens",
            "date_modified": "2024-04-12T17:06:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40010606\">Comments</a>",
            "url": "https://blog.allegro.tech/2024/04/ten-years-microservices.html",
            "title": "Ten Years and Counting: My Affair with Microservices",
            "date_modified": "2024-04-12T08:52:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=40008755\">Comments</a>",
            "url": "https://github.com/mhx/dwarfs",
            "title": "DwarFS – Deduplicating Warp-Speed Advanced Read-Only File System",
            "date_modified": "2024-04-12T02:04:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39981122\">Comments</a>",
            "url": "https://stiankri.substack.com/p/the-open-secret-about-confidential",
            "title": "The Open Secret about Confidential Computing",
            "date_modified": "2024-04-09T16:30:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39978577\">Comments</a>",
            "url": "https://cloud.google.com/blog/products/compute/introducing-googles-new-arm-based-cpu/",
            "title": "Google Axion Processors – Arm-based CPUs designed for the data center",
            "date_modified": "2024-04-09T12:12:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39967199\">Comments</a>",
            "url": "https://tembo.io/blog/managed-postgres-rust",
            "title": "Building a Managed Postgres Service in Rust",
            "date_modified": "2024-04-08T07:40:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39965028\">Comments</a>",
            "url": "https://neon.tech/blog/architecture-decisions-in-neon",
            "title": "Architecture decisions in Neon, a serverless Postgres service",
            "date_modified": "2024-04-08T00:04:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39961994\">Comments</a>",
            "url": "https://github.com/rpgp/rpgp",
            "title": "Rpgp: Pure Rust implementation of OpenPGP",
            "date_modified": "2024-04-07T16:50:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39960537\">Comments</a>",
            "url": "https://github.com/stackframe-projects/pgmock",
            "title": "Show HN: I open-sourced the in-memory PostgreSQL I built at work for E2E tests",
            "date_modified": "2024-04-07T13:13:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39957731\">Comments</a>",
            "url": "https://github.com/tazjin/nix-1p/blob/master/README.md",
            "title": "Nix – A One Pager",
            "date_modified": "2024-04-07T02:44:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39953842\">Comments</a>",
            "url": "https://breadchris.com/blog/my-favorite-button-on-the-internet/",
            "title": "My favorite button on the internet",
            "date_modified": "2024-04-06T17:19:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39952979\">Comments</a>",
            "url": "https://twitter.com/OpenTofuOrg/status/1776398008558493991",
            "title": "HashiCorp: OpenTofu was not respecting the terms of its BSL license",
            "date_modified": "2024-04-06T15:06:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39940707\">Comments</a>",
            "url": "https://lukeshaughnessy.medium.com/infrastructure-as-code-is-not-the-answer-cfaf4882dcba",
            "title": "Infrastructure as Code Is Not the Answer – By Luke Shaughnessy",
            "date_modified": "2024-04-05T10:16:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39940057\">Comments</a>",
            "url": "https://www.datanami.com/2024/04/01/opentelemetry-is-too-complicated-victoriametrics-says/",
            "title": "OpenTelemetry Is Too Complicated, VictoriaMetrics Says",
            "date_modified": "2024-04-05T08:40:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39939477\">Comments</a>",
            "url": "https://www.bytebase.com/blog/features-i-wish-postgres-had/",
            "title": "Features I wish PostgreSQL had as a developer",
            "date_modified": "2024-04-05T07:06:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39939036\">Comments</a>",
            "url": "https://leontrolski.github.io/pglockpy.html",
            "title": "Postgres locks explorer",
            "date_modified": "2024-04-05T05:45:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39932906\">Comments</a>",
            "url": "https://vercel.com/blog/improved-infrastructure-pricing",
            "title": "Vercel: Improved Infrastructure Pricing",
            "date_modified": "2024-04-04T16:52:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39932556\">Comments</a>",
            "url": "https://twitter.com/kelseyhightower/status/1775913633064894669",
            "title": "Developers, what marketing strategies work on you?",
            "date_modified": "2024-04-04T16:26:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39930908\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=39930908",
            "title": "Show HN: Managed GitHub Actions Runners for AWS",
            "date_modified": "2024-04-04T14:32:20.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/lawrhx/how_we_built_19_pib_logging_platform_with\">Comments</a></p>",
            "url": "https://clickhouse.com/blog/building-a-logging-platform-with-clickhouse-and-saving-millions-over-datadog",
            "title": "How we Built a 19 PiB Logging Platform with ClickHouse and Saved Millions",
            "date_modified": "2024-04-03T14:41:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39914047\">Comments</a>",
            "url": "https://github.com/0verread/goralim",
            "title": "Show HN: Goralim - a rate limiting pkg for Go to handle distributed workloads",
            "date_modified": "2024-04-03T05:52:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/baqaf9/data_flow_analysis_for_go\">Comments</a></p>",
            "url": "https://blog.jetbrains.com/go/2024/03/26/data-flow-analysis-for-go/",
            "title": "Data Flow Analysis for Go",
            "date_modified": "2024-04-02T18:29:40.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/jpoy4q/nixos_is_not_reproducible\">Comments</a></p>",
            "url": "https://linderud.dev/blog/nixos-is-not-reproducible/",
            "title": "NixOS is not reproducible",
            "date_modified": "2024-04-02T17:24:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39905441\">Comments</a>",
            "url": "https://blog.cloudflare.com/python-workers",
            "title": "Python Cloudflare Workers",
            "date_modified": "2024-04-02T13:20:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39903056\">Comments</a>",
            "url": "https://github.com/unikraft/unikraft",
            "title": "KraftCloud",
            "date_modified": "2024-04-02T06:58:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39902949\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=39902949",
            "title": "Unikraft Launches KraftCloud: Never Pay for Idle Again",
            "date_modified": "2024-04-02T06:35:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39876131\">Comments</a>",
            "url": "https://docs.kernel.org/userspace-api/landlock.html",
            "title": "Landlock: Unprivileged Access Control",
            "date_modified": "2024-03-30T16:06:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39875822\">Comments</a>",
            "url": "https://tailscale.com/blog/tls-outage-20240307",
            "title": "About the Tailscale.com outage on March 7, 2024",
            "date_modified": "2024-03-30T15:34:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39874012\">Comments</a>",
            "url": "https://externals.io/message/122811",
            "title": "PHP: In light of xz backdoor, consider removing autogenerated files from release",
            "date_modified": "2024-03-30T11:58:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39872686\">Comments</a>",
            "url": "https://tableplus.com/blog/2024/03/how-we-deal-with-ddos.html",
            "title": "We are under DDoS attack and we do nothing",
            "date_modified": "2024-03-30T07:35:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39871913\">Comments</a>",
            "url": "https://bpfman.io/main/",
            "title": "Bpfman: An eBPF Manager",
            "date_modified": "2024-03-30T04:49:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39868586\">Comments</a>",
            "url": "https://blog.ekern.me/2024/03/28/do-you-really-need-kubernetes.html",
            "title": "Do you really need Kubernetes?",
            "date_modified": "2024-03-29T20:22:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39868504\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/966869/4077c682c91ab5ab/",
            "title": "Radicle: Peer-to-Peer Collaboration with Git",
            "date_modified": "2024-03-29T20:12:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39866325\">Comments</a>",
            "url": "https://github.com/pojntfx/weron",
            "title": "Overlay Networks Based on WebRTC",
            "date_modified": "2024-03-29T17:00:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39866083\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=39866083",
            "title": "Beware Scaling on AWS in Early Days",
            "date_modified": "2024-03-29T16:41:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39837117\">Comments</a>",
            "url": "https://radicle.xyz/guides/protocol",
            "title": "How Radicle Works Under the Hood",
            "date_modified": "2024-03-27T09:47:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39836879\">Comments</a>",
            "url": "https://www.annwan.me/computers/what-why-how-containers/",
            "title": "The What, Why and How of Containers",
            "date_modified": "2024-03-27T09:00:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39831095\">Comments</a>",
            "url": "https://blog.podman.io/2024/03/podman-5-0-has-been-released/",
            "title": "Podman 5.0 has been released",
            "date_modified": "2024-03-26T18:20:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39830740\">Comments</a>",
            "url": "https://saltycrackers.dev/posts/bye-bye-async-false/",
            "title": "`async: false` is the worst",
            "date_modified": "2024-03-26T17:52:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39825686\">Comments</a>",
            "url": "https://goto.buzzsprout.com/1714721/14674925",
            "title": "Elixir's Impact: Shaping the Evolution of Erlang",
            "date_modified": "2024-03-26T09:14:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39823856\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/965631/f3db6d3dde3f83cb/",
            "title": "Nix at Scale",
            "date_modified": "2024-03-26T02:52:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39820432\">Comments</a>",
            "url": "https://nitric.io/blog/terraform-comparison-demo",
            "title": "Speeding up Azure development by not using Terraform",
            "date_modified": "2024-03-25T19:35:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39814877\">Comments</a>",
            "url": "https://github.com/pg-sharding/spqr/discussions/569",
            "title": "SPQR 1.3.0: a production-ready system for horizontal scaling of PostgreSQL",
            "date_modified": "2024-03-25T11:24:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39813588\">Comments</a>",
            "url": "https://divy.work/turbocall.html",
            "title": "Turbocall – Just-in-time compiler for Deno FFI",
            "date_modified": "2024-03-25T07:41:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39806618\">Comments</a>",
            "url": "https://yoyo-code.com/build-time-is-collective-responsibility/",
            "title": "Build time is a collective responsibility",
            "date_modified": "2024-03-24T11:53:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39805529\">Comments</a>",
            "url": "https://play.d2lang.com/",
            "title": "D2 Playground",
            "date_modified": "2024-03-24T06:56:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39804052\">Comments</a>",
            "url": "https://oxide.computer/",
            "title": "Oxide Cloud Computer. No Cables. No Assembly. Just Cloud",
            "date_modified": "2024-03-24T00:20:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39801435\">Comments</a>",
            "url": "https://ntex.rs/",
            "title": "Ntex: Powerful, pragmatic, fast framework for composable networking services",
            "date_modified": "2024-03-23T17:12:46.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/zwemnj/nix_planet\">Comments</a></p>",
            "url": "https://m.youtube.com/watch?v=6iviTZfiLGU",
            "title": "Nix The Planet",
            "date_modified": "2024-03-22T20:14:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39793805\">Comments</a>",
            "url": "https://pack.ac/note/pack",
            "title": "Pack: A New Container Format for Compressed Files",
            "date_modified": "2024-03-22T19:11:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39788909\">Comments</a>",
            "url": "https://blog.allegro.tech/2024/03/kafka-performance-analysis.html",
            "title": "Tackling Tail Latency with eBPF",
            "date_modified": "2024-03-22T09:43:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39775332\">Comments</a>",
            "url": "https://github.com/redis/redis/blob/unstable/LICENSE.txt",
            "title": "Redis License Changed",
            "date_modified": "2024-03-21T06:12:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39762807\">Comments</a>",
            "url": "https://benw.is/posts/how-i-improved-my-rust-compile-times-by-seventy-five-percent",
            "title": "I Improved My Rust Compile Times",
            "date_modified": "2024-03-20T04:14:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/1fthjp/build_system_schism_curse_meta_build\">Comments</a></p>",
            "url": "https://yzena.com/2024/03/build-system-schism-the-curse-of-meta-build-systems/",
            "title": "Build System Schism: The Curse Of Meta Build Systems",
            "date_modified": "2024-03-20T01:12:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39759312\">Comments</a>",
            "url": "https://www.stardewvalley.net/stardew-valley-1-6-update-full-changelog/",
            "title": "Stardew Valley 1.6 Changelog",
            "date_modified": "2024-03-19T19:21:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39758712\">Comments</a>",
            "url": "https://github.com/failsafe-go/failsafe-go",
            "title": "Fault tolerance and resilience patterns for Go",
            "date_modified": "2024-03-19T18:19:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39755858\">Comments</a>",
            "url": "https://causal.app",
            "title": "Show HN: Causal 2.0 – Modern Financial Planning for Startups",
            "date_modified": "2024-03-19T14:06:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39750061\">Comments</a>",
            "url": "https://rysana.com/inversion",
            "title": "Inversion: Fast, Reliable Structured LLMs",
            "date_modified": "2024-03-18T21:10:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39748001\">Comments</a>",
            "url": "https://xata.io/blog/serverless-postgres-platform",
            "title": "Xata, a new serverless Postgres platform",
            "date_modified": "2024-03-18T18:24:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39737220\">Comments</a>",
            "url": "https://www.nngroup.com/articles/homepage-design-principles/",
            "title": "Homepage Design: 5 Fundamental Principles",
            "date_modified": "2024-03-17T19:25:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39729307\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=39729307",
            "title": "Ask HN: Books that gave you different perspective on religion",
            "date_modified": "2024-03-16T20:39:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39720812\">Comments</a>",
            "url": "https://blog.hathora.dev/our-bare-metal-journey/",
            "title": "Hathora's Bare Metal Journey",
            "date_modified": "2024-03-15T21:17:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39720007\">Comments</a>",
            "url": "https://xeiaso.net/talks/2024/nix-docker-build/",
            "title": "Nix is a better Docker image builder than Docker's image builder",
            "date_modified": "2024-03-15T19:56:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39714155\">Comments</a>",
            "url": "https://matduggan.com/iam-is-the-worst/",
            "title": "IAM Is the Worst",
            "date_modified": "2024-03-15T10:55:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39712804\">Comments</a>",
            "url": "https://gopacker.dev/docs/",
            "title": "Programs written in Golang have no secrets",
            "date_modified": "2024-03-15T07:07:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39708591\">Comments</a>",
            "url": "https://go.dev/blog/execution-traces-2024",
            "title": "More powerful Go execution traces",
            "date_modified": "2024-03-14T20:30:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39698950\">Comments</a>",
            "url": "https://nanos.org",
            "title": "Nanos – A Unikernel",
            "date_modified": "2024-03-13T23:43:32.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/lphl0x/s3_as_universal_infrastructure_backend\">Comments</a></p>",
            "url": "https://medium.com/innovationendeavors/s3-as-the-universal-infrastructure-backend-a104a8cc6991",
            "title": "S3 as the universal infrastructure backend",
            "date_modified": "2024-03-13T19:46:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39692801\">Comments</a>",
            "url": "https://github.com/flox/flox",
            "title": "Show HN: Flox 1.0 – Open-source dev env as code with Nix",
            "date_modified": "2024-03-13T15:44:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39688545\">Comments</a>",
            "url": "https://fly.io/blog/jit-wireguard-peers/",
            "title": "JIT WireGuard",
            "date_modified": "2024-03-13T06:13:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39656657\">Comments</a>",
            "url": "https://calpaterson.com/s3.html",
            "title": "S3 is files, but not a filesystem",
            "date_modified": "2024-03-10T04:11:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39640678\">Comments</a>",
            "url": "https://twitter.com/seshubon/status/1765870717844050221",
            "title": "Claude Sonnet and Pi AI give exact same response to a prompt",
            "date_modified": "2024-03-08T13:03:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39620540\">Comments</a>",
            "url": "https://github.com/NilsIrl/dockerc",
            "title": "Show HN: dockerc – Docker image to static executable \"compiler\"",
            "date_modified": "2024-03-06T19:55:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39619041\">Comments</a>",
            "url": "https://yolken.net/blog/end-of-airplanedev",
            "title": "The End of Airplane.dev",
            "date_modified": "2024-03-06T18:16:58.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2024/03/Technical-deep-dive-for-Security-week.png\"></figure><p>The Linux kernel is the heart of many modern production systems. It decides when any code is allowed to run and which programs/users can access which resources. It manages memory, mediates access to hardware, and does a bulk of work under the hood on behalf of programs running on top. Since the kernel is always involved in any code execution, it is in the best position to protect the system from malicious programs, enforce the desired system security policy, and provide security features for safer production environments.</p>\n<p>In this post, we will review some Linux kernel security configurations we use at Cloudflare and how they help to block or minimize a potential system compromise.</p>\n<h2>Secure boot</h2>\n<p>When a machine (either a laptop or a server) boots, it goes through several boot stages:</p>\n<p><img src=\"http://blog.cloudflare.com/content/images/2024/03/image3-17.png\"></p>\n<p>Within a secure boot architecture each stage from the above diagram verifies the integrity of the next stage before passing execution to it, thus forming a so-called secure boot chain. This way “trustworthiness” is extended to every component in the boot chain, because if we verified the code integrity of a particular stage, we can trust this code to verify the integrity of the next stage.</p>\n<p>We <a>have previously covered</a> how Cloudflare implements secure boot in the initial stages of the boot process. In this post, we will focus on the Linux kernel.</p>\n<p>Secure boot is the cornerstone of any operating system security mechanism. The Linux kernel is the primary enforcer of the operating system security configuration and policy, so we have to be sure that the Linux kernel itself has not been tampered with. In our previous <a>post about secure boot</a> we showed how we use UEFI Secure Boot to ensure the integrity of the Linux kernel.</p>\n<p>But what happens next? After the kernel gets executed, it may try to load additional drivers, or as they are called in the Linux world, kernel modules. And kernel module loading is not confined just to the boot process. A module can be loaded at any time during runtime — a new device being plugged in and a driver is needed, some additional extensions in the networking stack are required (for example, for fine-grained firewall rules), or just manually by the system administrator.</p>\n<p>However, uncontrolled kernel module loading might pose a significant risk to system integrity. Unlike regular programs, which get executed as user space processes, kernel modules are pieces of code which get injected and executed directly in the Linux kernel address space. There is no separation between the code and data in different kernel modules and core kernel subsystems, so everything can access everything. This means that a rogue kernel module can completely nullify the trustworthiness of the operating system and make secure boot useless. As an example, consider a simple Debian 12 (Bookworm installation), but with <a>SELinux</a> configured and enforced:</p>\n<pre><code>ignat@dev:~$ lsb_release --all\nNo LSB modules are available.\nDistributor ID:\tDebian\nDescription:\tDebian GNU/Linux 12 (bookworm)\nRelease:\t12\nCodename:\tbookworm\nignat@dev:~$ uname -a\nLinux dev 6.1.0-18-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux\nignat@dev:~$ sudo getenforce\nEnforcing\n</code></pre>\n<p>Now we need to do some research. First, we see that we’re running 6.1.76 Linux Kernel. If we explore the source code, we would see that <a>inside the kernel, the SELinux configuration is stored in a singleton structure</a>, which <a>is defined</a> as follows:</p>\n<pre><code>struct selinux_state {\n#ifdef CONFIG_SECURITY_SELINUX_DISABLE\n\tbool disabled;\n#endif\n#ifdef CONFIG_SECURITY_SELINUX_DEVELOP\n\tbool enforcing;\n#endif\n\tbool checkreqprot;\n\tbool initialized;\n\tbool policycap[__POLICYDB_CAP_MAX];\n\n\tstruct page *status_page;\n\tstruct mutex status_lock;\n\n\tstruct selinux_avc *avc;\n\tstruct selinux_policy __rcu *policy;\n\tstruct mutex policy_mutex;\n} __randomize_layout;\n</code></pre>\n<p>From the above, we can see that if the kernel configuration has <code>CONFIG_SECURITY_SELINUX_DEVELOP</code> enabled, the structure would have a boolean variable <code>enforcing</code>, which controls the enforcement status of SELinux at runtime. This is exactly what the above <code>$ sudo getenforce</code> command returns. We can double check that the Debian kernel indeed has the configuration option enabled:</p>\n<pre><code>ignat@dev:~$ grep CONFIG_SECURITY_SELINUX_DEVELOP /boot/config-`uname -r`\nCONFIG_SECURITY_SELINUX_DEVELOP=y\n</code></pre>\n<p>Good! Now that we have a variable in the kernel, which is responsible for some security enforcement, we can try to attack it. One problem though is the <code>__randomize_layout</code> attribute: since <code>CONFIG_SECURITY_SELINUX_DISABLE</code> is actually not set for our Debian kernel, normally <code>enforcing</code> would be the first member of the struct. Thus if we know where the struct is, we immediately know the position of the <code>enforcing</code> flag. With <code>__randomize_layout</code>, during kernel compilation the compiler might place members at arbitrary positions within the struct, so it is harder to create generic exploits. But arbitrary struct randomization within the kernel <a>may introduce performance impact</a>, so is often disabled and it is disabled for the Debian kernel:</p>\n<pre><code>ignat@dev:~$ grep RANDSTRUCT /boot/config-`uname -r`\nCONFIG_RANDSTRUCT_NONE=y\n</code></pre>\n<p>We can also confirm the compiled position of the <code>enforcing</code> flag using the <a>pahole tool</a> and either kernel debug symbols, if available, or (on modern kernels, if enabled) in-kernel <a>BTF</a> information. We will use the latter:</p>\n<pre><code>ignat@dev:~$ pahole -C selinux_state /sys/kernel/btf/vmlinux\nstruct selinux_state {\n\tbool                       enforcing;            /*     0     1 */\n\tbool                       checkreqprot;         /*     1     1 */\n\tbool                       initialized;          /*     2     1 */\n\tbool                       policycap[8];         /*     3     8 */\n\n\t/* XXX 5 bytes hole, try to pack */\n\n\tstruct page *              status_page;          /*    16     8 */\n\tstruct mutex               status_lock;          /*    24    32 */\n\tstruct selinux_avc *       avc;                  /*    56     8 */\n\t/* --- cacheline 1 boundary (64 bytes) --- */\n\tstruct selinux_policy *    policy;               /*    64     8 */\n\tstruct mutex               policy_mutex;         /*    72    32 */\n\n\t/* size: 104, cachelines: 2, members: 9 */\n\t/* sum members: 99, holes: 1, sum holes: 5 */\n\t/* last cacheline: 40 bytes */\n};\n</code></pre>\n<p>So <code>enforcing</code> is indeed located at the start of the structure and we don’t even have to be a privileged user to confirm this.</p>\n<p>Great! All we need is the runtime address of the <code>selinux_state</code> variable inside the kernel:<br>\n(shell/bash)</p>\n<pre><code>ignat@dev:~$ sudo grep selinux_state /proc/kallsyms\nffffffffbc3bcae0 B selinux_state\n</code></pre>\n<p>With all the information, we can write an almost textbook simple kernel module to manipulate the SELinux state:</p>\n<p>Mymod.c:</p>\n<pre><code>#include &lt;linux/module.h&gt;\n\nstatic int __init mod_init(void)\n{\n\tbool *selinux_enforce = (bool *)0xffffffffbc3bcae0;\n\t*selinux_enforce = false;\n\treturn 0;\n}\n\nstatic void mod_fini(void)\n{\n}\n\nmodule_init(mod_init);\nmodule_exit(mod_fini);\n\nMODULE_DESCRIPTION(\"A somewhat malicious module\");\nMODULE_AUTHOR(\"Ignat Korchagin &lt;ignat@cloudflare.com&gt;\");\nMODULE_LICENSE(\"GPL\");\n</code></pre>\n<p>And the respective <code>Kbuild</code> file:</p>\n<pre><code>obj-m := mymod.o\n</code></pre>\n<p>With these two files we can build a full fledged kernel module according to <a>the official kernel docs</a>:</p>\n<pre><code>ignat@dev:~$ cd mymod/\nignat@dev:~/mymod$ ls\nKbuild  mymod.c\nignat@dev:~/mymod$ make -C /lib/modules/`uname -r`/build M=$PWD\nmake: Entering directory '/usr/src/linux-headers-6.1.0-18-cloud-amd64'\n  CC [M]  /home/ignat/mymod/mymod.o\n  MODPOST /home/ignat/mymod/Module.symvers\n  CC [M]  /home/ignat/mymod/mymod.mod.o\n  LD [M]  /home/ignat/mymod/mymod.ko\n  BTF [M] /home/ignat/mymod/mymod.ko\nSkipping BTF generation for /home/ignat/mymod/mymod.ko due to unavailability of vmlinux\nmake: Leaving directory '/usr/src/linux-headers-6.1.0-18-cloud-amd64'\n</code></pre>\n<p>If we try to load this module now, the system may not allow it due to the SELinux policy:</p>\n<pre><code>ignat@dev:~/mymod$ sudo insmod mymod.ko\ninsmod: ERROR: could not load module mymod.ko: Permission denied\n</code></pre>\n<p>We can workaround it by copying the module into the standard module path somewhere:</p>\n<pre><code>ignat@dev:~/mymod$ sudo cp mymod.ko /lib/modules/`uname -r`/kernel/crypto/\n</code></pre>\n<p>Now let’s try it out:</p>\n<pre><code>ignat@dev:~/mymod$ sudo getenforce\nEnforcing\nignat@dev:~/mymod$ sudo insmod /lib/modules/`uname -r`/kernel/crypto/mymod.ko\nignat@dev:~/mymod$ sudo getenforce\nPermissive\n</code></pre>\n<p>Not only did we disable the SELinux protection via a malicious kernel module, we did it quietly. Normal <code>sudo setenforce 0</code>, even if allowed, would go through the official <a>selinuxfs interface and would emit an audit message</a>. Our code manipulated the kernel memory directly, so no one was alerted. This illustrates why uncontrolled kernel module loading is very dangerous and that is why most security standards and commercial security monitoring products advocate for close monitoring of kernel module loading.</p>\n<p>But we don’t need to monitor kernel modules at Cloudflare. Let’s repeat the exercise on a Cloudflare production kernel (module recompilation skipped for brevity):</p>\n<pre><code>ignat@dev:~/mymod$ uname -a\nLinux dev 6.6.17-cloudflare-2024.2.9 #1 SMP PREEMPT_DYNAMIC Mon Sep 27 00:00:00 UTC 2010 x86_64 GNU/Linux\nignat@dev:~/mymod$ sudo insmod /lib/modules/`uname -r`/kernel/crypto/mymod.ko\ninsmod: ERROR: could not insert module /lib/modules/6.6.17-cloudflare-2024.2.9/kernel/crypto/mymod.ko: Key was rejected by service\n</code></pre>\n<p>We get a <code>Key was rejected by service</code> error when trying to load a module, and the kernel log will have the following message:</p>\n<pre><code>ignat@dev:~/mymod$ sudo dmesg | tail -n 1\n[41515.037031] Loading of unsigned module is rejected\n</code></pre>\n<p>This is because the Cloudflare kernel <a>requires all the kernel modules to have a valid signature</a>, so we don’t even have to worry about a malicious module being loaded at some point:</p>\n<pre><code>ignat@dev:~$ grep MODULE_SIG_FORCE /boot/config-`uname -r`\nCONFIG_MODULE_SIG_FORCE=y\n</code></pre>\n<p>For completeness it is worth noting that the Debian stock kernel also supports module signatures, but does not enforce it:</p>\n<pre><code>ignat@dev:~$ grep MODULE_SIG /boot/config-6.1.0-18-cloud-amd64\nCONFIG_MODULE_SIG_FORMAT=y\nCONFIG_MODULE_SIG=y\n# CONFIG_MODULE_SIG_FORCE is not set\n…\n</code></pre>\n<p>The above configuration means that the kernel will validate a module signature, if available. But if not - the module will be loaded anyway with a warning message emitted and the <a>kernel will be tainted</a>.</p>\n<h3>Key management for kernel module signing</h3>\n<p>Signed kernel modules are great, but it creates a key management problem: to sign a module we need a signing keypair that is trusted by the kernel. The public key of the keypair is usually directly embedded into the kernel binary, so the kernel can easily use it to verify module signatures. The private key of the pair needs to be protected and secure, because if it is leaked, anyone could compile and sign a potentially malicious kernel module which would be accepted by our kernel.</p>\n<p>But what is the best way to eliminate the risk of losing something? Not to have it in the first place! Luckily the kernel build system <a>will generate a random keypair</a> for module signing, if none is provided. At Cloudflare, we use that feature to sign all the kernel modules during the kernel compilation stage. When the compilation and signing is done though, instead of storing the key in a secure place, we just destroy the private key:</p>\n<p><img src=\"http://blog.cloudflare.com/content/images/2024/03/image1-19.png\"></p>\n<p>So with the above process:</p>\n<ol>\n<li>The kernel build system generated a random keypair, compiles the kernel and modules</li>\n<li>The public key is embedded into the kernel image, the private key is used to sign all the modules</li>\n<li>The private key is destroyed</li>\n</ol>\n<p>With this scheme not only do we not have to worry about module signing key management, we also use a different key for each kernel we release to production. So even if a particular build process is hijacked and the signing key is not destroyed and potentially leaked, the key will no longer be valid when a kernel update is released.</p>\n<p>There are some flexibility downsides though, as we can’t “retrofit” a new kernel module for an already released kernel (for example, for <a>a new piece of hardware we are adopting</a>). However, it is not a practical limitation for us as we release kernels often (roughly every week) to keep up with a steady stream of bug fixes and vulnerability patches in the Linux Kernel.</p>\n<h2>KEXEC</h2>\n<p><a>KEXEC</a> (or <code>kexec_load()</code>) is an interesting system call in Linux, which allows for one kernel to directly execute (or jump to) another kernel. The idea behind this is to switch/update/downgrade kernels faster without going through a full reboot cycle to minimize the potential system downtime. However, it was developed quite a while ago, when secure boot and system integrity was not quite a concern. Therefore its original design has security flaws and is known to be able to <a>bypass secure boot and potentially compromise system integrity</a>.</p>\n<p>We can see the problems just based on the <a>definition of the system call itself</a>:</p>\n<pre><code>struct kexec_segment {\n\tconst void *buf;\n\tsize_t bufsz;\n\tconst void *mem;\n\tsize_t memsz;\n};\n...\nlong kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment *segments, unsigned long flags);\n</code></pre>\n<p>So the kernel expects just a collection of buffers with code to execute. Back in those days there was not much desire to do a lot of data parsing inside the kernel, so the idea was to parse the to-be-executed kernel image in user space and provide the kernel with only the data it needs. Also, to switch kernels live, we need an intermediate program which would take over while the old kernel is shutting down and the new kernel has not yet been executed. In the kexec world this program is called <a>purgatory</a>. Thus the problem is evident: we give the kernel a bunch of code and it will happily execute it at the highest privilege level. But instead of the original kernel or purgatory code, we can easily provide code similar to the one demonstrated earlier in this post, which disables SELinux (or does something else to the kernel).</p>\n<p>At Cloudflare we have had <code>kexec_load()</code> disabled for some time now just because of this. The advantage of faster reboots with kexec comes with <a>a (small) risk of improperly initialized hardware</a>, so it was not worth using it even without the security concerns. However, kexec does provide one useful feature — it is the foundation of the Linux kernel <a>crashdumping solution</a>. In a nutshell, if a kernel crashes in production (due to a bug or some other error), a backup kernel (previously loaded with kexec) can take over, collect and save the memory dump for further investigation. This allows to more effectively investigate kernel and other issues in production, so it is a powerful tool to have.</p>\n<p>Luckily, since the <a>original problems with kexec were outlined</a>, Linux developed an alternative <a>secure interface for kexec</a>: instead of buffers with code it expects file descriptors with the to-be-executed kernel image and initrd and does parsing inside the kernel. Thus, only a valid kernel image can be supplied. On top of this, we can <a>configure</a> and <a>require</a> kexec to ensure the provided images are properly signed, so only authorized code can be executed in the kexec scenario. A secure configuration for kexec looks something like this:</p>\n<pre><code>ignat@dev:~$ grep KEXEC /boot/config-`uname -r`\nCONFIG_KEXEC_CORE=y\nCONFIG_HAVE_IMA_KEXEC=y\n# CONFIG_KEXEC is not set\nCONFIG_KEXEC_FILE=y\nCONFIG_KEXEC_SIG=y\nCONFIG_KEXEC_SIG_FORCE=y\nCONFIG_KEXEC_BZIMAGE_VERIFY_SIG=y\n…\n</code></pre>\n<p>Above we ensure that the legacy <code>kexec_load()</code> system call is disabled by disabling <code>CONFIG_KEXEC</code>, but still can configure Linux Kernel crashdumping via the new <code>kexec_file_load()</code> system call via <code>CONFIG_KEXEC_FILE=y</code> with enforced signature checks (<code>CONFIG_KEXEC_SIG=y</code> and <code>CONFIG_KEXEC_SIG_FORCE=y</code>).</p>\n<p>Note that stock Debian kernel has the legacy <code>kexec_load()</code> system call enabled and does not enforce signature checks for <code>kexec_file_load()</code> (similar to module signature checks):</p>\n<pre><code>ignat@dev:~$ grep KEXEC /boot/config-6.1.0-18-cloud-amd64\nCONFIG_KEXEC=y\nCONFIG_KEXEC_FILE=y\nCONFIG_ARCH_HAS_KEXEC_PURGATORY=y\nCONFIG_KEXEC_SIG=y\n# CONFIG_KEXEC_SIG_FORCE is not set\nCONFIG_KEXEC_BZIMAGE_VERIFY_SIG=y\n…\n</code></pre>\n<h2>Kernel Address Space Layout Randomization (KASLR)</h2>\n<p>Even on the stock Debian kernel if you try to repeat the exercise we described in the “Secure boot” section of this post after a system reboot, you will likely see it would fail to disable SELinux now. This is because we hardcoded the kernel address of the <code>selinux_state</code> structure in our malicious kernel module, but the address changed now:</p>\n<pre><code>ignat@dev:~$ sudo grep selinux_state /proc/kallsyms\nffffffffb41bcae0 B selinux_state\n</code></pre>\n<p><a>Kernel Address Space Layout Randomization (or KASLR)</a> is a simple concept: it slightly and randomly shifts the kernel code and data on each boot:</p>\n<p><img src=\"http://blog.cloudflare.com/content/images/2024/03/Screenshot-2024-03-06-at-13.53.23-2.png\"></p>\n<p>This is to combat targeted exploitation (like the malicious module in this post) based on the knowledge of the location of internal kernel structures and code. It is especially useful for popular Linux distribution kernels, like the Debian one, because most users use the same binary and anyone can download the debug symbols and the System.map file with all the addresses of the kernel internals. Just to note: it will not prevent the module loading and doing harm, but it will likely not achieve the targeted effect of disabling SELinux. Instead, it will modify a random piece of kernel memory potentially causing the kernel to crash.</p>\n<p>Both the Cloudflare kernel and the Debian one have this feature enabled:</p>\n<pre><code>ignat@dev:~$ grep RANDOMIZE_BASE /boot/config-`uname -r`\nCONFIG_RANDOMIZE_BASE=y\n</code></pre>\n<h3>Restricted kernel pointers</h3>\n<p>While KASLR helps with targeted exploits, it is quite easy to bypass since everything is shifted by a single random offset as shown on the diagram above. Thus if the attacker knows at least one runtime kernel address, they can recover this offset by subtracting the runtime address from the compile time address of the same symbol (function or data structure) from the kernel’s System.map file. Once they know the offset, they can recover the addresses of all other symbols by adjusting them by this offset.</p>\n<p>Therefore, modern kernels take precautions not to leak kernel addresses at least to unprivileged users. One of the main tunables for this is the <a>kptr_restrict sysctl</a>. It is a good idea to set it at least to <code>1</code> to not allow regular users to see kernel pointers:<br>\n(shell/bash)</p>\n<pre><code>ignat@dev:~$ sudo sysctl -w kernel.kptr_restrict=1\nkernel.kptr_restrict = 1\nignat@dev:~$ grep selinux_state /proc/kallsyms\n0000000000000000 B selinux_state\n</code></pre>\n<p>Privileged users can still see the pointers:</p>\n<pre><code>ignat@dev:~$ sudo grep selinux_state /proc/kallsyms\nffffffffb41bcae0 B selinux_state\n</code></pre>\n<p>Similar to <a>kptr_restrict sysctl</a> there is also <a>dmesg_restrict</a>, which if set, would prevent regular users from reading the kernel log (which may also leak kernel pointers via its messages). While you need to explicitly set <a>kptr_restrict sysctl</a> to a non-zero value on each boot (or use some system sysctl configuration utility, like <a>this one</a>), you can configure <a>dmesg_restrict</a> initial value via the <code>CONFIG_SECURITY_DMESG_RESTRICT</code> kernel configuration option. Both the Cloudflare kernel and the Debian one enforce <a>dmesg_restrict</a> this way:</p>\n<pre><code>ignat@dev:~$ grep CONFIG_SECURITY_DMESG_RESTRICT /boot/config-`uname -r`\nCONFIG_SECURITY_DMESG_RESTRICT=y\n</code></pre>\n<p>Worth noting that <code>/proc/kallsyms</code> and the kernel log are not the only sources of potential kernel pointer leaks. There is a lot of legacy in the Linux kernel and [new sources are continuously being found and patched]. That’s why it is very important to stay up to date with the latest kernel bugfix releases.</p>\n<h2>Lockdown LSM</h2>\n<p><a>Linux Security Modules (LSM)</a> is a hook-based framework for implementing security policies and Mandatory Access Control in the Linux Kernel. We have [covered our usage of another LSM module, BPF-LSM, previously].</p>\n<p>BPF-LSM is a useful foundational piece for our kernel security, but in this post we want to mention another useful LSM module we use — <a>the Lockdown LSM</a>. Lockdown can be in three states (controlled by the <code>/sys/kernel/security/lockdown</code> special file):</p>\n<pre><code>ignat@dev:~$ cat /sys/kernel/security/lockdown\n[none] integrity confidentiality\n</code></pre>\n<p><code>none</code> is the state where nothing is enforced and the module is effectively disabled. When Lockdown is in the <code>integrity</code> state, the kernel tries to prevent any operation, which may compromise its integrity. We already covered some examples of these in this post: loading unsigned modules and executing unsigned code via KEXEC. But there are other potential ways (which are mentioned in <a>the LSM’s man page</a>), all of which this LSM tries to block. <code>confidentiality</code> is the most restrictive mode, where Lockdown will also try to prevent any information leakage from the kernel. In practice this may be too restrictive for server workloads as it blocks all runtime debugging capabilities, like <code>perf</code> or eBPF.</p>\n<p>Let’s see the Lockdown LSM in action. On a barebones Debian system the initial state is <code>none</code> meaning nothing is locked down:</p>\n<pre><code>ignat@dev:~$ uname -a\nLinux dev 6.1.0-18-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux\nignat@dev:~$ cat /sys/kernel/security/lockdown\n[none] integrity confidentiality\n</code></pre>\n<p>We can switch the system into the <code>integrity</code> mode:</p>\n<pre><code>ignat@dev:~$ echo integrity | sudo tee /sys/kernel/security/lockdown\nintegrity\nignat@dev:~$ cat /sys/kernel/security/lockdown\nnone [integrity] confidentiality\n</code></pre>\n<p>It is worth noting that we can only put the system into a more restrictive state, but not back. That is, once in <code>integrity</code> mode we can only switch to <code>confidentiality</code> mode, but not back to <code>none</code>:</p>\n<pre><code>ignat@dev:~$ echo none | sudo tee /sys/kernel/security/lockdown\nnone\ntee: /sys/kernel/security/lockdown: Operation not permitted\n</code></pre>\n<p>Now we can see that even on a stock Debian kernel, which as we discovered above, does not enforce module signatures by default, we cannot load a potentially malicious unsigned kernel module anymore:</p>\n<pre><code>ignat@dev:~$ sudo insmod mymod/mymod.ko\ninsmod: ERROR: could not insert module mymod/mymod.ko: Operation not permitted\n</code></pre>\n<p>And the kernel log will helpfully point out that this is due to Lockdown LSM:</p>\n<pre><code>ignat@dev:~$ sudo dmesg | tail -n 1\n[21728.820129] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7\n</code></pre>\n<p>As we can see, Lockdown LSM helps to tighten the security of a kernel, which otherwise may not have other enforcing bits enabled, like the stock Debian one.</p>\n<p>If you compile your own kernel, you can go one step further and set <a>the initial state of the Lockdown LSM to be more restrictive than none from the start</a>. This is exactly what we did for the Cloudflare production kernel:</p>\n<pre><code>ignat@dev:~$ grep LOCK_DOWN /boot/config-6.6.17-cloudflare-2024.2.9\n# CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE is not set\nCONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY=y\n# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set\n</code></pre>\n<h2>Conclusion</h2>\n<p>In this post we reviewed some useful Linux kernel security configuration options we use at Cloudflare. This is only a small subset, and there are many more available and even more are being constantly developed, reviewed, and improved by the Linux kernel community. We hope that this post will shed some light on these security features and that, if you haven’t already, you may consider enabling them in your Linux systems.</p>\n<h2>Watch on Cloudflare TV</h2><div>\n  \n</div><p>Tune in for more news, announcements and thought-provoking discussions! Don't miss the full <a>Security Week hub page</a>.</p>",
            "url": "https://blog.cloudflare.com/linux-kernel-hardening",
            "title": "Linux kernel security tunables everyone should consider adopting",
            "date_modified": "2024-03-06T14:00:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39600031\">Comments</a>",
            "url": "https://github.com/ortuman/nuke",
            "title": "Nuke: A memory arena implementation for Go",
            "date_modified": "2024-03-05T06:22:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39593008\">Comments</a>",
            "url": "https://github.com/harshadmanglani/polaris",
            "title": "Show HN: Workflow Orchestrator in Golang",
            "date_modified": "2024-03-04T17:21:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39590489\">Comments</a>",
            "url": "https://gleam.run/news/gleam-version-1/?q",
            "title": "Gleam Version 1",
            "date_modified": "2024-03-04T13:53:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39581976\">Comments</a>",
            "url": "https://paulbutler.org/2024/the-haters-guide-to-kubernetes/",
            "title": "The hater's guide to Kubernetes",
            "date_modified": "2024-03-03T16:44:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39580076\">Comments</a>",
            "url": "https://labs.scaleway.com/en/em-rv1/",
            "title": "Scaleway launches RISC-V servers",
            "date_modified": "2024-03-03T11:01:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39578090\">Comments</a>",
            "url": "https://www.jbernier.com/p?id=nodejs-stream-async-iterator",
            "title": "How to convert Node.js stream callback functions into an Async Iterator",
            "date_modified": "2024-03-03T03:06:52.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/8bkizs/how_build_aws_compatible_apis_aws_sigv4\">Comments</a></p>",
            "url": "https://www.aspiring.dev/building-aws-sigv4-into-your-app/",
            "title": "How To Build AWS-Compatible APIs: AWS Sigv4",
            "date_modified": "2024-02-29T12:51:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39539252\">Comments</a>",
            "url": "https://pql.dev/",
            "title": "Pql, a pipelined query language that compiles to SQL (written in Go)",
            "date_modified": "2024-02-28T15:33:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39535969\">Comments</a>",
            "url": "https://github.com/cloudflare/pingora",
            "title": "Pingora: HTTP Server and Proxy Library, in Rust, by Cloudflare, Released",
            "date_modified": "2024-02-28T09:56:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39535969\">Comments</a>",
            "url": "https://github.com/cloudflare/pingora",
            "title": "Pingora: build fast, reliable and programmable networked systems",
            "date_modified": "2024-02-28T09:56:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39531536\">Comments</a>",
            "url": "https://testcontainers.com/",
            "title": "Testcontainers",
            "date_modified": "2024-02-27T23:15:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39522770\">Comments</a>",
            "url": "https://erikbern.com/2021/04/19/software-infrastructure-2.0-a-wishlist.html",
            "title": "Software Infrastructure 2.0: A Wishlist",
            "date_modified": "2024-02-27T11:23:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39518791\">Comments</a>",
            "url": "https://netflixtechblog.com/announcing-bpftop-streamlining-ebpf-performance-optimization-6a727c1ae2e5?gi=223d75ac1771",
            "title": "Bpftop: Streamlining eBPF performance optimization",
            "date_modified": "2024-02-27T01:04:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39518507\">Comments</a>",
            "url": "https://usefathom.com/blog/reduce-aws-bill",
            "title": "Reducing our AWS bill by $100k",
            "date_modified": "2024-02-27T00:16:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39507820\">Comments</a>",
            "url": "https://multi.app/",
            "title": "Multi – Multiplayer Collaboration for macOS",
            "date_modified": "2024-02-26T04:54:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39507758\">Comments</a>",
            "url": "https://www.baldurbjarnason.com/2024/disillusioned-with-deno/",
            "title": "Disillusioned with Deno",
            "date_modified": "2024-02-26T04:42:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39500397\">Comments</a>",
            "url": "https://www.aspiring.dev/fly-io-scheduler-part-2/",
            "title": "Building a Fly.io-Like Scheduler with Resource Requirements",
            "date_modified": "2024-02-25T12:53:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39500397\">Comments</a>",
            "url": "https://www.aspiring.dev/fly-io-scheduler-part-2/",
            "title": "Building a Fly.io-like scheduler with resource requirements",
            "date_modified": "2024-02-25T12:53:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39476446\">Comments</a>",
            "url": "https://resend.com/blog/incident-report-for-february-21-2024",
            "title": "Resend – Incident report for February 21st, 2024",
            "date_modified": "2024-02-23T03:00:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/hurio2/building_fly_io_like_scheduler\">Comments</a></p>",
            "url": "https://www.aspiring.dev/fly-io-scheduler-part-1/",
            "title": "Building a Fly.io-like Scheduler",
            "date_modified": "2024-02-22T12:19:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39465891\">Comments</a>",
            "url": "https://supernotes.app/open-source/sn-pro/",
            "title": "SN Pro Typeface",
            "date_modified": "2024-02-22T12:01:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39465740\">Comments</a>",
            "url": "https://github.com/ory/kratos/releases/tag/v1.1.0",
            "title": "Auth0 OSS alternative Ory Kratos now with passwordless and SMS support",
            "date_modified": "2024-02-22T11:41:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39457574\">Comments</a>",
            "url": "https://deno.com/blog/webhooks-suck",
            "title": "Webhooks suck, but here are alternatives",
            "date_modified": "2024-02-21T18:31:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39457562\">Comments</a>",
            "url": "https://grafana.com/blog/2024/02/21/how-the-open-source-caddy-server-uses-grafana-cloud-for-full-stack-observability/",
            "title": "How the open source Caddy server uses Grafana Cloud for full-stack observability",
            "date_modified": "2024-02-21T18:30:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39453767\">Comments</a>",
            "url": "https://remix.run/blog/remix-vite-stable",
            "title": "Remix Vite Is Now Stable",
            "date_modified": "2024-02-21T13:52:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39452024\">Comments</a>",
            "url": "https://github.com/readysettech/readyset",
            "title": "Readyset: A MySQL and Postgres wire-compatible caching layer",
            "date_modified": "2024-02-21T10:09:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39443679\">Comments</a>",
            "url": "http://databasearchitects.blogspot.com/2024/02/ssds-have-become-ridiculously-fast.html",
            "title": "SSDs have become fast, except in the cloud",
            "date_modified": "2024-02-20T16:59:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39442400\">Comments</a>",
            "url": "http://arighi.blogspot.com/2024/02/writing-scheduler-for-linux-in-rust.html",
            "title": "Writing a scheduler for Linux in Rust that runs in user-space",
            "date_modified": "2024-02-20T15:26:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39439986\">Comments</a>",
            "url": "https://liftingstones.org/articles/stonelifting-etiquette",
            "title": "Stonelifting Etiquette",
            "date_modified": "2024-02-20T11:19:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/gbtvqp/how_debug_your_initramfs_init\">Comments</a></p>",
            "url": "https://linus.schreibt.jetzt/posts/debugging-pid1.html",
            "title": "How to debug your initramfs init",
            "date_modified": "2024-02-20T09:50:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39428409\">Comments</a>",
            "url": "https://github.com/black7375/Firefox-UI-Fix/wiki/%5BArticle%5D-1.-How-to-make-a-better-default-Firefox-UI",
            "title": "How to make a better default Firefox UI",
            "date_modified": "2024-02-19T11:02:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39427630\">Comments</a>",
            "url": "https://taoofmac.com/space/blog/2024/02/03/2000",
            "title": "The Logitech G Cloud",
            "date_modified": "2024-02-19T09:18:20.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/kxxbci/software_infrastructure_2_0_wishlist\">Comments</a></p>",
            "url": "https://erikbern.com/2021/04/19/software-infrastructure-2.0-a-wishlist.html",
            "title": "Software infrastructure 2.0: a wishlist",
            "date_modified": "2024-02-18T20:27:24.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/icverw/tempo\">Comments</a></p>",
            "url": "https://tempo.formkit.com/",
            "title": "Tempo",
            "date_modified": "2024-02-18T17:35:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39418841\">Comments</a>",
            "url": "https://www.mydbops.com/blog/postgresql-schema-changes-with-pg_osc/",
            "title": "Revolutionizing PostgreSQL Schema Changes with pg-osc",
            "date_modified": "2024-02-18T13:22:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39414630\">Comments</a>",
            "url": "https://git.kernel.dk/cgit/liburing/tree/examples/proxy.c",
            "title": "Basic proxy implementation using io_uring",
            "date_modified": "2024-02-17T23:46:17.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/fud3fk/from_1s_4ms\">Comments</a></p>",
            "url": "https://registerspill.thorstenball.com/p/from-1s-to-4ms",
            "title": "From 1s to 4ms",
            "date_modified": "2024-02-17T23:23:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39412276\">Comments</a>",
            "url": "https://www.defined.net/blog/nebula-is-not-the-fastest-mesh-vpn/",
            "title": "Nebula is not the fastest mesh VPN, but neither are the others",
            "date_modified": "2024-02-17T18:55:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39409816\">Comments</a>",
            "url": "https://github.com/shepherdjerred/macos-cross-compiler",
            "title": "Show HN: macOS-cross-compiler – Compile binaries for macOS on Linux",
            "date_modified": "2024-02-17T14:41:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/3blthf/dependency_solving_nix\">Comments</a></p>",
            "url": "http://www.chriswarbo.net/projects/nixos/nix_dependencies.html",
            "title": "Dependency solving in Nix",
            "date_modified": "2024-02-16T00:24:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39373960\">Comments</a>",
            "url": "https://pears.com/news/holepunch-unveils-groundbreaking-open-source-peer-to-peer-app-development-platform-pear-runtime/",
            "title": "Holepunch Unveils P2P Platform \"Pear Runtime\"",
            "date_modified": "2024-02-14T19:21:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39373744\">Comments</a>",
            "url": "https://console.dataherald.ai/playground",
            "title": "Show HN: Natural Language to SQL \"Text-to-SQL\" API",
            "date_modified": "2024-02-14T19:03:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39371805\">Comments</a>",
            "url": "https://www.ycombinator.com/rfs",
            "title": "YC: Requests for Startups",
            "date_modified": "2024-02-14T16:31:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39369853\">Comments</a>",
            "url": "https://www.rerun.io/blog/primary-query-caching",
            "title": "We sped up time series by 20-30x",
            "date_modified": "2024-02-14T14:03:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39363499\">Comments</a>",
            "url": "https://fly.io/blog/fly-io-has-gpus-now/",
            "title": "Fly.io Has GPUs Now",
            "date_modified": "2024-02-13T22:06:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39360870\">Comments</a>",
            "url": "https://benhoyt.com/writings/flyio-and-tigris/",
            "title": "Switching from S3 to Tigris on Fly.io",
            "date_modified": "2024-02-13T18:22:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39343831\">Comments</a>",
            "url": "https://seb.jambor.dev/posts/systemd-by-example-part-1-minimization/",
            "title": "systemd by Example (2021)",
            "date_modified": "2024-02-12T11:48:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39342392\">Comments</a>",
            "url": "https://blog.stenmans.org/theBeamBook/",
            "title": "The Erlang Runtime System",
            "date_modified": "2024-02-12T07:24:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39341341\">Comments</a>",
            "url": "https://docs.google.com/spreadsheets/d/1OfRSXibZ2nIE9DGK6swwBZXgXwdCPKgp4SbPZwTexCg/htmlview",
            "title": "OpenZFS bug reports for native encryption",
            "date_modified": "2024-02-12T04:26:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39333220\">Comments</a>",
            "url": "https://yorickpeterse.com/articles/what-it-was-like-working-for-gitlab/",
            "title": "What it was like working for Gitlab",
            "date_modified": "2024-02-11T07:29:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39329677\">Comments</a>",
            "url": "https://getdeploying.com/reference/data-egress",
            "title": "How much 1 TB of egress costs by cloud provider",
            "date_modified": "2024-02-10T20:02:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39313891\">Comments</a>",
            "url": "https://github.com/honojs/hono/releases/tag/v4.0.0",
            "title": "Hono v4.0.0",
            "date_modified": "2024-02-09T11:55:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39313623\">Comments</a>",
            "url": "https://cep.dev/posts/every-infrastructure-decision-i-endorse-or-regret-after-4-years-running-infrastructure-at-a-startup/",
            "title": "Almost every infrastructure decision I endorse or regret",
            "date_modified": "2024-02-09T11:05:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39304188\">Comments</a>",
            "url": "https://github.com/awslabs/llrt",
            "title": "LLRT: A low-latency JavaScript runtime from AWS",
            "date_modified": "2024-02-08T16:46:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39300317\">Comments</a>",
            "url": "https://cyberus-technology.de/articles/vbox-kvm-public-release",
            "title": "VirtualBox KVM Public Release",
            "date_modified": "2024-02-08T10:20:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39299715\">Comments</a>",
            "url": "https://zenhorace.dev/blog/context-control-go/",
            "title": "Context Control in Go",
            "date_modified": "2024-02-08T08:40:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39299599\">Comments</a>",
            "url": "https://netflixtechblog.com/rebuilding-netflix-video-processing-pipeline-with-microservices-4e5e6310e359?gi=d48b3333df75",
            "title": "Rebuilding Netflix's video processing pipeline with microservices",
            "date_modified": "2024-02-08T08:22:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39298451\">Comments</a>",
            "url": "https://flawless.dev/essays/when-letting-it-crash-is-not-enough/",
            "title": "When \"letting it crash\" is not enough",
            "date_modified": "2024-02-08T05:07:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39290982\">Comments</a>",
            "url": "https://www.unison.cloud/",
            "title": "Unison Cloud",
            "date_modified": "2024-02-07T16:59:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39289246\">Comments</a>",
            "url": "https://ente.io/blog/tech/sqlite-objectbox-isar/",
            "title": "SQLite Isn't Enough",
            "date_modified": "2024-02-07T14:52:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39287225\">Comments</a>",
            "url": "https://www.luxcapital.com/securities/techcrunch-plus-termination",
            "title": "TechCrunch+ Termination",
            "date_modified": "2024-02-07T11:28:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39285878\">Comments</a>",
            "url": "https://pluralistic.net/2024/02/06/spoil-the-bunch/#dma",
            "title": "Apple to EU: \"Go fuck yourself\"",
            "date_modified": "2024-02-07T08:11:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39277451\">Comments</a>",
            "url": "https://depot.dev/blog/buildkit-in-depth",
            "title": "BuildKit in depth: Docker's build engine explained",
            "date_modified": "2024-02-06T17:31:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39271658\">Comments</a>",
            "url": "https://helixml.substack.com/p/how-we-got-fine-tuning-mistral-7b",
            "title": "How we got fine-tuning Mistral-7B to not suck",
            "date_modified": "2024-02-06T07:12:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39263332\">Comments</a>",
            "url": "https://twitter.com/monadic/status/1754530336120140116",
            "title": "Weaveworks Is Shuting Down",
            "date_modified": "2024-02-05T16:51:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39261486\">Comments</a>",
            "url": "https://github.com/cfahlgren1/natural-sql",
            "title": "Show HN: Natural-SQL-7B, a strong text-to-SQL model",
            "date_modified": "2024-02-05T14:22:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39259068\">Comments</a>",
            "url": "https://deno.com/blog/deno-in-2023",
            "title": "Deno in 2023",
            "date_modified": "2024-02-05T09:23:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39241472\">Comments</a>",
            "url": "https://folk.computer/",
            "title": "Folk Computer",
            "date_modified": "2024-02-03T15:52:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39221243\">Comments</a>",
            "url": "https://www.awwsmm.com/blog/make-invalid-states-unrepresentable",
            "title": "Make Invalid States Unrepresentable",
            "date_modified": "2024-02-01T20:49:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39204314\">Comments</a>",
            "url": "https://fly.io/blog/macaroons-escalated-quickly/",
            "title": "Macaroons Escalated Quickly",
            "date_modified": "2024-01-31T14:39:19.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/nxcrgl/introducing_foundations_our_open_source\">Comments</a></p>",
            "url": "https://blog.cloudflare.com/introducing-foundations-our-open-source-rust-service-foundation-library",
            "title": "Introducing Foundations - our open source Rust service foundation library",
            "date_modified": "2024-01-31T13:39:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39196960\">Comments</a>",
            "url": "https://github.com/timeplus-io/proton",
            "title": "Proton, a fast and lightweight alternative to Apache Flink",
            "date_modified": "2024-01-30T22:42:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39191870\">Comments</a>",
            "url": "https://www.ubicloud.com/use-cases/github-actions",
            "title": "Show HN: Open-source x64 and Arm GitHub runners. Reduces GitHub Actions bill 10x",
            "date_modified": "2024-01-30T16:14:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39191870\">Comments</a>",
            "url": "https://www.ubicloud.com/use-cases/github-actions",
            "title": "Show HN: Open-source x64 and Arm GitHub runners",
            "date_modified": "2024-01-30T16:14:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39180891\">Comments</a>",
            "url": "https://sudhir.io/the-big-little-guide-to-message-queues",
            "title": "The Big Little Guide to Message Queues",
            "date_modified": "2024-01-29T19:08:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39178886\">Comments</a>",
            "url": "https://twitter.com/AIatMeta/status/1752013879532782075",
            "title": "Meta AI releases Code Llama 70B",
            "date_modified": "2024-01-29T17:11:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39167219\">Comments</a>",
            "url": "https://www.todepond.com/wikiblogarden/better-computing/just/",
            "title": "Just",
            "date_modified": "2024-01-28T16:31:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39162499\">Comments</a>",
            "url": "https://missionlocal.org/2024/01/garry-tan-death-wish-sf-supervisors/",
            "title": "Garry Tan tech CEO and campaign donor wishes death upon SF politicians",
            "date_modified": "2024-01-28T04:12:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39159322\">Comments</a>",
            "url": "https://metebalci.com/blog/hello-ipv6/",
            "title": "Hello IPv6: a minimal tutorial for IPv4 users",
            "date_modified": "2024-01-27T20:09:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39142748\">Comments</a>",
            "url": "https://github.com/oasislinux/oasis",
            "title": "Oasis – a small, statically-linked Linux system",
            "date_modified": "2024-01-26T14:11:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39129806\">Comments</a>",
            "url": "https://blog.trailofbits.com/2024/01/25/we-build-x-509-chains-so-you-dont-have-to/",
            "title": "We build X.509 chains so you don't have to",
            "date_modified": "2024-01-25T14:20:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39119141\">Comments</a>",
            "url": "https://github.blog/2024-01-23-good-devex-increases-productivity/",
            "title": "Good DevEx increases productivity. Here is the data",
            "date_modified": "2024-01-24T16:18:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39094119\">Comments</a>",
            "url": "https://www.infoq.com/articles/cloud-computing-post-serverless-trends/",
            "title": "Post-Serverless Era Trends",
            "date_modified": "2024-01-22T19:26:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39093970\">Comments</a>",
            "url": "https://www.jotaen.net/iE3XC/",
            "title": "CLI tool, written in Rust, to diff directory snapshots",
            "date_modified": "2024-01-22T19:14:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39091416\">Comments</a>",
            "url": "https://tuananh.net/2024/01/21/cloud-cost-optimization-at-scale-part-1/",
            "title": "Cutting down AWS cost by $150k per year simply by shutting things off",
            "date_modified": "2024-01-22T16:33:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39087837\">Comments</a>",
            "url": "https://supabase.com/blog/should-i-open-source-my-company",
            "title": "Should I Open Source my Company?",
            "date_modified": "2024-01-22T09:40:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39081876\">Comments</a>",
            "url": "https://bitbytebit.substack.com/p/the-size-of-your-backlog-is-inversely",
            "title": "The size of abacklog is inversely proportional to how often we talk to customers",
            "date_modified": "2024-01-21T19:18:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39056902\">Comments</a>",
            "url": "https://sourcehut.org/blog/2024-01-19-outage-post-mortem/",
            "title": "Sourcehut network outage post-mortem",
            "date_modified": "2024-01-19T15:55:40.000Z"
        },
        {
            "content_html": "<div id=\"rss-wrap\">\n<figure>\n  <img src=\"https://cdn.arstechnica.net/wp-content/uploads/2024/01/list-800x713.jpg\" alt=\"How a 27-year-old busted the myth of Bitcoin’s anonymity\">\n      <p><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2024/01/list.jpg\" data-height=\"1722\" data-width=\"1932\">Enlarge</a> (credit: Sam Rodriguez)</p>  </figure>\n\n\n\n\n\n\n<div><a name=\"page-1\"></a></div>\n<p><b>JUST OVER A DECADE AGO</b>, <a href=\"https://www.wired.com/tag/bitcoin/\"><i>Bitcoin</i></a><i> appeared to many of its adherents to be the crypto-anarchist holy grail: truly private digital cash for the Internet.</i></p>\n<p><i>Satoshi Nakamoto, the cryptocurrency’s mysterious and unidentifiable inventor, had stated in an email introducing Bitcoin that “participants can be anonymous.” And the </i><a href=\"https://www.wired.com/2015/04/silk-road-1/\"><i>Silk Road</i></a><i> dark-web drug market seemed like living proof of that potential, enabling the sale of hundreds of millions of dollars in illegal drugs and other contraband for bitcoin while flaunting its impunity from law enforcement.</i></p>\n<p><i>This is the story of the revelation in late 2013 that Bitcoin was, in fact, the</i> opposite <i>of untraceable—that its blockchain would actually allow researchers, tech companies, and law enforcement to trace and identify users with even more transparency than the existing financial system. That discovery would upend the world of cybercrime. Bitcoin tracing would, over the next few years, solve the mystery of the theft of a half-billion dollar stash of bitcoins from the world’s first crypto exchange, help enable the </i><a href=\"https://www.wired.com/story/alphabay-series-part-1-the-shadow/\"><i>biggest dark-web drug market takedown in history</i></a><i>, lead to the arrest of hundreds of pedophiles around the world in the </i><a href=\"https://www.wired.com/story/tracers-in-the-dark-welcome-to-video-crypto-anonymity-myth/\"><i>bust of the dark web’s largest child sexual abuse video site</i></a><i>, and result in the </i><a href=\"https://www.wired.com/story/bitcoin-seizure-record-doj-crypto-tracing-monero/\"><i>first</i></a><i>-, </i><a href=\"https://www.wired.com/story/silk-road-bitcoin-seizure-james-zhong/\"><i>second</i></a><i>-, and </i><a href=\"https://www.wired.com/story/feds-seize-billion-stolen-silk-road-bitcoin/\"><i>third</i></a><i>-biggest law enforcement monetary seizures in the history of the US Justice Department.</i></p></div><p><a href=\"https://arstechnica.com/?p=1996584#p3\">Read 65 remaining paragraphs</a> | <a href=\"https://arstechnica.com/?p=1996584&amp;comments=1\">Comments</a></p>",
            "url": "https://arstechnica.com/?p=1996584",
            "title": "How a 27-year-old busted the myth of Bitcoin’s anonymity",
            "date_modified": "2024-01-18T11:00:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39032752\">Comments</a>",
            "url": "https://michael.stapelberg.ch/posts/2024-01-17-systemd-indefinite-service-restarts/",
            "title": "Systemd: Enable Indefinite Service Restarts",
            "date_modified": "2024-01-17T20:08:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39029961\">Comments</a>",
            "url": "https://lgug2z.com/articles/deploying-a-cloudflare-r2-backed-nix-binary-cache-attic-on-fly-io/",
            "title": "Cloudflare R2-Backed Nix Binary Cache on Fly.io",
            "date_modified": "2024-01-17T16:32:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39029439\">Comments</a>",
            "url": "https://read.engineerscodex.com/p/meta-xfaas-serverless-functions-explained",
            "title": "Meta's serverless platform processing trillions of function calls a day (2023)",
            "date_modified": "2024-01-17T15:57:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39028672\">Comments</a>",
            "url": "https://read.engineerscodex.com/p/how-apple-built-icloud-to-store-billions",
            "title": "Apple built iCloud to store billions of databases",
            "date_modified": "2024-01-17T15:05:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39026791\">Comments</a>",
            "url": "https://willowprotocol.org/",
            "title": "Willow Protocol",
            "date_modified": "2024-01-17T12:27:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39019119\">Comments</a>",
            "url": "https://status.kagi.com/issues/2024-01-12-kagi-down-on-some-regions/",
            "title": "Post-mortem for last week's incident at Kagi",
            "date_modified": "2024-01-16T21:04:39.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/fee2p7/netbsd_amd64_guest_can_now_boot_40ms\">Comments</a></p>",
            "url": "https://www.reddit.com/r/BSD/s/09ZWUI3Ufa",
            "title": "A NetBSD/amd64 guest can now boot in 40ms",
            "date_modified": "2024-01-16T20:42:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=39004785\">Comments</a>",
            "url": "https://nixcademy.com/2024/01/15/nix-on-macos/",
            "title": "Going declarative on macOS with Nix and Nix-Darwin",
            "date_modified": "2024-01-15T19:10:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38998516\">Comments</a>",
            "url": "https://www.bitsand.cloud/posts/slashing-data-transfer-costs/",
            "title": "Slashing Data Transfer Costs in AWS by 99%",
            "date_modified": "2024-01-15T08:19:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38995068\">Comments</a>",
            "url": "https://nixos.org/",
            "title": "NixOS: Declarative Builds and Deployments",
            "date_modified": "2024-01-14T22:27:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38993680\">Comments</a>",
            "url": "https://www.grepular.com/Skiff_Emails_Various_Privacy_Failures",
            "title": "Skiff: Various Privacy Failures",
            "date_modified": "2024-01-14T19:40:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38992292\">Comments</a>",
            "url": "https://www.ypsidanger.com/announcing-project-bluefin/",
            "title": "Project Bluefin: an immutable, developer-focused, Cloud-native Linux",
            "date_modified": "2024-01-14T17:23:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38988960\">Comments</a>",
            "url": "https://devopscycle.com/blog/the-ultimate-docker-cheat-sheet/",
            "title": "The Ultimate Docker Cheat Sheet",
            "date_modified": "2024-01-14T09:45:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38981844\">Comments</a>",
            "url": "https://betterstack.com/community/guides/scaling-docker/podman-vs-docker/",
            "title": "Exploring Podman: A More Secure Docker Alternative",
            "date_modified": "2024-01-13T17:00:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38977516\">Comments</a>",
            "url": "https://github.com/aspen-cloud/triplit",
            "title": "Triplit: Open-source DB that syncs data between server and browser in real-time",
            "date_modified": "2024-01-13T05:13:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38975912\">Comments</a>",
            "url": "https://github.com/aws/aws-lc-rs",
            "title": "AWS Libcrypto for Rust",
            "date_modified": "2024-01-13T00:11:00.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/de9gjb/exploring_podman_more_secure_docker\">Comments</a></p>",
            "url": "https://betterstack.com/community/guides/scaling-docker/podman-vs-docker/",
            "title": "Exploring Podman: A More Secure Docker Alternative",
            "date_modified": "2024-01-12T11:28:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38966601\">Comments</a>",
            "url": "https://www.tweag.io/blog/2023-10-05-cli-ux-in-topiary/",
            "title": "CLI user experience case study",
            "date_modified": "2024-01-12T11:01:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38966035\">Comments</a>",
            "url": "https://outage.sr.ht/",
            "title": "Statement regarding the ongoing Sourcehut outage",
            "date_modified": "2024-01-12T09:43:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38961262\">Comments</a>",
            "url": "https://www.trevoragilbert.com/posts/a-love-letter-to-tinkerable-software/",
            "title": "A Love Letter to Tinkerable Software",
            "date_modified": "2024-01-11T23:42:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38959668\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=38959668",
            "title": "Ask HN: How can I make local dev with containers hurt less?",
            "date_modified": "2024-01-11T21:48:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38953846\">Comments</a>",
            "url": "https://cloud.google.com/blog/products/networking/eliminating-data-transfer-fees-when-migrating-off-google-cloud/",
            "title": "Removing data transfer fees when moving off Google Cloud",
            "date_modified": "2024-01-11T15:49:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38951147\">Comments</a>",
            "url": "https://www.jonashietala.se/blog/2024/01/11/exploring_the_gleam_ffi/",
            "title": "Exploring the Gleam FFI",
            "date_modified": "2024-01-11T12:08:00.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/bm1gzi/dynamodb_foreign_data_wrapper_for\">Comments</a></p>",
            "url": "https://github.com/pgspider/dynamodb_fdw",
            "title": "DynamoDB Foreign Data Wrapper for PostgreSQL",
            "date_modified": "2024-01-11T04:48:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38943420\">Comments</a>",
            "url": "https://www.metahead.dev/",
            "title": "Metahead – An enterprise-grade, Git-based metarepo",
            "date_modified": "2024-01-10T21:27:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38937705\">Comments</a>",
            "url": "https://en.wikipedia.org/wiki/British_Post_Office_scandal",
            "title": "British Post Office Scandal",
            "date_modified": "2024-01-10T08:29:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38933021\">Comments</a>",
            "url": "https://www.nature.com/articles/d41586-024-00012-z",
            "title": "Ibogaine banishes PTSD, small study finds",
            "date_modified": "2024-01-09T22:15:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38932398\">Comments</a>",
            "url": "https://www.phoronix.com/news/Linux-6.8-Networking",
            "title": "Linux 6.8 Network Optimizations Can Boost TCP Performance by ~40%",
            "date_modified": "2024-01-09T21:32:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38930126\">Comments</a>",
            "url": "https://www.rabbit.tech/keynote",
            "title": "Rabbit: LLM-First Mobile Phone",
            "date_modified": "2024-01-09T18:44:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38929543\">Comments</a>",
            "url": "https://jade.fyi/blog/flakes-arent-real/",
            "title": "Flakes aren't real and cannot hurt you: using Nix flakes the non-flake way",
            "date_modified": "2024-01-09T18:05:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38928693\">Comments</a>",
            "url": "https://blog.bytebytego.com/p/how-discord-serves-15-million-users",
            "title": "Discord Serves 15M Users on One Server",
            "date_modified": "2024-01-09T17:12:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38914407\">Comments</a>",
            "url": "https://twitter.com/josevalim/status/1744395345872683471",
            "title": "Today we celebrate by announcing that Elixir is a gradually typed language",
            "date_modified": "2024-01-08T16:29:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38914407\">Comments</a>",
            "url": "https://twitter.com/josevalim/status/1744395345872683471",
            "title": "Elixir is now a gradually typed language",
            "date_modified": "2024-01-08T16:29:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38913425\">Comments</a>",
            "url": "https://github.com/wagoodman/dive",
            "title": "Dive: A tool for exploring a Docker image, layer contents and more",
            "date_modified": "2024-01-08T15:35:38.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/krvzop/adventures_linux_userspace_at_meta\">Comments</a></p>",
            "url": "https://media.ccc.de/v/all-systems-go-2023-193-adventures-of-linux-userspace-at-meta",
            "title": "Adventures of Linux Userspace at Meta",
            "date_modified": "2024-01-08T00:57:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38894424\">Comments</a>",
            "url": "https://vitaut.net/posts/2024/faster-cpp-compile-times/",
            "title": "Optimizing the unoptimizable: a journey to faster C++ compile times",
            "date_modified": "2024-01-06T19:21:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38877059\">Comments</a>",
            "url": "https://sentry.io/legal/changelog/",
            "title": "Sentry new TOS to use data to train AI with no opt-out",
            "date_modified": "2024-01-05T08:48:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38875318\">Comments</a>",
            "url": "https://github.com/binkley/modern-java-practices",
            "title": "Modern Java/JVM Build Practices",
            "date_modified": "2024-01-05T03:18:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38872362\">Comments</a>",
            "url": "https://commandcenter.blogspot.com/2024/01/what-we-got-right-what-we-got-wrong.html",
            "title": "Go: What we got right, what we got wrong",
            "date_modified": "2024-01-04T21:16:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38868610\">Comments</a>",
            "url": "https://blog.digger.dev/how-open-id-connect-works-illustrated/",
            "title": "How Open ID Connect Works",
            "date_modified": "2024-01-04T16:03:47.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/8cbqum/performance_benchmarks_cloud_virtual\">Comments</a></p>",
            "url": "https://bas.codes/posts/cloudbench2312",
            "title": "Performance Benchmarks of Cloud Virtual Machines",
            "date_modified": "2024-01-03T18:54:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38850496\">Comments</a>",
            "url": "https://github.com/google/gvisor",
            "title": "Gvisor: Application Kernel for Containers",
            "date_modified": "2024-01-03T03:50:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38839436\">Comments</a>",
            "url": "https://protocol.bryanjohnson.com/",
            "title": "Blueprint health protocol – Bryan Johnson (founder braintree/Venmo)",
            "date_modified": "2024-01-02T08:31:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38839436\">Comments</a>",
            "url": "https://protocol.bryanjohnson.com/",
            "title": "Blueprint health protocol",
            "date_modified": "2024-01-02T08:31:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38834780\">Comments</a>",
            "url": "https://thisisimportant.net/posts/content-as-a-graph/",
            "title": "Displaying Content as a Graph",
            "date_modified": "2024-01-01T19:52:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38833434\">Comments</a>",
            "url": "https://www.bloomberg.com/news/articles/2023-12-30/ant-completes-process-of-removing-billionaire-jack-ma-s-control",
            "title": "Ant Completes Process of Removing Jack Ma's Control",
            "date_modified": "2024-01-01T17:23:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38828040\">Comments</a>",
            "url": "https://github.com/ublue-os/bazzite",
            "title": "Bazzite – a SteamOS-like OCI image for desktop, living room, and handheld PCs",
            "date_modified": "2023-12-31T22:21:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38827599\">Comments</a>",
            "url": "https://matklad.github.io/2023/12/31/O(1)-build-file.html#O-1-Build-File",
            "title": "O(1) Build File",
            "date_modified": "2023-12-31T21:17:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38823252\">Comments</a>",
            "url": "http://devops-pipeline.com/",
            "title": "Mazzle – A Pipelines as Code Tool",
            "date_modified": "2023-12-31T11:15:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38823252\">Comments</a>",
            "url": "http://devops-pipeline.com/",
            "title": "Mazzle – A pipelines as code tool",
            "date_modified": "2023-12-31T11:15:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38814152\">Comments</a>",
            "url": "https://techbooks.substack.com/p/why-asking-your-customers-what-they",
            "title": "Asking your customers what they want doesn't work",
            "date_modified": "2023-12-30T10:30:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38809485\">Comments</a>",
            "url": "https://incident.io/blog/festive-macbooks",
            "title": "Tracking developer build times to decide if the M3 MacBook is worth upgrading",
            "date_modified": "2023-12-29T19:57:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38798233\">Comments</a>",
            "url": "https://pythonspeed.com/articles/alpine-docker-python/",
            "title": "Using Alpine can make Python Docker builds 50× slower",
            "date_modified": "2023-12-28T20:31:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38792753\">Comments</a>",
            "url": "https://loderunnerwebgame.com/game/",
            "title": "Lode Runner (HTML5 Remake)",
            "date_modified": "2023-12-28T12:24:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38791870\">Comments</a>",
            "url": "https://media.ccc.de/b/congress/2023",
            "title": "37C3 Video List",
            "date_modified": "2023-12-28T10:03:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38791575\">Comments</a>",
            "url": "https://media.ccc.de/v/37c3-12326-you_ve_just_been_fucked_by_psyops",
            "title": "You've just been fucked by psyops [video]",
            "date_modified": "2023-12-28T09:17:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38775579\">Comments</a>",
            "url": "https://github.com/macos-fuse-t/fuse-t",
            "title": "FUSE-T is a kext-less implementation of FUSE for macOS that uses NFSv4",
            "date_modified": "2023-12-26T20:13:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38772039\">Comments</a>",
            "url": "https://github.com/jstrieb/just.sh",
            "title": "Show HN: Just.sh – compiler that turns Justfiles into portable shell scripts",
            "date_modified": "2023-12-26T14:30:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38771561\">Comments</a>",
            "url": "https://obsidian.md/changelog/2023-12-26-desktop-v1.5.3/",
            "title": "Obsidian 1.5 Desktop (Public)",
            "date_modified": "2023-12-26T13:24:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38768997\">Comments</a>",
            "url": "https://lucumr.pocoo.org/2023/12/25/life-and-death-of-open-source/",
            "title": "The life and death of open source companies",
            "date_modified": "2023-12-26T04:45:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38755180\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=38755180",
            "title": "If only someone told me this before my first startup",
            "date_modified": "2023-12-24T17:54:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38745667\">Comments</a>",
            "url": "https://www.vulture.com/2023/12/hbomberguy-interview-james-somerton-plagiarism.html",
            "title": "Hbomberguy didn't want to make that 4-hour plagiarism video",
            "date_modified": "2023-12-23T16:56:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38745070\">Comments</a>",
            "url": "https://github.com/maypok86/otter",
            "title": "Otter, Fastest Go in-memory cache based on S3-FIFO algorithm",
            "date_modified": "2023-12-23T15:49:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38744050\">Comments</a>",
            "url": "https://hachyderm.io/@paulbiggar/111627367674590120",
            "title": "Paul Biggar removed from CircleCI board for pro-Palestine blog post",
            "date_modified": "2023-12-23T13:18:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38742692\">Comments</a>",
            "url": "https://read.engineerscodex.com/p/how-pinterest-scaled-to-11-million",
            "title": "How Pinterest scaled",
            "date_modified": "2023-12-23T08:51:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38733142\">Comments</a>",
            "url": "https://www.tokyodev.com/articles/obtaining-a-business-manager-visa-in-japan",
            "title": "How I Obtained a Business Manager Visa in Japan",
            "date_modified": "2023-12-22T11:20:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38729696\">Comments</a>",
            "url": "https://fusionauth.io/articles/identity-basics/what-is-oidc",
            "title": "What Is OIDC?",
            "date_modified": "2023-12-22T01:20:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38728435\">Comments</a>",
            "url": "https://www.builder.io/blog/structured-clone",
            "title": "Deep Cloning Objects in JavaScript, the Modern Way",
            "date_modified": "2023-12-21T23:13:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38721726\">Comments</a>",
            "url": "https://medium.com/@prajzler/webassembly-orchestration-with-visual-flows-eef4df56bde2",
            "title": "WASM Container Orchestration with Visual Flows",
            "date_modified": "2023-12-21T15:34:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38720615\">Comments</a>",
            "url": "https://isovalent.com/blog/post/cisco-acquires-isovalent/",
            "title": "Cisco to acquire Isovalent",
            "date_modified": "2023-12-21T14:15:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38720057\">Comments</a>",
            "url": "https://who.ldelossa.is/posts/ebpf-networking-technique-packet-redirection/",
            "title": "eBPF Networking Techniques – Packet Redirection",
            "date_modified": "2023-12-21T13:18:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38714607\">Comments</a>",
            "url": "https://www.panoptica.app/research/7-ways-to-escape-a-container",
            "title": "How to Escape a Container",
            "date_modified": "2023-12-20T22:40:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38708794\">Comments</a>",
            "url": "https://www.zachleat.com/web/netlify-and-nextjs/",
            "title": "Netlify's disingenuous survey-based attack on Next.js (and eleventy, too)",
            "date_modified": "2023-12-20T14:04:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38708436\">Comments</a>",
            "url": "https://antonz.org/trying-chdb/",
            "title": "Trying chDB, an embeddable ClickHouse engine",
            "date_modified": "2023-12-20T13:31:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38701899\">Comments</a>",
            "url": "https://github.com/wavetermdev/waveterm",
            "title": "Show HN: Wave – Modern Open-Source Terminal (macOS and Linux)",
            "date_modified": "2023-12-19T21:29:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38700794\">Comments</a>",
            "url": "https://x.com/dominicstpierre/status/1737069029976617420?s=46",
            "title": "Did the cloud made us over-engineer some systems that could have been simpler?",
            "date_modified": "2023-12-19T20:07:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38695750\">Comments</a>",
            "url": "https://jepsen.io/analyses/mysql-8.0.34",
            "title": "Jepsen: MySQL 8.0.34",
            "date_modified": "2023-12-19T14:17:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38695453\">Comments</a>",
            "url": "https://thebsdbox.co.uk/2023/12/08/Application-traffic-with-eBPF/",
            "title": "Application Traffic with eBPF",
            "date_modified": "2023-12-19T13:48:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38685393\">Comments</a>",
            "url": "https://fly.io/blog/fks/",
            "title": "Fly Kubernetes",
            "date_modified": "2023-12-18T17:21:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38681541\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=PeKMEXUrlq4",
            "title": "Behind the scenes scaling ChatGPT and the OpenAI APIs [video] - Eng Mgr @ OpenAI",
            "date_modified": "2023-12-18T12:22:36.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/wntq5o/maybe_we_don_t_need_uuidv7_after_all\">Comments</a></p>",
            "url": "https://lu.sagebl.eu/notes/maybe-we-dont-need-uuidv7/",
            "title": "Maybe We Don’t Need UUIDv7 After All",
            "date_modified": "2023-12-17T11:37:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38667876\">Comments</a>",
            "url": "https://jack-vanlightly.com/blog/2023/11/29/s3-express-one-zone-not-quite-what-i-hoped-for",
            "title": "S3 Express One Zone, not quite what I hoped for",
            "date_modified": "2023-12-16T21:29:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38664729\">Comments</a>",
            "url": "https://github.com/francoismichel/ssh3",
            "title": "SSH3: SSHv2 using HTTP/3 and QUIC",
            "date_modified": "2023-12-16T15:06:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38661890\">Comments</a>",
            "url": "https://github.com/wesql/wescale",
            "title": "Show HN: I've built a MySQL proxy that supports online DDL",
            "date_modified": "2023-12-16T05:03:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38656776\">Comments</a>",
            "url": "https://blaz.is/blog/post/mfio-release/",
            "title": "Mfio – Completion I/O for Rust",
            "date_modified": "2023-12-15T18:06:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38656382\">Comments</a>",
            "url": "https://changelog.com/posts/nixos-fatal-flaw",
            "title": "NixOS has one fatal flaw",
            "date_modified": "2023-12-15T17:31:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38651805\">Comments</a>",
            "url": "https://codesandbox.io/blog/how-we-clone-a-running-vm-in-2-seconds",
            "title": "We clone a running VM in 2 seconds",
            "date_modified": "2023-12-15T07:13:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38647484\">Comments</a>",
            "url": "https://www.hashicorp.com/blog/mitchell-reflects-as-he-departs-hashicorp",
            "title": "Mitchell reflects as he departs HashiCorp",
            "date_modified": "2023-12-14T21:27:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38643348\">Comments</a>",
            "url": "https://community.ui.com/questions/Security-Issue-Cloud-Site-Manager-presented-me-your-consoles-not-mine/376ec514-572d-476d-b089-030c4313888c",
            "title": "Ubiquiti showing other users' consoles",
            "date_modified": "2023-12-14T16:42:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38635617\">Comments</a>",
            "url": "https://www.biscuitsec.org/",
            "title": "Biscuit Authorization",
            "date_modified": "2023-12-13T23:18:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38629626\">Comments</a>",
            "url": "https://ubuntu.com/blog/ubuntu-performance-engineering-with-frame-pointers-by-default",
            "title": "Ubuntu 24.04 LTS will enable frame pointers by default",
            "date_modified": "2023-12-13T16:23:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38616691\">Comments</a>",
            "url": "https://supabase.com/blog/edge-functions-node-npm",
            "title": "Edge Functions: Node and native NPM compatibility",
            "date_modified": "2023-12-12T18:59:38.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/q1hiod/trying_chdb_embeddable_clickhouse\">Comments</a></p>",
            "url": "https://antonz.org/trying-chdb/",
            "title": "Trying chDB, an embeddable ClickHouse engine",
            "date_modified": "2023-12-12T12:39:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38607424\">Comments</a>",
            "url": "https://www.theverge.com/23945184/epic-v-google-fortnite-play-store-antitrust-trial-updates",
            "title": "Epic vs. Google: Google Loses",
            "date_modified": "2023-12-12T00:21:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38596634\">Comments</a>",
            "url": "https://www.pcgamer.com/for-dooms-30th-anniversary-the-johns-romero-and-carmack-reunited-to-celebrate-the-fps-that-changed-everything-i-want-to-thank-everybody-in-the-doom-community-for-keeping-this-game-alive/",
            "title": "John Carmack and John Romero reunited to talk DOOM on its 30th Anniversary",
            "date_modified": "2023-12-11T01:14:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38588858\">Comments</a>",
            "url": "https://trippy.cli.rs/",
            "title": "Trippy – A Network Diagnostic Tool",
            "date_modified": "2023-12-10T03:46:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38578771\">Comments</a>",
            "url": "https://github.com/philips-labs/terraform-aws-github-runner",
            "title": "Terraform module for scalable GitHub action runners on AWS",
            "date_modified": "2023-12-09T05:03:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38578247\">Comments</a>",
            "url": "https://www.theregister.com/2023/12/08/hashicorp_openbao_fork/",
            "title": "HashiCorp Vault Forked into OpenBao",
            "date_modified": "2023-12-09T03:27:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38570624\">Comments</a>",
            "url": "https://www.warpbuild.com/",
            "title": "Show HN: WarpBuild – x86-64 and arm GitHub Action runners for 30% faster builds",
            "date_modified": "2023-12-08T16:11:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38559192\">Comments</a>",
            "url": "https://tailscale.com/blog/choose-your-ip/",
            "title": "Choose your own IP",
            "date_modified": "2023-12-07T17:29:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38546520\">Comments</a>",
            "url": "https://spotlightjs.com/",
            "title": "Spotlight: Sentry for Development",
            "date_modified": "2023-12-06T17:03:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38542764\">Comments</a>",
            "url": "https://fly.io/blog/rethinking-serverless-with-flame/",
            "title": "Rethinking Serverless with Flame",
            "date_modified": "2023-12-06T12:03:39.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/i7k7zv/nix_super\">Comments</a></p>",
            "url": "https://github.com/privatevoid-net/nix-super",
            "title": "Nix Super",
            "date_modified": "2023-12-05T18:15:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38518994\">Comments</a>",
            "url": "https://practicalbetterments.com/switch-off-bad-tv-settings/",
            "title": "Switch off bad TV settings",
            "date_modified": "2023-12-04T16:08:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38512248\">Comments</a>",
            "url": "https://blog.carlosgaldino.com/writing-a-file-system-from-scratch-in-rust.html",
            "title": "Writing a file system from scratch in Rust",
            "date_modified": "2023-12-04T00:20:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38504178\">Comments</a>",
            "url": "https://blog.trailofbits.com/2023/11/06/adding-build-provenance-to-homebrew/",
            "title": "Adding Build Provenance to Homebrew",
            "date_modified": "2023-12-03T02:16:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38504134\">Comments</a>",
            "url": "https://rachelbythebay.com/w/2023/11/30/armv6/",
            "title": "Clang now makes binaries an original Pi B+ can't run",
            "date_modified": "2023-12-03T02:07:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38503671\">Comments</a>",
            "url": "https://github.com/infinyon/fluvio",
            "title": "Show HN: Fluvio – Distributed stream processing system written in Rust and WASM",
            "date_modified": "2023-12-03T00:35:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38502857\">Comments</a>",
            "url": "https://github.com/json-web-proofs/json-web-proofs",
            "title": "JSON Web Proofs",
            "date_modified": "2023-12-02T22:36:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38499134\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=38499134",
            "title": "Ask HN: What are some unpopular technologies you wish people knew more about?",
            "date_modified": "2023-12-02T15:16:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38496264\">Comments</a>",
            "url": "https://github.com/zitadel/oidc",
            "title": "Easy to use OpenID Connect client and server library written for Go",
            "date_modified": "2023-12-02T05:48:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38495204\">Comments</a>",
            "url": "https://blog.railway.app/p/gcp-incidents",
            "title": "GCP Incidents",
            "date_modified": "2023-12-02T02:14:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38490766\">Comments</a>",
            "url": "https://twitter.com/JustJake/status/1730644510592692627?s=20",
            "title": "I never got a response after Google assured me that they'd do a full retro",
            "date_modified": "2023-12-01T19:03:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38489307\">Comments</a>",
            "url": "https://sqlsync.dev/posts/stop-building-databases/",
            "title": "SQLSync – Stop Building Databases",
            "date_modified": "2023-12-01T17:19:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38487112\">Comments</a>",
            "url": "https://noiselith.com/",
            "title": "Easy Stable Diffusion XL in your device, offline",
            "date_modified": "2023-12-01T14:34:50.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/bhcdqn/how_pgroll_works_under_hood\">Comments</a></p>",
            "url": "https://xata.io/blog/pgroll-internals",
            "title": "How pgroll works under the hood",
            "date_modified": "2023-11-30T19:44:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38472124\">Comments</a>",
            "url": "https://www.phoronix.com/forums/forum/phoronix/latest-phoronix-articles/1424461-openzfs-is-still-battling-a-data-corruption-issue?p=1424496#post1424496",
            "title": "In OpenZFS and Btrfs, everyone was just guessing",
            "date_modified": "2023-11-30T11:13:51.000Z"
        },
        {
            "content_html": "<p>Today we are excited to announce the general availability of SaaS Quick Launch, a new feature in <a href=\"https://aws.amazon.com/marketplace/\">AWS Marketplace</a> that makes it easy and secure to deploy SaaS products.</p> \n<p>Before SaaS Quick Launch, configuring and launching third-party SaaS products could be time-consuming and costly, especially in certain categories like security and monitoring. Some products require hours of engineering time to manually set up permissions policies and cloud infrastructure. Manual multistep configuration processes also introduce risks when buyers rely on unvetted deployment templates and instructions from third-party resources.</p> \n<p>SaaS Quick Launch helps buyers make the deployment process easy, fast, and secure by offering step-by-step instructions and resource deployment using preconfigured <a href=\"https://aws.amazon.com/cloudformation/\">AWS CloudFormation</a> templates. The software vendor and AWS validate these templates to ensure that the configuration adheres to the latest AWS security standards.</p> \n<p><span><strong>Getting started with SaaS Quick Launch<br> </strong></span>It’s easy to find which SaaS products have Quick Launch enabled when you are browsing in AWS Marketplace. Products that have this feature configured have a <strong>Quick Launch</strong> tag in their description.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/07/sql_image01.jpeg\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/07/sql_image01.jpeg\" alt=\"Quick Launch tag in AWS Marketplace\" width=\"1148\" height=\"943\"></a></p> \n<p>After completing the purchase process for a Quick Launch–enabled product, you will see a button to set up your account. That button will take you to the <strong>Configure and launch</strong> page, where you can complete the registration to set up your SaaS account, deploy any required AWS resources, and launch the SaaS product.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/06/new-image03.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/06/new-image03.png\" alt=\"Step 1 - set permissions\" width=\"2074\" height=\"1084\"></a></p> \n<p>The first step ensures that your account has the required AWS permissions to configure the software.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/09/sql_image02.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/09/sql_image02.png\" alt=\"Step 1 - set permissions\" width=\"1047\" height=\"307\"></a></p> \n<p>The second step involves configuring the vendor account, either to sign in to an existing account or to create a new account on the vendor website. After signing in, the vendor site may pass essential keys and parameters that are needed in the next step to configure the integration.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/06/new-image07.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/06/new-image07.png\" alt=\"Step 2 - Log into the vendor account\" width=\"2060\" height=\"498\"></a></p> \n<p>The third step allows you to configure the software and AWS integration. In this step, the vendor provides one or more CloudFormation templates that provision the required AWS resources to configure and use the product.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/09/sql_image03.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/09/sql_image03.png\" alt=\"Step 3 - Configure your software and AWS integration\" width=\"1073\" height=\"608\"></a></p> \n<p>The final step is to launch the software once everything is configured.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/06/new-image06.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/11/06/new-image06.png\" alt=\"Step 6 - Launch your software\" width=\"2056\" height=\"496\"></a></p> \n<p><span><strong>Availability<br> </strong></span>Sellers can enable this feature in their SaaS product. If you are a seller and want to learn how to set this up in your product, check the <a href=\"https://docs.aws.amazon.com/marketplace/latest/userguide/saas-product-settings.html#saas-quick-launch\">Seller Guide</a> for detailed instructions.</p> \n<p>To learn more about SaaS in AWS Marketplace, visit<a href=\"https://aws.amazon.com/marketplace/features/software-as-a-service-saas/\"> the service page</a> and <a href=\"https://aws.amazon.com/marketplace/search/results?FULFILLMENT_OPTION_TYPE=SAAS&amp;filters=FULFILLMENT_OPTION_TYPE\">view all the available SaaS products currently in AWS Marketplace</a>.</p> \n<p>— <a href=\"https://twitter.com/mavi888uy\" target=\"_blank\" rel=\"noopener\">Marcia</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/easily-deploy-saas-products-with-new-quick-launch-in-aws-marketplace/",
            "title": "Easily deploy SaaS products with new Quick Launch in AWS Marketplace",
            "date_modified": "2023-11-30T00:05:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38464057\">Comments</a>",
            "url": "https://github.com/Mozilla-Ocho/llamafile",
            "title": "Llamafile lets you distribute and run LLMs with a single file",
            "date_modified": "2023-11-29T19:29:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38463953\">Comments</a>",
            "url": "https://github.com/reddit/redditsans",
            "title": "Reddit Sans",
            "date_modified": "2023-11-29T19:22:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38461121\">Comments</a>",
            "url": "https://deno.com/blog/cron",
            "title": "Deno Cron",
            "date_modified": "2023-11-29T16:03:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38455505\">Comments</a>",
            "url": "https://sourcegraph.com/blog/ex-googler-guide-dev-tools",
            "title": "An ex-Googler's guide to dev tools (2020)",
            "date_modified": "2023-11-29T04:16:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38423469\">Comments</a>",
            "url": "https://www.darlinghq.org/",
            "title": "Darling: Run macOS Software on Linux",
            "date_modified": "2023-11-26T17:52:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38418359\">Comments</a>",
            "url": "https://computer.rip/2023-11-25-the-curse-of-docker.html",
            "title": "The Curse of Docker",
            "date_modified": "2023-11-26T01:14:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38410423\">Comments</a>",
            "url": "https://discourse.nixos.org/t/oci-images-is-there-something-similar-to-distroless-images-built-with-nix/35957",
            "title": "Fat OCI images are a cultural problem",
            "date_modified": "2023-11-25T01:56:45.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/uoasj5/zero_downtime_live_migration_stateful\">Comments</a></p>",
            "url": "https://www.youtube.com/watch?v=HrtX0JrjekE",
            "title": "Zero-Downtime Live Migration of Stateful VMs on Kubernetes",
            "date_modified": "2023-11-24T22:36:52.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/aysyan/dockerfile_arg_footgun\">Comments</a></p>",
            "url": "https://jmtd.net/log/dockerfile_arg_footgun/",
            "title": "Dockerfile ARG footgun",
            "date_modified": "2023-11-24T17:51:02.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/acn2wc/fastify_vite_titans_combined\">Comments</a></p>",
            "url": "https://fastify-vite.dev/",
            "title": "@fastify/vite: Titans Combined",
            "date_modified": "2023-11-24T16:00:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38399262\">Comments</a>",
            "url": "https://mastodon.social/@tdp_org/111462161186925649",
            "title": "BBC Outage",
            "date_modified": "2023-11-24T00:05:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38391934\">Comments</a>",
            "url": "https://blog.abctaylor.com/how-i-discovered-caching-cdns-were-throttling-my-everyday-browsing/",
            "title": "I discovered caching CDNs were throttling my everyday browsing",
            "date_modified": "2023-11-23T12:15:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38368073\">Comments</a>",
            "url": "https://blog.matiaspan.dev/posts/exploring-dagger-streamlining-ci-cd-pipelines-with-code/",
            "title": "Streamlining CI/CD Pipelines with Code",
            "date_modified": "2023-11-21T18:41:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38362874\">Comments</a>",
            "url": "https://tmpout.sh/3/20.html",
            "title": "Easylkb: Easy Linux Kernel Builder",
            "date_modified": "2023-11-21T12:42:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38344196\">Comments</a>",
            "url": "https://twitter.com/satyanadella/status/1726509045803336122",
            "title": "Sam Altman, Greg Brockman and others to join Microsoft",
            "date_modified": "2023-11-20T07:56:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38341035\">Comments</a>",
            "url": "https://github.com/kamranahmedse/local-ses",
            "title": "Trap and test AWS SES emails locally",
            "date_modified": "2023-11-20T01:38:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38340742\">Comments</a>",
            "url": "https://github.com/rsms/inter/releases/tag/v4.0",
            "title": "The Inter font family version 4.0",
            "date_modified": "2023-11-20T01:04:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38334102\">Comments</a>",
            "url": "https://shavingtheyak.com/2023/10/28/hashicorps-terraform-cloud-rum-pricing-sticker-shock/",
            "title": "Terraform Cloud Pricing Changes Sticker Shock",
            "date_modified": "2023-11-19T16:23:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38322793\">Comments</a>",
            "url": "https://berkeleygraphics.com/typefaces/berkeley-mono/",
            "title": "Berkeley Mono Typeface",
            "date_modified": "2023-11-18T18:32:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38320707\">Comments</a>",
            "url": "https://etcha.dev",
            "title": "Show HN: Etcha – Infinite scale, serverless config management",
            "date_modified": "2023-11-18T15:39:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38300425\">Comments</a>",
            "url": "https://www.uber.com/en-NL/blog/nilaway-practical-nil-panic-detection-for-go/",
            "title": "Practical nil panic detection for Go",
            "date_modified": "2023-11-17T06:59:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38300297\">Comments</a>",
            "url": "https://pganalyze.com/blog/automatic-indexing-system-postgres-pganalyze-indexing-engine",
            "title": "An automatic indexing system for Postgres",
            "date_modified": "2023-11-17T06:38:18.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/1d2neg/neat_github_actions_patterns_for_github\">Comments</a></p>",
            "url": "https://boinkor.net/2023/11/neat-github-actions-patterns-for-github-merge-queues/",
            "title": "Neat GitHub Actions patterns for GitHub Merge Queues",
            "date_modified": "2023-11-17T00:35:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38294623\">Comments</a>",
            "url": "https://linear.app/blog/planning-for-unplanned-work",
            "title": "Planning for Unplanned Work in Linear",
            "date_modified": "2023-11-16T19:58:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38294569\">Comments</a>",
            "url": "https://blog.oneuptime.com/moving-from-aws-to-bare-metal/",
            "title": "Moving from AWS to Bare-Metal saved us $230k per year",
            "date_modified": "2023-11-16T19:54:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38280664\">Comments</a>",
            "url": "https://blog.artie.so/best-practices-on-running-redshift-at-scale",
            "title": "Running Redshift at Scale",
            "date_modified": "2023-11-15T18:48:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38270265\">Comments</a>",
            "url": "https://inko-lang.org",
            "title": "Inko Programming Language",
            "date_modified": "2023-11-14T21:47:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38268958\">Comments</a>",
            "url": "https://github.com/eunomia-bpf/bpftime",
            "title": "Bpftime: Userspace eBPF runtime for fast Uprobe and Syscall hook and Plugins",
            "date_modified": "2023-11-14T20:10:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38255923\">Comments</a>",
            "url": "https://www.macchaffee.com/blog/2023/solarwinds-hack-lessons-learned/",
            "title": "We've learned nothing from the SolarWinds hack",
            "date_modified": "2023-11-13T21:56:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38240278\">Comments</a>",
            "url": "https://felix-knorr.net/posts/2023-11-11-github-actions.html",
            "title": "GitHub Actions Are a Problem",
            "date_modified": "2023-11-12T14:22:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38227428\">Comments</a>",
            "url": "https://insights.adadot.com/2023/11/10/serverless-at-scale-lessons-from-200-million-lambda-invocations/",
            "title": "Serverless at Scale: Lessons from 200M Lambda Invocations",
            "date_modified": "2023-11-11T03:39:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38226743\">Comments</a>",
            "url": "https://alan.norbauer.com/articles/browser-debugging-tricks",
            "title": "Weird debugging tricks the browser doesn’t want you to know",
            "date_modified": "2023-11-11T01:35:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38225724\">Comments</a>",
            "url": "https://www.system-transparency.org/",
            "title": "System Transparency: a security architecture for bare-metal servers",
            "date_modified": "2023-11-10T22:59:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38221379\">Comments</a>",
            "url": "https://github.com/githubnext/monaspace/blob/main/docs/Texture%20Healing.md",
            "title": "Texture Healing for Monospace Fonts",
            "date_modified": "2023-11-10T17:07:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38214915\">Comments</a>",
            "url": "https://xeiaso.net/notes/cursorless-alien-magic/",
            "title": "Cursorless is alien magic from the future",
            "date_modified": "2023-11-10T04:04:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38210574\">Comments</a>",
            "url": "https://monaspace.githubnext.com/",
            "title": "Monaspace",
            "date_modified": "2023-11-09T20:16:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38176053\">Comments</a>",
            "url": "https://aftermath.site/welcome-to-aftermath",
            "title": "Ex-Kotaku staff go independent and launch Aftermath",
            "date_modified": "2023-11-07T12:33:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38155544\">Comments</a>",
            "url": "https://www.wirehub.org/",
            "title": "Show HN: WireHub – easily create and share WireGuard networks",
            "date_modified": "2023-11-05T20:54:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38152109\">Comments</a>",
            "url": "https://github.com/ekzhang/sshx",
            "title": "Show HN: Sshx, a web-based collaborative terminal",
            "date_modified": "2023-11-05T15:44:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38142782\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=38142782",
            "title": "Ask HN: How would French police locate suspects by tapping their devices?",
            "date_modified": "2023-11-04T16:52:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38140242\">Comments</a>",
            "url": "https://www.fermyon.com/blog/introducing-spin-v2",
            "title": "Spin 2.0 – open-source tool for building and running WASM apps",
            "date_modified": "2023-11-04T12:01:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38126060\">Comments</a>",
            "url": "https://charm.sh/blog/the-next-generation/",
            "title": "Charm has raised $6M in funding",
            "date_modified": "2023-11-03T08:46:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38118577\">Comments</a>",
            "url": "https://dansdatathoughts.substack.com/p/from-s3-to-r2-an-economic-opportunity",
            "title": "From S3 to R2: An economic opportunity",
            "date_modified": "2023-11-02T19:15:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38096955\">Comments</a>",
            "url": "https://cra.mr/sentry-from-the-beginning/",
            "title": "Sentry: From the Beginning",
            "date_modified": "2023-11-01T11:35:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38086387\">Comments</a>",
            "url": "https://github.com/streamdal/streamdal",
            "title": "Show HN: Streamdal – an open-source tail -f for your data",
            "date_modified": "2023-10-31T15:31:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38082171\">Comments</a>",
            "url": "https://rehanvdm.com/blog/should-you-use-a-lambda-monolith-lambdalith-for-the-api",
            "title": "Should you use a Lambda Monolith, a.k.a. Lambdalith, for your API?",
            "date_modified": "2023-10-31T09:29:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38062649\">Comments</a>",
            "url": "https://www.openstatus.dev/blog/migration-backend-from-vercel-to-fly",
            "title": "Migrating our backend from Vercel to Fly.io",
            "date_modified": "2023-10-29T20:50:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38057591\">Comments</a>",
            "url": "https://discourse.nixos.org/t/nixos-reproducible-builds-minimal-installation-iso-successfully-independently-rebuilt/34756",
            "title": "NixOS Reproducible Builds: minimal ISO successfully independently rebuilt",
            "date_modified": "2023-10-29T11:41:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38056395\">Comments</a>",
            "url": "https://boards.greenhouse.io/supabase/jobs/5005843004",
            "title": "Supabase (YC S20) Is Hiring a Technical Product Marketer (Fully Remote)",
            "date_modified": "2023-10-29T07:01:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38055991\">Comments</a>",
            "url": "https://github.com/CycodeLabs/raven",
            "title": "Raven – CI/CD Security Analyzer",
            "date_modified": "2023-10-29T05:23:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38052864\">Comments</a>",
            "url": "https://fly.io/phoenix-files/elixir-and-phoenix-can-do-it-all/",
            "title": "Elixir and Phoenix can do it all",
            "date_modified": "2023-10-28T19:57:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38044062\">Comments</a>",
            "url": "https://www.theguardian.com/media/2023/oct/27/us-asks-qatar-to-turn-down-the-volume-of-al-jazeera-news-coverage",
            "title": "US asks Qatar to 'turn down the volume' of Al Jazeera news coverage",
            "date_modified": "2023-10-27T21:15:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38039905\">Comments</a>",
            "url": "https://pakikiproxy.com/",
            "title": "Show HN: Pākiki Proxy – An intercepting proxy for penetration testing",
            "date_modified": "2023-10-27T15:35:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38039831\">Comments</a>",
            "url": "https://fusionauth.io/blog/grammarly-proves-ciam-not-optional",
            "title": "Grammarly's OAuth Mistakes",
            "date_modified": "2023-10-27T15:31:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38028480\">Comments</a>",
            "url": "https://nextjs.org/blog/next-14",
            "title": "Next.js 14",
            "date_modified": "2023-10-26T17:02:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38023891\">Comments</a>",
            "url": "https://oxide.computer/blog/the-cloud-computer",
            "title": "Oxide: The Cloud Computer",
            "date_modified": "2023-10-26T10:43:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38018217\">Comments</a>",
            "url": "https://www.epicweb.dev/why-i-wont-use-nextjs",
            "title": "I Won't Use Next.js",
            "date_modified": "2023-10-25T20:52:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38016849\">Comments</a>",
            "url": "https://nandovillalba.medium.com/why-i-think-gcp-is-better-than-aws-ea78f9975bda",
            "title": "I think GCP is better than AWS (2020)",
            "date_modified": "2023-10-25T19:01:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38012311\">Comments</a>",
            "url": "https://github.com/orbitalapi/orbital",
            "title": "Show HN: Orbital – Dynamically unifying APIs and data with no glue code",
            "date_modified": "2023-10-25T12:59:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38010267\">Comments</a>",
            "url": "https://flawless.dev/",
            "title": "Flawless – Durable execution engine for Rust",
            "date_modified": "2023-10-25T07:39:11.000Z"
        },
        {
            "content_html": "<p>The AWS European Sovereign Cloud will allow government agencies, regulated industries, and the independent software vendors (ISVs) that support them to store sensitive data and run critical workloads on AWS infrastructure that is operated and supported by AWS employees located in and residents of the European Union (EU). The first Region will be located in Germany.</p> \n<p><span><strong>Background</strong></span><br> Late last year we announced the <a href=\"https://aws.amazon.com/blogs/security/aws-digital-sovereignty-pledge-control-without-compromise/\">AWS Digital Sovereignty Pledge</a> and made a commitment to offer you (and all AWS customers) the most advanced set of sovereignty controls and features available in the cloud. Since that announcement we have taken several important steps forward in fulfillment of that pledge:</p> \n<p><strong>May 2023</strong> – We <a href=\"https://aws.amazon.com/blogs/security/delivering-on-the-aws-digital-sovereignty-pledge-control-without-compromise/\">announced</a> that <a href=\"https://aws.amazon.com/ec2/nitro/\">AWS Nitro System</a> had been validated by an independent third-party to confirm that it contains no mechanism that allows anyone at AWS to access your data on AWS hosts. At the same time we announced that the <a href=\"https://aws.amazon.com/kms/\">AWS Key Management Service (KMS) External Key Store</a> allows you to store keys outside of AWS and use them to encrypt data stored in AWS.</p> \n<p><strong>August 2023</strong> – We <a href=\"https://aws.amazon.com/blogs/security/aws-digital-sovereignty-pledge-announcing-new-dedicated-infrastructure-options/\">announced</a> <a href=\"https://aws.amazon.com/dedicatedlocalzones\">AWS Dedicated Local Zones</a>, infrastructure that is fully managed by AWS and built for exclusive use by a customer or community, and placed in a customer-specified location or data center.</p> \n<p><span><strong>AWS European Sovereign Cloud</strong></span><br> The upcoming AWS European Sovereign Cloud will be separate from, and independent of, the eight existing AWS Regions already open in Frankfurt, Ireland, London, Milan, Paris, Stockholm, Spain, and Zurich. It will give you additional options for deployment, while providing AWS services, APIs, and tools that you are already familiar with. The design will help you meet your data residency, operational autonomy, and resiliency needs.</p> \n<p>In order to maintain separation between this cloud and the existing AWS Global Cloud you will need to create a fresh AWS account. The metadata you create such as data labels, categories, permissions, and configurations will be stored within the EU. This does not apply to AWS account information such as spend and billing data, which will be aggregated and used to ensure that you get favorable pricing within any applicable volume usage tiers.</p> \n<p>As I mentioned earlier, this cloud will be operated and supported by AWS employees located in and residents of the EU, with support available 24/7/365.</p> \n<p>The AWS European Sovereign Cloud will be operationally independent of the other regions, with separate in-Region billing and usage metering systems.</p> \n<p><span><strong>Initial Region</strong></span><br> The initial region will be located in Germany. It will launch with multiple Availability Zones, each in separate and distinct geographic locations, with enough distance between them to significantly reduce the risk of a single event impacting your business continuity.&nbsp;We will have additional details on the list of available services, instance types, and so forth as we get closer to the launch.</p> \n<p>Over time, this and other regions in this cloud will also function as parent regions for <a href=\"https://aws.amazon.com/outposts/\">AWS Outposts</a> and Dedicated Local Zones. These options give you even more flexibility with regard to isolation and in-country data residency. If you would like to express your interest in <a href=\"https://aws.amazon.com/dedicatedlocalzones/\">Dedicated Local Zones</a> in your country, please contact your AWS account manager.</p> \n<p><span><strong>Get Ready</strong></span><br> You can start to build applications today in any of the existing regions and move them to the AWS European Sovereign Cloud when the region launches. You can also initiate conversations with your local regulatory authorities in order to better understand any issues that are specific to your particular location.</p> \n<p>— <a href=\"https://twitter.com/jeffbarr\">Jeff</a>;</p>",
            "url": "https://aws.amazon.com/blogs/aws/in-the-works-aws-european-sovereign-cloud/",
            "title": "In the Works – AWS European Sovereign Cloud",
            "date_modified": "2023-10-25T05:06:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=38004178\">Comments</a>",
            "url": "https://gittuf.github.io/",
            "title": "Gittuf – a security layer for Git",
            "date_modified": "2023-10-24T19:31:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37994725\">Comments</a>",
            "url": "https://www.jetbrains.com/writerside/",
            "title": "Writerside – a new technical writing environment from JetBrains",
            "date_modified": "2023-10-24T03:57:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37993702\">Comments</a>",
            "url": "https://arstechnica.com/security/2023/10/1password-detects-suspicious-activity-in-its-internal-okta-account/",
            "title": "1Password detects \"suspicious activity\" in its internal Okta account",
            "date_modified": "2023-10-24T00:55:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37985777\">Comments</a>",
            "url": "https://mswjs.io/blog/introducing-msw-2.0/",
            "title": "Introducing MSW 2.0",
            "date_modified": "2023-10-23T14:01:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37985777\">Comments</a>",
            "url": "https://mswjs.io/blog/introducing-msw-2.0/",
            "title": "MSW 2.0 – Mock Service Worker",
            "date_modified": "2023-10-23T14:01:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37985450\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37985450",
            "title": "Ask HN: Was any Starfighter postmortem ever published?",
            "date_modified": "2023-10-23T13:39:47.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/wqky9h/build_farm_visualizations\">Comments</a></p>",
            "url": "https://medium.com/snowflake/build-farm-visualizations-5a079477502d",
            "title": "Build farm visualizations",
            "date_modified": "2023-10-23T11:26:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37973937\">Comments</a>",
            "url": "https://eval.blog/research/microsoft-account-token-leaks-in-harvest/",
            "title": "Microsoft Account's OAuth tokens leaking via open redirect in Harvest",
            "date_modified": "2023-10-22T09:15:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37961464\">Comments</a>",
            "url": "https://old.reddit.com/r/hetzner/comments/17ccp3i/hetzner_does_run_a_mitm_proxy_in_front_of_my/",
            "title": "Hetzner does run a (MitM) proxy in front of my server",
            "date_modified": "2023-10-20T21:01:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37961166\">Comments</a>",
            "url": "https://www.devever.net/~hl/xmpp-incident",
            "title": "How to mitigate the Hetzner/Linode XMPP.ru MitM interception incident",
            "date_modified": "2023-10-20T20:31:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37955264\">Comments</a>",
            "url": "https://notes.valdikss.org.ru/jabber.ru-mitm/",
            "title": "Encrypted traffic interception on Hetzner and Linode targeting Jabber service",
            "date_modified": "2023-10-20T12:40:53.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yic12j/using_temporal_scale_data\">Comments</a></p>",
            "url": "https://blog.peerdb.io/using-temporal-to-scale-data-synchronization-at-peerdb",
            "title": "Using Temporal to Scale Data Synchronization at PeerDB",
            "date_modified": "2023-10-19T19:46:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37946203\">Comments</a>",
            "url": "https://fasterthanli.me/articles/just-paying-figma-15-dollars",
            "title": "Just paying Figma because nothing else works",
            "date_modified": "2023-10-19T17:58:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37942791\">Comments</a>",
            "url": "https://github.com/Exein-io/pulsar",
            "title": "Linux runtime security agent powered by eBPF",
            "date_modified": "2023-10-19T13:42:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37934488\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37934488",
            "title": "Ask HN: Is anyone using Cloud Dev Environments (e.g. Codespaces/Replit) at work?",
            "date_modified": "2023-10-18T20:41:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37934488\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37934488",
            "title": "Ask HN: Is anyone using cloud dev environments (e.g. Codespaces/Replit) at work?",
            "date_modified": "2023-10-18T20:41:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37933497\">Comments</a>",
            "url": "https://engineering.aviatrix.com/bpf-tailcalls-8ef4273747a5",
            "title": "BPF Tailcalls",
            "date_modified": "2023-10-18T19:23:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37922272\">Comments</a>",
            "url": "https://blog.jetbrains.com/blog/2023/10/16/ai-graphics-at-jetbrains-story/",
            "title": "AI Graphics at JetBrains",
            "date_modified": "2023-10-17T22:08:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37911989\">Comments</a>",
            "url": "https://github.com/TeamPiped/Piped",
            "title": "Piped – An alternative privacy-friendly YouTube front end",
            "date_modified": "2023-10-17T08:19:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37901692\">Comments</a>",
            "url": "https://www.prequel.co/blog/building-cross-cloud-identity-federation-in-go",
            "title": "Building cross-cloud identity federation in Go for secure data sharing",
            "date_modified": "2023-10-16T15:55:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37900458\">Comments</a>",
            "url": "https://invidious.io/",
            "title": "Invidious – An open source alternative front-end to YouTube",
            "date_modified": "2023-10-16T14:38:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37888728\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37888728",
            "title": "Ask HN: What is your experience with Nano-Hydroxyapatite toothpaste?",
            "date_modified": "2023-10-15T11:01:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37888135\">Comments</a>",
            "url": "https://www.infoq.com/news/2023/10/cloudflare-sippy-migrate-s3/",
            "title": "Cloudflare Sippy: Incrementally Migrate Data from AWS S3 to Reduce Egress Fees",
            "date_modified": "2023-10-15T08:55:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37887049\">Comments</a>",
            "url": "https://twitter.com/vercel_support/status/1713295474998772215",
            "title": "Vercel employee used customer information to pursue a personal trademark matter",
            "date_modified": "2023-10-15T05:10:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37847454\">Comments</a>",
            "url": "https://cloud.google.com/blog/products/databases/announcing-cloud-spanner-price-performance-updates",
            "title": "Google Cloud Spanner is now half the cost of Amazon DynamoDB",
            "date_modified": "2023-10-11T17:25:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37841891\">Comments</a>",
            "url": "https://buildkite.com/pricing",
            "title": "Buildkite has quietly removed their $9 \"Team\" plan",
            "date_modified": "2023-10-11T07:34:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37834908\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37834908",
            "title": "Ask HN: SaaS Founders, What 3 advice would you give your younger selves?",
            "date_modified": "2023-10-10T17:26:37.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/bd53ai/introducing_pulumi_esc_easy_secure\">Comments</a></p>",
            "url": "https://www.pulumi.com/blog/environments-secrets-configurations-management/",
            "title": "Introducing Pulumi ESC: Easy and Secure Environments, Secrets and Configuration",
            "date_modified": "2023-10-10T13:00:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37812142\">Comments</a>",
            "url": "https://tailscale.dev/blog/docker-mod-tailscale",
            "title": "The Tailscale Universal Docker Mod",
            "date_modified": "2023-10-08T16:51:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37811814\">Comments</a>",
            "url": "https://render.com/blog/knative",
            "title": "Scaling Knative to 100K+ Webapps",
            "date_modified": "2023-10-08T16:21:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37805849\">Comments</a>",
            "url": "https://discuss.linuxcontainers.org/t/incus-0-1-has-been-released/18036",
            "title": "Initial release of Incus, the LXD community fork",
            "date_modified": "2023-10-07T21:48:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37797503\">Comments</a>",
            "url": "https://ghiculescu.substack.com/p/11-years-of-saas-product-strategy",
            "title": "Looking back on SaaS product strategy",
            "date_modified": "2023-10-06T23:12:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37792300\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37792300",
            "title": "Ask HN: Sales Tips for Solo Devs",
            "date_modified": "2023-10-06T15:44:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37787130\">Comments</a>",
            "url": "https://github.com/rails/rails/releases/tag/v7.1.0",
            "title": "Rails 7.1 Released",
            "date_modified": "2023-10-06T04:31:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37786781\">Comments</a>",
            "url": "https://blog.sigstore.dev/openpubkey-and-sigstore/",
            "title": "OpenPubKey and Sigstore",
            "date_modified": "2023-10-06T03:12:45.000Z"
        },
        {
            "content_html": "<div><p data-block-key=\"5qq2m\">In the face of rapid digital transformation, a positive organizational culture and user-centric design are the backbone of successful software delivery. And while Artificial Intelligence (AI) is the center of so many contemporary technical conversations, the impact of AI development tools on teams is still in its infancy.</p><p data-block-key=\"9ammq\">These are just some of the findings from the <a href=\"https://cloud.google.com/devops/state-of-devops/\">2023 Accelerate State of DevOps Report</a>, the annual report from Google Cloud’s DevOps Research and Assessment (<a href=\"https://www.devops-research.com/research.html\" target=\"_blank\">DORA</a>) team.</p><p data-block-key=\"7fgqj\">For nine years, the State of DevOps survey has assembled data from more than 36,000 professionals worldwide, making it the largest and longest-running research of its kind. This year, we took a deep dive into how high-performing DevOps performers bake these technical, process, and cultural capabilities into their development practices to drive success. Specifically, we explored three key outcomes of a having a DevOps practice and the capabilities that contribute to achieving them:</p><ul><li data-block-key=\"v9b9\">Organizational performance - generating value for customers and community</li><li data-block-key=\"86ek5\">Team performance - empowering teams to innovate and collaborate</li><li data-block-key=\"ftqvo\">Employee well-being - reducing burnout and increasing satisfaction/productivity</li></ul><p data-block-key=\"28jqs\">This year, we were working with a particularly robust data set: the total number of organic respondents increased by 3.6x compared to last year, allowing us to perform a deeper analysis of the relationship between ways of working and outcomes. Thank you to everyone who took the survey this year!</p><h3 data-block-key=\"em7bc\">Measuring software delivery performance</h3><p data-block-key=\"d05ig\">Our research shows that an organization’s level of software delivery performance predicts overall performance, team performance, and employee well-being. In turn, we use the following measures to understand the throughput and stability of software changes:</p><ul><li data-block-key=\"4krfi\">Change lead time: how long it takes a code change to go from committed to deployed</li><li data-block-key=\"128hp\">Deployment frequency: how frequently changes are pushed to production</li><li data-block-key=\"cmc3k\">Change failure rate: how frequently a software deployment introduces a failure that requires immediate intervention</li><li data-block-key=\"628pt\">Failed deployment recovery time: how long it takes to recover from a failed deployment</li></ul><p data-block-key=\"2lkch\">Our analysis revealed four performance levels, including the return of the Elite performance level, which <a href=\"https://cloud.google.com/blog/products/devops-sre/dora-2022-accelerate-state-of-devops-report-now-out\">we did not detect in last year’s cohort</a>. Elite performers around the world are able to achieve both throughput and stability.</p></div><div><div><div><figure><img alt=\"2\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/original_images/1_-_elite_performer_graph.png\"></figure></div></div></div><div><h2 data-block-key=\"5qq2m\">Five key insights</h2><p data-block-key=\"1aqp5\">There are <a href=\"https://services.google.com/fh/gumdrop/preview/misc/2023_infographic_sodr.pdf\" target=\"_blank\">several key takeaways</a> for teams who want to understand how to improve their software delivery capabilities. Here are some of the key insights from this year’s report:</p><p data-block-key=\"ca8aq\">1. <b>Establish a healthy culture</b></p><p data-block-key=\"1u0er\">Culture is foundational to building technical capabilities, igniting technical performance, reaching organizational performance goals, and helping employees be successful. A healthy culture can help reduce burnout, increase productivity, and increase job satisfaction. <i>Teams with</i> <a href=\"https://dora.dev/devops-capabilities/cultural/generative-organizational-culture/\" target=\"_blank\"><i>generative cultures</i></a><i>, composed of people who felt included and like they belonged on their team, have 30% higher organizational performance than organizations without a generative culture</i>.</p></div><div><div><div><figure><img alt=\"3\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/original_images/3_BLLLS0n.png\"><figcaption><p data-block-key=\"0vl6x\">The aspects of culture that can improve employee well-being</p></figcaption></figure></div></div></div><div><p data-block-key=\"5qq2m\">2. <b>Build with users in mind</b></p><p data-block-key=\"snoh\">Teams can deploy as fast and successfully as they'd like, but without the user in mind, it might be for naught. Our research shows that a user-centric approach to building applications and services is one of the strongest predictors of overall organizational performance. In fact, building with the user in mind appears to inform and drive improvements across all of the technical, process, and cultural capabilities we explore in the DORA research. <i>Teams that focus on the user have 40% higher organizational performance than teams that don’t</i>.</p></div><div><div><div><figure><img alt=\"4\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/original_images/4_vgusHRM.png\"></figure></div></div></div><div><p data-block-key=\"5qq2m\">3. <b>Amplify technical capabilities with quality documentation</b></p><p data-block-key=\"1njaa\">High-quality documentation amplifies the impact that DevOps technical capabilities (for example, continuous integration and trunk-based development) have on organizational performance. This means that quality documentation not only helps establish these technical capabilities, but helps them matter. For example, SRE practices are estimated to have 1.4x more impact on organizational performance when high-quality documentation is in place. Overall, <i>high-quality documentation leads to 25% higher team performance relative to low-quality documentation.</i></p><p data-block-key=\"1oduq\">4. <b>Distribute work fairly</b></p><p data-block-key=\"ad31l\">People who identify as underrepresented and women or those who chose to self-describe their gender have higher levels of burnout. There are likely multiple systemic and environmental factors that cause this. Unsurprisingly, we find that respondents who take on more repetitive work are more likely to experience higher levels of burnout, and members of underrepresented groups are more likely to take on more repetitive work: Underrepresented respondents report 24% more burnout than those who are not underrepresented. Underrepresented respondents do 29% more repetitive work than those who are not underrepresented.<i>And women or those who self-reported their gender do 40% more repetitive work than men.</i></p><p data-block-key=\"jrhr\">5. <b>Increase infrastructure flexibility with cloud</b></p><p data-block-key=\"cjs8k\">Teams can get the most value out of the cloud by leveraging the characteristics of cloud like rapid elasticity and on-demand self-service. These characteristics predict a more flexible infrastructure. Using a public cloud, for example, leads to a 22% increase in infrastructure flexibility relative to not using the cloud. This flexibility, in turn, leads to teams with <i>30% higher organizational performance</i> than those with inflexible infrastructures.</p><h3 data-block-key=\"cg6cc\">AI: we're just getting started</h3><p data-block-key=\"cug51\">There is a lot of enthusiasm about the potential of AI development tools. We saw this in this year’s results — in fact a majority of respondents are incorporating at least some AI into the tasks we included in our survey. But we anticipate that it will take some time for AI-powered tools to come into widespread and coordinated use in the industry. We are very interested in seeing how adoption grows over time and the impact that growth will have on performance measures and outcomes that are important to organizations. Here’s where we are seeing the adoption of AI tools today:</p></div><div><div><div><figure><img alt=\"5\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/original_images/5_IVN9ceZ.png\"></figure></div></div></div><div><h3 data-block-key=\"5qq2m\">Applying insights from DORA in your context</h3><p data-block-key=\"bhbqs\">The key takeaway from DORA’s research is that high performance requires continuous improvement. Regularly measure outcomes across your organization, teams, and employees. Identify areas for optimization and make incremental changes to dial up performance.</p><p data-block-key=\"9jlc9\">Don't let these insights sit on a shelf — put them into action. Contextualize the findings based on your team's current practices and pain points. Have open conversations about your bottlenecks. Comparing your metrics year-over-year is more meaningful than comparing yourself to other companies. Sustainable success comes from repeatedly finding and fixing your weaknesses. DORA's framework can help you determine which <a href=\"https://dora.dev/devops-capabilities/\" target=\"_blank\">capabilities</a> to focus on next for the biggest performance boost.</p><p data-block-key=\"ct2un\">We hope the Accelerate State of DevOps Report helps organizations of all sizes, industries, and regions improve their DevOps capabilities, and we look forward to hearing your thoughts and feedback. To learn more about the report and implementing DevOps with Google Cloud:</p><ul><li data-block-key=\"c6o3c\"><a href=\"https://cloud.google.com/devops/state-of-devops/\">Download the full report</a>.</li><li data-block-key=\"7nt2k\">Measure your team’s software delivery performance in less than a minute using DORA's <a href=\"https://dora.dev/quickcheck/\" target=\"_blank\">DevOps Quick Check.</a></li><li data-block-key=\"vhlj\">Model your organization around the DevOps capabilities of <a href=\"https://cloud.google.com/devops\">elite-performing teams</a>.</li><li data-block-key=\"17ddq\">Share your experiences, learn from others, and get inspiration by joining the <a href=\"https://dora.community/\" target=\"_blank\">DORA community</a>.</li></ul></div>",
            "url": "https://cloud.google.com/blog/products/devops-sre/announcing-the-2023-state-of-devops-report/",
            "title": "2023 State of DevOps Report: Culture is everything",
            "date_modified": "2023-10-05T11:00:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37776079\">Comments</a>",
            "url": "https://blog.bittacklr.be/the-workflow-pattern.html",
            "title": "The Workflow Pattern",
            "date_modified": "2023-10-05T08:17:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37763822\">Comments</a>",
            "url": "https://github.com/shuttle-hq/shuttle",
            "title": "Show HN: Shuttle – Build and ship backends without writing infrastructure files",
            "date_modified": "2023-10-04T11:51:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37762380\">Comments</a>",
            "url": "https://github.com/kata-containers/",
            "title": "Kata Containers: The speed of containers, the security of VMs",
            "date_modified": "2023-10-04T08:24:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37752366\">Comments</a>",
            "url": "https://xata.io/blog/pgroll-schema-migrations-postgres",
            "title": "Pgroll: zero-downtime, undoable, schema migrations for Postgres",
            "date_modified": "2023-10-03T14:20:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37742429\">Comments</a>",
            "url": "https://www.aidemos.info/dalle-3-examples/",
            "title": "Dalle-3 Examples",
            "date_modified": "2023-10-02T18:30:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37734759\">Comments</a>",
            "url": "https://floorp.app/en/",
            "title": "Floorp – a customisable Firefox fork from Japan",
            "date_modified": "2023-10-02T06:52:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37733763\">Comments</a>",
            "url": "https://github.com/NAlexPear/pg_branch",
            "title": "Pg_branch: Experimental Postgres extension brings Neon-like branching",
            "date_modified": "2023-10-02T03:49:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37733036\">Comments</a>",
            "url": "https://buildkite.com/blog/goodbye-integers-hello-uuids",
            "title": "Goodbye integers, hello UUIDv7",
            "date_modified": "2023-10-02T01:44:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37728915\">Comments</a>",
            "url": "https://abidmoon.hashnode.dev/2023-devops-is-terrible",
            "title": "2023 DevOps Is Terrible",
            "date_modified": "2023-10-01T18:29:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37723688\">Comments</a>",
            "url": "https://diziet.dreamwidth.org/16025.html",
            "title": "DKIM: Rotate and Publish Your Keys",
            "date_modified": "2023-10-01T07:44:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37717689\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37717689",
            "title": "Ask HN: SaaS pricing pages with high prices and not “contact sales”",
            "date_modified": "2023-09-30T17:29:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37708582\">Comments</a>",
            "url": "https://github.com/ArchGPT/insomnium",
            "title": "Insomnium – Local, privacy-focused fork of Insomnia API client",
            "date_modified": "2023-09-29T18:49:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37702632\">Comments</a>",
            "url": "https://prdeving.wordpress.com/2023/09/29/mmo-architecture-source-of-truth-dataflows-i-o-bottlenecks-and-how-to-solve-them/",
            "title": "MMO Architecture: Source of truth, Dataflows, I/O bottlenecks and how to solve",
            "date_modified": "2023-09-29T12:09:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37700847\">Comments</a>",
            "url": "https://posthog.com/blog/dev-marketing-paid-ads",
            "title": "Burning money on paid ads for a dev tool – what we've learned",
            "date_modified": "2023-09-29T08:30:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37695154\">Comments</a>",
            "url": "https://www.dockerstatus.com/",
            "title": "Docker Hub Registry timming out",
            "date_modified": "2023-09-28T20:12:31.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2023/09/Running-Serverless-Puppeteer-with-Workers-and-Durable-Objects.png\"></figure><p>Last year, we <a>announced the Browser Rendering API</a> – letting users running Puppeteer, a browser automation library, directly in Workers. Puppeteer is one of the most popular libraries used to interact with a headless browser instance to accomplish tasks like taking screenshots, generating PDFs, crawling web pages, and testing <a>web applications</a>. We’ve heard from developers that configuring and maintaining their own serverless browser automation systems can be quite painful.</p><p>The <a>Workers Browser Rendering API</a> solves this. It makes the Puppeteer library available directly in your Worker, connected to a real web browser, without the need to configure and manage infrastructure or keep browser sessions warm yourself. You can use <a>@cloudflare/puppeteer</a> to run the full Puppeteer API directly on Workers! </p><p>We’ve seen so much interest from the developer community since launching last year. While the Browser Rendering API is still in beta (sign up to our <a>waitlist</a> to get access), we wanted to share a way to get more out of our <a>current limits</a> by using the Browser Rendering API with <a>Durable Objects</a>. We’ll also be sharing pricing for the Rendering API, so you can build knowing exactly what you’ll pay for.</p><h3>Building a responsive web design testing tool with the Browser Rendering API</h3><p>As a designer or frontend developer, you want to make sure that content is well-designed for visitors browsing on different screen sizes. With the number of possible devices that users are browsing on are growing, it becomes difficult to test all the possibilities manually. While there are many testing tools on the market, we want to show how easy it is to create your own Chromium based tool with the Workers Browser Rendering API and Durable Objects. </p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/09/pasted-image-0--6--2.png\"></figure><p>We’ll be using the Worker to handle any incoming requests, pass them to the Durable Object to take screenshots and store them in an R2 bucket. The Durable Object is used to create a browser session that’s persistent. By using <a>Durable Object Alarms</a> we can keep browsers open for longer and reuse browser sessions across requests.</p><p>Let’s dive into how we can build this application:</p><ol><li>Create a Worker with a Durable Object, Browser Rendering API binding and R2 bucket. This is the resulting wrangler.toml:</li></ol><pre><code>name = \"rendering-api-demo\"\nmain = \"src/index.js\"\ncompatibility_date = \"2023-09-04\"\ncompatibility_flags = [ \"nodejs_compat\"]\naccount_id = \"c05e6a39aa4ccdd53ad17032f8a4dc10\"\n\n\n# Browser Rendering API binding\nbrowser = { binding = \"MYBROWSER\" }\n\n# Bind an R2 Bucket\n[[r2_buckets]]\nbinding = \"BUCKET\"\nbucket_name = \"screenshots\"\n\n# Binding to a Durable Object\n[[durable_objects.bindings]]\nname = \"BROWSER\"\nclass_name = \"Browser\"\n\n[[migrations]]\ntag = \"v1\" # Should be unique for each entry\nnew_classes = [\"Browser\"] # Array of new classes\n</code></pre><p>2. Define the Worker</p><p>This Worker simply passes the request onto the Durable Object.</p><pre><code>export default {\n\tasync fetch(request, env) {\n\n\t\tlet id = env.BROWSER.idFromName(\"browser\");\n\t\tlet obj = env.BROWSER.get(id);\n\t  \n\t\t// Send a request to the Durable Object, then await its response.\n\t\tlet resp = await obj.fetch(request.url);\n\t\tlet count = await resp.text();\n\t  \n\t\treturn new Response(\"success\");\n\t}\n};\n</code></pre><p>3. Define the Durable Object class</p><pre><code>const KEEP_BROWSER_ALIVE_IN_SECONDS = 60;\n\nexport class Browser {\n\tconstructor(state, env) {\n\t\tthis.state = state;\n\t\tthis.env = env;\n\t\tthis.keptAliveInSeconds = 0;\n\t\tthis.storage = this.state.storage;\n\t}\n  \n\tasync fetch(request) {\n\t\t// screen resolutions to test out\n\t\tconst width = [1920, 1366, 1536, 360, 414]\n\t\tconst height = [1080, 768, 864, 640, 896]\n\n\t\t// use the current date and time to create a folder structure for R2\n\t\tconst nowDate = new Date()\n\t\tvar coeff = 1000 * 60 * 5\n\t\tvar roundedDate = (new Date(Math.round(nowDate.getTime() / coeff) * coeff)).toString();\n\t\tvar folder = roundedDate.split(\" GMT\")[0]\n\n\t\t//if there's a browser session open, re-use it\n\t\tif (!this.browser) {\n\t\t\tconsole.log(`Browser DO: Starting new instance`);\n\t\t\ttry {\n\t\t\t  this.browser = await puppeteer.launch(this.env.MYBROWSER);\n\t\t\t} catch (e) {\n\t\t\t  console.log(`Browser DO: Could not start browser instance. Error: ${e}`);\n\t\t\t}\n\t\t  }\n\t\t\n\t\t// Reset keptAlive after each call to the DO\n\t\tthis.keptAliveInSeconds = 0;\n\t\t\n\t\tconst page = await this.browser.newPage();\n\n\t\t// take screenshots of each screen size \n\t\tfor (let i = 0; i &lt; width.length; i++) {\n\t\t\tawait page.setViewport({ width: width[i], height: height[i] });\n\t\t\tawait page.goto(\"https://workers.cloudflare.com/\");\n\t\t\tconst fileName = \"screenshot_\" + width[i] + \"x\" + height[i]\n\t\t\tconst sc = await page.screenshot({\n\t\t\t\tpath: fileName + \".jpg\"\n\t\t\t}\n\t\t\t);\n\n\t\t\tthis.env.BUCKET.put(folder + \"/\"+ fileName + \".jpg\", sc);\n\t\t  }\n\t\t\n\t\t// Reset keptAlive after performing tasks to the DO.\n\t\tthis.keptAliveInSeconds = 0;\n\n\t\t// set the first alarm to keep DO alive\n\t\tlet currentAlarm = await this.storage.getAlarm();\n\t\tif (currentAlarm == null) {\n\t\tconsole.log(`Browser DO: setting alarm`);\n\t\tconst TEN_SECONDS = 10 * 1000;\n\t\tthis.storage.setAlarm(Date.now() + TEN_SECONDS);\n\t\t}\n\t\t\n\t\tawait this.browser.close();\n\t\treturn new Response(\"success\");\n\t}\n\n\tasync alarm() {\n\t\tthis.keptAliveInSeconds += 10;\n\t\n\t\t// Extend browser DO life\n\t\tif (this.keptAliveInSeconds &lt; KEEP_BROWSER_ALIVE_IN_SECONDS) {\n\t\t  console.log(`Browser DO: has been kept alive for ${this.keptAliveInSeconds} seconds. Extending lifespan.`);\n\t\t  this.storage.setAlarm(Date.now() + 10 * 1000);\n\t\t} else console.log(`Browser DO: cxceeded life of ${KEEP_BROWSER_ALIVE_IN_SECONDS}. Browser DO will be shut down in 10 seconds.`);\n\t  }\n\n  }\n</code></pre><p>That’s it! With less than a hundred lines of code, you can fully customize a powerful tool to automate responsive web design testing. You can even incorporate it into your CI pipeline to automatically test different window sizes with each build and verify the result is as expected by using an automated library like <a>pixelmatch</a>.</p><h3>How much will this cost?</h3><p>We’ve spoken to many customers deploying a Puppeteer service on their own infrastructure, on <a>public cloud containers</a> or <a>functions</a> or using managed services. The common theme that we’ve heard is that these services are costly – costly to maintain and expensive to run. </p><p>While you won’t be billed for the Browser Rendering API yet, we want to be transparent with you about costs you start building. We know it’s important to understand the pricing structure so that you don’t get a surprise bill and so that you can design your application efficiently.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/09/pasted-image-0--7--2.png\"></figure><p>You pay based on two usage metrics:</p><ol><li>Number of sessions: A Browser Session is a new instance of a browser being launched</li><li>Number of concurrent sessions: Concurrent Sessions is the number of browser instances open at once</li></ol><p>Using Durable Objects to persist browser sessions improves performance by eliminating the time that it takes to spin up a new browser session. Since it re-uses sessions, it cuts down on the number of concurrent sessions needed. We highly encourage this model of session re-use if you expect to see consistent traffic for applications that you build on the Browser Rendering API.</p><p>If you have feedback about this pricing, we’re all ears. Feel free to reach out through <a>Discord</a> (channel name: browser-rendering-api-beta) and share your thoughts.</p><h3>Get Started</h3><p>Sign up to our <a>waitlist</a> to get access to the Workers Browser Rendering API. We’re so excited to see what you build! Share your creations with us on Twitter/X <a>@CloudflareDev</a> or on our Discord community.</p>",
            "url": "https://blog.cloudflare.com/running-serverless-puppeteer-workers-durable-objects",
            "title": "Running Serverless Puppeteer with Workers and Durable Objects",
            "date_modified": "2023-09-28T13:00:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37655477\">Comments</a>",
            "url": "https://macoscontainers.org/",
            "title": "macOS Containers v0.0.1",
            "date_modified": "2023-09-26T07:01:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37654891\">Comments</a>",
            "url": "https://major.io/p/quadlets-replace-docker-compose/",
            "title": "Quadlets might make me finally stop using Docker-compose – Major Hayden",
            "date_modified": "2023-09-26T05:39:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37649222\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37649222",
            "title": "Ask HN: Is “Distributed CI” Possible?",
            "date_modified": "2023-09-25T19:25:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37639010\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37639010",
            "title": "Tell HN: There is a highlights page on HN",
            "date_modified": "2023-09-25T02:17:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37636841\">Comments</a>",
            "url": "https://adriano.fyi/posts/2023-09-24-choose-postgres-queue-technology/",
            "title": "Choose Postgres queue technology",
            "date_modified": "2023-09-24T20:30:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37630628\">Comments</a>",
            "url": "https://blog.liblab.com/typescript-npm-packages-done-right/",
            "title": "TypeScript NPM Packages Done Right",
            "date_modified": "2023-09-24T06:28:30.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/lxa416/semgrep_static_analysis_journey\">Comments</a></p>",
            "url": "https://semgrep.dev/blog/2021/semgrep-a-static-analysis-journey/",
            "title": "Semgrep: a static analysis journey",
            "date_modified": "2023-09-23T23:49:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37626731\">Comments</a>",
            "url": "https://bottlerocket.dev",
            "title": "Bottlerocket – Minimal, immutable Linux OS with verified boot",
            "date_modified": "2023-09-23T19:44:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37612420\">Comments</a>",
            "url": "https://blog.yossarian.net/2023/09/22/GitHub-Actions-could-be-so-much-better",
            "title": "GitHub Actions could be so much better",
            "date_modified": "2023-09-22T14:21:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37611213\">Comments</a>",
            "url": "https://tailscale.com/kb/1236/kubernetes-operator/",
            "title": "Tailscale Kubernetes Operator",
            "date_modified": "2023-09-22T12:39:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37611136\">Comments</a>",
            "url": "https://docs.getunleash.io/topics/feature-flags/feature-flag-best-practices",
            "title": "11 Principles for building and scaling feature flag systems",
            "date_modified": "2023-09-22T12:31:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37588520\">Comments</a>",
            "url": "https://www.sequoiacap.com/article/generative-ai-act-two/",
            "title": "Generative AI's Act Two",
            "date_modified": "2023-09-20T19:02:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37587506\">Comments</a>",
            "url": "https://strada.hotwired.dev/",
            "title": "Strada – Create fully native controls, driven by your web app",
            "date_modified": "2023-09-20T18:00:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37581132\">Comments</a>",
            "url": "https://github.com/opentofu",
            "title": "OpenTF renames itself to OpenTofu",
            "date_modified": "2023-09-20T06:44:42.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/uwiqnf/lxd_containers_for_human_beings\">Comments</a></p>",
            "url": "https://secluded.site/lxd-containers-for-human-beings/",
            "title": "LXD: Containers for Human Beings",
            "date_modified": "2023-09-19T22:01:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37570929\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37570929",
            "title": "Show HN: Graphite – Stacked Diffs on GitHub",
            "date_modified": "2023-09-19T15:03:24.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/pt3cft/creating_base_oci_image_for_nix_flake\">Comments</a></p>",
            "url": "https://blog.cyplo.dev/posts/2023/09/act-runner-image/",
            "title": "Creating a base OCI image for Nix flake builds within Gitea/Forgejo",
            "date_modified": "2023-09-18T16:13:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37552085\">Comments</a>",
            "url": "https://blog.the-pans.com/notes-on-the-foundationdb-paper/",
            "title": "How FoundationDB works and why it works (2021)",
            "date_modified": "2023-09-18T04:00:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37531801\">Comments</a>",
            "url": "https://www.subdomain.center/",
            "title": "Subdomain.center – discover all subdomains for a domain",
            "date_modified": "2023-09-16T03:32:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37531048\">Comments</a>",
            "url": "https://www.helloinbox.net",
            "title": "Show HN: Hello Inbox – Free email deliverability checklist for marketers",
            "date_modified": "2023-09-16T01:18:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37481513\">Comments</a>",
            "url": "https://earthly.dev/blog/shutting-down-earthly-ci/",
            "title": "We built the fastest CI and it failed",
            "date_modified": "2023-09-12T14:07:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37440631\">Comments</a>",
            "url": "https://msprout.notion.site/LogoScale-A-Method-for-Vectorizing-Small-Crappy-Logos-dc0035b7473c44f8b94ffc4026d286c0",
            "title": "LogoScale – A method for vectorizing small, crappy logos",
            "date_modified": "2023-09-08T23:38:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37434117\">Comments</a>",
            "url": "https://bun.sh/blog/bun-v1.0",
            "title": "Bun v1.0.0",
            "date_modified": "2023-09-08T14:41:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37433218\">Comments</a>",
            "url": "https://rivet.ironcladapp.com/",
            "title": "Show HN: Rivet – open-source AI Agent dev env with real-world applications",
            "date_modified": "2023-09-08T13:29:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37431560\">Comments</a>",
            "url": "https://jpcamara.com/2023/04/12/pgbouncer-is-useful.html",
            "title": "PgBouncer is useful, important, and fraught with peril",
            "date_modified": "2023-09-08T09:43:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37430595\">Comments</a>",
            "url": "https://napi.rs/",
            "title": "Napi: Build compiled Node.js add-ons in Rust",
            "date_modified": "2023-09-08T07:27:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37403799\">Comments</a>",
            "url": "https://github.com/leandromoreira/linux-network-performance-parameters",
            "title": "Linux network performance parameters",
            "date_modified": "2023-09-06T11:56:35.000Z"
        },
        {
            "content_html": "<p>Vercel implemented Cron Jobs using Amazon EventBridge Scheduler, enabling their customers to create, manage, and run scheduled tasks at scale. The adoption of this feature was rapid, reaching over 7 million weekly cron invocations within a few months of release. This article shows how they did it and how they handle the massive scale they’re experiencing.</p> \n<p><a href=\"https://vercel.com/\" target=\"_blank\" rel=\"noopener\">Vercel</a> builds a front-end cloud that makes it easier for engineers to deploy and run their front-end applications. <a href=\"https://youtu.be/7bY-YH70h8g\" target=\"_blank\" rel=\"noopener\">With more than 100 million deployments in Vercel in the last two years</a>, Vercel helps users take advantage of best-in-class AWS infrastructure with zero configuration by relying heavily on serverless technology. Vercel provides a lot of features that help developers host their front-end applications. However, until the beginning of this year, they hadn’t built Cron Jobs yet.</p> \n<p>A cron job is a scheduled task that automates running specific commands or scripts at predetermined intervals or fixed times. It enables users to set up regular, repetitive actions, such as backups, sending notification emails to customers, or processing payments when a subscription needs to be renewed. Cron jobs are widely used in computing environments to improve efficiency and automate routine operations, and they were a commonly requested feature from Vercel’s customers.</p> \n<p>In December 2022, Vercel hosted an internal hackathon to foster innovation. That’s where <a href=\"https://www.linkedin.com/in/vvoyer/\" target=\"_blank\" rel=\"noopener\">Vincent Voyer</a>&nbsp;and <a href=\"https://www.linkedin.com/in/andreas-schneider-79043021a/\" target=\"_blank\" rel=\"noopener\">Andreas Schneider</a> joined forces to build a prototype cron job feature for the Vercel platform. They formed a team of five people and worked on the feature for a week. The team worked on different tasks, from building a user interface to display the cron jobs to creating the backend implementation of the feature.</p> \n<p><span><strong>Amazon EventBridge Scheduler<br> </strong></span>When the hackathon team started thinking about solving the cron job problem, their first idea was to use <a href=\"https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html\" target=\"_blank\" rel=\"noopener\">Amazon EventBridge rules that run on a schedule</a>. However, they realized quickly that this feature has a limit of 300 rules per account per AWS Region, which wasn’t enough for their intended use. Luckily, one of the team members had read <a href=\"https://aws.amazon.com/blogs/compute/introducing-amazon-eventbridge-scheduler/\" target=\"_blank\" rel=\"noopener\">the announcement of Amazon EventBridge Scheduler</a> in the AWS Compute blog and they thought this would be a perfect tool for their problem.</p> \n<p>By using <a href=\"https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html\" target=\"_blank\" rel=\"noopener\">EventBridge Scheduler</a>, they could schedule one-time or recurrently millions of tasks across over 270 AWS services without provisioning or managing the underlying infrastructure.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/03-how-itworks-1.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/03-how-itworks-1.png\" alt=\"How cron jobs work\" width=\"1798\" height=\"614\"></a></p> \n<p>For creating a new cron job in Vercel, a customer needs to define the frequency in which this task will run and the API they want to invoke. Vercel, in the backend, uses EventBridge Scheduler and creates a new schedule when a new cron job is created.</p> \n<p>To call the endpoint, the team used an <a href=\"https://aws.amazon.com/lambda/\" target=\"_blank\" rel=\"noopener\">AWS Lambda</a> function that receives the path that needs to be invoked as input parameters.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/04-how-it-works-2.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/04-how-it-works-2.png\" alt=\"How cron jobs works\" width=\"2022\" height=\"506\"></a></p> \n<p>When the time comes for the cron job to run, EventBridge Scheduler invokes the function, which then calls the customer website endpoint that was configured.</p> \n<p>By the end of the week, Vincent and his team had a working prototype version of the cron jobs feature, and they won a prize at the hackathon.</p> \n<p><span><strong>Building Vercel Cron Jobs<br> </strong></span>After working for one week on this prototype in December, the hackathon ended, and Vincent and his team returned to their regular jobs. In early January 2023, Vincent and the Vercel team decided to take the project and turn it into a real product.</p> \n<p>During the hackathon, the team built the fundamental parts of the feature, but there were some details that they needed to polish to make it production ready. Vincent and Andreas worked on the feature, and in less than two months, on February 22, 2023, they announced Vercel Cron Jobs to the public. The <a href=\"https://twitter.com/vercel/status/1628439610454843400?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1628439610454843400%7Ctwgr%5E%7Ctwcon%5Es1_&amp;ref_url=notion%3A%2F%2Fwww.notion.so%2Fmavi888%2FWork-28038053883f41f4a4fd8bf1ac4b768b%3Fp%3D47b26211a2f04d3692d1f6e6645712f5pm%3Ds\" target=\"_blank\" rel=\"noopener\">announcement tweet</a> got over 400 thousand views, and the community loved the launch.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/05-tweet.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/05-tweet-880x1024.png\" alt=\"Tweet from Vercel announcing cron jobs\" width=\"880\" height=\"1024\"></a></p> \n<p>The adoption of this feature was very rapid. Within a few months of launching Cron Jobs, Vercel reached over 7 million cron invocations per week, and they expect the adoption to continue growing.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/08/08/cron-invocations-week.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/08/08/cron-invocations-week.png\" alt=\"Cron jobs adoption\" width=\"1846\" height=\"834\"></a></p> \n<p><span><strong>How Vercel Cron Jobs Handles Scale<br> </strong></span>With this pace of adoption, scaling this feature is crucial for Vercel. In order to scale the amount of cron invocations at this pace, they had to make some business and architectural decisions.</p> \n<p>From the business perspective, they defined limits for their free-tier customers. Free-tier customers can create a maximum of two cron jobs in their account, and they can only have hourly schedules. This means that free customers cannot run a cron job every 30 minutes; instead, they can do it at most every hour. Only customers on Vercel paid tiers can take advantage of <a href=\"https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html#cron-based\" target=\"_blank\" rel=\"noopener\">EventBridge Scheduler minute granularity</a> for scheduling tasks.</p> \n<p>Also, for free customers, minute precision isn’t guaranteed. To achieve this, Vincent took advantage of the time window configuration from EventBridge Scheduler. The <a href=\"https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-flexible-time-windows.html\" target=\"_blank\" rel=\"noopener\">flexible time window</a> configuration allows you to start a schedule within a window of time. This means that the scheduled tasks are dispersed across the time window to reduce the impact of multiple requests on downstream services. This is very useful if, for example, many customers want to run their jobs at midnight. By using the flexible time window, the load can spread across a set window of time.</p> \n<p>From the architectural perspective, Vercel took advantage of hosting the APIs and owning the functions that the cron jobs invoke.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/06-validate.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/06-validate.png\" alt=\"Validating the calls\" width=\"2230\" height=\"806\"></a></p> \n<p>This means that when the Lambda function is started by EventBridge Scheduler, the function ends its run without waiting for a response from the API. Then Vercel validates if the cron job ran by checking if the API and Vercel function ran correctly from its observability mechanisms. In this way, the function duration is very short, less than 400 milliseconds. This allows Vercel to run a lot of functions per second without affecting their concurrency limits.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/02-lambdainfo.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/21/02-lambdainfo.png\" alt=\"Lambda invocations and duration dashboard\" width=\"3002\" height=\"550\"></a></p> \n<p><span><strong>What Was The Impact?<br> </strong></span>Vercel’s implementation of Cron Jobs is an excellent example of what serverless technologies enable. In two months, with two people working full time, they were able to launch a feature that their community needed and enthusiastically adopted. This feature shows the completeness of Vercel’s platform and is an important feature to convince their customers to move to a paid account.</p> \n<p>If you want to get started with EventBridge Scheduler, see <a href=\"https://serverlessland.com/patterns?services=eventbridge-scheduler\" target=\"_blank\" rel=\"noopener\">Serverless Land patterns for EventBridge Scheduler</a>, where you’ll find a broad range of examples to help you.</p> \n<p>— <a href=\"https://twitter.com/mavi888uy\" target=\"_blank\" rel=\"noopener\">Marcia</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/how-vercel-shipped-cron-jobs-in-2-months-using-amazon-eventbridge-scheduler/",
            "title": "How Vercel Shipped Cron Jobs in 2 Months Using Amazon EventBridge Scheduler",
            "date_modified": "2023-09-05T20:04:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37394979\">Comments</a>",
            "url": "https://maxibestof.one/typefaces",
            "title": "Browse Websites by Fonts",
            "date_modified": "2023-09-05T17:24:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37371426\">Comments</a>",
            "url": "https://vale.sh/",
            "title": "Vale.sh – A Linter for Prose",
            "date_modified": "2023-09-03T15:58:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37358541\">Comments</a>",
            "url": "https://brooker.co.za/blog/2023/07/28/ds-testing.html",
            "title": "Invariants: A Better Debugger?",
            "date_modified": "2023-09-02T04:21:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37356925\">Comments</a>",
            "url": "https://avestura.dev/blog/explaining-the-postgres-meme",
            "title": "Explaining the Postgres iceberg",
            "date_modified": "2023-09-01T22:43:55.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/1rbawx/explaining_postgres_meme\">Comments</a></p>",
            "url": "https://www.avestura.dev/blog/explaining-the-postgres-meme",
            "title": "Explaining The Postgres Meme",
            "date_modified": "2023-09-01T22:41:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37350422\">Comments</a>",
            "url": "https://www.shuttle.rs/blog/2022/07/28/patterns-with-rust-types",
            "title": "Patterns with Rust Types",
            "date_modified": "2023-09-01T13:16:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37345727\">Comments</a>",
            "url": "https://www.mux.com/blog/what-are-react-server-components",
            "title": "Things I wish I knew before moving 50K lines of code to React Server Components",
            "date_modified": "2023-09-01T01:29:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37338857\">Comments</a>",
            "url": "https://determinate.systems/posts/flake-schemas",
            "title": "Show HN: Flake schemas – teaching Nix about your flake outputs",
            "date_modified": "2023-08-31T15:35:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37335945\">Comments</a>",
            "url": "https://matthiasportzel.com/docker/",
            "title": "Docker Is Four Things",
            "date_modified": "2023-08-31T12:11:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37334486\">Comments</a>",
            "url": "https://github.com/opentffoundation/roadmap/issues/24",
            "title": "HashiCorp silently amend Terraform Registry TOS",
            "date_modified": "2023-08-31T09:19:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37324197\">Comments</a>",
            "url": "https://taras.glek.net/post/lightweight-virtualization-metallize-libkrun-vsock/",
            "title": "Lightweight Virtualization: Libkrun, Vsock, Metallize",
            "date_modified": "2023-08-30T16:09:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37323604\">Comments</a>",
            "url": "https://gist.github.com/kj800x/be3001c07c49fdb36970633b0bc6defb",
            "title": "Hacking the LG Monitor's EDID",
            "date_modified": "2023-08-30T15:24:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37316919\">Comments</a>",
            "url": "https://buildkite.com/blog/applying-sre-principles-to-cicd",
            "title": "Applying SRE Principles to CI/CD",
            "date_modified": "2023-08-30T02:24:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37295097\">Comments</a>",
            "url": "https://bit.kevinslin.com/p/opentelemetry-in-2023",
            "title": "OpenTelemetry in 2023",
            "date_modified": "2023-08-28T14:58:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37293911\">Comments</a>",
            "url": "https://seacrew.github.io/helm-compose/",
            "title": "Helm-Compose – The Docker-compose like tool for K8s development",
            "date_modified": "2023-08-28T13:48:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37281000\">Comments</a>",
            "url": "https://newsletter.systemdesign.one/p/whatsapp-engineering",
            "title": "Why WhatsApp Was Able to Support 50B Messages a Day with 32 Engineers",
            "date_modified": "2023-08-27T09:42:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37262440\">Comments</a>",
            "url": "https://opentf.org/announcement",
            "title": "OpenTF Announces Fork of Terraform",
            "date_modified": "2023-08-25T14:56:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37253035\">Comments</a>",
            "url": "https://www.usenix.org/publications/loginonline/freebsd-firecracker",
            "title": "FreeBSD on Firecracker",
            "date_modified": "2023-08-24T18:52:46.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ajarjp/freebsd_on_firecracker\">Comments</a></p>",
            "url": "https://www.usenix.org/publications/loginonline/freebsd-firecracker",
            "title": "FreeBSD on Firecracker",
            "date_modified": "2023-08-24T08:21:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37237583\">Comments</a>",
            "url": "https://generated.photos/human-generator/",
            "title": "AI Real-Time Human Full-Body Photo Generator",
            "date_modified": "2023-08-23T15:28:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37229030\">Comments</a>",
            "url": "https://pglocks.org/",
            "title": "PostgreSQL Lock Conflicts",
            "date_modified": "2023-08-22T21:53:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37226805\">Comments</a>",
            "url": "https://godly.website/",
            "title": "Godly – Astronomically good web design inspiration",
            "date_modified": "2023-08-22T18:33:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37224377\">Comments</a>",
            "url": "https://www.points411.com/",
            "title": "Show HN: Points and Miles Database",
            "date_modified": "2023-08-22T15:43:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37224148\">Comments</a>",
            "url": "https://flakehub.com/",
            "title": "Show HN: FlakeHub – Discover and publish Nix flakes",
            "date_modified": "2023-08-22T15:29:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37221622\">Comments</a>",
            "url": "https://frunc.de/how-to/chrome-to-firefox-tips/",
            "title": "Switching from Chrome to Firefox",
            "date_modified": "2023-08-22T12:20:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37216800\">Comments</a>",
            "url": "https://blog.podman.io/2023/08/celebrating-500k-downloads-the-podman-desktop-journey-%f0%9f%8e%89/",
            "title": "Podman Desktop celebrates 500k downloads",
            "date_modified": "2023-08-21T23:44:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37199495\">Comments</a>",
            "url": "https://nivenly.org/blog/2023/08/19/an-announcement-regarding-kris-n%C3%B3va/",
            "title": "Kris Nova passed away",
            "date_modified": "2023-08-20T14:30:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37188659\">Comments</a>",
            "url": "https://github.com/rivet-gg/rivet",
            "title": "Show HN: Rivet – Open-source game server management with Nomad and Rust",
            "date_modified": "2023-08-19T13:38:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37172477\">Comments</a>",
            "url": "https://github.com/systeminit/si",
            "title": "System Initiative: remove the papercuts from DevOps work",
            "date_modified": "2023-08-18T07:46:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37159712\">Comments</a>",
            "url": "https://www.koyeb.com/",
            "title": "Show HN: Run globally distributed full-stack apps on high-performance MicroVMs",
            "date_modified": "2023-08-17T10:45:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37144925\">Comments</a>",
            "url": "https://github.com/EuropeanRemote/european-remote-software-companies",
            "title": "Show HN: Repo with a list of 80 decent companies hiring remotely in Europe",
            "date_modified": "2023-08-16T10:09:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37138691\">Comments</a>",
            "url": "https://arstechnica.com/gadgets/2023/08/the-printers-that-require-ink-to-scan-and-fax/",
            "title": "Requiring ink to scan a document–yet another insult from the printer industry",
            "date_modified": "2023-08-15T20:06:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37137110\">Comments</a>",
            "url": "https://blog.redplanetlabs.com/2023/08/15/how-we-reduced-the-cost-of-building-twitter-at-twitter-scale-by-100x/",
            "title": "We reduced the cost of building Mastodon at Twitter-scale by 100x",
            "date_modified": "2023-08-15T17:54:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37134293\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37134293",
            "title": "Show HN: Layerform – Open-source development environments using Terraform files",
            "date_modified": "2023-08-15T14:15:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37107455\">Comments</a>",
            "url": "https://github.com/devcontainers/templates",
            "title": "Microsoft Docker Development Container Templates",
            "date_modified": "2023-08-13T07:17:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37102442\">Comments</a>",
            "url": "https://old.reddit.com/r/startups/comments/15p8qrx/my_0100m0_in_5_years_story/",
            "title": "My $0->$100M->$0 in 5 years story",
            "date_modified": "2023-08-12T17:37:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37079909\">Comments</a>",
            "url": "https://graphite.dev/blog/how-an-aws-aurora-feature-cut-db-costs",
            "title": "Aurora I/O optimized config saved 90% DB cost",
            "date_modified": "2023-08-10T18:29:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37062007\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=37062007",
            "title": "Show HN: Infracost (YC W21): Be proactive with your cloud costs",
            "date_modified": "2023-08-09T13:01:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37059533\">Comments</a>",
            "url": "https://www.theregister.com/2023/08/08/amazon_arm_servers/",
            "title": "Amazon has more than half of all Arm server CPUs in the world",
            "date_modified": "2023-08-09T07:05:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37059400\">Comments</a>",
            "url": "https://supabase.com/blog/supabase-local-dev",
            "title": "Supabase Local Dev: migrations, branching, and observability",
            "date_modified": "2023-08-09T06:40:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37055368\">Comments</a>",
            "url": "https://packaging-con.org",
            "title": "PackagingCon – a conference only for software package management",
            "date_modified": "2023-08-08T20:59:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37055368\">Comments</a>",
            "url": "https://packaging-con.org",
            "title": "PackagingCon – A conference only for software package management",
            "date_modified": "2023-08-08T20:59:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37052622\">Comments</a>",
            "url": "https://dukope.itch.io/lcd-please",
            "title": "LCD, Please – de-make of “Papers, please”, celebrating 10 years since launch",
            "date_modified": "2023-08-08T17:30:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37048409\">Comments</a>",
            "url": "https://www.ory.dev/global-identity-and-access-management-multi-region/",
            "title": "Show HN: Blueprint for a distributed multi-region IAM with Go and CockroachDB",
            "date_modified": "2023-08-08T13:05:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37045455\">Comments</a>",
            "url": "https://tinloof.com/blog/the-seo-scam-62000-dollars-later",
            "title": "The SEO scam: $62,000 later",
            "date_modified": "2023-08-08T06:58:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37037286\">Comments</a>",
            "url": "https://discuss.linuxcontainers.org/t/introducing-incus/17781",
            "title": "Incus, a community fork of LXD, now part of Linux Containers",
            "date_modified": "2023-08-07T17:05:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37020610\">Comments</a>",
            "url": "https://github.com/supabase/postgres_lsp",
            "title": "Postgres Language Server",
            "date_modified": "2023-08-06T10:26:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37010449\">Comments</a>",
            "url": "https://jamesconroyfinn.com/github-actions-and-vanity-metrics",
            "title": "GitHub Actions and Vanity Metrics",
            "date_modified": "2023-08-05T09:37:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37001345\">Comments</a>",
            "url": "https://hocus.dev/blog/virtualizing-development-environments/",
            "title": "Virtualizing Development Environments in 2023",
            "date_modified": "2023-08-04T15:28:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=37001345\">Comments</a>",
            "url": "https://hocus.dev/blog/virtualizing-development-environments/",
            "title": "Virtualizing development environments in 2023",
            "date_modified": "2023-08-04T15:28:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36987920\">Comments</a>",
            "url": "https://hydra-so.notion.site/Hydra-1-0-beta-318504444825401e8ce21796dcadd589",
            "title": "Show HN: Hydra 1.0 – open-source column-oriented Postgres",
            "date_modified": "2023-08-03T16:19:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36972347\">Comments</a>",
            "url": "https://ai.meta.com/blog/audiocraft-musicgen-audiogen-encodec-generative-ai-audio/",
            "title": "Meta Open Sources AudioCraft: Generative AI for Audio",
            "date_modified": "2023-08-02T15:36:31.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2023/08/Fixing-Workers-KV-Reliability-1.png\" alt=\"Hardening Workers KV\" width=\"2401\" height=\"1350\"></figure><img src=\"http://blog.cloudflare.com/content/images/2023/08/Fixing-Workers-KV-Reliability.png\" alt=\"Hardening Workers KV\"><p>Over the last couple of months, Workers KV has suffered from a series of incidents, culminating in three back-to-back incidents during the week of July 17th, 2023. These incidents have directly impacted customers that rely on KV — and this isn’t good enough.</p><p>We’re going to share the work we have done to understand why KV has had such a spate of incidents and, more importantly, share in depth what we’re doing to dramatically improve how we deploy changes to KV going forward.</p><h3 id=\"workers-kv\">Workers KV?</h3><p><a href=\"https://www.cloudflare.com/developer-platform/workers-kv/\">Workers KV</a> — or just “KV” — is a key-value service for storing data: specifically, data with high read throughput requirements. It’s especially useful for user configuration, service routing, small assets and/or authentication data.</p><p>We use KV extensively inside Cloudflare too, with <a href=\"https://www.cloudflare.com/zero-trust/products/access/\">Cloudflare Access</a> (part of our Zero Trust suite) and <a href=\"https://pages.cloudflare.com/\">Cloudflare Pages</a> being some of our highest profile internal customers. Both teams benefit from KV’s ability to keep regularly accessed key-value pairs close to where they’re accessed, as well its ability to scale out horizontally without any need to become an expert in operating KV.</p><p>Given Cloudflare’s extensive use of KV, it wasn’t just external customers impacted. Our own internal teams felt the pain of these incidents, too.</p><h3 id=\"the-summary-of-the-post-mortem\">The summary of the post-mortem</h3><p>Back in June 2023, <a href=\"http://blog.cloudflare.com/faster-workers-kv-architecture/\">we announced the move to a new architecture</a> for KV, which is designed to address two major points of customer feedback we’ve had around KV: high latency for infrequently accessed keys (or a key accessed in different regions), and working to ensure the upper bound on KV’s eventual consistency model for writes is 60 seconds — not “mostly 60 seconds”.</p><p>At the time of the blog, we’d already been testing this internally, including early access with our community champions and running a small % of production traffic to validate stability and performance expectations beyond what we could emulate within a staging environment.</p><p>However, in the weeks between mid-June and culminating in the series of incidents during the week of July 17th, we would continue to increase the volume of new traffic onto the new architecture. When we did this, we would encounter previously unseen problems (many of these customer-impacting) — then immediately roll back, fix bugs, and repeat. Internally, we’d begun to identify that this pattern was becoming unsustainable — each attempt to cut traffic onto the new architecture would surface errors or behaviors we hadn’t seen before and couldn’t immediately explain, and thus we would roll back and assess.</p><p>The issues at the root of this series of incidents proved to be significantly challenging to track and observe. Once identified, the two causes themselves proved to be quick to fix, but an (1) observability gap in our error reporting and (2) a mutation to local state that resulted in an unexpected mutation of global state were both hard to observe and reproduce over the days following the customer-facing impact ending.</p><h3 id=\"the-detail\">The detail</h3><p>One important piece of context to understand before we go into detail on the post-mortem: Workers KV is composed of two separate Workers scripts – internally referred to as the Storage Gateway Worker and SuperCache. SuperCache is an optional path in the Storage Gateway Worker workflow, and is the basis for KV's new (faster) backend (<a href=\"http://blog.cloudflare.com/faster-workers-kv-architecture/\">refer to the blog</a>).</p><p>Here is a timeline of events:</p>\n<table width=\"100%\">\n<thead>\n  <tr>\n    <th><span>Time</span></th>\n    <th><span>Description</span></th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td><span>2023-07-17 21:52 UTC</span></td>\n    <td><span>Cloudflare observes alerts showing 500 HTTP status codes in the MEL01 data-center (Melbourne, AU) and begins investigating.</span><br><span>We also begin to see a small set of customers reporting HTTP 500s being returned via multiple channels. It is not immediately clear if this is a data-center-wide issue or KV specific, as there had not been a recent KV deployment, and the issue directly correlated with three data-centers being brought back online.</span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-18 00:09 UTC</span></td>\n    <td><span>We disable the new backend for KV in MEL01 in an attempt to mitigate the issue (noting that there had not been a recent deployment or change to the % of users on the new backend).</span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-18 05:42 UTC</span></td>\n    <td><span>Investigating alerts showing 500 HTTP status codes in VIE02 (Vienna, AT) and JNB01 (Johannesburg, SA).</span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-18 13:51 UTC</span></td>\n    <td><span>The new backend is disabled globally after seeing issues in VIE02 (Vienna, AT) and JNB01 (Johannesburg, SA) data-centers, similar to MEL01. In both cases, they had also recently come back online after maintenance, but it remained unclear as to why KV was failing.</span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-20 19:12 UTC</span></td>\n    <td><span>The new backend is inadvertently re-enabled while deploying the update due to a misconfiguration in a deployment script. </span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-20 19:33 UTC</span></td>\n    <td><span>The new backend is (re-) disabled globally as HTTP 500 errors return.</span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-20 23:46 UTC</span></td>\n    <td><span>Broken Workers script pipeline deployed as part of gradual rollout due to incorrectly defined pipeline configuration in the deployment script.</span><br><span>Metrics begin to report that a subset of traffic is being black-holed.</span></td>\n  </tr>\n  <tr>\n    <td><span>2023-07-20 23:56 UTC</span></td>\n    <td><span>Broken pipeline rolled back; errors rates return to pre-incident (normal) levels.</span></td>\n  </tr>\n</tbody>\n</table><p><em>All timestamps referenced are in Coordinated Universal Time (UTC).</em></p><p>We initially observed alerts showing 500 HTTP status codes in the MEL01 data-center (Melbourne, AU) at 21:52 UTC on July 17th, and began investigating. We also received reports from a small set of customers reporting HTTP 500s being returned via multiple channels. This correlated with three data centers being brought back online, and it was not immediately clear if it related to the data centers or was KV-specific — especially given there had not been a recent KV deployment. On 05:42, we began investigating alerts showing 500 HTTP status codes in VIE02 (Vienna) and JNB02 (Johannesburg) data-centers; while both had recently come back online after maintenance, it was still unclear why KV was failing. At 13:51 UTC, we made the decision to disable the new backend globally.</p><p>Following the incident on July 18th, we attempted to deploy an allow-list configuration to reduce the scope of impacted accounts. However, while attempting to roll out a change for the Storage Gateway Worker at 19:12 UTC on July 20th, an older configuration was progressed causing the new backend to be enabled again, leading to the third event. As the team worked to fix this and deploy this configuration, they attempted to manually progress the deployment at 23:46 UTC, which resulted in the passing of a malformed configuration value that caused traffic to be sent to an invalid Workers script configuration.</p><p>After all deployments and the broken Workers configuration (pipeline) had been rolled back at 23:56 on the 20th July, we spent the following three days working to identify the root cause of the issue. We lacked observability as KV's Worker script (responsible for much of KV's logic) was throwing an unhandled exception very early on in the request handling process. This was further exacerbated by prior work to disable error reporting in a disabled data-center due to the noise generated, which had previously resulted in logs being rate-limited upstream from our service.</p><p>This previous mitigation prevented us from capturing meaningful logs from the Worker, including identifying the exception itself, as an uncaught exception terminates request processing. This has raised the priority of improving how unhandled exceptions are reported and surfaced in a Worker (see Recommendations, below, for further details). This issue was exacerbated by the fact that KV's Worker script would fail to re-enter its \"healthy\" state when a Cloudflare data center was brought back online, as the Worker was mutating an environment variable perceived to be in request scope, but that was in global scope and persisted across requests. This effectively left the Worker “frozen” with the previous, invalid configuration for the affected locations.</p><p>Further, the introduction of a new progressive release process for Workers KV, designed to de-risk rollouts (as an action from a prior incident), prolonged the incident. We found a bug in the deployment logic that led to a broader outage due to an incorrectly defined configuration.</p><p>This configuration effectively caused us to drop a single-digit % of traffic until it was rolled back 10 minutes later. This code is untested at scale, and we need to spend more time hardening it before using it as the default path in production.</p><p>Additionally: although the root cause of the incidents was limited to three Cloudflare data-centers (Melbourne, Vienna, and Johannesburg), traffic across these regions still uses these data centers to route reads and writes to our system of record. Because these three data centers participate in KV’s new backend as regional tiers, a portion of traffic across the Oceania, Europe, and African regions was affected. Only a portion of keys from enrolled namespaces use any given data center as a regional tier in order to limit a single (regional) point of failure, so while traffic across <em>all</em> data centers in the region was impacted, nowhere was <em>all</em> traffic in a given data center affected.</p><p>We estimated the affected traffic to be 0.2-0.5% of KV's global traffic (based on our error reporting), however we observed some customers with error rates approaching 20% of their total KV operations. The impact was spread across KV namespaces and keys for customers within the scope of this incident.</p><p>Both KV’s high total traffic volume and its role as a critical dependency for many customers amplify the impact of even small error rates. In all cases, once the changes were rolled back, errors returned to normal levels and did not persist.</p><h3 id=\"thinking-about-risks-in-building-software\">Thinking about risks in building software</h3><p>Before we dive into what we’re doing to significantly improve how we build, test, deploy and observe Workers KV going forward, we think there are lessons from the real world that can equally apply to how we improve the safety factor of the software we ship.</p><p>In traditional engineering and construction, there is an extremely common procedure known as a &nbsp; “JSEA”, or <a href=\"https://en.wikipedia.org/wiki/Job_safety_analysis\">Job Safety and Environmental Analysis</a> (sometimes just “JSA”). A JSEA is designed to help you iterate through a list of tasks, the potential hazards, and most importantly, the controls that will be applied to prevent those hazards from damaging equipment, injuring people, or worse.</p><p>One of the most critical concepts is the “hierarchy of controls” — that is, what controls should be applied to mitigate these hazards. In most practices, these are elimination, substitution, engineering, administration and personal protective equipment. Elimination and substitution are fairly self-explanatory: is there a different way to achieve this goal? Can we eliminate that task completely? Engineering and administration ask us whether there is additional engineering work, such as changing the placement of a panel, or using a horizontal boring machine to lay an underground pipe vs. opening up a trench that people can fall into.</p><p>The last and lowest on the hierarchy, is personal protective equipment (PPE). A hard hat can protect you from severe injury from something falling from above, but it’s a last resort, and it certainly isn’t guaranteed. In engineering practice, any hazard that <em>only</em> lists PPE as a mitigating factor is unsatisfactory: there must be additional controls in place. For example, instead of only wearing a hard hat, we should <em>engineer</em> the floor of scaffolding so that large objects (such as a wrench) cannot fall through in the first place. Further, if we require that all tools are attached to the wearer, then it significantly reduces the chance the tool can be dropped in the first place. These controls ensure that there are multiple degrees of mitigation — defense in depth — before your hard hat has to come into play.</p><p>Coming back to software, we can draw parallels between these controls: engineering can be likened to improving automation, gradual rollouts, and detailed metrics. Similarly, personal protective equipment can be likened to code review: useful, but code review cannot be the only thing protecting you from shipping bugs or untested code. Automation with linters, more robust testing, and new metrics are all vastly <em>safer</em> ways of shipping software.</p><p>As we spent time assessing where to improve our existing controls and how to put new controls in place to mitigate risks and improve the reliability (safety) of Workers KV, we took a similar approach: eliminating unnecessary changes, engineering more resilience into our codebase, automation, deployment tooling, and only then looking at human processes.</p><h3 id=\"how-we-plan-to-get-better\">How we plan to get better</h3><p>Cloudflare is undertaking a larger, more structured review of KV's observability tooling, release infrastructure and processes to mitigate not only the contributing factors to the incidents within this report, but recent incidents related to KV. Critically, we see tooling and automation as the most powerful mechanisms for preventing incidents, with process improvements designed to provide an additional layer of protection. Process improvements alone cannot be the only mitigation.</p><p>Specifically, we have identified and prioritized the below efforts as the most important next steps towards meeting our own availability SLOs, and (above all) make KV a service that customers building on Workers can rely on for storing configuration and service data in the hot path of their traffic:</p><ul><li>Substantially improve the existing observability tooling for unhandled exceptions, both for internal teams and customers building on Workers. This is especially critical for high-volume services, where traditional logging alone can be too noisy (and not specific enough) to aid in tracking down these cases. The existing ongoing work to land this will be prioritized further. In the meantime, we have directly addressed the specific uncaught exception with KV's primary Worker script.</li><li>Improve the safety around the mutation of environmental variables in a Worker, which currently operate at \"global\" (per-isolate) scope, but can appear to be per-request. Mutating an environmental variable in request scope mutates the value for all requests transiting that same isolate (in a given location), which can be unexpected. Changes here will need to take backwards compatibility in mind.</li><li>Continue to expand KV’s test coverage to better address the above issues, in parallel with the aforementioned observability and tooling improvements, as an additional layer of defense. This includes allowing our test infrastructure to simulate traffic from any source data-center, which would have allowed us to more quickly reproduce the issue and identify a root cause.</li><li>Improvements to our release process, including how KV changes and releases are reviewed and approved, going forward. We will enforce a higher level of scrutiny for future changes, and where possible, reduce the number of changes deployed at once. This includes taking on new infrastructure dependencies, which will have a higher bar for both design and testing.</li><li>Additional logging improvements, including sampling, throughout our request handling process to improve troubleshooting &amp; debugging. A significant amount of the challenge related to these incidents was due to the lack of logging around specific requests (especially non-2xx requests)</li><li>Review and, where applicable, improve alerting thresholds surrounding error rates. As mentioned previously in this report, sub-% error rates at a global scale can have severe negative impact on specific users and/or locations: ensuring that errors are caught and not lost in the noise is an ongoing effort.</li><li>Address maturity issues with our progressive deployment tooling for Workers, which is net-new (and will eventually be exposed to customers directly).</li></ul><p>This is not an exhaustive list: we're continuing to expand on preventative measures associated with these and other incidents. These changes will not only improve KVs reliability, but other services across Cloudflare that KV relies on, or that rely on KV.</p><p>We recognize that KV hasn’t lived up to our customers’ expectations recently. Because we rely on KV so heavily internally, we’ve felt that pain first hand as well. The work to fix the issues that led to this cycle of incidents is already underway. That work will not only improve KV’s reliability but also improve the reliability of any software written on the Cloudflare Workers developer platform, whether by our customers or by ourselves.</p>",
            "url": "http://blog.cloudflare.com/workers-kv-restoring-reliability/",
            "title": "Hardening Workers KV",
            "date_modified": "2023-08-02T13:05:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36941299\">Comments</a>",
            "url": "https://zknill.io/posts/edge-database/",
            "title": "So, you want to deploy on the edge?",
            "date_modified": "2023-07-31T11:54:00.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ucf9dz/serverless_functions_post_mortem\">Comments</a></p>",
            "url": "https://matduggan.com/serverless-functions-post-mortem/",
            "title": "Serverless Functions Post-Mortem",
            "date_modified": "2023-07-28T20:37:02.000Z"
        },
        {
            "content_html": "<p>We are introducing a new charge for public IPv4 addresses. Effective February 1, 2024 there will be a charge of $0.005 per IP per hour for all public IPv4 addresses, whether attached to a service or not (there is already a charge for public IPv4 addresses you allocate in your account but don’t attach to an EC2 instance).</p> \n<p><span><strong>Public IPv4 Charge</strong></span><br> As you may know, IPv4 addresses are an increasingly scarce resource and the cost to acquire a single public IPv4 address has risen more than 300% over the past 5 years. This change reflects our own costs and is also intended to encourage you to be a bit more frugal with your use of public IPv4 addresses and to think about accelerating your adoption of IPv6 as a modernization and conservation measure.</p> \n<p>This change applies to all AWS services including <a href=\"https://aws.amazon.com/ec2/\">Amazon Elastic Compute Cloud (Amazon EC2)</a>, <a href=\"https://aws.amazon.com/rds/\">Amazon Relational Database Service (RDS)</a> database instances, <a href=\"https://aws.amazon.com/eks/\">Amazon Elastic Kubernetes Service (EKS)</a> nodes, and other AWS services that can have a public IPv4 address allocated and attached, in all AWS regions (commercial, <a href=\"https://www.amazonaws.cn/en/about-aws/china/\">AWS China</a>, and <a href=\"https://aws.amazon.com/govcloud-us/\">GovCloud</a>). Here’s a summary in tabular form:</p> \n<table cellpadding=\"8\"> \n <tbody> \n  <tr> \n   <td><strong>Public IP Address Type</strong></td> \n   <td><strong>Current Price/Hour (USD)</strong></td> \n   <td><strong>New Price/Hour (USD)</strong><br> <strong>(Effective February 1, 2024)</strong></td> \n  </tr> \n  <tr> \n   <td width=\"50%\">In-use Public IPv4 address (including Amazon provided public IPv4 and Elastic IP) assigned to resources in your VPC, Amazon Global Accelerator, and AWS Site-to-site VPN tunnel</td> \n   <td>No charge</td> \n   <td>$0.005</td> \n  </tr> \n  <tr> \n   <td>Additional (secondary) <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html\">Elastic IP Address</a> on a running EC2 instance</td> \n   <td>$0.005</td> \n   <td>$0.005</td> \n  </tr> \n  <tr> \n   <td>Idle Elastic IP Address in account</td> \n   <td>$0.005</td> \n   <td>$0.005</td> \n  </tr> \n </tbody> \n</table> \n<p>The <a href=\"https://aws.amazon.com/free/\">AWS Free Tier</a> for EC2 will include 750 hours of public IPv4 address usage per month for the first 12 months, effective February 1, 2024. You will not be charged for IP addresses that you own and bring to AWS using <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-byoip.html\">Amazon BYOIP</a>.</p> \n<p>Starting today, your <a href=\"https://docs.aws.amazon.com/cur/latest/userguide/what-is-cur.html\">AWS Cost and Usage Reports</a> automatically include public IPv4 address usage. When this price change goes in to effect next year you will also be able to use <a href=\"https://aws.amazon.com/aws-cost-management/aws-cost-explorer/\">AWS Cost Explorer</a> to see and better understand your usage.</p> \n<p>As I noted earlier in this post, I would like to encourage you to consider accelerating your adoption of IPv6. A new <a href=\"https://aws.amazon.com/blogs/networking-and-content-delivery/identify-and-optimize-public-ipv4-address-usage-on-aws/\">blog post</a> shows you how to use Elastic Load Balancers and NAT Gateways for ingress and egress traffic, while avoiding the use of a public IPv4 address for each instance that you launch. Here are some resources to show you how you can use IPv6 with widely used services such as EC2, <a href=\"https://aws.amazon.com/vpc/\">Amazon Virtual Private Cloud (Amazon VPC)</a>, <a href=\"https://aws.amazon.com/eks/\">Amazon Elastic Kubernetes Service (EKS)</a>, <a href=\"https://aws.amazon.com/elasticloadbalancing\">Elastic Load Balancing</a>, and <a href=\"https://aws.amazon.com/rds/\">Amazon Relational Database Service (RDS)</a>:</p> \n<ul> \n <li><a href=\"https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/IPv6-reference-architectures-for-AWS-and-hybrid-networks-ra.pdf\">Dual Stack and IPv6-only Amazon VPC Reference Architectures</a> (pdf)</li> \n <li><a href=\"https://aws.amazon.com/blogs/networking-and-content-delivery/dual-stack-ipv6-architectures-for-aws-and-hybrid-networks/\">Dual-stack IPv6 architectures for AWS and hybrid networks</a> – Part 1</li> \n <li><a href=\"https://aws.amazon.com/blogs/networking-and-content-delivery/dual-stack-architectures-for-aws-and-hybrid-networks-part-2/\">Dual-stack IPv6 architectures for AWS and hybrid networks</a> – Part 2</li> \n <li><a href=\"https://aws.amazon.com/vpc/ipv6/\">IPv6 on AWS</a></li> \n <li><a href=\"https://docs.aws.amazon.com/vpc/latest/userguide/aws-ipv6-support.html\">AWS Services that Support IPv6</a></li> \n</ul> \n<p>Earlier this year we <a href=\"https://aws.amazon.com/blogs/compute/secure-connectivity-from-public-to-private-introducing-ec2-instance-connect-endpoint-june-13-2023/\">enhanced EC2 Instance Connect</a> and gave it the ability to connect to your instances using private IPv4 addresses. As a result, you no longer need to use public IPv4 addresses for administrative purposes (generally using SSH or RDP).</p> \n<p><span><strong>Public IP Insights</strong></span><br> In order to make it easier for you to monitor, analyze, and audit your use of public IPv4 addresses, today we are launching Public IP Insights, a new feature of <a href=\"https://docs.aws.amazon.com/vpc/latest/ipam/what-it-is-ipam.html\">Amazon VPC IP Address Manager</a> that is available to you at no cost. In addition to helping you to make efficient use of public IPv4 addresses, Public IP Insights will give you a better understanding of your security profile. You can see the breakdown of public IP types and EIP usage, with multiple filtering options:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/19/pip_top_1.png\" width=\"900\" height=\"767\"></p> \n<p>You can also see, sort, filter, and learn more about each of the public IPv4 addresses that you are using:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/23/pip_bottom_2.png\" width=\"900\" height=\"458\"></p> \n<p><span><strong>Using IPv4 Addresses Efficiently</strong></span><br> By using the new IP Insights tool and following the guidance that I shared above, you should be ready to update your application to minimize the effect of the new charge. You may also want to consider using <a href=\"https://aws.amazon.com/directconnect/\">AWS Direct Connect</a> to set up a dedicated network connection to AWS.</p> \n<p>Finally, be sure to read our new blog post, <a href=\"https://aws.amazon.com/blogs/networking-and-content-delivery/identify-and-optimize-public-ipv4-address-usage-on-aws/\">Identify and Optimize Public IPv4 Address Usage on AWS</a>, for more information on how to make the best use of public IPv4 addresses.</p> \n<p>— <a href=\"https://twitter.com/jeffbarr\">Jeff</a>;</p>",
            "url": "https://aws.amazon.com/blogs/aws/new-aws-public-ipv4-address-charge-public-ip-insights/",
            "title": "New – AWS Public IPv4 Address Charge + Public IP Insights",
            "date_modified": "2023-07-28T18:01:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36899316\">Comments</a>",
            "url": "https://envoy-playground.apoxy.dev",
            "title": "Show HN: Envoy playground in the browser",
            "date_modified": "2023-07-27T19:59:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36894932\">Comments</a>",
            "url": "https://www.allthingsdistributed.com/2023/07/building-and-operating-a-pretty-big-storage-system.html",
            "title": "Building and operating a pretty big storage system called S3",
            "date_modified": "2023-07-27T15:20:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36821951\">Comments</a>",
            "url": "https://jcs.org/2023/07/12/api",
            "title": "Advice for Operating a Public-Facing API",
            "date_modified": "2023-07-22T00:36:29.000Z"
        },
        {
            "content_html": "<div><div><p><a href=\"https://cloud.google.com/workforce-identity-federation\">Workforce Identity Federation</a> allows use of an external identity provider (IdP) to authenticate and authorize users (including employees, partners, and contractors) to Google Cloud resources without provisioning identities in <a href=\"https://cloud.google.com/identity\">Cloud Identity</a>. Before its introduction, only identities existing within Cloud Identity could be used with Cloud Identity Access Management (IAM).&nbsp;</p><p>Here’s how to configure an example Javascript web application hosted in Google Cloud to call Google Cloud APIs after being authenticated with an Azure AD using Workforce Identity Federation.</p><p>Workforce Identity can be used with IdPs supporting <a href=\"https://openid.net/connect/\" target=\"_blank\">OpenID Connect</a> (OIDC) or <a href=\"http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html\" target=\"_blank\">SAML 2.0</a>. You can read more about it in our <a href=\"https://cloud.google.com/blog/products/identity-security/introducing-workforce-identity-federation\">blog post</a> and <a href=\"https://cloud.google.com/iam/docs/workforce-identity-federation\">product documentation page</a>.&nbsp;</p><h3>Configuring Workforce Identity Federation</h3><p>There will be three high level configuration steps required:</p><ol><li><p>Prepare your external IdP and get required configuration parameters.</p></li><li><p>Create a logical container for your external identities in Google Cloud in the form of Workforce Identity Pool.</p></li><li><p>Establish relation between your Workforce Identity Pool and external IdP by configuring WorkforceIdentity Pool Provider using information gathered in the first step.</p></li></ol><p>Before Workforce Identity Federation can be used, a one-way trust relationship must be established between your Google Cloud environment and external IdP. This is achieved by configuring the following resources in Google Cloud: 1) a Workforce Identity Pool, which is a logical container for external identities and 2) a corresponding Workforce Identity Pool Provider, encapsulating technical details of external IdP integration.</p><p>Understanding of OIDC flow may be helpful to understand integration with your application code. We will focus on a single page web application which calls Google Cloud APIs. For simplicity, we omit details of protocol, such as audiences and claims, as they are irrelevant to understanding the flow:</p></div></div><div><div><div><figure><img alt=\"1 Workforce Identity Federation.png\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/1_Workforce_Identity_Federation.max-1000x1000.png\"></figure></div></div></div><div><div><ol><li><p>Client downloads a web app with JS code. In our example, static content is exposed from the GCS storage bucket.</p></li><li><p>The unauthenticated user is redirected to an external IdP login page for authentication.</p></li><li><p>On successful login, the external IdP returns the authentication result including an ID Token.&nbsp;</p></li><li><p>The ID Token contains information about identity and can be exchanged into an “access token”. This is accomplished on the Google Cloud side with a service called Secure Token Service (STS) (<a href=\"https://cloud.google.com/iam/docs/reference/sts/rest\">API documentation</a>).</p></li><li><p>STS verifies the ID Token and if successful returns a Google Identity access token.</p></li><li><p>Access token can be used as a bearer token in subsequent Google Cloud API calls. Please note: by default, access tokens are good for 1 hour (3,600 seconds). When the access token has expired, your token management code must get a new one.</p></li></ol><p>As an example of incorporating this flow within your application we will use Azure Active Directory as an external IdP.</p><h3>IdP configuration</h3><p>Azure AD requires following steps to act as IdP for Workforce Identity Federation:</p><ol><li><p>Registering application&nbsp;</p></li><li><p>Assigning users (and groups) to the enterprise application</p></li></ol><p>Azure AD performs identity management only for registered applications. This is why the first thing we need to do is to create a new application registration (go to Azure Active Directory and select App registrations). An important parameter to assign is the type of redirect URI, in our case we choose “Single-page application (SPA)” If our goal is to provide integration with Google Cloud Federated Console (console.cloud.google) we need to choose a type of “Web”. More information about configuration and the federated version of Cloud Console is provided in <a href=\"https://cloud.google.com/iam/docs/workforce-sign-in-azure-ad\">Configure Azure AD-based workforce identity federation</a>.</p></div></div><div><div><div><figure><img alt=\"2 Workforce Identity Federation.png\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/2_Workforce_Identity_Federation.max-1000x1000.png\"><figcaption><div><i>Registering an application in Azure Active Directory</i></div></figcaption></figure></div></div></div><div><div><p>Next step is to choose “ID Tokens” from the list of tokens issued by authorization endpoint. For details see <a href=\"https://openid.net/connect/\" target=\"_blank\">OpenID Connect</a> (OIDC).&nbsp;</p><p>From the information screen after you finish registration note “Application (client) ID” (this is “client id” needed for Identity Workforce Pool Provider configuration in Google Cloud), and click on “Endpoints” above.</p></div></div><div><div><div><figure><img alt=\"3 Workforce Identity Federation.png\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/3_Workforce_Identity_Federation.max-1000x1000.png\"></figure></div></div></div><div><div><p>From the endpoints window copy the “OpenID Connect metadata document” URL, navigate to it and look for the “issuer” field. Value of this field (in the form of https://login.microsoftonline.com/TENANT_ID/v2.0) will be required for the Workforce Identity Federation).</p></div></div><div><div><div><figure><img alt=\"4 Workforce Identity Federation.png\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/4_Workforce_Identity_Federation.max-1000x1000.png\"></figure></div></div></div><div><div><p>Last step is to go to “Enterprise applications”, find your application and assign users and groups that you want to give access to your application.</p><h3>Configure Google Cloud Workforce Identity Federation</h3><p>Configurations steps to be executed on Google Cloud:</p><ol><li><p>Specify billing project</p></li><li><p>Enable APIs</p></li><li><p>Create Workforce Identity Pool</p></li><li><p>Create Workforce Identity Pool Provider</p></li><li><p>Assign required permissions to external identities from the Pool</p></li></ol><p>Before you begin make sure APIs are enabled on the billing project (as Workforce Identity Federation artifacts are at organization level, you need to specify the project which will be used for billing associated with those resources):&nbsp;</p><ul><li><p>IAM API</p></li><li><p>Security Token Service API</p></li></ul><p>You can find detailed information in the “Before you begin” section of <a href=\"https://cloud.google.com/iam/docs/workforce-obtaining-short-lived-credentials#before_you_begin\">product documentation</a>.</p><h3>Create Workforce Identity Pool</h3></div></div><div><dl><dt>code_block</dt><dd>[StructValue([(u'code', u'gcloud iam workforce-pools create $WORKFORCE_POOL_ID \\\\\\r\\n --organization=$ORGANIZATION_ID \\\\\\r\\n --description=\"$WORKFORCE_POOL_DESCR\" \\\\\\r\\n --location=global \\\\\\r\\n --billing-project=\"$PROJECT_ID\"'), (u'language', u''), (u'caption', &lt;wagtail.wagtailcore.rich_text.RichText object at 0x3e0d449ccad0&gt;)])]</dd></dl></div><div><div><p>Please consult details of configuration parameters in the corresponding section of <a href=\"https://cloud.google.com/sdk/gcloud/reference/iam/workforce-pools/create\">Cloud SDK documentation</a>.</p><h3>Create Workforce Identity Pool Provider</h3></div></div><div><dl><dt>code_block</dt><dd>[StructValue([(u'code', u'gcloud iam workforce-pools providers create-oidc $WORKFORCE_PROVIDER_ID \\\\\\r\\n --workforce-pool=$WORKFORCE_POOL_ID \\\\\\r\\n --display-name=\"$WORKFORCE_PROVIDER_ID\" \\\\\\r\\n --description=\"$WORKFORCE_PROVIDER_ID\" \\\\\\r\\n --issuer-uri=\"$ISSUER_URI\" \\\\\\r\\n --client-id=\"$CLIENT_ID\" \\\\ --attribute-mapping=\"google.subject=assertion.preferred_username,google.groups=assertion.groups\" \\\\\\r\\n --location=global \\\\\\r\\n --billing-project=\"$PROJECT_ID\"'), (u'language', u''), (u'caption', &lt;wagtail.wagtailcore.rich_text.RichText object at 0x3e0d449cc750&gt;)])]</dd></dl></div><div><div><p>This is a crucial step in the configuration, as here we establish one-way trust to our Idp. We will need information from the Azure environment: issuer uri and client id, we should have them from the previous step.&nbsp;</p><p>Please note the attribute mapping parameter where we decide which attribute (assertion) from IdP we will use for the required google.subject attribute. In our example we use preferred_username assertion which in case of Azure AD carries email of authenticated user. This determines the syntax we will use for referencing external identities in Google Cloud as described in <a href=\"https://cloud.google.com/iam/docs/workforce-identity-federation#representing-workforce-users\">Represent workforce pool users in IAM policies</a>.</p><p>Now we just need to assign the correct set of roles to our external identities. In the following example we assign the <i>serviceUserConsumer</i> role, which is required to consume any Google Cloud API, so will also be necessary for your external identities.</p></div></div><div><dl><dt>code_block</dt><dd>[StructValue([(u'code', u'gcloud projects add-iam-policy-binding $PROJECT_ID \\\\\\r\\n --role=\"roles/serviceusage.serviceUsageConsumer\" \\\\\\r\\n--member=\"principal://iam.googleapis.com/locations/global/workforcePools/$WORKFORCE_POOL_ID/subject/$TEST_SUBJECT\"'), (u'language', u''), (u'caption', &lt;wagtail.wagtailcore.rich_text.RichText object at 0x3e0d4519bd10&gt;)])]</dd></dl></div><div><div><p>$TEST_SUBJECT in our case is an email of one of Azure AD users we assigned to our enterprise application.</p><p>Now our Workforce Identity Federation should be ready to rock’n’roll!</p><h2>Example Web Application</h2><p>To use Azure AD ID Tokens in a Web Application, Microsoft recommends using the <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js\" target=\"_blank\">Microsoft Authentication Library for JavaScript (MSAL.js)</a>. Several wrappers exist for this library to be used in e.g. <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-node\" target=\"_blank\">Node.js</a>, <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-react\" target=\"_blank\">React</a> and <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular\" target=\"_blank\">Angular</a>.&nbsp;</p><p>To demonstrate using the Workforce Identity Federation, the following example is provided in Javascript using the <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-browser\" target=\"_blank\">Microsoft Authentication Library for JavaScript (MSAL.js) 2.0 for Browser-Based Single-Page Applications</a>.</p><p>As a first step, the MSAL.js library must be loaded and initialized. For simplicity we are using the <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-browser#installation\" target=\"_blank\">CDN version of the library</a>, in most cases the NPM package should be used instead. The script is loaded in the HTML body with async and defer options to ensure that it does not interfere with page loading time.</p></div></div><div><dl><dt>code_block</dt><dd>[StructValue([(u'code', u'&lt;html&gt;\\r\\n&lt;body&gt;\\r\\n &lt;script\\r\\n async\\r\\n defer\\r\\n type=\"text/javascript\"\\r\\n src=\"https://alcdn.msauth.net/browser/2.3.33/js/msal-browser.min.js\"\\r\\n integrity=\"sha384-sIyEdUUBfHyE7nRpvFHA2odaB/z+HNe9frVsvOKkncXSRPnyek6HeakhZhNdJLrj\"\\r\\n crossorigin=\"anonymous\"&gt;&lt;/script&gt;\\r\\n&lt;/body&gt;\\r\\n&lt;/html&gt;'), (u'language', u''), (u'caption', &lt;wagtail.wagtailcore.rich_text.RichText object at 0x3e0d4519b910&gt;)])]</dd></dl></div><div><div><p>MSAL.js can use a <a href=\"https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md#login-the-user\" target=\"_blank\">popup or redirect login</a>. Without a backend to handle the redirect, the popup method is the recommended method. To show a popup without triggering the popup blocker in modern browsers, the popup needs to be triggered by a user action (e.g. a button click) and must happen within a short period of time after the button was clicked.</p><p>As a result an HTML button is added to trigger the login popup and inline Javascript with a setup function called after the MSAL.js library is loaded, which enables the login button and initializes a PublicClientApplication from the MSAL.js library. The PublicClientApplication initialization requires the clientId and authority as defined in the AD Single Page Setup above.</p></div></div><div><dl><dt>code_block</dt><dd>[StructValue([(u'code', u'&lt;html&gt;\\r\\n &lt;script&gt;\\r\\n function setup() {\\r\\n const configuration = {\\r\\n auth: { \\r\\n clientId: process.env.CLIENT_ID, \\r\\n authority: process.env.AD_AUTHORITY\\r\\n }\\r\\n };\\r\\n const PublicClientApplication = new PublicClientApplication(configuration);\\r\\n document.getElementById(\\'login\\').disabled = false; \\r\\n }\\r\\n &lt;/script&gt;\\r\\n&lt;body&gt;\\r\\n &lt;button id=\"login\" disabled&gt;login&lt;/button&gt;\\r\\n &lt;script\\r\\n async\\r\\n defer\\r\\n type=\"text/javascript\"\\r\\n src=\"https://alcdn.msauth.net/browser/2.33.0/js/msal-browser.min.js\"\\r\\n integrity=\"sha384-sIyEdUUBfHyE7nRpvFHA2odaB/z+HNe9frVsvOKkncXSRPnyek6HeakhZhNdJLrj\"\\r\\n crossorigin=\"anonymous\"\\r\\n onload=\"setup()\"\\r\\n &gt;&lt;/script&gt;\\r\\n&lt;/body&gt;\\r\\n&lt;/html&gt;'), (u'language', u''), (u'caption', &lt;wagtail.wagtailcore.rich_text.RichText object at 0x3e0d4519b210&gt;)])]</dd></dl></div><div><div><p>Now the login functionality is added to open the login popup and handle the login response. The response contains an access token and an ID token. The ID token will be sent to STS to be exchanged for a Google Identity access token. The access token returned after successful login is intended to be used with Azure.&nbsp;</p><p>In our example, it is used to query the Graph API to retrieve the user information from AD. To access Google Cloud resources we need to exchange an ID token from Azure for another access token using Google Cloud STS, based on trust established by the Workforce Identity Federation setup. The Google Identity access token can now be used to access Google APIs, like e.g. the resourcemanager API to retrieve the list of projects visible to the current user (requires that the user has the IAM permission <code>resourcemanager.projects.get</code>)</p><p>Please note that in calling Google Cloud STS we need to provide a proper audience, which is the URI of our Workforce Identity Federation pool provider.</p></div></div><div><dl><dt>code_block</dt><dd>[StructValue([(u'code', u'&lt;html&gt;\\r\\n &lt;script&gt;\\r\\n var publicClientApplication;\\r\\n function setup() {\\r\\n const configuration = {\\r\\n auth: { \\r\\n clientId: process.env.CLIENT_ID, \\r\\n authority: process.env.AD_AUTHORITY\\r\\n }\\r\\n };\\r\\n publicClientApplication = new msal.PublicClientApplication(configuration);\\r\\n document.getElementById(\\'login\\').disabled = false;\\r\\n }\\r\\n async function login() {\\r\\n const loginResponse = await publicClientApplication.loginPopup({\\r\\n scopes: [\"openid\", \"profile\", \"email\"],\\r\\n });\\r\\n if (loginResponse.idToken) {\\r\\n // call STS for token exchange\\r\\n const stsUrl = \"https://sts.googleapis.com/v1/token\";\\r\\n const body = JSON.stringify({\\r\\n grantType: \"urn:ietf:params:oauth:grant-type:token-exchange\",\\r\\n audience: \"//iam.googleapis.com/locations/global/workforcePools/my-workforce-pool/providers/my-test-provider\",\\r\\n scope: \"https://www.googleapis.com/auth/cloud-platform\",\\r\\n requestedTokenType: \"urn:ietf:params:oauth:token-type:access_token\",\\r\\n subjectToken: loginResponse.idToken,\\r\\n subjectTokenType: \"urn:ietf:params:oauth:token-type:id_token\",\\r\\n options: JSON.stringify({userProject:\"ffeldhaus-permanent\"})\\r\\n });\\r\\n const stsTokenResponse = await fetch(stsUrl, {\\r\\n method: \"POST\",\\r\\n headers: {\\r\\n \"Content-Type\": \"application/json\"\\r\\n },\\r\\n body: body\\r\\n });\\r\\n const stsTokenResponseJson = await stsTokenResponse.json();\\r\\n let response;\\r\\n let userInfo;\\r\\n userInfoUrl = \"https://graph.microsoft.com/oidc/userinfo\";\\r\\n accessToken = loginResponse.accessToken;\\r\\n response = await fetch(\\r\\n userInfoUrl,\\r\\n {\\r\\n headers: {\\r\\n authorization: `Bearer ${accessToken}`,\\r\\n },\\r\\n }\\r\\n );\\r\\n userInfo = await response.json();\\r\\n document.getElementById(\"userInfo\").innerHTML = \"&lt;pre&gt;\" + JSON.stringify(userInfo, null, 2) + \"&lt;/pre&gt;\";\\r\\n listProjectsUrl = \"https://cloudresourcemanager.googleapis.com/v1/projects\";\\r\\n accessToken = stsTokenResponseJson.access_token;\\r\\n response = await fetch(\\r\\n listProjectsUrl,\\r\\n {\\r\\n headers: {\\r\\n authorization: `Bearer ${accessToken}`,\\r\\n },\\r\\n }\\r\\n );\\r\\n listProjects = await response.json();\\r\\n document.getElementById(\"projectList\").innerHTML = \"&lt;pre&gt;\" + JSON.stringify(listProjects, null, 2) + \"&lt;/pre&gt;\";\\r\\n };\\r\\n }\\r\\n &lt;/script&gt;\\r\\n &lt;body&gt;\\r\\n &lt;button id=\"login\" onclick=\"login()\" disabled&gt;login&lt;/button&gt;\\r\\n &lt;div id=\"userInfo\"&gt;&lt;/div&gt;\\r\\n &lt;div id=\"projectList\"&gt;&lt;/div&gt;\\r\\n &lt;script\\r\\n async\\r\\n defer\\r\\n type=\"text/javascript\"\\r\\n src=\"https://alcdn.msauth.net/browser/2.33.0/js/msal-browser.min.js\"\\r\\n integrity=\"sha384-sIyEdUUBfHyE7nRpvFHA2odaB/z+HNe9frVsvOKkncXSRPnyek6HeakhZhNdJLrj\"\\r\\n crossorigin=\"anonymous\"\\r\\n onload=\"setup()\"\\r\\n &gt;&lt;/script&gt;\\r\\n &lt;/body&gt;\\r\\n&lt;/html&gt;'), (u'language', u''), (u'caption', &lt;wagtail.wagtailcore.rich_text.RichText object at 0x3e0d4519b350&gt;)])]</dd></dl></div><div><div><p>The access token can then be used to authenticate against all <a href=\"https://cloud.google.com/iam/docs/federated-identity-supported-services\">APIs and services which support Workforce Identity Federation</a>.</p></div></div><div><div><div><figure><img alt=\"5 Workforce Identity Federation.gif\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/original_images/5_Workforce_Identity_Federation.gif\"></figure></div></div></div><div><div><p>The code shared above must be hosted on a valid HTTPS endpoint which is configured in AD as an endpoint for the single page application. This can be achieved with <a href=\"https://cloud.google.com/load-balancing/docs/https/setup-global-ext-https-buckets\">GCLB and a public GCS bucket</a>.</p><p>Users and Groups from Active Directory can be <a href=\"https://cloud.google.com/iam/docs/configuring-workforce-identity-federation#representing-workforce-users\">represented in IAM policies</a> using the following format:</p></div></div><div><div><div><figure><img alt=\"6 Workforce Identity Federation.png\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/6_Workforce_Identity_Federation.max-1000x1000.png\"></figure></div></div></div><div><div><p><b>Important</b>: All principals must have the role <code>roles/serviceusage.serviceUsageConsumer</code> which contains IAM permission <code>serviceusage.services.use</code>.</p><h3>Troubleshooting</h3><p>As with every software development exercise, sooner or later something goes wrong, here is the list of hints we think may be useful when things are not going as planned.</p><p><b>Familiarize yourself with IAM logging</b>: Read the <a href=\"https://cloud.google.com/iam/docs/audit-logging/examples-workforce-identity\">Example logs for Workforce Identity Federation</a> section of IAM documentation.</p><p><b>Use Cloud Logging</b>: This should always be the first thing to do, check the logs in Logs Explorer looking for errors, unauthorized calls, and so on. Remember, to view logs you need corresponding permissions on the project.</p><p><b>Check permissions</b>: In the text we mentioned permissions required for the external identity to call Google Cloud APIs, check if your external identity has been assigned permissions including <code>roles/serviceusage.serviceUsageConsumer</code> role and roles associated with APIs being called. Check <a href=\"https://cloud.google.com/iam/docs/workforce-obtaining-short-lived-credentials#before_you_begin\">preliminary requirements</a> again.</p><p><b>Check your audience</b>: When calling STS for token exchange you must have the correct audience set, which is referring to your Workforce Identity Pool Provider, check our code example for syntax.</p></div></div><div><div><section><a data-analytics=\"{\n                       &quot;event&quot;: &quot;page interaction&quot;,\n                       &quot;category&quot;: &quot;article lead&quot;,\n                       &quot;action&quot;: &quot;related article - inline&quot;,\n                       &quot;label&quot;: &quot;article: {slug}&quot;\n                     }\" href=\"https://gweb-cloudblog-publish.appspot.com/products/identity-security/introducing-workforce-identity-federation/\"><div><p>Related Article</p><div><div><div></div></div><div><h4>Introducing Workforce Identity Federation to easily manage workforce access to Google Cloud</h4><p>Workforce Identity Federation can help users onboard to Google Cloud using their identity and credentials that currently exist with their...</p><div><span>Read Article<svg role=\"presentation\"><use xlink:href=\"#mi-arrow-forward\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"></use></svg></span></div></div></div></div></a></section></div></div>",
            "url": "https://cloud.google.com/blog/products/identity-security/using-workforce-identity-federation-with-api-based-web-applications/",
            "title": "Using Workforce Identity Federation with API-based web applications",
            "date_modified": "2023-07-21T16:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36808296\">Comments</a>",
            "url": "https://webcache.googleusercontent.com/search?q=cache:2T9NpG8thZgJ:https://community.fly.io/t/service-interruption-cant-destroy-machine-deploy-or-restart/14227&cd=9&hl=en&ct=clnk&gl=au",
            "title": "Fly.io Postgres cluster down for 3 days, no word from them about it",
            "date_modified": "2023-07-20T23:42:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36794430\">Comments</a>",
            "url": "https://www.infoq.com/news/2023/07/linkedin-protocol-buffers-restli/",
            "title": "LinkedIn adopts protocol buffers and reduces latency up to 60%",
            "date_modified": "2023-07-19T22:33:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36781077\">Comments</a>",
            "url": "https://github.com/derailed/k9s",
            "title": "K9s: A lazier way to manage Kubernetes Clusters",
            "date_modified": "2023-07-19T01:18:08.000Z"
        },
        {
            "content_html": "<p>While developing with containers is becoming an increasingly popular way for deploying and scaling applications, there are still areas where improvements can be made. One of the main issues with scaling containerized applications is the long startup time, especially during scale up when newer instances need to be added. This issue can have a negative impact on the customer experience, for example when a website needs to scale out to serve additional traffic.</p> \n<p>A <a href=\"https://www.usenix.org/conference/fast16/technical-sessions/presentation/harter\">research paper</a> shows that container image downloads account for 76 percent of container startup time, but on average only 6.4 percent of the data is needed for the container to start doing useful work. Starting and scaling out containerized applications requires downloading container images from a remote container registry. This may introduce a non-trivial latency, as the entire image must be downloaded and unpacked before the applications can be started.</p> \n<p>One solution to this problem is lazy loading (also known as asynchronous loading) container images. This approach downloads data from the container registry in parallel with the application startup, such as <a href=\"https://github.com/containerd/stargz-snapshotter\">stargz-snapshotter</a>, a project that aims to improve the overall container start time.</p> \n<p>Last year, we <a href=\"https://aws.amazon.com/about-aws/whats-new/2022/09/introducing-seekable-oci-lazy-loading-container-images/\">introduced Seekable OCI (SOCI)</a>, a technology open sourced by Amazon Web Services (AWS) that enables container runtimes to implement lazy loading the container image to start applications faster without modifying the container images. As part of that effort, we open sourced <a href=\"https://github.com/awslabs/soci-snapshotter/tree/main\">SOCI Snapshotter</a>, a snapshotter plugin that enables lazy loading with SOCI in containerd.</p> \n<p><span><strong>AWS Fargate Support for SOCI</strong></span><br> Today, I’m excited to share that AWS Fargate now supports Seekable OCI (SOCI), which helps applications deploy and scale out faster by enabling containers to start without waiting to download the entire container image. At launch, this new capability is available for <a href=\"https://aws.amazon.com/ecs/\">Amazon Elastic Container Service (Amazon ECS)</a> applications running on <a href=\"https://aws.amazon.com/fargate/\">AWS Fargate</a>.</p> \n<p>Here’s a quick look to show how AWS Fargate support for SOCI works:</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/14/soci-quicklook-rev.gif\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/14/soci-quicklook-rev.gif\" width=\"920\" height=\"517\"></a></p> \n<p>SOCI works by creating an index (SOCI index) of the files within an existing container image. This index is a key enabler to launching containers faster, providing the capability to extract an individual file from a container image without having to download the entire image. Your applications no longer need to wait to complete pulling and unpacking a container image before your applications start running. This allows you to deploy and scale out applications more quickly and reduce the rollout time for application updates.</p> \n<p>A SOCI index is generated and stored separately from the container images. This means that your container images don’t need to be converted to use SOCI, therefore not breaking secure hash algorithm (SHA)-based security, such as container image signing. The index is then stored in the registry alongside the container image. At release, AWS Fargate support for SOCI works with <a href=\"https://aws.amazon.com/ecr/\">Amazon Elastic Container Registry (Amazon ECR)</a>.</p> \n<p>When you use Amazon ECS with AWS Fargate to run your SOCI-indexed containerized images, AWS Fargate automatically detects if a SOCI index for the image exists and starts the container without waiting for the entire image to be pulled. This also means that AWS Fargate will still continue to run container images that don’t have SOCI indexes.</p> \n<p><span><strong>Let’s Get Started</strong></span><br> There are two ways to create SOCI indexes for container images.</p> \n<ul> \n <li><strong>Use AWS SOCI Index Builder</strong> – <a href=\"https://aws-ia.github.io/cfn-ecr-aws-soci-index-builder/\">AWS SOCI Index Builder</a> is a serverless solution for indexing container images in the AWS Cloud. This <a href=\"https://aws.amazon.com/cloudformation/\">AWS CloudFormation</a> stack deploys an <a href=\"https://aws.amazon.com/eventbridge\">Amazon EventBridge</a> rule to identify Amazon ECR action events and invoke an <a href=\"https://aws.amazon.com/lambda/\">AWS Lambda</a> function to match the defined filter. Then, another <a href=\"https://aws.amazon.com/lambda/\">AWS Lambda</a> function generates and pushes SOCI indexes to repositories in the Amazon ECR registry.</li> \n <li><strong>Create SOCI indexes manually</strong> – This approach provides more flexibility on in how the SOCI indexes are created, including for existing container images in Amazon ECR repositories. To create SOCI indexes, you can use the <code>soci</code> CLI provided by the <a href=\"https://github.com/awslabs/soci-snapshotter/tree/main\">soci-snapshotter</a> project.</li> \n</ul> \n<p>The&nbsp;<a href=\"https://aws-ia.github.io/cfn-ecr-aws-soci-index-builder/\">AWS SOCI Index Builder</a> provides you with an automated process to get started and build SOCI indexes for your container images. The <code>soci</code>CLI provides you with more flexibility around index generation and the ability to natively integrate index generation in your CI/CD pipelines.</p> \n<p>In this article, I manually generate SOCI indexes using the <code>soci</code> CLI from the <code>soci-snapshotter</code> project.</p> \n<p><strong>Create a Repository and Push Container Images<br> </strong>First, I create an Amazon ECR repository called <code>pytorch-soci</code>for my container image using AWS CLI.</p> \n<pre><code>$ aws ecr create-repository --region us-east-1 --repository-name pytorch-soci</code></pre> \n<p>I keep the Amazon ECR URI output and define it as a variable to make it easier for me to refer to the repository in the next step.</p> \n<pre><code>$ ECRSOCIURI=xyz.dkr.ecr.us-east-1.amazonaws.com/pytorch-soci:latest</code></pre> \n<p>For the sample application, I use a <a href=\"https://docs.aws.amazon.com/deep-learning-containers/latest/devguide/deep-learning-containers-ecs-tutorials-training.html#deep-learning-containers-ecs-tutorials-training-pytorch\">PyTorch training (CPU-based)</a> container image from <a href=\"https://aws.amazon.com/machine-learning/containers/\">AWS Deep Learning Containers</a>. I use the <a href=\"https://github.com/containerd/nerdctl\"><code>nerdctl</code></a> CLI to pull the container image because, by default, the Docker Engine stores the container image in the Docker Engine image store, not the containerd image store.</p> \n<pre><code>$ SAMPLE_IMAGE=\"763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-training:1.5.1-cpu-py36-ubuntu16.04\" \n$ aws ecr get-login-password --region us-east-1 | sudo nerdctl login --username AWS --password-stdin xyz.dkr.ecr.ap-southeast-1.amazonaws.com\n$ sudo nerdctl pull --platform linux/amd64 $SAMPLE_IMAGE</code></pre> \n<p>Then, I tag the container image for the repository that I created in the previous step.</p> \n<pre><code>$ sudo nerdctl tag $SAMPLE_IMAGE $ECRSOCIURI</code></pre> \n<p>Next, I need to push the container image into the ECR repository.</p> \n<pre><code>$ sudo nerdctl push $ECRSOCIURI</code></pre> \n<p>At this point, my container image is already in my Amazon ECR repository.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/08/soci-13-repo.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/08/soci-13-repo-1024x529.png\" width=\"1024\" height=\"529\"></a></p> \n<p><strong>Create SOCI Indexes<br> </strong>Next, I need to create SOCI index.</p> \n<p>A SOCI index is an artifact that enables lazy loading of container images. A SOCI index consists of 1) a SOCI index manifest and 2) a set of zTOCs. The following image illustrates the components in a SOCI index manifest, and how it refers to a container image manifest.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/13/soci-index.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/13/soci-index.png\" width=\"743\" height=\"657\"></a></p> \n<p>The SOCI index manifest contains the list of zTOCs and a reference to the image for which the manifest was generated. A zTOC, or table of contents for compressed data, consists of two parts:</p> \n<ol> \n <li>TOC, a table of contents containing file metadata and the corresponding offset in the decompressed TAR archive.</li> \n <li>zInfo, a collection of checkpoints representing the state of the compression engine at various points in the layer.</li> \n</ol> \n<p>To learn more about the concept and term, please visit <code>soci-snapshotter</code> <a href=\"https://github.com/awslabs/soci-snapshotter/blob/main/docs/glossary.md\">Terminology</a> page.</p> \n<p>Before I can create SOCI indexes, I need to install the <code>soci</code>CLI. To learn more about how to install the <code>soci</code>, visit <a href=\"https://github.com/awslabs/soci-snapshotter/blob/main/docs/getting-started.md\">Getting Started with soci-snapshotter</a>.</p> \n<p>To create SOCI indexes, I use the <code>soci create</code> command.</p> \n<pre><code>$ sudo soci create $ECRSOCIURI\nlayer sha256:4c6ec688ebe374ea7d89ce967576d221a177ebd2c02ca9f053197f954102e30b -&gt; ztoc skipped\nlayer sha256:ab09082b308205f9bf973c4b887132374f34ec64b923deef7e2f7ea1a34c1dad -&gt; ztoc skipped\nlayer sha256:cd413555f0d1643e96fe0d4da7f5ed5e8dc9c6004b0731a0a810acab381d8c61 -&gt; ztoc skipped\nlayer sha256:eee85b8a173b8fde0e319d42ae4adb7990ed2a0ce97ca5563cf85f529879a301 -&gt; ztoc skipped\nlayer sha256:3a1b659108d7aaa52a58355c7f5704fcd6ab1b348ec9b61da925f3c3affa7efc -&gt; ztoc skipped\nlayer sha256:d8f520dcac6d926130409c7b3a8f77aea639642ba1347359aaf81a8b43ce1f99 -&gt; ztoc skipped\nlayer sha256:d75d26599d366ecd2aa1bfa72926948ce821815f89604b6a0a49cfca100570a0 -&gt; ztoc skipped\nlayer sha256:a429d26ed72a85a6588f4b2af0049ae75761dac1bb8ba8017b8830878fb51124 -&gt; ztoc skipped\nlayer sha256:5bebf55933a382e053394e285accaecb1dec9e215a5c7da0b9962a2d09a579bc -&gt; ztoc skipped\nlayer sha256:5dfa26c6b9c9d1ccbcb1eaa65befa376805d9324174ac580ca76fdedc3575f54 -&gt; ztoc skipped\nlayer sha256:0ba7bf18aa406cb7dc372ac732de222b04d1c824ff1705d8900831c3d1361ff5 -&gt; ztoc skipped\nlayer sha256:4007a89234b4f56c03e6831dc220550d2e5fba935d9f5f5bcea64857ac4f4888 -&gt; ztoc sha256:0b4d78c856b7e9e3d507ac6ba64e2e2468997639608ef43c088637f379bb47e4\nlayer sha256:089632f60d8cfe243c5bc355a77401c9a8d2f415d730f00f6f91d44bb96c251b -&gt; ztoc sha256:f6a16d3d07326fe3bddbdb1aab5fbd4e924ec357b4292a6933158cc7cc33605b\nlayer sha256:f18dd99041c3095ade3d5013a61a00eeab8b878ba9be8545c2eabfbca3f3a7f3 -&gt; ztoc sha256:95d7966c964dabb54cb110a1a8373d7b88cfc479336d473f6ba0f275afa629dd\nlayer sha256:69e1edcfbd217582677d4636de8be2a25a24775469d677664c8714ed64f557c3 -&gt; ztoc sha256:ac0e18bd39d398917942c4b87ac75b90240df1e5cb13999869158877b400b865\n</code></pre> \n<p>From the above output, I can see that <code>soci</code>CLI created zTOCs for four layers, which and this means only these four layers will be lazily pulled and the other container image layers will be downloaded in full before the container image starts. This is because there is less of a launch time impact in lazy loading very small container image layers. However, you can configure this behavior using the <code>--min-layer-size</code> flag when you run <code>soci create</code>.</p> \n<p><strong>Verify and Push SOCI Indexes<br> </strong>The <code>soci</code> CLI also provides several commands that can help you to review the SOCI Indexes that have been generated.<strong><br> </strong></p> \n<p>To see a list of all index manifests, I can run the following command.</p> \n<pre><code>$ sudo soci index list\n\nDIGEST                                                                     SIZE    IMAGE REF                                                                                   PLATFORM       MEDIA TYPE                                    CREATED\nsha256:ea5c3489622d4e97d4ad5e300c8482c3d30b2be44a12c68779776014b15c5822    1931    xyz.dkr.ecr.us-east-1.amazonaws.com/pytorch-soci:latest                                     linux/amd64    application/vnd.oci.image.manifest.v1+json    10m4s ago\nsha256:ea5c3489622d4e97d4ad5e300c8482c3d30b2be44a12c68779776014b15c5822    1931    763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-training:1.5.1-cpu-py36-ubuntu16.04    linux/amd64    application/vnd.oci.image.manifest.v1+json    10m4s ago\n</code></pre> \n<p>While optional, if I need to see the list of zTOC, I can use the following command.</p> \n<pre><code>$ sudo soci ztoc list\nDIGEST                                                                     SIZE        LAYER DIGEST\nsha256:0b4d78c856b7e9e3d507ac6ba64e2e2468997639608ef43c088637f379bb47e4    2038072     sha256:4007a89234b4f56c03e6831dc220550d2e5fba935d9f5f5bcea64857ac4f4888\nsha256:95d7966c964dabb54cb110a1a8373d7b88cfc479336d473f6ba0f275afa629dd    11442416    sha256:f18dd99041c3095ade3d5013a61a00eeab8b878ba9be8545c2eabfbca3f3a7f3\nsha256:ac0e18bd39d398917942c4b87ac75b90240df1e5cb13999869158877b400b865    36277264    sha256:69e1edcfbd217582677d4636de8be2a25a24775469d677664c8714ed64f557c3\nsha256:f6a16d3d07326fe3bddbdb1aab5fbd4e924ec357b4292a6933158cc7cc33605b    10152696    sha256:089632f60d8cfe243c5bc355a77401c9a8d2f415d730f00f6f91d44bb96c251b\n</code></pre> \n<p>This series of zTOCs contains all of the information that SOCI needs to find a given file in a layer. To review the zTOC for each layer, I can use one of the digest sums from the preceding output and use the following command.</p> \n<pre><code>$ sudo soci ztoc info sha256:0b4d78c856b7e9e3d507ac6ba64e2e2468997639608ef43c088637f379bb47e4\n{\n  \"version\": \"0.9\",\n  \"build_tool\": \"AWS SOCI CLI v0.1\",\n  \"size\": 2038072,\n  \"span_size\": 4194304,\n  \"num_spans\": 33,\n  \"num_files\": 5552,\n  \"num_multi_span_files\": 26,\n  \"files\": [\n    {\n      \"filename\": \"bin/\",\n      \"offset\": 512,\n      \"size\": 0,\n      \"type\": \"dir\",\n      \"start_span\": 0,\n      \"end_span\": 0\n    },\n    {\n      \"filename\": \"bin/bash\",\n      \"offset\": 1024,\n      \"size\": 1037528,\n      \"type\": \"reg\",\n      \"start_span\": 0,\n      \"end_span\": 0\n    }\n\n---Trimmed for brevity---\n</code></pre> \n<p>Now, I need to use the following command to push all SOCI-related artifacts into the Amazon ECR.</p> \n<pre><code>$ PASSWORD=$(aws ecr get-login-password --region us-east-1)\n$ sudo soci push --user AWS:$PASSWORD $ECRSOCIURI</code></pre> \n<p>If I go to my Amazon ECR repository, I can verify the index is created. Here, I can see that two additional objects are listed alongside my container image: a <strong>SOCI Index</strong> and an <strong>Image index</strong>. The image index allows AWS Fargate to look up SOCI indexes associated with my container image.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/08/soci-14-indexes-in-ecr.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/08/soci-14-indexes-in-ecr-1024x700.png\" width=\"1024\" height=\"700\"></a></p> \n<p><strong>Understanding SOCI Performance<br> </strong>The main objective of SOCI is to minimize the required time to start containerized applications. To measure the performance of AWS Fargate lazy loading container images using SOCI, I need to understand how long it takes for my container images to start with SOCI and without SOCI.</p> \n<p>To understand the duration needed for each container image to start, I can use metrics available from the <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeTasks.html\"><code>DescribeTasks</code></a> API on Amazon ECS. The first metric is <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_Task.html#ECS-Type-Task-createdAt\"><code>createdAt</code></a>, the timestamp for the time when the task was created and entered the <code>PENDING</code> state. The second metric is <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_Task.html#ECS-Type-Task-startedAt\"><code>startedAt</code></a>, the time when the task transitioned from the <code>PENDING</code> state to the <code>RUNNING</code> state.</p> \n<p>For this, I have created another Amazon ECR repository using the same container image but without generating a SOCI index, called <code>pytorch-without-soci</code>.&nbsp;If I compare these container images, I have two additional objects in <code>pytorch-soci</code>(an image index and a SOCI index) that don’t exist in <code>pytorch-without-soci</code>.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/08/soci-15-comparison.png\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/08/soci-15-comparison-1024x441.png\" width=\"1024\" height=\"441\"></a></p> \n<p><strong>Deploy and Run Applications<br> </strong>To run the applications, I have created an Amazon ECS cluster called <code>demo-pytorch-soci-cluster</code>, a VPC and the required ECS task execution role. If you’re new to Amazon ECS, you can follow <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/getting-started.html\">Getting started with Amazon ECS</a> to be more familiar with how to deploy and run your containerized applications.</p> \n<p>Now, let’s deploy and run both the container images with <code>FARGATE</code> as the launch type. I deﬁne five tasks for each <code>pytorch-soci</code>and <code>pytorch-without-soci</code>.</p> \n<pre><code>$ aws ecs \\ \n    --region us-east-1 \\ \n    run-task \\ \n    --count 5 \\ \n    --launch-type FARGATE \\ \n    --task-definition arn:aws:ecs:us-east-1:XYZ:task-definition/pytorch-soci \\ \n    --cluster socidemo \n\n$ aws ecs \\ \n    --region us-east-1 \\ \n    run-task \\ \n    --count 5 \\ \n    --launch-type FARGATE \\ \n    --task-definition arn:aws:ecs:us-east-1:XYZ:task-definition/pytorch-without-soci \\ \n    --cluster socidemo</code></pre> \n<p>After a few minutes, there are 10 running tasks on my ECS cluster.</p> \n<p><a href=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/04/soci-6-rev-1.jpg\"><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/04/soci-6-rev-1.jpg\" width=\"1000\" height=\"933\"></a></p> \n<p>After verifying that all my tasks are running, I run the following script to get two metrics: <code>createdAt</code> and <code>startedAt</code>.</p> \n<pre><code>#!/bin/bash\nCLUSTER=&lt;CLUSTER_NAME&gt;\nTASKDEF=&lt;TASK_DEFINITION&gt;\nREGION=\"us-east-1\"\nTASKS=$(aws ecs list-tasks \\\n    --cluster $CLUSTER \\\n    --family $TASKDEF \\\n    --region $REGION \\\n    --query 'taskArns[*]' \\\n    --output text)\n\naws ecs describe-tasks \\\n    --tasks $TASKS \\\n    --region $REGION \\\n    --cluster $CLUSTER \\\n    --query \"tasks[] | reverse(sort_by(@, &amp;createdAt)) | [].[{startedAt: startedAt, createdAt: createdAt, taskArn: taskArn}]\" \\\n    --output table\n</code></pre> \n<p>Running the above command for the container image without SOCI indexes — <code>pytorch-without-soci</code>— produces following output:</p> \n<pre><code>-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n|                                                                                   DescribeTasks                                                                                   |\n+----------------------------------+-----------------------------------+------------------------------------------------------------------------------------------------------------+\n|             createdAt            |             startedAt             |                                                  taskArn                                                   |\n+----------------------------------+-----------------------------------+------------------------------------------------------------------------------------------------------------+\n|  2023-07-07T17:43:59.233000+00:00|  2023-07-07T17:46:09.856000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/dcdf19b6e66444aeb3bc607a3114fae0   |\n|  2023-07-07T17:43:59.233000+00:00|  2023-07-07T17:46:09.459000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/9178b75c98ee4c4e8d9c681ddb26f2ca   |\n|  2023-07-07T17:43:59.233000+00:00|  2023-07-07T17:46:21.645000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/7da51e036c414cbab7690409ce08cc99   |\n|  2023-07-07T17:43:59.233000+00:00|  2023-07-07T17:46:00.606000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/5ee8f48194874e6dbba75a5ef753cad2   |\n|  2023-07-07T17:43:59.233000+00:00|  2023-07-07T17:46:02.461000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/58531a9e94ed44deb5377fa997caec36   |\n+----------------------------------+-----------------------------------+------------------------------------------------------------------------------------------------------------+\n</code></pre> \n<p>From the average aggregated delta time (between <code>startedAt</code> and <code>createdAt</code>) for each task, the <code>pytorch-without-soci</code> (without SOCI indexes) successfully ran after 129 seconds.</p> \n<p>Next, I’m running same command but for <code>pytorch-soci</code>which comes with SOCI indexes.</p> \n<pre><code>-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n|                                                                                   DescribeTasks                                                                                   |\n+----------------------------------+-----------------------------------+------------------------------------------------------------------------------------------------------------+\n|             createdAt            |             startedAt             |                                                  taskArn                                                   |\n+----------------------------------+-----------------------------------+------------------------------------------------------------------------------------------------------------+\n|  2023-07-07T17:43:53.318000+00:00|  2023-07-07T17:44:51.076000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/c57d8cff6033494b97f6fd0e1b797b8f   |\n|  2023-07-07T17:43:53.318000+00:00|  2023-07-07T17:44:52.212000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/6d168f9e99324a59bd6e28de36289456   |\n|  2023-07-07T17:43:53.318000+00:00|  2023-07-07T17:45:05.443000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/4bdc43b4c1f84f8d9d40dbd1a41645da   |\n|  2023-07-07T17:43:53.318000+00:00|  2023-07-07T17:44:50.618000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/43ea53ea84154d5aa90f8fdd7414c6df   |\n|  2023-07-07T17:43:53.318000+00:00|  2023-07-07T17:44:50.777000+00:00 |  arn:aws:ecs:ap-southeast-1:xyz:task/demo-pytorch-soci-cluster/0731bea30d42449e9006a5d8902756d5   |\n+----------------------------------+-----------------------------------+------------------------------------------------------------------------------------------------------------+</code></pre> \n<p>Here, I see my container image with SOCI-enabled — <code>pytorch-soci</code> — was started 60 seconds after being created.</p> \n<p>This means that running my sample application with SOCI indexes on AWS Fargate is approximately 50 percent faster compared to running without SOCI indexes.</p> \n<p>It’s recommended to benchmark the startup and scaling-out time of your application with and without SOCI. This helps you to have a better understanding of how your application behaves and if your applications benefit from AWS Fargate support for SOCI.</p> \n<p><span><strong>Customer Voices</strong></span><br> During the private preview period, we heard lots of feedback from our customers about AWS Fargate support for SOCI. Here’s what our customers say:</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/07/Autodesk_Logo@2x-2.9396df1d739f5a3b99ce8985231b0a48f7767c05-300x200.png\" width=\"300\" height=\"200\"></p> \n<p><strong><a href=\"https://www.autodesk.com/\">Autodesk</a></strong> provides critical design, make, and operate software solutions across the architecture, engineering, construction, manufacturing, media, and entertainment industries. “SOCI has given us a 50% improvement in startup performance for our time-sensitive simulation workloads running on Amazon ECS with AWS Fargate. This allows our application to scale out faster, enabling us to quickly serve increased user demand and save on costs by reducing idle compute capacity. The AWS Partner Solution for creating the SOCI index is easy to configure and deploy.” – Boaz Brudner, Head of Innovyze SaaS Engineering, AI and Architecture, Autodesk.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/13/soci-flywire-logo-300x100.png\" width=\"300\" height=\"100\"></p> \n<p><strong><a href=\"https://www.flywire.com/\">Flywire</a></strong> is a global payments enablement and software company, on a mission to deliver the world’s most important and complex payments. “We run multi-step deployment pipelines on Amazon ECS with AWS Fargate which can take several minutes to complete. With SOCI, the total pipeline duration is reduced by over 50% without making any changes to our applications, or the deployment process. This allowed us to drastically reduce the rollout time for our application updates. For some of our larger images of over 750MB, SOCI improved the task startup time by more than 60%.”, Samuel Burgos, Sr. Cloud Security Engineer, Flywire.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2023/07/13/soci-virtuoso-1-300x118.png\" width=\"300\" height=\"118\"></p> \n<p><strong><a href=\"https://www.virtuoso.qa/\">Virtuoso</a></strong> is a leading software corporation that makes functional UI and end-to-end testing software. “SOCI has helped us reduce the lag between demand and availability of compute. We have very bursty workloads which our customers expect to start as fast as possible. SOCI helps our ECS tasks spin-up 40% faster, allowing us to quickly scale our application and reduce the pool of idle compute capacity, enabling us to deliver value more efficiently. Setting up SOCI was really easy. We opted to use the quick-start AWS Partner’s solution with which we could leave our build and deployment pipelines untouched.”, Mathew Hall, Head of Site Reliability Engineering, Virtuoso.</p> \n<p><span><strong>Things to Know<br> </strong></span><strong>Availability</strong> — AWS Fargate support for SOCI is available in all AWS Regions where Amazon ECS, AWS Fargate, and Amazon ECR are available.</p> \n<p><strong>Pricing</strong> — AWS Fargate support for SOCI is available at no additional cost and you will only be charged for storing the SOCI indexes in Amazon ECR.</p> \n<p><strong>Get Started</strong> — Learn more about benefits and how to get started on the <a href=\"https://docs.aws.amazon.com/AmazonECS/latest/userguide/container-considerations.html#fargate-tasks-soci-images\">AWS Fargate Support for SOCI</a> page.</p> \n<p>Happy building.<br> — <a href=\"https://www.linkedin.com/in/donnieprakoso\">Donnie</a></p>",
            "url": "https://aws.amazon.com/blogs/aws/aws-fargate-enables-faster-container-startup-using-seekable-oci/",
            "title": "AWS Fargate Enables Faster Container Startup using Seekable OCI",
            "date_modified": "2023-07-17T19:29:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36757688\">Comments</a>",
            "url": "https://katacontainers.io/",
            "title": "Kata Containers: Virtual Machines that feel and perform like containers",
            "date_modified": "2023-07-17T12:55:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36736795\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=36736795",
            "title": "Ask HN: How to price your first Enterprise customer?",
            "date_modified": "2023-07-15T13:56:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36731490\">Comments</a>",
            "url": "https://www.fastly.com/blog/debunking-cloudflares-recent-performance-tests",
            "title": "Lies, damned lies: debunking Cloudflare’s recent performance tests",
            "date_modified": "2023-07-14T23:44:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36731490\">Comments</a>",
            "url": "https://www.fastly.com/blog/debunking-cloudflares-recent-performance-tests",
            "title": "Debunking Cloudflare’s recent performance tests (2021)",
            "date_modified": "2023-07-14T23:44:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36708907\">Comments</a>",
            "url": "https://github.com/ory/kratos/releases/tag/v1.0.0",
            "title": "Show HN: Open-source IAM Ory Kratos v1.0 with Passkeys, MFA and multi-region",
            "date_modified": "2023-07-13T13:37:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36702939\">Comments</a>",
            "url": "https://digest.browsertech.com/archive/browsertech-digest-figma-is-a-file-editor/",
            "title": "Figma Is a File Editor",
            "date_modified": "2023-07-12T23:13:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36697693\">Comments</a>",
            "url": "https://github.com/cncf/toc/commit/25b2ead46d56e3c39d48a6a87e3c558c2120d179",
            "title": "Istio moved to CNCF Graduation stage",
            "date_modified": "2023-07-12T16:57:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36696800\">Comments</a>",
            "url": "https://www.inngest.com/blog/announcing-inngest-seed-financing",
            "title": "Inngest raises $3M seed to build the reliable workflow platform for every dev",
            "date_modified": "2023-07-12T16:11:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36689756\">Comments</a>",
            "url": "https://commitmono.com/",
            "title": "Commit Mono – Neutral programming typeface",
            "date_modified": "2023-07-12T02:25:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36682070\">Comments</a>",
            "url": "https://neon.tech/blog/http-vs-websockets-for-postgres-queries-at-the-edge",
            "title": "HTTP vs. WebSockets: Which one is the fastest for Postgres queries at the edge?",
            "date_modified": "2023-07-11T15:06:42.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/3xzdhe/mirrord_as_alternative_telepresence\">Comments</a></p>",
            "url": "https://metalbear.co/blog/mirrord-as-an-alternative-to-telepresence/",
            "title": "mirrord as an alternative to Telepresence",
            "date_modified": "2023-07-11T13:11:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36678079\">Comments</a>",
            "url": "https://www.suse.com/news/SUSE-Preserves-Choice-in-Enterprise-Linux/",
            "title": "SUSE is forking RHEL",
            "date_modified": "2023-07-11T08:41:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36673945\">Comments</a>",
            "url": "https://qemu.readthedocs.io/en/latest/system/i386/microvm.html",
            "title": "MicroVM by QEMU",
            "date_modified": "2023-07-10T22:35:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36667469\">Comments</a>",
            "url": "https://bpapillon.com/post/feature-flags-theory-vs-reality/",
            "title": "Feature Flags: Theory vs. Reality",
            "date_modified": "2023-07-10T15:02:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36666830\">Comments</a>",
            "url": "https://edgenode.com/",
            "title": "EdgeNode: Deploy Serverless Containers on Edge",
            "date_modified": "2023-07-10T14:17:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36666782\">Comments</a>",
            "url": "https://hocus.dev/blog/qemu-vs-firecracker/",
            "title": "We replaced Firecracker with QEMU",
            "date_modified": "2023-07-10T14:15:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36657829\">Comments</a>",
            "url": "https://community.influxdata.com/t/getting-weird-results-from-gcp-europe-west1/30615",
            "title": "InfluxDB Cloud shuts down in Belgium; some weren't notified before data deletion",
            "date_modified": "2023-07-09T19:11:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36625908\">Comments</a>",
            "url": "https://blog.railway.app/p/introducing-trial-hobby-pro-plans",
            "title": "The $5 Plan",
            "date_modified": "2023-07-07T01:40:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36617433\">Comments</a>",
            "url": "https://planetscale.com/blog/announcing-scaler-pro",
            "title": "PlanetScale Scaler Pro",
            "date_modified": "2023-07-06T15:23:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36616023\">Comments</a>",
            "url": "https://www.fastmail.com/blog/partial-outage-on-30-june-2023/",
            "title": "Fastmail 30 June outage post-mortem",
            "date_modified": "2023-07-06T14:08:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36614924\">Comments</a>",
            "url": "https://finance.yahoo.com/news/digitalocean-acquires-paperspace-expand-ai-120000933.html",
            "title": "DigitalOcean acquires Paperspace (YC W15) for $111M in cash",
            "date_modified": "2023-07-06T12:45:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36595673\">Comments</a>",
            "url": "https://arxiv.org/abs/2307.01045",
            "title": "Cloud Native Software Engineering",
            "date_modified": "2023-07-05T05:01:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36549077\">Comments</a>",
            "url": "https://deno.com/blog/2023-06-25-outage-post-mortem",
            "title": "June 25th, 2023 Deno Deploy Postmortem",
            "date_modified": "2023-07-01T11:44:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36519448\">Comments</a>",
            "url": "https://linear.app/blog/scaling-the-linear-sync-engine",
            "title": "Scaling Linear's Sync Engine",
            "date_modified": "2023-06-29T12:23:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36511012\">Comments</a>",
            "url": "https://blog.jonlu.ca/posts/us-east-1-latency",
            "title": "Noticing when an app is only hosted in us-east-1",
            "date_modified": "2023-06-28T19:03:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36510701\">Comments</a>",
            "url": "https://upvpn.app",
            "title": "Show HN: Serverless VPN, pay as you go, unlimited devices, no subscriptions",
            "date_modified": "2023-06-28T18:43:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36503983\">Comments</a>",
            "url": "https://jmmv.dev/2023/06/fast-machines-slow-machines.html",
            "title": "Fast machines, slow machines",
            "date_modified": "2023-06-28T08:53:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36501548\">Comments</a>",
            "url": "https://cohost.org/tef/post/1764930-how-not-to-write-a",
            "title": "How not to write a pipeline",
            "date_modified": "2023-06-28T02:24:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36496102\">Comments</a>",
            "url": "https://www.docker.com/blog/mutagen-acquisition/",
            "title": "Docker Acquires Mutagen",
            "date_modified": "2023-06-27T17:31:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36495931\">Comments</a>",
            "url": "https://www.influxdata.com/blog/influxdb-3-0-system-architecture/",
            "title": "InfluxDB 3.0 System Architecture",
            "date_modified": "2023-06-27T17:17:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36489847\">Comments</a>",
            "url": "https://github.com/wolfi-dev",
            "title": "Wolfi: A community Linux OS designed for the container and cloud-native era",
            "date_modified": "2023-06-27T07:19:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36488733\">Comments</a>",
            "url": "https://www.winglang.io/blog/2022/11/23/manifesto",
            "title": "Cloud, Why So Difficult?",
            "date_modified": "2023-06-27T04:01:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36488461\">Comments</a>",
            "url": "https://cubxity.dev/blog/folia-test-june-2023",
            "title": "Testing a 1,000 player Minecraft server with Folia",
            "date_modified": "2023-06-27T03:13:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36488356\">Comments</a>",
            "url": "https://akashrajpurohit.com/blog/build-your-own-docker-with-linux-namespaces-cgroups-and-chroot-handson-guide/",
            "title": "Build Your Own Docker with Linux Namespaces, Cgroups, and Chroot",
            "date_modified": "2023-06-27T02:50:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36484609\">Comments</a>",
            "url": "https://determinate.systems/posts/magic-nix-cache",
            "title": "The Magic Nix Cache, a GitHub Action for speeding up your Nix workflows",
            "date_modified": "2023-06-26T19:32:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36480230\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=36480230",
            "title": "Launch HN: Argonaut (YC S21) – Easily Deploy Apps and Infra to AWS and GCP",
            "date_modified": "2023-06-26T14:41:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36478854\">Comments</a>",
            "url": "https://spin.atomicobject.com/2023/06/26/dexec-docker/",
            "title": "Working with Docker Containers Made Easy with the Dexec Bash Script",
            "date_modified": "2023-06-26T12:29:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36470943\">Comments</a>",
            "url": "https://blog.warrant.dev/why-zanzibar-shines-at-building-authorization/",
            "title": "Why Google Zanzibar shines at building authorization",
            "date_modified": "2023-06-25T17:59:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36448655\">Comments</a>",
            "url": "https://blog.val.town/blog/introducing-valrun",
            "title": "Launching val.run: subdomains & arbitrary paths",
            "date_modified": "2023-06-23T15:56:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36434852\">Comments</a>",
            "url": "https://changelog.com/posts/webassembly-runtimes-will-replace-container-runtimes-by-2030",
            "title": "“WebAssembly runtimes will replace container-based runtimes by 2030”",
            "date_modified": "2023-06-22T16:40:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36420259\">Comments</a>",
            "url": "https://lwn.net/Articles/935592/",
            "title": "Red Hat cutting back RHEL source availability",
            "date_modified": "2023-06-21T16:00:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36419537\">Comments</a>",
            "url": "https://www.systeminit.com/blog-second-wave-devops/",
            "title": "System Initiative: Second Wave DevOps",
            "date_modified": "2023-06-21T15:13:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36415124\">Comments</a>",
            "url": "https://github.com/juspay/hyperswitch/wiki/Why-I-won%27t-pay-on-your-website",
            "title": "I won't pay on your website",
            "date_modified": "2023-06-21T06:33:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36405004\">Comments</a>",
            "url": "https://render.com/blog/render-series-b",
            "title": "Render Raises $50M Series B",
            "date_modified": "2023-06-20T14:58:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36403014\">Comments</a>",
            "url": "https://www.inngest.com",
            "title": "Show HN: Inngest – Developer platform for background jobs and workflows",
            "date_modified": "2023-06-20T12:24:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36368586\">Comments</a>",
            "url": "https://blog.webp.se/hetzner-arm64-en/",
            "title": "Review of Hetzner ARM64 Servers and Experience of WebP Cloud Services on Them",
            "date_modified": "2023-06-17T09:14:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36367667\">Comments</a>",
            "url": "https://github.com/petykowski/London-Underground-Dot-Matrix-Typeface",
            "title": "London Underground Dot Matrix Typeface",
            "date_modified": "2023-06-17T05:52:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36333321\">Comments</a>",
            "url": "https://llm-utils.org/Nvidia+H100+and+A100+GPUs+-+comparing+available+capacity+at+GPU+cloud+providers",
            "title": "Nvidia H100 and A100 GPUs – comparing available capacity at GPU cloud providers",
            "date_modified": "2023-06-14T21:56:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36327911\">Comments</a>",
            "url": "https://vercel.com/blog/vercel-ai-accelerator",
            "title": "Vercel's AI Accelerator",
            "date_modified": "2023-06-14T16:00:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36327347\">Comments</a>",
            "url": "https://blog.hellolanding.tech/move-fast-avoid-sharp-edges-5d3a07f19a59",
            "title": "Migrating from Heroku to EKS",
            "date_modified": "2023-06-14T15:22:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36280772\">Comments</a>",
            "url": "https://collabfund.com/blog/mental-liquidity/",
            "title": "Mental Liquidity",
            "date_modified": "2023-06-11T12:31:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36261492\">Comments</a>",
            "url": "https://discourse.nixos.org/t/s3-update-and-recap-of-community-call/28942",
            "title": "Nix/NixOS S3 Update and Recap of Community Call",
            "date_modified": "2023-06-09T17:56:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36256167\">Comments</a>",
            "url": "https://old.reddit.com/r/apolloapp/comments/144l6se/apollo_backend_just_made_public_the_goal_of/",
            "title": "Apollo Back end just made public",
            "date_modified": "2023-06-09T11:19:58.000Z"
        },
        {
            "content_html": "<div id=\"rss-wrap\">\n<figure>\n  <img src=\"https://cdn.arstechnica.net/wp-content/uploads/2023/06/bliss-update-800x533.jpg\" alt=\"A high-res rendered hill inspired by Windows XP's familiar &quot;Bliss&quot; wallpaper (visit Microsoft's site to get it at full resolution.) \">\n      <p><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2023/06/bliss-update.jpg\" data-height=\"1280\" data-width=\"1920\">Enlarge</a> <span>/</span> A high-res rendered hill inspired by Windows XP's familiar \"Bliss\" wallpaper (visit Microsoft's site to get it at full resolution.)  (credit: Microsoft)</p>  </figure>\n\n\n\n\n\n\n<div><a name=\"page-1\"></a></div>\n\n<p>Did you read the news about the Windows XP activation algorithm getting cracked and suddenly get nostalgic for the blue skies and bluer taskbar of that old Windows release? Or maybe you just like attractive, high-resolution desktop wallpapers and you want to make a change? It turns out that Microsoft's design team has rendered <a href=\"https://msdesign.blob.core.windows.net/wallpapers/Microsoft_Nostalgic_Windows_Wallpaper_4k.jpg\">an updated 4K version of the default Windows XP wallpaper</a>—you might know it by its name, \"<a href=\"https://en.wikipedia.org/wiki/Bliss_(image)\">Bliss</a>.\"</p>\n<p>It's one of several retro-themed wallpapers on <a href=\"https://wallpapers.microsoft.design/\">this Microsoft Design site</a>, including photorealistic renderings of <em>Solitaire</em>, Paint, and (of course) Clippy. The site has been around for a while and hasn't been updated since December 2022, but Windows engineer Jennifer Gentleman <a href=\"https://twitter.com/JenMsft/status/1666649807803547648?s=20\">tweeted about it yesterday</a>—it's new to me and maybe to you, too. The most recent wallpapers appear to be products of Microsoft's&nbsp;<a href=\"https://medium.com/@MicrosoftDesign/microsoft-design-week-64fdcd611303\">Design Week event</a>.</p>\n<p>Among others, the Microsoft Design site also hosts the default wallpapers that have come with several Surface PCs, quite a few Pride Month-themed wallpaper designs, and several images focused on the company's recent emoji redesigns and the icons for the Microsoft 365 apps.</p></div><p><a href=\"https://arstechnica.com/?p=1946449#p3\">Read 2 remaining paragraphs</a> | <a href=\"https://arstechnica.com/?p=1946449&amp;comments=1\">Comments</a></p>",
            "url": "https://arstechnica.com/?p=1946449",
            "title": "Today I stumbled upon Microsoft’s 4K rendering of the Windows XP wallpaper",
            "date_modified": "2023-06-08T20:22:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36218330\">Comments</a>",
            "url": "https://twitter.com/davidecci/status/1665835119331135488",
            "title": "Apple Releases New Static Linker",
            "date_modified": "2023-06-06T19:59:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36208772\">Comments</a>",
            "url": "https://spacedimp.com/blog/dockerless-setting-up-an-elixir-webapp-using-podman-and-plug/",
            "title": "Show HN: Dockerless, Elixir Web Application Using Podman and Plug",
            "date_modified": "2023-06-06T06:06:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36196410\">Comments</a>",
            "url": "https://blog.cloudnativefolks.org/ebpf-for-cybersecurity-part-2",
            "title": "eBPF for Cybersecurity – Part 2",
            "date_modified": "2023-06-05T14:11:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36191691\">Comments</a>",
            "url": "https://blog.michael.kuron-germany.de/2023/06/emulating-ppc64-inside-docker/",
            "title": "Emulating PPC64 Inside Docker",
            "date_modified": "2023-06-05T02:49:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36189821\">Comments</a>",
            "url": "https://blog.cloudnativefolks.org/ebpf-for-cybersecurity-part-1",
            "title": "eBPF for Cybersecurity – Part 1",
            "date_modified": "2023-06-04T21:36:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36189107\">Comments</a>",
            "url": "https://github.com/containers/crun",
            "title": "Crun: Fast and lightweight OCI runtime and C library for running containers",
            "date_modified": "2023-06-04T20:09:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36173020\">Comments</a>",
            "url": "https://discourse.nixos.org/t/the-nixos-foundations-call-to-action-s3-costs-require-community-support/28672",
            "title": "The NixOS Foundation’s Call to Action: S3 Costs Require Community Support",
            "date_modified": "2023-06-03T02:10:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36161397\">Comments</a>",
            "url": "https://mitchellh.com/writing/building-large-technical-projects",
            "title": "My Approach to Building Large Technical Projects – Mitchell Hashimoto",
            "date_modified": "2023-06-02T05:32:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36151286\">Comments</a>",
            "url": "https://www.cnbc.com/2023/06/01/meta-quest-3-unveiled-ahead-of-apples-planned-vr-headset-debut.html",
            "title": "Zuckerberg unveils Meta’s newest VR headset days before Apple reveals its own",
            "date_modified": "2023-06-01T14:12:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36151234\">Comments</a>",
            "url": "https://meltano.com/blog/introducing-meltano-cloud-you-build-the-pipelines-we-manage-the-infrastructure/",
            "title": "Show HN: Meltano Cloud (GitLab spinout) – Managed infra for open source ELT",
            "date_modified": "2023-06-01T14:09:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36144674\">Comments</a>",
            "url": "https://github.com/OAI/moonwalk",
            "title": "OpenAPI v4 (aka Moonwalk) Proposal",
            "date_modified": "2023-05-31T21:38:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36098695\">Comments</a>",
            "url": "https://www.open-fire.dev/",
            "title": "Show HN: Open Fire Serverless CI",
            "date_modified": "2023-05-27T21:50:11.000Z"
        },
        {
            "content_html": "<figure><img width=\"2560\" height=\"1200\" src=\"https://blog.jetbrains.com/wp-content/uploads/2023/05/9-ways-to-prevent-a-supply-chain-attack-on-your-cicd-server-1.png\"></figure>\n\n\n\n<p>CI/CD servers are high-value targets for attackers because of their central role in critical development processes. They provide access to source code, a valuable asset for software companies, and can deploy code to production environments, creating serious risks if not adequately secured. Even a single vulnerability can enable attackers to compromise the supply chain, inject malware, and seize control of systems.&nbsp;</p>\n\n\n\n<p>According to “The State of Software Supply Chain Security 2023”, this has led to a rise in supply chain attacks since 2020, and 57% of organizations have suffered security incidents related to DevOps toolchain exposures.&nbsp;</p>\n\n\n\n<p>To avoid data breaches and business disruptions, securing CI/CD servers should be a top priority. Furthermore, Google’s “<a href=\"https://blog.jetbrains.com/space/2022/11/16/2022-state-of-devops-report-takeaways/\" target=\"_blank\" rel=\"noreferrer noopener\">2022 Accelerate State of DevOps Report</a>” suggests that implementing proper security controls can have a positive impact on software delivery performance.</p>\n\n\n\n<p>In this whitepaper, we present 9 effective ways to prevent a supply chain attack on your CI/CD server, providing practical guidance and best practices to help you strengthen security and protect critical development processes. </p>\n\n\n\n<p>By implementing these strategies, you can minimize the risk of a supply chain attack and ensure the integrity, availability, and confidentiality of your software supply chain.</p>\n\n\n\n<p align=\"center\">\n    <a title=\"Read the full case study\" href=\"https://www.jetbrains.com/lp/teamcity-cicd-security-whitepaper/\">Download the whitepaper</a>\n</p>",
            "url": "https://blog.jetbrains.com/teamcity/2023/05/cicd-security-whitepaper/",
            "title": "[Whitepaper] 9 Ways to Prevent a Supply Chain Attack on Your CI/CD Server",
            "date_modified": "2023-05-25T14:07:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36068896\">Comments</a>",
            "url": "https://deno.com/blog/v1.34",
            "title": "Deno 1.34: Deno compile supports NPM packages",
            "date_modified": "2023-05-25T10:16:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36059247\">Comments</a>",
            "url": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9106553/",
            "title": "Man Cures Acid Reflux By Eating Upside-Down",
            "date_modified": "2023-05-24T15:12:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36054997\">Comments</a>",
            "url": "https://www.theregister.com/2023/05/24/windows_365_boot_preview/",
            "title": "Microsoft enables booting PCs directly into cloud PCs",
            "date_modified": "2023-05-24T07:11:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36046222\">Comments</a>",
            "url": "https://developers.redhat.com/articles/2023/05/23/podman-desktop-now-generally-available",
            "title": "Podman Desktop 1.0",
            "date_modified": "2023-05-23T15:59:13.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/gmlvct/how_host_your_side_projects_for_free_2023\">Comments</a></p>",
            "url": "https://dev.to/livecycle/how-to-host-your-side-projects-for-free-in-2023-from-auth-to-database-42im",
            "title": "How to host your side-projects for free in 2023: from Auth to Database",
            "date_modified": "2023-05-22T21:20:52.000Z"
        },
        {
            "content_html": "<p>Etrian Odyssey Origins Collection new trailer showcases difficulty options, auto mapping, improved graphics, and more for Switch and Steam.</p>\n<p></p>\n<hr>\n<p>\nTo read <a rel=\"nofollow\" href=\"https://themakoreactor.com/news/etrian-odyssey-origins-collection-new-difficulty-options-gameplay-trailer-nintendo-switch-pc-steam-deck/51158/\">Etrian Odyssey Origins Collection New Trailer Showcases Difficulty Options, Auto Mapping, Improved Graphics, and More</a> in full, please visit <a rel=\"nofollow\" href=\"https://themakoreactor.com\">The Mako Reactor</a>. Thank you.</p>",
            "url": "https://themakoreactor.com/news/etrian-odyssey-origins-collection-new-difficulty-options-gameplay-trailer-nintendo-switch-pc-steam-deck/51158/",
            "title": "Etrian Odyssey Origins Collection New Trailer Showcases Difficulty Options, Auto Mapping, Improved Graphics, and More",
            "date_modified": "2023-05-22T13:58:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36030969\">Comments</a>",
            "url": "https://rosenzweig.io/blog/growing-up-alyssa.html",
            "title": "Growing Up Alyssa",
            "date_modified": "2023-05-22T13:09:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36029032\">Comments</a>",
            "url": "https://brr.fyi/posts/polar-night",
            "title": "Polar Night",
            "date_modified": "2023-05-22T09:15:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36020612\">Comments</a>",
            "url": "https://blog.min.io/erasure-coding-cpu-utilization/",
            "title": "Benchmarking Intel, AMD and Graviton Using Erasure Coding Workloads",
            "date_modified": "2023-05-21T13:23:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36015196\">Comments</a>",
            "url": "https://github.com/leomos/dwgd",
            "title": "Dwgd: Docker WireGuard Driver",
            "date_modified": "2023-05-20T20:19:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36010467\">Comments</a>",
            "url": "https://www.duckware.com/tech/solving-intermittent-cable-modem-issues.html",
            "title": "Solving Intermittent Cable Modem Issues",
            "date_modified": "2023-05-20T09:22:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36009768\">Comments</a>",
            "url": "https://netflixtechblog.com/debugging-a-fuse-deadlock-in-the-linux-kernel-c75cd7989b6d?gi=06e75507c327",
            "title": "Debugging a FUSE deadlock in the Linux kernel at Netflix",
            "date_modified": "2023-05-20T06:17:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=36006561\">Comments</a>",
            "url": "https://queue.acm.org/detail.cfm?id=3595878",
            "title": "Devex: What actually drives productivity",
            "date_modified": "2023-05-19T20:34:22.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image1-56.png\" alt=\"Workers Browser Rendering API enters open beta\" width=\"1801\" height=\"1013\"></figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image1-55.png\" alt=\"Workers Browser Rendering API enters open beta\"><p>The Workers Browser Rendering API allows developers to programmatically control and interact with a headless browser instance and create automation flows for their applications and products.</p><p>Since the <a href=\"http://blog.cloudflare.com/introducing-workers-browser-rendering-api/\">private beta announcement</a>, based on the feedback we've been receiving and our own roadmap, the team has been working on the developer experience and improving the platform architecture for the best possible performance and reliability. <strong>Today we enter the open beta and will start onboarding the customers on the </strong><a href=\"https://www.cloudflare.com/lp/workers-browser-rendering-api?ref=blog.cloudflare.com\"><strong>wait list</strong></a><strong>.</strong></p><h3 id=\"developer-experience\">Developer experience</h3><p>Starting today, <a href=\"https://developers.cloudflare.com/workers/wrangler/\">Wrangler</a>, our command-line tool for configuring, building, and deploying applications with Cloudflare developer products, has support for the Browser Rendering API bindings.</p><p>You can install Wrangler Beta using <a href=\"https://www.npmjs.com/package/npm\">npm</a>:</p><pre><code>npm install wrangler --save-dev\n</code></pre>\n<p>Bindings allow your Workers to interact with resources on the Cloudflare developer platform. In this case, they will provide your Worker script with an authenticated endpoint to interact with a dedicated Chromium browser instance.</p><p>This is all you need in your <code>wrangler.toml</code> once this service is enabled for your account:</p><pre><code>browser = { binding = \"MYBROWSER\", type = \"browser\" }\n</code></pre>\n<p>Now you can deploy any Worker script that requires Browser Rendering capabilities. You can spawn Chromium instances and interact with them programmatically in any way you typically do manually behind your browser.</p><p>Under the hood, the Browser Rendering API gives you access to a WebSocket endpoint that speaks the <a href=\"https://chromedevtools.github.io/devtools-protocol/\">DevTools Protocol</a>. DevTools is what allows us to instrument a Chromium instance running in our global network, and it's the same protocol that Chrome uses on your computer when you inspect a page.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image2-35.png\" alt=\"Workers Browser Rendering API enters open beta\" width=\"1726\" height=\"938\"></figure><p>With enough dedication, you can, in fact, implement your own DevTools client and talk the protocol directly. But that'd be crazy; almost no one does that.</p><p>So…</p><h3 id=\"puppeteer\">Puppeteer</h3><p><a href=\"https://pptr.dev/\">Puppeteer</a> is one of the most popular libraries that abstract the lower-level DevTools protocol from developers and provides a high-level API that you can use to easily instrument Chrome/Chromium and automate browsing sessions. It's widely used for things like creating screenshots, crawling pages, and testing web applications.</p><p>Puppeteer typically <a href=\"https://pptr.dev/api/puppeteer.puppeteer.connect\">connects</a> to a local Chrome or Chromium browser using the DevTools port.</p><p>We forked a version of Puppeteer and patched it to connect to the Workers Browser Rendering API instead. The <a href=\"https://github.com/cloudflare/puppeteer/blob/main/src/puppeteer-core.ts\">changes</a> are minimal; after connecting the developers can then use the full Puppeteer API as they would on a standard setup.</p><p>Our version is <a href=\"https://github.com/cloudflare/puppeteer\">open sourced here</a>, and the npm can be installed from <a href=\"https://www.npmjs.com/\">npmjs</a> as <a href=\"https://www.npmjs.com/package/@cloudflare/puppeteer\">@cloudflare/puppeteer</a>. Using it from a Worker is as easy as:</p><pre><code>import puppeteer from \"@cloudflare/puppeteer\";\n</code></pre>\n<p>And then all it takes to launch a browser from your script is:</p><pre><code>const browser = await puppeteer.launch(env.MYBROWSER);\n</code></pre>\n<p>In the long term, we will update Puppeteer to keep matching the version of our Chromium instances infrastructure running in our network.</p><h3 id=\"developer-documentation\">Developer documentation</h3><p>Following the tradition with other Developer products, we created a dedicated section for the Browser Rendering APIs in our <a href=\"https://developers.cloudflare.com/browser-rendering\">Developer's Documentation site</a>.</p><p>You can access this page to learn more about how the service works, Wrangler support, APIs, and limits, and find examples of starter templates for common applications.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/download-16.png\" alt=\"Workers Browser Rendering API enters open beta\" width=\"1600\" height=\"777\"></figure><h3 id=\"an-example-application-taking-screenshots\">An example application: taking screenshots</h3><p>Taking screenshots from web pages is one of the typical cases for browser automation.</p><p>Let's create a Worker that uses the Browser Rendering API to do just that. This is a perfect example of how to set up everything and get an application running in minutes, it will give you a good overview of the steps involved and the basics of the Puppeteer API, and then you can move from here to other more sophisticated use-cases.</p><p>Step one, start a project, install Wrangler and Cloudflare’s fork of Puppeteer:</p><pre><code>npm init -f\nnpm install wrangler -save-dev\nnpm install @cloudflare/puppeteer -save-dev\n</code></pre>\n<p>Step two, let’s create the simplest possible wrangler.toml configuration file with the Browser Rendering API binding:</p><pre><code>name = \"browser-worker\"\nmain = \"src/index.ts\"\ncompatibility_date = \"2023-03-14\"\nnode_compat = true\nworkers_dev = true\n\nbrowser = { binding = \"MYBROWSER\", type = \"browser\" }\n</code></pre>\n<p>Step three, create src/index.ts with your Worker code:</p><pre><code>import puppeteer from \"@cloudflare/puppeteer\";\n\nexport default {\n    async fetch(request: Request, env: Env): Promise&lt;Response&gt; {\n        const { searchParams } = new URL(request.url);\n        let url = searchParams.get(\"url\");\n        let img: Buffer;\n        if (url) {\n            const browser = await puppeteer.launch(env.MYBROWSER);\n            const page = await browser.newPage();\n            await page.goto(url);\n            img = (await page.screenshot()) as Buffer;\n            await browser.close();\n            return new Response(img, {\n                headers: {\n                    \"content-type\": \"image/jpeg\",\n                },\n            });\n        } else {\n            return new Response(\n                \"Please add the ?url=https://example.com/ parameter\"\n            );\n        }\n    },\n};\n</code></pre>\n<p>That's it, no more steps. This Worker instantiates a browser using Puppeteer, opens a new page, navigates to whatever you put in the \"url\" parameter, takes a screenshot of the page, closes the browser, and responds with the JPEG image of the screenshot. It can't get any easier to get started with the Browser Rendering API.</p><p>Run <code>npx wrangler dev –remote</code> to test it and <code>npx wrangler publish</code> when you’re done.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image4-21.png\" alt=\"Workers Browser Rendering API enters open beta\" width=\"1999\" height=\"1655\"></figure><p>You can explore the <a href=\"https://github.com/cloudflare/puppeteer/blob/main/docs/api/index.md\">entire Puppeteer API</a> and implement other functionality and logic from here. And, because it's Workers, you can add other <a href=\"https://developers.cloudflare.com/\">developer products</a> to your code. You might need a <a href=\"https://developers.cloudflare.com/d1/\">relational database</a>, or a <a href=\"https://developers.cloudflare.com/workers/runtime-apis/kv/#kv\">KV store</a> to cache your screenshots, or an <a href=\"https://developers.cloudflare.com/r2/\">R2 bucket</a> to archive your crawled pages and assets, or maybe use a <a href=\"https://developers.cloudflare.com/workers/runtime-apis/durable-objects/#durable-objects\">Durable Object</a> to keep your browser instance alive and share it with multiple requests, or <a href=\"https://developers.cloudflare.com/queues/\">queues</a> to handle your jobs asynchronous, we have all of this and <a href=\"https://developers.cloudflare.com/\">more</a>.</p><p>You can also find this and other examples of how to use Browser Rendering in the <a href=\"https://developers.cloudflare.com/browser-rendering\">Developer Documentation</a>.</p><h3 id=\"how-do-we-use-browser-rendering\">How do we use Browser Rendering</h3><p>Dogfooding our products is one of the best ways to test and improve them, and in some cases, our internal needs dictate or influence our roadmap. Workers Browser Rendering is a good example of that; it was born out of our necessities before we realized it could be a product. We've been using it extensively for things like taking screenshots of pages for social sharing or dashboards, testing web software in CI, or gathering page load performance metrics of our applications.</p><p>But there's one product we've been using to stress test and push the limits of the Browser Rendering API and drive the engineering sprints that brought us to open the beta to our customers today: The <a href=\"https://radar.cloudflare.com/scan\">Cloudflare Radar URL Scanner</a>.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image3-22.png\" alt=\"Workers Browser Rendering API enters open beta\" width=\"1999\" height=\"1310\"></figure><p>The URL Scanner scans any URL and compiles a full report containing technical, performance, privacy, and security details about that page. It's processing thousands of scans per day currently. It was built on top of Workers and uses a combination of the Browser Rendering APIs with Puppeteer to create enriched <a href=\"https://en.wikipedia.org/wiki/HAR_(file_format)\">HAR archives</a> and page screenshots, Durable Objects to reuse browser instances, Queues to handle customers' load and execute jobs asynchronously, and R2 to store the final reports.</p><p>This tool will soon have its own \"how we built it\" blog. Still, we wanted to let you know about it now because it is a good example of how you can build sophisticated applications using Browser Rendering APIs at scale starting today.</p><h3 id=\"future-plans\">Future plans</h3><p>The team will keep improving the Browser Rendering API, but a few things are worth mentioning today.</p><p>First, we are looking into upstreaming the changes in our Puppeteer fork to the main project so that using the official library with the Cloudflare Workers Browser Rendering API becomes as easy as a configuration option.</p><p>Second, one of the reasons why we decided to expose the <a href=\"https://chromedevtools.github.io/devtools-protocol/\">DevTools</a> protocol bare naked in the Worker binding is so that it can support other browser instrumentalization libraries in the future. <a href=\"https://playwright.dev/docs/api/class-playwright\">Playwright</a> is a good example of another popular library that developers want to use.</p><p>And last, we are also keeping an eye on and testing <a href=\"https://w3c.github.io/webdriver-bidi/\">WebDriver BiDi</a>, a \"new standard browser automation protocol that bridges the gap between the WebDriver Classic and CDP (DevTools) protocols.\" Click here to know more about the <a href=\"https://developer.chrome.com/blog/webdriver-bidi-2023/\">status of WebDriver BiDi.</a></p><h3 id=\"final-words\">Final words</h3><p>The Workers Browser Rendering API enters open beta today. We will gradually be enabling the customers in the <a href=\"https://www.cloudflare.com/en-gb/lp/workers-browser-rendering-api/?ref=blog.cloudflare.com\">wait list</a> in batches and sending them emails. We look forward to seeing what you will be building with it and want to hear from you.</p><p>As usual, you can talk to us on our <a href=\"https://discord.cloudflare.com/\">Developers Discord</a> or the <a href=\"https://community.cloudflare.com/\">Community forum</a>; the team will be listening.</p><h3 id=\"watch-on-cloudflare-tv\">Watch on Cloudflare TV</h3><div><iframe src=\"https://customer-rhnwzxvb3mg4wz3v.cloudflarestream.com/56ddbbea460d0e736d45e6b4944c637d/iframe?preload=true&amp;poster=https%3A%2F%2Fcustomer-rhnwzxvb3mg4wz3v.cloudflarestream.com%2F56ddbbea460d0e736d45e6b4944c637d%2Fthumbnails%2Fthumbnail.jpg%3Ftime%3D1s%26height%3D600\"></iframe></div>",
            "url": "http://blog.cloudflare.com/browser-rendering-open-beta/",
            "title": "Workers Browser Rendering API enters open beta",
            "date_modified": "2023-05-19T13:00:32.000Z"
        },
        {
            "content_html": "<img src=\"http://blog.cloudflare.com/content/images/2023/05/image4-16.png\" alt=\"Announcing Cloudflare Secrets Store\"><p><small>This post is also available in <a href=\"http://blog.cloudflare.com/zh-cn/secrets-store-zh-cn/\">简体中文</a>,  <a href=\"http://blog.cloudflare.com/ja-jp/secrets-store-ja-jp/\">日本語</a>, <a href=\"http://blog.cloudflare.com/de-de/secrets-store-de-de/\">Deutsch</a>, <a href=\"http://blog.cloudflare.com/fr-fr/secrets-store-fr-fr/\">Français</a> and <a href=\"http://blog.cloudflare.com/es-es/secrets-store-es-es/\">Español</a>.</small></p>\n<figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image4-15.png\" alt=\"Announcing Cloudflare Secrets Store\" width=\"1999\" height=\"1125\"></figure><p>We’re excited to announce Secrets Store - Cloudflare’s new secrets management offering!</p><p>A secrets store does exactly what the name implies - it stores secrets. Secrets are variables that are used by developers that contain sensitive information - information that only authorized users and systems should have access to.</p><p>If you’re building an application, there are various types of secrets that you need to manage. Every system should be designed to have identity &amp; authentication data that verifies some form of identity in order to grant access to a system or application. One example of this is API tokens for making read and write requests to a database. Failure to store these tokens securely could lead to unauthorized access of information - intentional or accidental.</p><p>The stakes with secret’s management are high. Every gap in the storage of these values has potential to lead to a data leak or compromise. A security administrator’s worst nightmare.</p><p>Developers are primarily focused on creating applications, they want to build quickly, they want their system to be performant, and they want it to scale. For them, secrets management is about ease of use, performance, and reliability. On the other hand, security administrators are tasked with ensuring that these secrets remain secure. It’s their responsibility to safeguard sensitive information, ensure that security best practices are met, and to manage any fallout of an incident such as a data leak or breach. It’s their job to verify that developers at their company are building in a secure and foolproof manner.</p><p>In order for developers to build at high velocity and for security administrators to feel at ease, companies need to adopt a highly reliable and secure secrets manager. This should be a system that ensures that sensitive information is stored with the highest security measures, while maintaining ease of use that will allow engineering teams to efficiently build.</p><h3 id=\"why-cloudflare-is-building-a-secrets-store\">Why Cloudflare is building a secrets store</h3><p>Cloudflare’s mission is to help build a better Internet - that means a more secure Internet. We recognize our customers’ need for a secure, centralized repository for storing sensitive data. Within the Cloudflare ecosystem, are various places where customers need to store and access API and authorization tokens, shared secrets, and sensitive information. It’s our job to make it easy for customers to manage these values securely.</p><p>The need for secrets management goes beyond Cloudflare. Customers have sensitive data that they manage everywhere - at their cloud provider, on their own infrastructure, across machines. Our plan is to make our Secrets Store a one-stop shop for all of our customer’s secrets.</p><h3 id=\"the-evolution-of-secrets-at-cloudflare\">The evolution of secrets at Cloudflare</h3><p>In 2020, we launched <a href=\"http://blog.cloudflare.com/workers-secrets-environment/\">environment variables and secrets</a> for Cloudflare Workers, allowing customers to create and encrypt variables across their Worker scripts. By doing this, developers can obfuscate the value of a variable so that it’s no longer available in plaintext and can only be accessed by the Worker.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image2-26.png\" alt=\"Announcing Cloudflare Secrets Store\" width=\"1600\" height=\"437\"></figure><p>Adoption and use of these secrets is quickly growing. We now have more than three million Workers scripts that reference variables and secrets managed through Cloudflare. One piece of feedback that we continue to hear from customers is that these secrets are scoped too narrowly. </p><p>Today, customers can only use a variable or secret within the Worker that it’s associated with. Instead, customers have secrets that they share across Workers. They don’t want to re-create those secrets and focus their time on keeping them in sync. They want account level secrets that are managed in one place but are referenced across multiple Workers scripts and functions.</p><p>Outside of Workers, there are many use cases for secrets across Cloudflare services.</p><p>Inside our <a href=\"https://www.cloudflare.com/waf/\">Web Application Firewall (WAF)</a>, customers can make rules that look for authorization headers in order to grant or deny access to requests. Today, when customers create these rules, they put the authorization header value in plaintext, so that anyone with WAF access in the Cloudflare account can see its value. What we’ve heard from our customers is that even internally, engineers should not have access to this type of information. Instead, what our customers want is one place to manage the value of this header or token, so that only authorized users can see, create, and rotate this value. Then when creating a WAF rule, engineers can just reference the associated secret e.g.“account.mysecretauth”. By doing this, we help our customers secure their system by reducing the access scope and enhance management of this value by keeping it updated in one place.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image1-45.png\" alt=\"Announcing Cloudflare Secrets Store\" width=\"1999\" height=\"515\"></figure><p>With new Cloudflare products and features quickly developing, we’re hearing more and more use cases for a centralized secrets manager. One that can be used to store <a href=\"https://developers.cloudflare.com/cloudflare-one/identity/service-tokens/\">Access Service tokens</a> or shared secrets for <a href=\"https://developers.cloudflare.com/fundamentals/notifications/create-notifications/configure-webhooks/\">Webhooks</a>.</p><p>With the new account level Secrets Store, we’re excited to give customers the tools they need to manage secrets across Cloudflare services.</p><h3 id=\"securing-the-secret-store\">Securing the Secret Store</h3><p>To have a secrets store, there are a number of measures that need to be in place, and we’re committing to providing these for our customers.</p><p>First, we’re going to give the tools that our customers need to restrict access to secrets. We will have scope permissions that will allow admins to choose which users can view, create, edit, or remove secrets. We also plan to add the same level of granularity to our services - giving customers the ability to say “only allow this Worker to access this secret and only allow this set of Firewall rules to access that secret”.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/05/image3-15.png\" alt=\"Announcing Cloudflare Secrets Store\" width=\"1999\" height=\"516\"></figure><p>Next, we’re going to give our customers extensive audits that will allow them to track the access and use of their secrets. Audit logs are crucial for security administrators. They can be used to alert team members that a secret was used by an unauthorized service or that a compromised secret is being accessed when it shouldn’t be. We will give customers audit logs for every secret-related event, so that customers can see exactly who is making changes to secrets and which services are accessing and when.</p><p>In addition to the built-in security of the Secrets Store, we’re going to give customers the tools to rotate their encryption keys on-demand or at a cadence that fits the right security posture for them.</p><h2 id=\"sign-up-for-the-beta\">Sign up for the beta</h2><p>We’re excited to get the Secrets Store in our customer’s hands. If you’re interested in using this, please fill out this <a href=\"http://www.cloudflare.com/lp/secrets-store\">form</a>, and we’ll reach out to you when it’s ready to use.</p><h3 id=\"watch-on-cloudflare-tv\">Watch on Cloudflare TV</h3><div><iframe src=\"https://customer-rhnwzxvb3mg4wz3v.cloudflarestream.com/75b97f7a31ac535296f806385031b84a/iframe?preload=true&amp;poster=https%3A%2F%2Fcustomer-rhnwzxvb3mg4wz3v.cloudflarestream.com%2F75b97f7a31ac535296f806385031b84a%2Fthumbnails%2Fthumbnail.jpg%3Ftime%3D1s%26height%3D600\"></iframe></div>",
            "url": "http://blog.cloudflare.com/secrets-store/",
            "title": "Announcing Cloudflare Secrets Store",
            "date_modified": "2023-05-18T13:00:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35974845\">Comments</a>",
            "url": "https://smyachenkov.com/posts/book-review-the-staff-engineers-path/",
            "title": "The Staff Engineer&#x27;s Path – Book Review",
            "date_modified": "2023-05-17T13:18:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35974080\">Comments</a>",
            "url": "https://retrocomputermuseum.co.uk/",
            "title": "Retro Computer Museum – Leicester, UK",
            "date_modified": "2023-05-17T12:02:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35972047\">Comments</a>",
            "url": "https://www.pixelmator.com/blog/2023/05/16/photomator-for-mac-is-out-now/",
            "title": "Photomator for Mac",
            "date_modified": "2023-05-17T06:52:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35958715\">Comments</a>",
            "url": "https://oblivus.com",
            "title": "Show HN: Oblivus GPU Cloud – Affordable and scalable GPU servers from $0.29&#x2F;hr",
            "date_modified": "2023-05-16T07:30:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35942583\">Comments</a>",
            "url": "https://github.com/brexhq/prompt-engineering",
            "title": "Brex’s Prompt Engineering Guide",
            "date_modified": "2023-05-15T00:12:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35937554\">Comments</a>",
            "url": "https://hpbn.co/",
            "title": "High Performance Browser Networking",
            "date_modified": "2023-05-14T12:47:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35936999\">Comments</a>",
            "url": "https://cyberhost.uk/the-hidden-macos-speedtest-tool-networkquality/",
            "title": "MacOS networkQuality",
            "date_modified": "2023-05-14T10:45:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35934287\">Comments</a>",
            "url": "https://blog.metaobject.com/2023/05/setting-up-hetzner-arm-instances-with.html",
            "title": "Setting up Hetzner ARM instances with and for Objective-S",
            "date_modified": "2023-05-14T00:45:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35864114\">Comments</a>",
            "url": "https://domainverification.org",
            "title": "Show HN: Automatic Domain Verification",
            "date_modified": "2023-05-08T17:06:01.000Z"
        },
        {
            "content_html": "<div id=\"rss-wrap\">\n<figure>\n  <img src=\"https://cdn.arstechnica.net/wp-content/uploads/2023/05/passkey-target-illustration-800x450.jpg\" alt=\"Google passkeys are a no-brainer. You’ve turned them on, right?\">\n      <p><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2023/05/passkey-target-illustration.jpg\" data-height=\"1080\" data-width=\"1920\">Enlarge</a> (credit: Aurich Lawson | Getty Images)</p>  </figure>\n\n\n\n\n\n\n<div><a name=\"page-1\"></a></div>\n<p>By now, you’ve likely heard that passwordless Google accounts have <a href=\"https://arstechnica.com/gadgets/2023/05/passwordless-google-accounts-are-here-you-can-now-switch-to-passkey-only/\">finally arrived</a>. The replacement for passwords is known as \"passkeys.\"</p>\n<p>There are many misconceptions about passkeys, both in terms of their usability and the security and privacy benefits they offer compared with current authentication methods. That’s not surprising, given that passwords have been in use for the past 60 years, and passkeys are so new. The long and short of it is that with a few minutes of training, passkeys are easier to use than passwords, and in a matter of months—once a dozen or so industry partners finish rolling out the remaining pieces—using passkeys will be easier still. Passkeys are also vastly more secure and privacy-preserving than passwords, for reasons I'll explain later.</p>\n<h2>What is a passkey anyway?</h2>\n<p>This article provides a primer to get people started with Google's implementation of passkeys and explains the technical underpinnings that make them a much easier and more effective way to protect against account takeovers. A handful of smaller sites—specifically, PayPal, Instacart, Best Buy, Kayak, Robinhood, Shop Pay, and Cardpointers—have rolled out various options for logging in with passkeys, but those choices are more proofs of concept than working solutions. Google is the first major online service to make passkeys available, and its offering is refined and comprehensive enough that I’m recommending people turn them on today.</p></div><p><a href=\"https://arstechnica.com/?p=1937113#p3\">Read 26 remaining paragraphs</a> | <a href=\"https://arstechnica.com/?p=1937113&amp;comments=1\">Comments</a></p>",
            "url": "https://arstechnica.com/?p=1937113",
            "title": "Google passkeys are a no-brainer. You’ve turned them on, right?",
            "date_modified": "2023-05-08T13:50:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35837330\">Comments</a>",
            "url": "https://twitter.com/TurnerNovak/status/1654577231937544192/photo/1",
            "title": "A cryptocurrency company had a $65M bill, per Datadog’s Q1 earnings call",
            "date_modified": "2023-05-06T02:17:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35829733\">Comments</a>",
            "url": "https://quii.dev/HTMX_is_the_Future",
            "title": "Htmx Is the Future",
            "date_modified": "2023-05-05T14:32:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35817422\">Comments</a>",
            "url": "https://nextjs.org/blog/next-13-4",
            "title": "Next.js 13.4",
            "date_modified": "2023-05-04T15:53:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35811741\">Comments</a>",
            "url": "https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90",
            "title": "Prime Video Abandons Serverless Distributed Stack for Monolith, saves 90%",
            "date_modified": "2023-05-04T06:04:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/vs4kf8/devops_capabilities\">Comments</a></p>",
            "url": "https://cloud.google.com/architecture/devops",
            "title": "DevOps capabilities",
            "date_modified": "2023-05-02T09:09:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35779902\">Comments</a>",
            "url": "https://service-markup.vercel.app",
            "title": "Vercel Service Markup",
            "date_modified": "2023-05-01T22:10:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35762175\">Comments</a>",
            "url": "https://github.com/Textualize/frogmouth",
            "title": "Show HN: Frogmouth – A Markdown browser for your terminal",
            "date_modified": "2023-04-30T13:05:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35738072\">Comments</a>",
            "url": "https://www.inferless.com/serverless-gpu-market",
            "title": "The State of Serverless GPUs",
            "date_modified": "2023-04-28T06:21:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35737442\">Comments</a>",
            "url": "https://orbstack.dev/",
            "title": "OrbStack – Fast, lightweight Docker Desktop and Colima alternative for macOS",
            "date_modified": "2023-04-28T04:21:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35690258\">Comments</a>",
            "url": "https://anonymoushash.vmbrasseur.com/2023/04/24/software-bill-of-materials-sbom",
            "title": "An Intro to SBOMs",
            "date_modified": "2023-04-24T17:01:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35688878\">Comments</a>",
            "url": "https://glorifiedgluer.com/blog/2023/developing-with-nix-and-make/",
            "title": "I use Nix and make(1) to develop",
            "date_modified": "2023-04-24T15:31:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35686023\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=JiuBeLDSGR0",
            "title": "Why People Aren&#x27;t Deploying to Vercel Anymore",
            "date_modified": "2023-04-24T11:04:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35684220\">Comments</a>",
            "url": "https://philbooth.me/blog/nine-ways-to-shoot-yourself-in-the-foot-with-postgresql",
            "title": "Ways to shoot yourself in the foot with PostgreSQL",
            "date_modified": "2023-04-24T06:11:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35649459\">Comments</a>",
            "url": "https://lwn.net/Articles/926882/",
            "title": "Standardizing BPF",
            "date_modified": "2023-04-21T03:19:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35645464\">Comments</a>",
            "url": "https://evidence.dev",
            "title": "evidence.dev – Business Intelligence as Code",
            "date_modified": "2023-04-20T20:01:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35628642\">Comments</a>",
            "url": "https://github.com/ory/kratos/releases/tag/v0.13.0",
            "title": "Show HN: Open-source Auth0 alternative Ory Kratos v0.13 released – nearing v1.0",
            "date_modified": "2023-04-19T14:30:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35617198\">Comments</a>",
            "url": "https://astral.sh/",
            "title": "Astral",
            "date_modified": "2023-04-18T17:38:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35615848\">Comments</a>",
            "url": "https://tailscale.com/blog/pricing-v3/",
            "title": "Changes to Tailscale Pricing and Plans",
            "date_modified": "2023-04-18T16:04:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35595967\">Comments</a>",
            "url": "https://security-blog-prod-wp01.azurewebsites.net/en-us/security/blog/2023/04/06/devops-threat-matrix/",
            "title": "DevOps Threat Matrix",
            "date_modified": "2023-04-17T01:44:40.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/bdxddf/google_assured_oss\">Comments</a></p>",
            "url": "https://sethmlarson.dev/google-assured-oss",
            "title": "Google Assured OSS",
            "date_modified": "2023-04-14T14:28:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35559646\">Comments</a>",
            "url": "https://tailscale.com/blog/more-throughput/",
            "title": "Surpassing 10Gb&#x2F;S over Tailscale",
            "date_modified": "2023-04-13T18:11:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35540084\">Comments</a>",
            "url": "https://www.hetzner.com/press-release/arm64-cloud/",
            "title": "Hetzner Introduces ARM64 Cloud Servers",
            "date_modified": "2023-04-12T14:17:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35530268\">Comments</a>",
            "url": "https://tailscale.dev/blog/tailscale-sucks",
            "title": "Tailscale Sucks",
            "date_modified": "2023-04-11T19:34:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35525683\">Comments</a>",
            "url": "https://www.vitling.xyz/toys/swarm/",
            "title": "Swarm",
            "date_modified": "2023-04-11T14:30:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35525222\">Comments</a>",
            "url": "https://supabase.com/blog/edge-runtime-self-hosted-deno-functions",
            "title": "Supabase Edge Runtime: Self-Hosted Deno Functions",
            "date_modified": "2023-04-11T13:57:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35482706\">Comments</a>",
            "url": "https://www.nationalgeographic.com/magazine/article/jesus-tomb-archaeology",
            "title": "What Archaeology Is Telling Us About the Real Jesus",
            "date_modified": "2023-04-07T15:34:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35470371\">Comments</a>",
            "url": "https://engineering.fb.com/2023/04/06/open-source/buck2-open-source-large-scale-build-system/",
            "title": "Buck2: Our open source build system",
            "date_modified": "2023-04-06T16:05:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35450007\">Comments</a>",
            "url": "https://github.com/cilium/pwru",
            "title": "Packet, where are you? – eBPF-based Linux kernel networking debugger",
            "date_modified": "2023-04-05T06:11:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35432958\">Comments</a>",
            "url": "https://www.onefootprint.com/blog/inside-the-enclave-part-2",
            "title": "Engineering with Enclaves",
            "date_modified": "2023-04-03T23:36:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35427343\">Comments</a>",
            "url": "https://github.com/hocus-dev/hocus",
            "title": "Show HN: Hocus – self-hosted alternative to GitHub Codespaces using Firecracker",
            "date_modified": "2023-04-03T17:00:42.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/eaymy2/using_tailscale_without_using_tailscale\">Comments</a></p>",
            "url": "https://tailscale.dev/blog/headscale-funnel",
            "title": "Using Tailscale without using Tailscale",
            "date_modified": "2023-04-01T12:47:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35393292\">Comments</a>",
            "url": "https://planetscale.com/blog/announcing-the-planetscale-github-actions",
            "title": "The PlanetScale GitHub Actions",
            "date_modified": "2023-03-31T20:38:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35374302\">Comments</a>",
            "url": "https://tailscale.com/blog/tailscale-funnel-beta/",
            "title": "Tailscale Funnel now available in beta",
            "date_modified": "2023-03-30T15:31:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35371291\">Comments</a>",
            "url": "https://github.blog/2023-03-28-introducing-self-service-sboms/",
            "title": "Self-Service SBOMs",
            "date_modified": "2023-03-30T12:00:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35369983\">Comments</a>",
            "url": "https://github.com/frain-dev/convoy",
            "title": "Show HN: Open-Source Webhooks Gateway for Platform Engineers",
            "date_modified": "2023-03-30T09:11:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35354729\">Comments</a>",
            "url": "https://lwn.net/Articles/927262/",
            "title": "Ubuntu stops shipping Flatpak by default",
            "date_modified": "2023-03-29T09:37:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35327743\">Comments</a>",
            "url": "https://ergomake.dev/blog/docker-compose-as-a-universal-interface/",
            "title": "Docker-compose.yml as a universal infrastructure interface",
            "date_modified": "2023-03-27T15:12:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35313943\">Comments</a>",
            "url": "https://blog.mnus.de/2023/03/archlinux-zfs/",
            "title": "When root on ZFS breaks on Arch Linux",
            "date_modified": "2023-03-26T12:57:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35307640\">Comments</a>",
            "url": "https://computer.rip/2023-03-24-docker.html",
            "title": "Docker",
            "date_modified": "2023-03-25T21:33:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35305561\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=L_Guz73e6fw",
            "title": "OpenAI CEO Sam Altman on Lex Fridman Podcast",
            "date_modified": "2023-03-25T18:31:04.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/7hvf6e/docker_docker_inc_docker_hub_their\">Comments</a></p>",
            "url": "https://computer.rip/2023-03-24-docker.html",
            "title": "Docker, Docker Inc., Docker Hub, and their relation to the broader world of containerization",
            "date_modified": "2023-03-25T14:43:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35296165\">Comments</a>",
            "url": "https://podman-desktop.io/downloads",
            "title": "Podman Desktop: Same functionality as Docker Desktop but open source",
            "date_modified": "2023-03-24T21:53:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35295807\">Comments</a>",
            "url": "https://www.docker.com/blog/no-longer-sunsetting-the-free-team-plan/",
            "title": "Docker: We’re No Longer Sunsetting the Free Team Plan",
            "date_modified": "2023-03-24T21:26:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35286681\">Comments</a>",
            "url": "https://taxheaven3000.com/",
            "title": "Anime dating sim that also does your taxes",
            "date_modified": "2023-03-24T09:01:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35285390\">Comments</a>",
            "url": "https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/",
            "title": "We updated our RSA SSH host key",
            "date_modified": "2023-03-24T05:28:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35283967\">Comments</a>",
            "url": "https://xdgbasedirectoryspecification.com/",
            "title": "Use the XDG Base Directory Specification",
            "date_modified": "2023-03-24T01:37:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35263886\">Comments</a>",
            "url": "https://fly.io/ruby-dispatch/mrsk-vs-flyio/",
            "title": "MRSK vs. Fly.io",
            "date_modified": "2023-03-22T16:56:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35263285\">Comments</a>",
            "url": "https://dev.37signals.com/bringing-our-apps-back-home/",
            "title": "De-cloud and de-k8s – bringing our apps back home",
            "date_modified": "2023-03-22T16:12:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35247242\">Comments</a>",
            "url": "https://planetscale.com/courses/mysql-for-developers/introduction/course-introduction",
            "title": "MySQL for Developers",
            "date_modified": "2023-03-21T15:05:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35238822\">Comments</a>",
            "url": "https://clickhouse.com/blog/building-clickhouse-cloud-from-scratch-in-a-year",
            "title": "Building ClickHouse Cloud from scratch in a year",
            "date_modified": "2023-03-20T21:12:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35235764\">Comments</a>",
            "url": "https://www.infoworld.com/article/3691292/dockers-bad-week.html",
            "title": "Docker’s Bad Week",
            "date_modified": "2023-03-20T17:59:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35208402\">Comments</a>",
            "url": "https://20th.nixos.org/",
            "title": "20 Years of Nix",
            "date_modified": "2023-03-18T12:09:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35183806\">Comments</a>",
            "url": "https://www.warp.dev/blog/introducing-warp-ai",
            "title": "Warp AI – AI directly integrated into the terminal",
            "date_modified": "2023-03-16T16:05:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35182240\">Comments</a>",
            "url": "https://content.citymapper.com/news/2582/citymapper-joins-via",
            "title": "Citymapper Joins Via",
            "date_modified": "2023-03-16T14:12:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35180648\">Comments</a>",
            "url": "https://github.com/chainloop-dev/chainloop",
            "title": "Show HN: Chainloop, A Software Supply Chain Attestation solution devs won&#x27;t hate",
            "date_modified": "2023-03-16T11:34:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35174574\">Comments</a>",
            "url": "https://status.flyio.net/incidents/sq7fsdlrg92f",
            "title": "Fly.io outage, recently deployed apps down, deployments disabled",
            "date_modified": "2023-03-15T20:54:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35171689\">Comments</a>",
            "url": "https://zed.dev",
            "title": "Zed, the new code editor from Atom developers, has entered open beta",
            "date_modified": "2023-03-15T17:23:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35168652\">Comments</a>",
            "url": "https://modernfontstacks.com/",
            "title": "Modern Font Stacks",
            "date_modified": "2023-03-15T14:16:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35166579\">Comments</a>",
            "url": "https://twitter.com/josevalim/status/1635767176769462272",
            "title": "Elixir: Docker now charges open source orgs $300",
            "date_modified": "2023-03-15T11:28:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35166317\">Comments</a>",
            "url": "https://blog.alexellis.io/docker-is-deleting-open-source-images/",
            "title": "Docker is deleting Open Source organisations - what you need to know",
            "date_modified": "2023-03-15T10:57:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35155944\">Comments</a>",
            "url": "https://github.com/awslabs/mountpoint-s3",
            "title": "Mountpoint – file client for S3 written in Rust, from AWS",
            "date_modified": "2023-03-14T18:12:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35150345\">Comments</a>",
            "url": "https://modernfontstacks.com/",
            "title": "Show HN: Modern Font Stacks – New system font stack CSS for modern OSs",
            "date_modified": "2023-03-14T12:24:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35137327\">Comments</a>",
            "url": "https://discuss.systems/@dan/110008052977994607",
            "title": "Audiophile forum debating which versions of memcpy had the highest sound quality (2013)",
            "date_modified": "2023-03-13T15:44:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35100297\">Comments</a>",
            "url": "https://nickdesaulniers.github.io/blog/2023/03/10/disambiguating-arm/",
            "title": "Disambiguating Arm, Arm ARM, ARMv9, ARM9, ARM64, AArch64, A64, A78, ...",
            "date_modified": "2023-03-10T20:16:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35096691\">Comments</a>",
            "url": "https://blog.cloudflare.com/introducing-oxy/",
            "title": "Oxy is Cloudflare&#x27;s Rust-based next generation proxy framework",
            "date_modified": "2023-03-10T16:35:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35096366\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=35096366",
            "title": "Launch HN: Defer (YC W23) – Zero-infrastructure background jobs for Node.js",
            "date_modified": "2023-03-10T16:16:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35094407\">Comments</a>",
            "url": "https://tech.ahrefs.com/how-ahrefs-saved-us-400m-in-3-years-by-not-going-to-the-cloud-8939dd930af8?gi=16d65c893933",
            "title": "Ahrefs Saved US$400M in 3 Years by Not Going to the Cloud",
            "date_modified": "2023-03-10T14:04:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35083665\">Comments</a>",
            "url": "https://fluxkeyboard.com/",
            "title": "Flux Keyboard",
            "date_modified": "2023-03-09T17:15:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35067224\">Comments</a>",
            "url": "https://blog.cachix.org/posts/2023-03-08-cachix-1-3-uploads-unleashed/",
            "title": "Cachix 1.3: Nix uploads unleashed",
            "date_modified": "2023-03-08T10:12:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35067224\">Comments</a>",
            "url": "https://blog.cachix.org/posts/2023-03-08-cachix-1-3-uploads-unleashed/",
            "title": "Cachix 1.3: Uploads unleashed",
            "date_modified": "2023-03-08T10:12:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35066920\">Comments</a>",
            "url": "https://coutant.org/1.html",
            "title": "Microphones",
            "date_modified": "2023-03-08T09:23:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35064079\">Comments</a>",
            "url": "https://dagster.io/blog/fast-deploys-with-pex-and-docker",
            "title": "How we deploy faster with warm Docker containers",
            "date_modified": "2023-03-08T01:42:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35059992\">Comments</a>",
            "url": "https://earthly.dev/blog/remote-code-execution/",
            "title": "Remote Code Execution as a Service",
            "date_modified": "2023-03-07T19:29:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35059770\">Comments</a>",
            "url": "https://www.richardtaylor.dev/articles/globally-distributed-elixir-over-tailscale",
            "title": "Globally Distributed Elixir over Tailscale",
            "date_modified": "2023-03-07T19:12:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35057092\">Comments</a>",
            "url": "https://vercel.com/blog/framework-defined-infrastructure",
            "title": "Framework-Defined Infrastructure",
            "date_modified": "2023-03-07T16:17:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=35040194\">Comments</a>",
            "url": "https://matthewgrohman.substack.com/p/want-an-unfair-advantage-in-your",
            "title": "Want an unfair advantage in your tech career? Consume content for other roles",
            "date_modified": "2023-03-06T12:27:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34981929\">Comments</a>",
            "url": "https://www.githubstatus.com/incidents/bypf7bqdh4g0",
            "title": "GitHub Packages Is Down",
            "date_modified": "2023-03-01T13:07:29.000Z"
        },
        {
            "content_html": "<p>Recently I started using a Mac for the first time. The biggest downside I’ve\nnoticed so far is that the package management is much worse than on Linux.\nAt some point I got frustrated with homebrew because I felt like it was\nspending too much time upgrading when I installed new packages, and so I\nthought – maybe I’ll try the <a href=\"https://nixos.org/\">nix</a> package manager!</p>\n\n<p>nix has a reputation for being confusing (it has its whole\nown programming language!), so I’ve been trying to figure out how to use nix in\na way that’s as simple as possible and does not involve managing any\nconfiguration files or learning a new programming language. Here’s what I’ve\nfigured out so far! We’ll talk about how to:</p>\n\n<ol>\n<li>install packages with nix</li>\n<li>build a custom nix package for a C++ program called <a href=\"https://mj.ucw.cz/sw/paperjam/\">paperjam</a></li>\n<li>install a 5-year-old version of <a href=\"https://github.com/gohugoio/hugo/\">hugo</a> with nix</li>\n</ol>\n\n<p>As usual I’ve probably gotten some stuff wrong in this post since I’m still\npretty new to nix. I’m also still not sure how much I like nix – it’s very\nconfusing! But it’s helped me compile some software that I was struggling to\ncompile otherwise, and in general it seems to install things faster than\nhomebrew.</p>\n\n<h3 id=\"what-s-interesting-about-nix\">what’s interesting about nix?</h3>\n\n<p>People often describe nix as “declarative package management”. I don’t\ncare that much about declarative package management, so here are two things\nthat I appreciate about nix:</p>\n\n<ol>\n<li>It provides binary packages (hosted at <a href=\"https://cache.nixos.org/\">cache.nixos.org/</a>) that you can quickly download and install</li>\n<li>For packages which don’t have binary packages, it makes it easier to compile them</li>\n</ol>\n\n<p>I think that the reason nix is good at compiling software is that:</p>\n\n<ul>\n<li>you can have multiple versions of the same library or program installed at a time (you could have 2 different versions of libc for instance). For example I have two versions of node on my computer right now, one at <code>/nix/store/4ykq0lpvmskdlhrvz1j3kwslgc6c7pnv-nodejs-16.17.1</code> and one at <code>/nix/store/5y4bd2r99zhdbir95w5pf51bwfg37bwa-nodejs-18.9.1</code>.</li>\n<li>when nix builds a package, it builds it in isolation, using only the\nspecific versions of its dependencies that you explicitly declared. So\nthere’s no risk that the package secretly depends on another package on your\nsystem that you don’t know about. No more fighting with <code>LD_LIBRARY_PATH</code>!</li>\n<li>a lot of people have put a lot of work into writing down all of the\ndependencies of packages</li>\n</ul>\n\n<p>I’ll give a couple of examples later in this post of two times nix made it easier for me to compile software.</p>\n\n<h3 id=\"how-i-got-started-with-nix\">how I got started with nix</h3>\n\n<p>here’s how I got started with nix:</p>\n\n<ol>\n<li>Install nix. I forget exactly how I did this, but it looks like there’s an <a href=\"https://nixos.org/download\">official installer</a> and an <a href=\"https://zero-to-nix.com/concepts/nix-installer\">unofficial installer from zero-to-nix.com</a>. The <a href=\"https://nixos.org/manual/nix/stable/installation/installing-binary.html#macos\">instructions for uninstalling nix on MacOS with the standard multi-user install</a> are a bit complicated, so it might be worth choosing an installation method with simpler uninstall instructions.</li>\n<li>Put <code>~/.nix-profile/bin</code> on my PATH</li>\n<li>Install packages with <code>nix-env -iA nixpkgs.NAME</code></li>\n<li>That’s it.</li>\n</ol>\n\n<p>Basically the idea is to treat <code>nix-env -iA</code> like <code>brew install</code> or <code>apt-get install</code>.</p>\n\n<p>For example, if I want to install <code>fish</code>, I can do that like this:</p>\n\n<pre><code>nix-env -iA nixpkgs.fish\n</code></pre>\n\n<p>This seems to just download some binaries from <a href=\"https://cache.nixos.org\">cache.nixos.org</a> – pretty simple.</p>\n\n<p>Some people use nix to install their Node and Python and Ruby packages, but I haven’t\nbeen doing that – I just use <code>npm install</code> and <code>pip install</code> the same way I\nalways have.</p>\n\n<h3 id=\"some-nix-features-i-m-not-using\">some nix features I’m not using</h3>\n\n<p>There are a bunch of nix features/tools that I’m not using, but that I’ll\nmention. I originally thought that you <em>had</em> to use these features to use nix,\nbecause most of the nix tutorials I’ve read talk about them. But you don’t have to use them.</p>\n\n<ul>\n<li>NixOS (a Linux distribution)</li>\n<li><a href=\"https://nixos.org/guides/nix-pills/developing-with-nix-shell.html\">nix-shell</a></li>\n<li><a href=\"https://nixos.wiki/wiki/Flakes\">nix flakes</a></li>\n<li><a href=\"https://github.com/nix-community/home-manager\">home-manager</a></li>\n<li><a href=\"https://devenv.sh/\">devenv.sh</a></li>\n</ul>\n\n<p>I won’t go into these because I haven’t really used them and there are lots of\nexplanations out there.</p>\n\n<h3 id=\"where-are-nix-packages-defined\">where are nix packages defined?</h3>\n\n<p>I think packages in the main nix package repository are defined in <a href=\"https://github.com/NixOS/nixpkgs/\">github.com/NixOS/nixpkgs/</a></p>\n\n<p>It looks like you can search for packages at <a href=\"https://search.nixos.org/packages\">search.nixos.org/packages</a>. The two official ways to search packages seem to be:</p>\n\n<ul>\n<li><code>nix-env -qaP NAME</code>, which is very extremely slow and which I haven’t been able to get to actually work</li>\n<li><code>nix --extra-experimental-features 'nix-command flakes' search nixpkgs NAME</code>, which does seem to work but is kind of a mouthful. Also all of the packages it prints out start with <code>legacyPackages</code> for some reason</li>\n</ul>\n\n<p>I found a way to search nix packages from the command line that I liked better:</p>\n\n<ol>\n<li>Run <code>nix-env -qa '*' &gt; nix-packages.txt</code> to get a list of every package in the Nix repository</li>\n<li>Write a short <code>nix-search</code> script that just greps <code>packages.txt</code> (<code>cat ~/bin/nix-packages.txt | awk '{print $1}' | rg \"$1\"</code>)</li>\n</ol>\n\n<h3 id=\"everything-is-installed-with-symlinks\">everything is installed with symlinks</h3>\n\n<p>One of nix’s major design choices is that there isn’t one single <code>bin</code> with all\nyour packages, instead you use symlinks.  There are a lot of layers of symlinks. A few examples of symlinks:</p>\n\n<ul>\n<li><code>~/.nix-profile</code> on my machine is (indirectly) a symlink to <code>/nix/var/nix/profiles/per-user/bork/profile-111-link/</code></li>\n<li><code>~/.nix-profile/bin/fish</code> is a symlink to <code>/nix/store/afkwn6k8p8g97jiqgx9nd26503s35mgi-fish-3.5.1/bin/fish</code></li>\n</ul>\n\n<p>When I install something, it creates a new <code>profile-112-link</code> directory with new symlinks and updates my <code>~/.nix-profile</code> to point to that directory.</p>\n\n<p>I think this means that if I install a new version of <code>fish</code> and I don’t like it, I can\neasily go back just by running <code>nix-env --rollback</code> – it’ll move me to my previous profile directory.</p>\n\n<h3 id=\"uninstalling-packages-doesn-t-delete-them\">uninstalling packages doesn’t delete them</h3>\n\n<p>If I uninstall a nix package like this, it doesn’t actually free any hard drive space, it just removes the symlinks.</p>\n\n<pre><code>$ nix-env --uninstall oil\n</code></pre>\n\n<p>I’m still not sure how to actually delete the package – I ran a garbage collection like this, which seemed to delete some things:</p>\n\n<pre><code>$ nix-collect-garbage\n...\n85 store paths deleted, 74.90 MiB freed\n</code></pre>\n\n<p>But I still have <code>oil</code> on my system at <code>/nix/store/8pjnk6jr54z77jiq5g2dbx8887dnxbda-oil-0.14.0</code>.</p>\n\n<p>There’s a more aggressive version of <code>nix-collect-garbage</code> that also deletes old versions of your profiles (so that you can’t rollback)</p>\n\n<pre><code>$ nix-collect-garbage -d --delete-old\n</code></pre>\n\n<p>That doesn’t delete <code>/nix/store/8pjnk6jr54z77jiq5g2dbx8887dnxbda-oil-0.14.0</code> either though and I’m not sure why.</p>\n\n<h3 id=\"upgrading\">upgrading</h3>\n\n<p>It looks like you can upgrade nix packages like this:</p>\n\n<pre><code>nix-channel --update\nnix-env --upgrade\n</code></pre>\n\n<p>(similar to <code>apt-get update &amp;&amp; apt-get upgrade</code>)</p>\n\n<p>I haven’t really upgraded anything yet. I think that if something goes wrong with an upgrade, you can roll back (because everything is immutable in nix!) with</p>\n\n<pre><code>nix-env --rollback\n</code></pre>\n\n<p>Someone linked me to <a href=\"https://ianthehenry.com/posts/how-to-learn-nix/my-first-package-upgrade/\">this post from Ian Henry</a> that\ntalks about some confusing problems with <code>nix-env --upgrade</code> – maybe it\ndoesn’t work the way you’d expect? I guess I’ll be wary around upgrades.</p>\n\n<h3 id=\"next-goal-make-a-custom-package-of-paperjam\">next goal: make a custom package of <code>paperjam</code></h3>\n\n<p>After a few months of installing existing packages, I wanted to make a custom package with nix for a program called <a href=\"https://mj.ucw.cz/sw/paperjam/\">paperjam</a> that wasn’t already packaged.</p>\n\n<p>I was actually struggling to compile <code>paperjam</code> at all even without nix  because the version I had\nof <code>libiconv</code> I has on my system was wrong. I thought it might be easier to\ncompile it with nix even though I didn’t know how to make nix packages yet. And\nit actually was!</p>\n\n<p>But figuring out how to get there was VERY confusing, so here are some notes about how I did it.</p>\n\n<h3 id=\"how-to-build-an-example-package\">how to build an example package</h3>\n\n<p>Before I started working on my <code>paperjam</code> package, I wanted to build an example existing package just to\nmake sure I understood the process for building a package. I was really\nstruggling to figure out how to do this, but I asked in Discord and someone\nexplained to me how I could get a working package from <a href=\"https://github.com/NixOS/nixpkgs/\">github.com/NixOS/nixpkgs/</a> and build it. So here\nare those instructions:</p>\n\n<p><strong>step 1:</strong> Download some arbitrary package from <a href=\"https://github.com/NixOS/nixpkgs/\">nixpkgs</a> on github, for example the <code>dash</code> package:</p>\n\n<pre><code>wget https://raw.githubusercontent.com/NixOS/nixpkgs/47993510dcb7713a29591517cb6ce682cc40f0ca/pkgs/shells/dash/default.nix -O dash.nix\n</code></pre>\n\n<p><strong>step 2</strong>: Replace the first statement (<code>{ lib , stdenv , buildPackages , autoreconfHook , pkg-config , fetchurl , fetchpatch , libedit , runCommand , dash }:</code> with <code>with import &lt;nixpkgs&gt; {};</code> I don’t know why you have to do this,\nbut it works.</p>\n\n<p><strong>step 3</strong>: Run <code>nix-build dash.nix</code></p>\n\n<p>This compiles the package</p>\n\n<p><strong>step 4</strong>: Run <code>nix-env -i -f dash.nix</code></p>\n\n<p>This installs the package into my <code>~/.nix-profile</code></p>\n\n<p>That’s all! Once I’d done that, I felt like I could modify the <code>dash</code> package and make my own package.</p>\n\n<h3 id=\"how-i-made-my-own-package\">how I made my own package</h3>\n\n<p><code>paperjam</code> has one dependency (<code>libpaper</code>) that also isn’t packaged yet, so I needed to build <code>libpaper</code> first.</p>\n\n<p>Here’s <code>libpaper.nix</code>. I basically just wrote this by copying and pasting from\nother packages in the <a href=\"https://github.com/NixOS/nixpkgs/\">nixpkgs</a> repository.\nMy guess is what’s happening here is that nix has some default rules for\ncompiling C packages (like “run <code>make install</code>”), so the <code>make install</code> happens\ndefault and I don’t need to configure it explicitly.</p>\n\n<pre><code>with import &lt;nixpkgs&gt; {};\n\nstdenv.mkDerivation rec {\n  pname = \"libpaper\";\n  version = \"0.1\";\n\n  src = fetchFromGitHub {\n    owner = \"naota\";\n    repo = \"libpaper\";\n    rev = \"51ca11ec543f2828672d15e4e77b92619b497ccd\";\n    hash = \"sha256-S1pzVQ/ceNsx0vGmzdDWw2TjPVLiRgzR4edFblWsekY=\";\n  };\n\n  buildInputs = [ ];\n\n  meta = with lib; {\n    homepage = \"https://github.com/naota/libpaper\";\n    description = \"libpaper\";\n    platforms = platforms.unix;\n    license = with licenses; [ bsd3 gpl2 ];\n  };\n}\n</code></pre>\n\n<p>Basically this just tells nix how to download the source from GitHub.</p>\n\n<p>I built this by running <code>nix-build libpaper.nix</code></p>\n\n<p>Next, I needed to compile <code>paperjam</code>. Here’s a link to the <a href=\"https://github.com/jvns/nixpkgs/blob/22b70a48a797538c76b04261b3043165896d8f69/paperjam.nix\">nix package I wrote</a>. The main things I needed to do other than telling it where to download the source were:</p>\n\n<ol>\n<li>add some extra build dependencies (like <code>asciidoc</code>)</li>\n<li>set some environment variables for the install (<code>installFlags = [ \"PREFIX=$(out)\" ];</code>) so that it installed in the correct directory instead of <code>/usr/local/bin</code>.</li>\n</ol>\n\n<p>I set the hashes by first leaving the hash empty, then running <code>nix-build</code> to get an error message complaining about a mismatched hash. Then I copied the correct hash out of the error message.</p>\n\n<p>I figured out how to set <code>installFlags</code> just by running <code>rg PREFIX</code>\nin the nixpkgs repository – I figured that needing to set a <code>PREFIX</code> was\npretty common and someone had probably done it before, and I was right. So I\njust copied and pasted that line from another package.</p>\n\n<p>Then I ran:</p>\n\n<pre><code>nix-build paperjam.nix\nnix-env -i -f paperjam.nix\n</code></pre>\n\n<p>and then everything worked and I had <code>paperjam</code> installed! Hooray!</p>\n\n<h3 id=\"next-goal-install-a-5-year-old-version-of-hugo\">next goal: install a 5-year-old version of <code>hugo</code></h3>\n\n<p>Right now I build this blog using Hugo 0.40, from 2018. I don’t need any new\nfeatures so I haven’t felt a need to upgrade. On Linux this is easy: Hugo’s\nreleases are a static binary, so I can just download the 5-year-old binary from\nthe <a href=\"https://github.com/gohugoio/hugo/releases/tag/v0.40\">releases page</a> and\nrun it. Easy!</p>\n\n<p>But on this Mac I ran into some complications. Mac hardware has changed in the\nlast 5 years, so the Mac Hugo binary I downloaded crashed. And when I tried to\nbuild it from source with <code>go build</code>, that didn’t work either because Go build\nnorms have changed in the last 5 years as well.</p>\n\n<p>I was working around this by running Hugo in a Linux docker container, but I\ndidn’t love that: it was kind of slow and it felt silly. It shouldn’t be that\nhard to compile one Go program!</p>\n\n<p>Nix to the rescue! Here’s what I did to install the old version of Hugo with\nnix.</p>\n\n<h3 id=\"installing-hugo-0-40-with-nix\">installing Hugo 0.40 with nix</h3>\n\n<p>I wanted to install Hugo 0.40 and put it in my PATH as <code>hugo-0.40</code>. Here’s how\nI did it. I did this in a kind of weird way, but it worked (<a href=\"https://lazamar.github.io/download-specific-package-version-with-nix/\">Searching and installing old versions of Nix packages</a>\ndescribes a probably more normal method).</p>\n\n<p><strong>step 1</strong>: Search through the nixpkgs repo to find Hugo 0.40</p>\n\n<p>I found the <code>.nix</code> file here <a href=\"https://github.com/NixOS/nixpkgs/blob/17b2ef2/pkgs/applications/misc/hugo/default.nix\">github.com/NixOS/nixpkgs/blob/17b2ef2/p…</a></p>\n\n<p><strong>step 2</strong>: Download that file and build it</p>\n\n<p>I downloaded that file (and another file called <code>deps.nix</code> in the same directory), replaced the first line with <code>with import &lt;nixpkgs&gt; {};</code>, and built it with <code>nix-build hugo.nix</code>.</p>\n\n<p>That almost worked without any changes, but I had to make two changes:</p>\n\n<ul>\n<li>replace <code>with stdenv.lib</code> to <code>with lib</code> for some reason.</li>\n<li>rename the package to <code>hugo040</code> so that it wouldn’t conflict with the other version of <code>hugo</code> that I had installed</li>\n</ul>\n\n<p><strong>step 3</strong>: Rename <code>hugo</code> to <code>hugo-0.40</code></p>\n\n<p>I write a little post install script to rename the Hugo binary.</p>\n\n<pre><code>  postInstall = ''\n    mv $out/bin/hugo $out/bin/hugo-0.40\n  '';\n</code></pre>\n\n<p>I figured out how to run this by running <code>rg 'mv '</code> in the nixpkgs repository and just copying and modifying something that seemed related.</p>\n\n<p><strong>step 4</strong>: Install it</p>\n\n<p>I installed into my <code>~/.nix-profile/bin</code> by running <code>nix-env -i -f hugo.nix</code>.</p>\n\n<p>And it all works! I put the final <code>.nix</code> file into my own personal <a href=\"https://github.com/jvns/nixpkgs/\">nixpkgs repo</a> so that I can use it again later if I\nwant.</p>\n\n<h3 id=\"reproducible-builds-aren-t-magic-they-re-really-hard\">reproducible builds aren’t magic, they’re really hard</h3>\n\n<p>I think it’s worth noting here that this <code>hugo.nix</code> file isn’t magic – the\nreason I can easily compile Hugo 0.40 today is that many people worked for a long time to make it possible to\npackage that version of Hugo in a reproducible way.</p>\n\n<h3 id=\"that-s-all\">that’s all!</h3>\n\n<p>Installing <code>paperjam</code> and this 5-year-old version of Hugo were both\nsurprisingly painless and actually much easier than compiling it without nix,\nbecause nix made it much easier for me to compile the <code>paperjam</code> package with\nthe right version of <code>libiconv</code>, and because someone 5 years ago had already\ngone to the trouble of listing out the exact dependencies for Hugo.</p>\n\n<p>I don’t have any plans to get much more complicated with nix (and it’s still\nvery possible I’ll get frustrated with it and go back to homebrew!), but we’ll\nsee what happens! I’ve found it much easier to start in a simple way and then\nstart using more features if I feel the need instead of adopting a whole bunch\nof complicated stuff all at once.</p>\n\n<p>I probably won’t use nix on Linux – I’ve always been happy enough with <code>apt</code>\n(on Debian-based distros) and <code>pacman</code> (on Arch-based distros), and they’re\nmuch less confusing. But on a Mac it seems like it might be worth it. We’ll\nsee! It’s very possible in 3 months I’ll get frustrated with nix and just go back to homebrew.</p>\n\n<h3 id=\"5-month-update-rebuilding-my-nix-profile\">5-month update: rebuilding my nix profile</h3>\n\n<p>Update from 5 months in: nix is still going well, and I’ve only run into 1\nproblem, which is that every <code>nix-env -iA</code> package installation started failing\nwith the error “bad meta.outputsToInstall”.</p>\n\n<p><a href=\"https://github.com/zombiezen/dotfiles/blob/main/nix/nix-rebuild-profile/nix-rebuild-profile.sh\">This script</a>\nfrom Ross Light fixes that problem though. It lists every derivation installed\nin my current profile and creates a new profile with the exact same\nderivations. This feels like a nix bug (surely creating a new profile with the\nexact same derivations should be a no-op?) but I haven’t looked into it more yet.</p>",
            "url": "https://jvns.ca/blog/2023/02/28/some-notes-on-using-nix/",
            "title": "Some notes on using nix",
            "date_modified": "2023-02-28T23:16:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34953242\">Comments</a>",
            "url": "https://benhoyt.com/writings/flyio/",
            "title": "From Go on EC2 to Fly.io",
            "date_modified": "2023-02-27T05:13:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34929572\">Comments</a>",
            "url": "https://blog.railway.app/p/scaling-railway-automating-support",
            "title": "Serving 250k Developers with One Support Engineer",
            "date_modified": "2023-02-24T20:22:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34925275\">Comments</a>",
            "url": "https://deno.com/blog/v1.31",
            "title": "Deno 1.31: Package.json Support",
            "date_modified": "2023-02-24T15:16:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34913193\">Comments</a>",
            "url": "https://www.psypost.org/2023/02/camouflaging-of-autistic-traits-linked-to-internalizing-symptoms-such-as-anxiety-and-depression-68382",
            "title": "Camouflaging autistic traits linked to internalizing symptoms anxiety depression",
            "date_modified": "2023-02-23T17:05:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34899372\">Comments</a>",
            "url": "https://www.vice.com/en/article/y3py9j/ai-companion-replika-erotic-roleplay-updates",
            "title": "AI Companion Users Are in Crisis, Reporting Sudden Sexual Rejection",
            "date_modified": "2023-02-22T17:37:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34898253\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34898253",
            "title": "Launch HN: Depot (YC W23) – Fast Docker Builds in the Cloud",
            "date_modified": "2023-02-22T16:40:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34897645\">Comments</a>",
            "url": "https://github.com/highlight/highlight",
            "title": "Show HN: We’re open-sourcing our session replay tool",
            "date_modified": "2023-02-22T16:02:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34891854\">Comments</a>",
            "url": "https://techcrunch.com/2023/02/21/soylent-acquired-starco-brands-nutrition/",
            "title": "Soylent Acquired by Starco Brands",
            "date_modified": "2023-02-22T05:02:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34885077\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34885077",
            "title": "Launch HN: Moonrepo (YC W23) – Open-source build system",
            "date_modified": "2023-02-21T18:48:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34882051\">Comments</a>",
            "url": "https://www.fastly.com/blog/announcing-certainly-fastlys-own-tls-certification-authority",
            "title": "Certainly: Fastly’s Own TLS Certification Authority",
            "date_modified": "2023-02-21T15:13:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34881967\">Comments</a>",
            "url": "https://goteleport.com/blog/passkeys/",
            "title": "Passkeys for Infrastructure",
            "date_modified": "2023-02-21T15:06:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34874014\">Comments</a>",
            "url": "https://github.com/containers/podman/releases/tag/v4.4.0",
            "title": "Podman 4.4",
            "date_modified": "2023-02-20T22:55:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34870581\">Comments</a>",
            "url": "https://www.1place.app/blog/the-saas-2-0-manifesto",
            "title": "Show HN: The SaaS 2.0 Manifesto",
            "date_modified": "2023-02-20T17:46:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34866901\">Comments</a>",
            "url": "https://about.gitlab.com/handbook/acquisitions/acquisition-process/",
            "title": "Gitlab&#x27;s Startup Acquisition Process",
            "date_modified": "2023-02-20T10:32:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34860226\">Comments</a>",
            "url": "https://blog.newsblur.com/2021/06/28/story-of-a-hacking/",
            "title": "A Docker footgun led to a vandal deleting NewsBlur&#x27;s MongoDB database (2021)",
            "date_modified": "2023-02-19T18:16:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34836027\">Comments</a>",
            "url": "https://www.tinybird.co/blog-posts/better-ci-pipeline-with-data",
            "title": "We cut our CI pipeline execution time in half",
            "date_modified": "2023-02-17T15:50:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34824983\">Comments</a>",
            "url": "https://github.com/BretFisher/awesome-swarm",
            "title": "Bret Fisher – Awesome-Swarm",
            "date_modified": "2023-02-16T20:01:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34817222\">Comments</a>",
            "url": "https://brew.sh/2023/02/16/homebrew-4.0.0/",
            "title": "Homebrew 4.0.0",
            "date_modified": "2023-02-16T11:00:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34775061\">Comments</a>",
            "url": "https://twitter.com/BigscreenVR/status/1625152589624135698",
            "title": "Bigscreen VR announces Beyond: the world&#x27;s smallest VR headset",
            "date_modified": "2023-02-13T15:52:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34750522\">Comments</a>",
            "url": "https://twitter.com/kelseyhightower/status/1624081136073994240",
            "title": "Databases on Kubernetes is fundamentally same as a database on a VM",
            "date_modified": "2023-02-11T08:03:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34742946\">Comments</a>",
            "url": "https://status.flyio.net",
            "title": "Ask HN: Are people considering moving off of Fly.io?",
            "date_modified": "2023-02-10T17:39:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34719137\">Comments</a>",
            "url": "https://www.linode.com/docs/guides/podman-vs-docker/",
            "title": "Podman vs. Docker: Comparing the two containerization tools",
            "date_modified": "2023-02-09T02:46:38.000Z"
        },
        {
            "content_html": "<div id=\"rss-wrap\">\n<figure>\n  <img src=\"https://cdn.arstechnica.net/wp-content/uploads/2023/02/listing-800x667.jpg\" alt=\"Hand typing on OnePlus Featuring Keyboard 81 Pro\">\n      <p><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2023/02/listing.jpg\" data-height=\"1600\" data-width=\"1920\">Enlarge</a> (credit: <a rel=\"nofollow\" href=\"https://www.oneplus.com/us/product/oneplus-featuring-keyboard-81-pro\">OnePlus</a>)</p>  </figure>\n\n\n\n\n\n\n<div><a name=\"page-1\"></a></div>\n<p>OnePlus is finally ready to detail its first mechanical keyboard. No, we didn't need another company to start making mechanical keyboards. But if you're looking for a new Bluetooth keyboard that plays particularly well with Macs, has a compact layout, and a rotary knob that looks stylish and functional, OnePlus will have one more choice for you come April.</p>\n<p>Announced today, OnePlus is jumping into the <a href=\"https://arstechnica.com/gadgets/2022/03/the-ars-technica-guide-to-mechanical-keyboards/\">mechanical keyboard</a> race with a strange name, the <a href=\"https://www.oneplus.com/us/product/oneplus-featuring-keyboard-81-pro\">Featuring Keyboard 81 Pro</a>. The \"81\" refers to the key count, while \"Pro\" is assumably meant to make workers and power users think the keyboard's a good fit; but the name doesn't quite roll off the tongue. The outlier here is the \"Featuring\" bit, which refers to the OnePlus Featuring \"co-creation\" platform that builds products based off user feedback. Community users are said to have contributed to the 81 Pro's design, including its proprietary switches. OnePlus' press release today claimed it will release \"many\" more Featuring products.</p>\n<div><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2023/02/listing-1.jpg\"><img alt=\"OnePlus-y touches include a logo-stamped Esc key.\" src=\"https://cdn.arstechnica.net/wp-content/uploads/2023/02/listing-1-640x427.jpg\"></a><p><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2023/02/listing-1.jpg\" rel=\"nofollow\">OnePlus-y touches include a logo-stamped Esc key.</a> (credit: <a rel=\"nofollow\" href=\"https://twitter.com/oneplus/status/1622974925798768640/photo/1\">OnePlus/Twitter</a>)</p></div>\n<p>Another huge influence on the 81 Pro is keyboard-maker Keychron, which is said to have helped engineer the product. That includes its layout, which matches the layout of the <a href=\"https://www.keychron.com/products/keychron-q1-pro-qmk-via-wireless-custom-mechanical-keyboard\">Q1 Pro</a> that Keychron is currently <a href=\"https://www.kickstarter.com/projects/keytron/keychron-q1-pro-qmk-via-wireless-custom-mechanical-keyboard?ref=975sox\">crowdfunding</a>. In addition to macOS, the keyboard is supposed to work with Windows, Linux, and Android, OnePlus' press release said. The keyboard's product page also claims support with iOS. Similar to some wireless Keychron keyboards, like the <a href=\"https://arstechnica.com/gadgets/2022/01/keychron-k14-review-the-rare-mac-ready-wireless-mechanical-keyboard/\">Keychron K14</a>, there's a toggle&nbsp;on the keyboard's side for switching from Mac to Windows. Considering the lack of USB-A ports among Macs, the Bluetooth 5.1 keyboard charges over a USB-C to USB-C cable (there's also a USB-C to USB-A adapter).</p></div><p><a href=\"https://arstechnica.com/?p=1915672#p3\">Read 7 remaining paragraphs</a> | <a href=\"https://arstechnica.com/?p=1915672&amp;comments=1\">Comments</a></p>",
            "url": "https://arstechnica.com/?p=1915672",
            "title": "OnePlus unveils its first mechanical keyboard: Mac layout, custom switches",
            "date_modified": "2023-02-07T19:10:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34696849\">Comments</a>",
            "url": "https://www.theverge.com/2023/2/7/23587454/microsoft-bing-edge-chatgpt-ai",
            "title": "Microsoft announces new Bing and Edge browser powered by upgraded ChatGPT AI",
            "date_modified": "2023-02-07T18:27:42.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2023/02/BLOG-1707-header-1.png\" alt=\"How Cloudflare erroneously throttled a customer’s web traffic\"></figure><img src=\"http://blog.cloudflare.com/content/images/2023/02/BLOG-1707-header.png\" alt=\"How Cloudflare erroneously throttled a customer’s web traffic\"><p>Over the years when Cloudflare has had an <a href=\"http://blog.cloudflare.com/tag/outage/\">outage</a> that affected our customers we have very quickly blogged about what happened, why, and what we are doing to address the causes of the outage. Today’s post is a little different. It’s about a single customer’s website <a href=\"https://news.ycombinator.com/item?id=34639212\">not working correctly</a> because of incorrect action taken by Cloudflare.</p><p>Although the customer was not in any way banned from Cloudflare, or lost access to their account, their website didn’t work. And it didn’t work because Cloudflare applied a bandwidth throttle between us and their origin server. The effect was that the website was unusable.</p><p>Because of this unusual throttle there was some internal confusion for our customer support team about what had happened. They, incorrectly, believed that the customer had been limited because of a breach of section 2.8 of our <a href=\"https://www.cloudflare.com/terms/\">Self-Serve Subscription Agreement</a> which prohibits use of our self-service CDN to serve excessive non-HTML content, such as images and video, without a paid plan that includes those services (this is, for example, designed to prevent someone building an image-hosting service on Cloudflare and consuming a huge amount of bandwidth; for that sort of use case we have paid <a href=\"https://www.cloudflare.com/products/cloudflare-images/\">image</a> and <a href=\"https://www.cloudflare.com/products/cloudflare-stream/\">video</a> plans).</p><p>However, this customer wasn’t breaking section 2.8, and they were both a paying customer and a paying customer of Cloudflare Workers through which the throttled traffic was passing. This throttle should not have happened. In addition, there is and was no need for the customer to upgrade to some other plan level.</p><p>This incident has set off a number of workstreams inside Cloudflare to ensure better communication between teams, prevent such an incident happening, and to ensure that communications between Cloudflare and our customers are much clearer.</p><p>Before we explain our own mistake and how it came to be, we’d like to apologize to the customer. We realize the serious impact this had, and how we fell short of expectations. In this blog post, we want to explain what happened, and more importantly what we’re going to change to make sure it does not happen again.</p><h3 id=\"background\">Background</h3><p>On February 2, an on-call network engineer received an alert for a congesting interface with Equinix IX in our Ashburn data center. While this is not an unusual alert, this one stood out for two reasons. First, it was the second day in a row that it happened, and second, the congestion was due to a sudden and extreme spike of traffic.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/02/image2-1.png\" alt=\"How Cloudflare erroneously throttled a customer’s web traffic\"></figure><p>The engineer in charge identified the customer’s domain, tardis.dev, as being responsible for this sudden spike of traffic between Cloudflare and their origin network, a storage provider. Because this congestion happens on a physical interface connected to external peers, there was an immediate impact to many of our customers and peers. A port congestion like this one typically incurs packet loss, slow throughput and higher than usual latency. While we have automatic mitigation in place for congesting interfaces, in this case the mitigation was unable to resolve the impact completely.</p><p>The traffic from this customer went suddenly from an average of 1,500 requests per second, and a 0.5 MB payload per request, to 3,000 requests per second (2x) and more than 12 MB payload per request (25x).</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/02/image1-4.png\" alt=\"How Cloudflare erroneously throttled a customer’s web traffic\"></figure><p>The congestion happened between Cloudflare and the origin network. Caching did not happen because the requests were all unique URLs going to the origin, and therefore we had no ability to serve from cache.</p><p><strong>A Cloudflare engineer decided to apply a throttling mechanism to prevent the zone from pulling so much traffic from their origin. Let's be very clear on this action: Cloudflare does not have an established process to throttle customers that consume large amounts of bandwidth, and does not intend to have one. This remediation was a mistake, it was not sanctioned, and we deeply regret it.</strong></p><p>We lifted the throttle through internal escalation 12 hours and 53 minutes after having set it up.</p><h3 id=\"what-s-next\">What's next</h3><p>To make sure a similar incident does not happen, we are establishing clear rules to mitigate issues like this one. Any action taken against a customer domain, paying or not, will require multiple levels of approval and clear communication to the customer. Our tooling will be improved to reflect this. We have many ways of traffic shaping in situations where a huge spike of traffic affects a link and could have applied a different mitigation in this instance.</p><p>We are in the process of rewriting our terms of service to better reflect the type of services that our customers deliver on our platform today. We are also committed to explaining to our users in plain language what is permitted under self-service plans. As a developer-first company with transparency as one of its core principles, we know we can do better here. We will follow up with a blog post dedicated to these changes later.</p><p>Once again, we apologize to the customer for this action and for the confusion it created for other Cloudflare customers.</p>",
            "url": "https://blog.cloudflare.com/how-cloudflare-erroneously-throttled-a-customers-web-traffic/",
            "title": "How Cloudflare erroneously throttled a customer’s web traffic",
            "date_modified": "2023-02-07T18:20:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34693477\">Comments</a>",
            "url": "https://floxdev.com/blog/flox-open-beta",
            "title": "The Flox Open Beta",
            "date_modified": "2023-02-07T14:50:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34692749\">Comments</a>",
            "url": "https://www.vcinsights.co/",
            "title": "Show HN: Database of every VC investment memo",
            "date_modified": "2023-02-07T13:56:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34678121\">Comments</a>",
            "url": "https://ochagavia.nl/blog/crafting-container-images-without-dockerfiles/",
            "title": "Crafting container images without Dockerfiles",
            "date_modified": "2023-02-06T14:54:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34654580\">Comments</a>",
            "url": "https://protocol.ai/blog/2023-02-03-crypto-winter-update/",
            "title": "Protocol Labs is laying off 21% of staff (89 people)",
            "date_modified": "2023-02-04T14:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34648760\">Comments</a>",
            "url": "https://webapp.io/hosting",
            "title": "Show HN: Webapp.io - Free firecracker-based full-stack hosting",
            "date_modified": "2023-02-03T22:35:25.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--9PhJuZYQ--/c_fit,fl_progressive,q_80,w_636/76a380374b3e30769eb2242e6ab232da.jpg\"><p>So you’ve played, <a href=\"https://kotaku.com/witcher-3-ps5-free-next-gen-upgrade-60fps-photo-mode-1849884681\">or re-played</a>, the epic fantasy saga that is <em>The Witcher 3: Wild Hunt</em> and its two critically acclaimed expansions, <em>Hearts of Stone </em>and <em>Blood and Wine</em>. Now you’re on the hunt for your next fantasy epic, but which to choose? </p><p><a href=\"https://kotaku.com/games-like-witcher-3-open-world-rpg-cyberpunk-geralt-1850071540\">Read more...</a></p>",
            "url": "https://kotaku.com/games-like-witcher-3-open-world-rpg-cyberpunk-geralt-1850071540",
            "title": "15+ Epic Games To Play After The Witcher 3: Wild Hunt",
            "date_modified": "2023-02-03T19:45:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34628889\">Comments</a>",
            "url": "https://fly.io/blog/carving-the-scheduler-out-of-our-orchestrator/",
            "title": "Carving the scheduler out of our orchestrator",
            "date_modified": "2023-02-02T16:30:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34600126\">Comments</a>",
            "url": "https://www.hashicorp.com/blog/introducing-hermes-an-open-source-document-management-system",
            "title": "Hermes, an Open Source Document Management System",
            "date_modified": "2023-01-31T19:06:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34597089\">Comments</a>",
            "url": "https://tailscale.dev/blog/weaponizing-hyperfocus",
            "title": "Weaponizing hyperfocus: Becoming the first DevRel at Tailscale",
            "date_modified": "2023-01-31T16:17:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34597089\">Comments</a>",
            "url": "https://tailscale.dev/blog/weaponizing-hyperfocus",
            "title": "Becoming the first DevRel at Tailscale",
            "date_modified": "2023-01-31T16:17:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34583520\">Comments</a>",
            "url": "https://berkeleygraphics.com/public-affairs/bulletins/BT-002/",
            "title": "Berkeley Mono Ligatures Release",
            "date_modified": "2023-01-30T17:46:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34548077\">Comments</a>",
            "url": "https://www.awspuritytest.com/",
            "title": "AWS Purity Test",
            "date_modified": "2023-01-27T16:40:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34516252\">Comments</a>",
            "url": "https://podcast.bitreach.io/episodes/jason-lengstorf",
            "title": "DevRel should be a process not a project",
            "date_modified": "2023-01-25T09:25:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34513877\">Comments</a>",
            "url": "https://containers.dev/",
            "title": "Development Containers",
            "date_modified": "2023-01-25T03:03:55.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/mm7fcy/ideal_ci_cd_system\">Comments</a></p>",
            "url": "https://matt-rickard.com/an-ideal-ci-cd-system",
            "title": "An Ideal CI/CD System",
            "date_modified": "2023-01-25T02:07:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34507170\">Comments</a>",
            "url": "https://astro.build/blog/astro-2/",
            "title": "Astro 2.0",
            "date_modified": "2023-01-24T17:48:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34506657\">Comments</a>",
            "url": "https://github.com/koskimas/kysely",
            "title": "Kysely: TypeScript SQL Query Builder",
            "date_modified": "2023-01-24T17:17:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34500822\">Comments</a>",
            "url": "https://github.com/SigNoz/logs-benchmark",
            "title": "Elastic, Loki and SigNoz – A Perf Benchmark of Open-Source Logging Platforms",
            "date_modified": "2023-01-24T08:03:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34462105\">Comments</a>",
            "url": "https://temporal.io/blog/building-reliable-distributed-systems-in-node",
            "title": "Building Reliable Distributed Systems in Node.js",
            "date_modified": "2023-01-21T00:06:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34460904\">Comments</a>",
            "url": "https://www.mux.com/blog/the-building-blocks-of-great-docs",
            "title": "The building blocks of great docs",
            "date_modified": "2023-01-20T22:21:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34441346\">Comments</a>",
            "url": "https://retool.com/products/mobile",
            "title": "Show HN: Retool Mobile",
            "date_modified": "2023-01-19T15:31:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34396443\">Comments</a>",
            "url": "https://www.mikesukmanowsky.com/blog/authentication-with-django-and-spas",
            "title": "Use cookies and sessions (not JWTs) for authentication",
            "date_modified": "2023-01-16T03:24:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34374710\">Comments</a>",
            "url": "https://github.com/sickcodes/Docker-OSX",
            "title": "Sickcodes/Docker-OS X: Run macOS VM in a Docker",
            "date_modified": "2023-01-13T22:20:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34372140\">Comments</a>",
            "url": "https://sacra.com/p/docker-plg-pivot/",
            "title": "Docker 2.0 went from $11M to $135M in 2 years",
            "date_modified": "2023-01-13T18:49:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34313597\">Comments</a>",
            "url": "https://dagster.io/blog/declarative-scheduling",
            "title": "Data pipelines are not workflows",
            "date_modified": "2023-01-09T18:06:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34310674\">Comments</a>",
            "url": "https://sourcehut.org/blog/2023-01-09-gomodulemirror/",
            "title": "Sourcehut will blacklist the Go module mirror",
            "date_modified": "2023-01-09T14:28:39.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2023/01/image2-5.png\" alt=\"Weave your own global, private, virtual Zero Trust network on Cloudflare with WARP-to-WARP\"></figure><img src=\"http://blog.cloudflare.com/content/images/2023/01/image2-4.png\" alt=\"Weave your own global, private, virtual Zero Trust network on Cloudflare with WARP-to-WARP\"><p>Millions of users rely on <a href=\"https://1.1.1.1/\">Cloudflare WARP</a> to connect to the Internet through Cloudflare’s network. Individuals download the mobile or desktop application and rely on the Wireguard-based tunnel to make their browser faster and more private. Thousands of enterprises trust Cloudflare WARP to connect employees to our <a href=\"https://www.cloudflare.com/products/zero-trust/gateway/\">Secure Web Gateway</a> and other <a href=\"https://www.cloudflare.com/products/zero-trust/\">Zero Trust services</a> as they navigate the Internet.</p><p>We’ve heard from both groups of users that they also want to connect to other devices running WARP. Teams can build a private network on Cloudflare’s network today by connecting WARP on one side to a <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/private-net/\">Cloudflare Tunnel</a>, <a href=\"https://developers.cloudflare.com/magic-wan/how-to/configure-tunnels/\">GRE tunnels</a>, or <a href=\"https://developers.cloudflare.com/magic-wan/how-to/ipsec/\">IPSec tunnels</a> on the other end. However, what if both devices already run WARP?</p><p>Starting today, we’re excited to make it even easier to build a network on Cloudflare with the launch of WARP-to-WARP connectivity. With a <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/private-net/connect-private-networks/\">single click</a>, any device running WARP in your organization can reach any other device running WARP. Developers can connect to a teammate's machine to test a web server. Administrators can reach employee devices to troubleshoot issues. The feature works with our existing private network on-ramps, like the tunnel options listed above. All with <a href=\"https://developers.cloudflare.com/cloudflare-one/policies/filtering/\">Zero Trust rules</a> built in.</p><p>To get started, <a href=\"http://cloudflare.com/lp/warp-peering\">sign-up</a> to receive early access to our closed beta. If you’re interested in learning more about how it works and what else we will be launching in the future, keep scrolling.</p><h3 id=\"the-bridge-to-zero-trust\">The bridge to Zero Trust</h3><p>We understand that adopting a <a href=\"https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/\">Zero Trust architecture</a> can feel overwhelming at times. With <a href=\"https://www.cloudflare.com/cloudflare-one/\">Cloudflare One</a>, our mission is to make Zero Trust prescriptive and approachable regardless of where you are on your journey today. To help users navigate the uncertain, we created resources like our vendor-agnostic <a href=\"https://zerotrustroadmap.org/\">Zero Trust Roadmap</a> which lays out a battle-tested path to Zero Trust. Within our own products and services, we’ve launched a number of features to <a href=\"http://blog.cloudflare.com/stronger-bridge-to-zero-trust/\">bridge the gap</a> between the networks you manage today and the network you hope to build for your organization in the future.</p><p>Ultimately, our goal is to enable you to overlay your network on Cloudflare however you want, whether that be with existing hardware in the field, a carrier you already partner with, through existing technology standards like <a href=\"https://developers.cloudflare.com/magic-wan/how-to/ipsec/\">IPsec tunnels</a>, or more Zero Trust approaches like <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/\">WARP</a> or <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/private-net/\">Tunnel</a>. It shouldn’t matter which method you chose to start with, the point is that you need the flexibility to get started no matter where you are in this journey. We call these connectivity options on-ramps and off-ramps.</p><h3 id=\"a-recap-of-warp-to-tunnel\">A recap of WARP to Tunnel</h3><p>The model laid out above allows users to start by defining their specific needs and then customize their deployment by choosing from a set of fully composable on and offramps to connect their users and devices to Cloudflare. This means that customers are able to leverage <strong>any</strong> of these solutions together to route traffic seamlessly between devices, offices, data centers, cloud environments, and self-hosted or SaaS applications.</p><p>One example of a deployment we’ve seen thousands of customers be successful with is what we call WARP-to-Tunnel. In this deployment, the on-ramp <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/\">Cloudflare WARP</a> ensures end-user traffic reaches Cloudflare’s global network in a secure and performant manner. The off-ramp <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/\">Cloudflare Tunnel</a> then ensures that, after your Zero Trust rules have been enforced, we have secure, redundant, and reliable paths to land user traffic back in your distributed, private network.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/01/image3-5.png\" alt=\"Weave your own global, private, virtual Zero Trust network on Cloudflare with WARP-to-WARP\"></figure><p>This is a great example of a deployment that is ideal for users that need to support public to private traffic flows (i.e. North-South)</p><p>But what happens when you need to support private to private traffic flows (i.e. East-West) within this deployment?</p><h3 id=\"with-warp-to-warp-connecting-just-got-easier\">With WARP-to-WARP, connecting just got easier</h3><p>Starting today, devices on-ramping to Cloudflare with WARP will also be able to off-ramp to each other. With this announcement, we’re adding yet another tool to leverage in new or existing deployments that provides users with stronger network fabric to connect users, devices, and autonomous systems.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2023/01/image1-7.png\" alt=\"Weave your own global, private, virtual Zero Trust network on Cloudflare with WARP-to-WARP\"></figure><p>This means any of your Zero Trust-enrolled devices will be able to securely connect to any other device on your Cloudflare-defined network, regardless of physical location or network configuration. This unlocks the ability for you to address any device running WARP in the exact same way you are able to send traffic to services behind a Cloudflare Tunnel today. Naturally, all of this traffic flows through our in-line Zero Trust services, regardless of how it gets to Cloudflare, and this new connectivity announced today is no exception.</p><p>To power all of this, we now track where WARP devices are connected to, in Cloudflare’s global network, the same way we do for Cloudflare Tunnel. Traffic meant for a specific WARP device is relayed across our network, <a href=\"https://www.cloudflare.com/en-gb/products/argo-smart-routing/\">using Argo Smart Routing</a>, and piped through the <a href=\"http://blog.cloudflare.com/warp-technical-challenges/\">transport</a> that routes IP packets to the appropriate WARP device. Since this traffic goes through our <a href=\"https://www.cloudflare.com/en-gb/products/zero-trust/gateway/\">Zero Trust Secure Web Gateway</a> — allowing various types of filtering — it means we upgrade and downgrade traffic from purely routed IP packets to fully proxied TLS connections (as well as other protocols). In the case of using SSH to remotely access a colleague’s WARP device, this means that your traffic is eligible for <a href=\"http://blog.cloudflare.com/ssh-command-logging/\">SSH command auditing</a> as well.</p><h3 id=\"get-started-today-with-these-use-cases\">Get started today with these use cases</h3><p>If you already deployed Cloudflare WARP to your organization, then your IT department will be excited to learn they can use this new connectivity to reach out to any device running Cloudflare WARP. Connecting via SSH, RDP, SMB, or any other service running on the device is now simpler than ever. All of this provides Zero Trust access for the IT team members, with their actions being secured in-line, audited, and pushed to your organization’s logs.</p><p>Or, maybe you are done with designing a new function of an existing product and want to let your team members check it out at their own convenience. Sending them a link with your private IP — assigned by Cloudflare — will do the job. Their devices will see your machine as if they were in the same physical network, despite being across the other side of the world.</p><p>The usefulness doesn’t end with humans on both sides of the interaction: the weekend has arrived, and you have finally set out to move your local NAS to a host provider where you run a virtual machine. By running Cloudflare WARP on it, similarly to your laptop, you can now access your photos using the virtual machine’s private IP. This was already possible with <a href=\"https://developers.cloudflare.com/cloudflare-one/tutorials/warp-to-tunnel/\">WARP to Tunnel</a>; but with WARP-to-WARP, you also get connectivity in reverse direction, where you can have the virtual machine periodically rsync/scp files from your laptop as well. This means you can make any server initiate traffic towards the rest of your Zero Trust organization with this new type of connectivity.</p><h3 id=\"what-s-next\">What’s next?</h3><p>This feature will be available on all plans at no additional cost. To get started with this new feature, <a href=\"http://cloudflare.com/lp/warp-peering\">add your name to the closed beta</a>, and we’ll notify you once you’ve been enrolled. Then, you’ll simply ensure that at least two devices are enrolled in Cloudflare Zero Trust and have the latest version of Cloudflare WARP installed.</p><p>This new feature builds upon the existing benefits of Cloudflare Zero Trust, which include enhanced connectivity, improved performance, and streamlined access controls. With the ability to connect to any other device in their deployment, Zero Trust users will be able to take advantage of even more robust security and connectivity options.</p><p>To get started in minutes, <a href=\"https://dash.cloudflare.com/sign-up/teams?lang=en-US\">create a Zero Trust account</a>, download the WARP agent, enroll these devices into your Zero Trust organization, and start creating Zero Trust policies to establish fast, secure connectivity between these devices. That’s it.</p>",
            "url": "https://blog.cloudflare.com/warp-to-warp/",
            "title": "Weave your own global, private, virtual Zero Trust network on Cloudflare with WARP-to-WARP",
            "date_modified": "2023-01-09T14:00:00.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/rhmgug/comprehensive_guide_for_fastest\">Comments</a></p>",
            "url": "https://www.sliceofexperiments.com/p/a-comprehensive-guide-for-the-fastest",
            "title": "A comprehensive guide for the fastest possible Docker builds in human existence",
            "date_modified": "2023-01-09T03:03:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34305972\">Comments</a>",
            "url": "https://longform.asmartbear.com/posts/extreme-questions/",
            "title": "Extreme questions to trigger new, better ideas",
            "date_modified": "2023-01-09T02:23:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34295853\">Comments</a>",
            "url": "http://www.mupuf.org/blog/2023/01/04/setting-up-a-ci-system-part-5-time-sharing-your-test-machines/",
            "title": "Setting Up a CI System Part 5: Time-sharing your test machines",
            "date_modified": "2023-01-08T03:51:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34290798\">Comments</a>",
            "url": "https://queue.acm.org/detail.cfm?id=2898444",
            "title": "Lessons learned from three container-management systems over a decade",
            "date_modified": "2023-01-07T18:11:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34290249\">Comments</a>",
            "url": "https://www.enterpriseready.io/",
            "title": "EnterpriseReady",
            "date_modified": "2023-01-07T17:22:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34287685\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34287685",
            "title": "Ask HN: Main things to consider when building an app for business/enterprise",
            "date_modified": "2023-01-07T13:10:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34285539\">Comments</a>",
            "url": "https://developers.redhat.com/e-books/podman-action",
            "title": "Podman in Action",
            "date_modified": "2023-01-07T05:31:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34285186\">Comments</a>",
            "url": "https://devbase.fyi/",
            "title": "Show HN: Devbase – Find products to make your next fantastic project",
            "date_modified": "2023-01-07T04:24:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34283125\">Comments</a>",
            "url": "https://github.com/artsy/README",
            "title": "Artsy Engineering Handbook",
            "date_modified": "2023-01-06T23:52:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34275951\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34275951",
            "title": "Ask HN: Why are URNs not more popular?",
            "date_modified": "2023-01-06T15:44:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34265206\">Comments</a>",
            "url": "https://clickhouse.com/blog/extracting-converting-querying-local-files-with-sql-clickhouse-local",
            "title": "Show HN: ClickHouse-local – a small tool for serverless data analytics",
            "date_modified": "2023-01-05T19:30:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34264487\">Comments</a>",
            "url": "https://github.com/Owez/yark",
            "title": "Yark: Advanced and easy YouTube archiver now stable",
            "date_modified": "2023-01-05T18:45:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34261656\">Comments</a>",
            "url": "https://www.ycombinator.com/blog/the-yc-founder-directory",
            "title": "The YC Founder Directory",
            "date_modified": "2023-01-05T16:00:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34255599\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34255599",
            "title": "Ask HN: As a startup, what are your must-have sections for a landing page?",
            "date_modified": "2023-01-05T03:38:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34255319\">Comments</a>",
            "url": "https://circleci.com/blog/january-4-2023-security-alert/",
            "title": "Rotate any secrets stored in CircleCI",
            "date_modified": "2023-01-05T02:57:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34255319\">Comments</a>",
            "url": "https://circleci.com/blog/january-4-2023-security-alert/",
            "title": "CircleCI security alert: Rotate any secrets stored in CircleCI",
            "date_modified": "2023-01-05T02:57:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34247817\">Comments</a>",
            "url": "https://planetscale.com/blog/faster-mysql-with-http3",
            "title": "Faster MySQL with HTTP/3",
            "date_modified": "2023-01-04T16:40:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34238150\">Comments</a>",
            "url": "https://terrateam.io/blog/flying-away-from-aws",
            "title": "Migrating from AWS to Fly.io",
            "date_modified": "2023-01-03T21:13:45.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZAVlauNp--/c_fit,fl_progressive,q_80,w_636/688aa7d637ab127e795c2d3a0eb8165d.jpg\"><p>In the bright afternoon hours of New Year’s Day 2023, I squint through hungover eyes at my phone screen. My Twitter feed is blowing up about some movie I’ve never heard of before: <em>Strange Days</em>, a ‘90s Kathryn Bigelow sci-fi flick starring Ralph Fiennes, Angela Bassett, and Juliette Lewis. </p><p><a href=\"https://kotaku.com/strange-days-streaming-cyberpunk-2077-braindance-1849945692\">Read more...</a></p>",
            "url": "https://kotaku.com/strange-days-streaming-cyberpunk-2077-braindance-1849945692",
            "title": "You Should Watch This ‘90s Movie That’s Basically Just Cyberpunk 2077",
            "date_modified": "2023-01-03T20:55:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34230641\">Comments</a>",
            "url": "http://blogs.newardassociates.com/blog/2023/you-want-modules-not-microservices.html",
            "title": "You Want Modules, Not Microservices",
            "date_modified": "2023-01-03T12:35:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34225669\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34225669",
            "title": "Ask HN: A Better Docker Compose?",
            "date_modified": "2023-01-03T00:15:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34212130\">Comments</a>",
            "url": "https://manual.withcompound.com/chapters/interview-with-benjamin-de-cock-early-designer-at-stripe",
            "title": "Interview with Benjamin de Cock, early designer at Stripe",
            "date_modified": "2023-01-01T23:13:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34206333\">Comments</a>",
            "url": "https://hexmos.com/lama2/index.html",
            "title": "Show HN: Lama2 - Plain-Text Powered REST API Client for Teams",
            "date_modified": "2023-01-01T14:06:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34201157\">Comments</a>",
            "url": "https://workers.tools/",
            "title": "Worker Tools: Tools for Writing HTTP Servers in Worker Runtimes",
            "date_modified": "2022-12-31T21:53:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34190966\">Comments</a>",
            "url": "https://thenewstack.io/product-led-growth-for-dev-first-business-is-it-inevitable/",
            "title": "Product-led growth for dev-first business: Is it inevitable?",
            "date_modified": "2022-12-30T22:39:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34188461\">Comments</a>",
            "url": "https://www.germanvelasco.com/blog/phoenix-1-7-is-view-less",
            "title": "Phoenix 1.7 is View-less",
            "date_modified": "2022-12-30T18:52:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34186484\">Comments</a>",
            "url": "https://erthalion.info/2022/12/30/bpf-performance/",
            "title": "Running fast and slow: experiments with BPF programs' performance",
            "date_modified": "2022-12-30T16:23:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34181319\">Comments</a>",
            "url": "https://prql-lang.org/",
            "title": "PRQL a simple, powerful, pipelined SQL replacement",
            "date_modified": "2022-12-30T03:04:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34179426\">Comments</a>",
            "url": "https://withinboredom.info/blog/2022/12/29/golang-is-evil-on-shitty-networks/",
            "title": "Golang is evil on shitty networks",
            "date_modified": "2022-12-29T23:17:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34172989\">Comments</a>",
            "url": "https://planetscale.com/blog/why-we-chose-nanoids-for-planetscales-api",
            "title": "We Chose NanoIDs for PlanetScale’s API",
            "date_modified": "2022-12-29T14:38:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34163624\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34163624",
            "title": "Ask HN: What's a build vs. buy decision that you got wrong?",
            "date_modified": "2022-12-28T17:49:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34142131\">Comments</a>",
            "url": "https://dri.es/acquia-cloud-next-a-journey-in-platform-modernization",
            "title": "Acquia Cloud Next, a journey in platform modernization",
            "date_modified": "2022-12-26T21:33:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34125628\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=34125628",
            "title": "Ask HN: What are you predictions for 2023?",
            "date_modified": "2022-12-25T09:25:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34115098\">Comments</a>",
            "url": "https://github.com/runfinch/finch",
            "title": "AWS releases Finch: An open source client for container development",
            "date_modified": "2022-12-24T08:36:53.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--971mPzEE--/c_fit,fl_progressive,q_80,w_636/daded2a32ada6a5c8edae2b5f06070fb.jpg\"><p><a href=\"https://kotaku.com/netflix-witcher-s4-henry-cavill-liam-hemsworth-superman-1849719667\">Henry Cavill may be out as Geralt in the Netflix <em>Witcher</em> series</a>, but we will always have Doug Cockle as Geralt in <em>The Witcher 3: Wild Hunt</em>. And now, thanks to the latest next-gen update to the <em>Witcher 3</em>, we can have the best of both worlds. The new update brings “In The Eternal Fire’s Shadow,” a <em>Witcher</em>-worthy side…</p><p><a href=\"https://kotaku.com/witcher-3-netflix-armor-quest-next-gen-update-1849920425\">Read more...</a></p>",
            "url": "https://kotaku.com/witcher-3-netflix-armor-quest-next-gen-update-1849920425",
            "title": "Let’s Get Geralt The Netflix Series Armor In The New Witcher 3 Quest",
            "date_modified": "2022-12-21T20:25:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34055006\">Comments</a>",
            "url": "https://www.buildbuddy.io/blog/whats-new-in-bazel-6-0/",
            "title": "What's New in Bazel 6.0",
            "date_modified": "2022-12-19T17:44:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34044784\">Comments</a>",
            "url": "https://www.uber.com/blog/devpod-improving-developer-productivity-at-uber/",
            "title": "Devpod: Remote development environment at Uber",
            "date_modified": "2022-12-18T22:44:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34035755\">Comments</a>",
            "url": "https://queue.acm.org/detail.cfm?id=3570937",
            "title": "Reinventing backend subsetting at Google",
            "date_modified": "2022-12-18T07:45:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=34002599\">Comments</a>",
            "url": "https://www.getdbt.com/blog/dbt-cloud-package-update/",
            "title": "DBT Cloud increase Team plan price by 100% and limit features at the same time",
            "date_modified": "2022-12-15T17:13:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33986430\">Comments</a>",
            "url": "https://tailscale.com/blog/tailnet-lock/",
            "title": "Tailnet Lock",
            "date_modified": "2022-12-14T17:07:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33926452\">Comments</a>",
            "url": "https://redmonk.com/sogrady/2022/12/09/faster-horse/",
            "title": "A Faster Horse: Infrastructure was the cloud’s first act. What’s its second?",
            "date_modified": "2022-12-09T21:06:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33909616\">Comments</a>",
            "url": "https://neon.tech/blog/serverless-driver-for-postgres/",
            "title": "Edge-compatible Serverless Driver for Postgres",
            "date_modified": "2022-12-08T16:18:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33906520\">Comments</a>",
            "url": "https://github.com/getezy/ezy/releases/tag/v1.0.0-beta.13.2",
            "title": "Show HN: Ezy – open-source gRPC client, alternative to Postman and Insomnia",
            "date_modified": "2022-12-08T10:50:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33900488\">Comments</a>",
            "url": "https://circleci.com/blog/ceo-jim-rose-email-to-circleci-employees/",
            "title": "CircleCI Layoffs",
            "date_modified": "2022-12-07T21:23:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33896513\">Comments</a>",
            "url": "https://git.herrbischoff.com/awesome-macos-command-line/about/",
            "title": "macOS Command Line",
            "date_modified": "2022-12-07T16:39:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33895237\">Comments</a>",
            "url": "https://getconvoy.io/blog/configuring-your-outbound-webhook-requests-with-static-ips/",
            "title": "Configuring Your Outbound Webhook Requests with Static IPs",
            "date_modified": "2022-12-07T15:14:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33892076\">Comments</a>",
            "url": "https://docs.docker.com/desktop/wasm/",
            "title": "New Docker Desktop: Run WASM Applications Alongside Linux Containers in Docker",
            "date_modified": "2022-12-07T08:40:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33874596\">Comments</a>",
            "url": "https://www.nan.fyi/magic-motion",
            "title": "Framer's Magic Motion",
            "date_modified": "2022-12-06T01:19:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33871865\">Comments</a>",
            "url": "https://sifted.eu/articles/vc-scout-programme-problems/",
            "title": "VCs using scouts means founders get the short end of the stick",
            "date_modified": "2022-12-05T21:00:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33841298\">Comments</a>",
            "url": "https://frycos.github.io/vulns4free/2022/12/02/rce-in-20-minutes.html",
            "title": "Pre-Auth RCE with CodeQL in Under 20 Minutes",
            "date_modified": "2022-12-03T06:44:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33834118\">Comments</a>",
            "url": "https://github.blog/2022-12-02-introducing-mona-sans-and-hubot-sans/",
            "title": "Mona Sans and Hubot Sans",
            "date_modified": "2022-12-02T17:59:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33823946\">Comments</a>",
            "url": "https://bugs.chromium.org/p/apvi/issues/detail?id=100",
            "title": "Android platform signing key compromised",
            "date_modified": "2022-12-01T22:35:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33810755\">Comments</a>",
            "url": "https://www.washingtonpost.com/technology/2022/11/30/trustcor-internet-authority-mozilla/",
            "title": "Mozilla, Microsoft yank TrustCor's root certificate authority",
            "date_modified": "2022-12-01T01:02:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33806770\">Comments</a>",
            "url": "https://squeaky.ai/blog/development/how-switching-to-aws-graviton-slashed-our-infrastructure-bill-by-35-percent",
            "title": "Switching to AWS Graviton slashed our infrastructure bill",
            "date_modified": "2022-11-30T20:01:31.000Z"
        },
        {
            "content_html": "<p>Today I would like to tell you about the next generation of Intel-powered general purpose, compute-optimized, and memory-optimized instances. All three of these instance families are powered by 3rd generation Intel Xeon Scalable processors (Ice Lake) running at 3.5 GHz, and are designed to support your data-intensive workloads with up to 200 Gbps of network bandwidth, the highest EBS performance in EC2 (up to 80 Gbps of bandwidth and up to 350,000 IOPS), and the ability to handle up to twice as many packets per second (PPS) as earlier instances.</p> \n<p><span><strong>New General Purpose (M6in/M6idn) Instances</strong></span><br> The original general purpose EC2 instance (<strong>m1.small</strong>) was <a href=\"https://aws.amazon.com/blogs/aws/amazon_ec2_beta/\">launched in 2006</a> and was the one and only instance type for a little over a year, until we launched the <strong>m1.large</strong> and <strong>m1.xlarge</strong> in late <a href=\"https://aws.amazon.com/articles/feature-guide-new-instance-types/\">2007</a>. After that, we added the <strong>m3</strong> in <a href=\"https://aws.amazon.com/blogs/aws/new-ec2-second-generation-standard-instances-and-price-reductions-1/\">2012</a>, <strong>m4</strong> in <a href=\"https://aws.amazon.com/blogs/aws/the-new-m4-instance-type-bonus-price-reduction-on-m3-c4/\">2015</a>, and the first in a very long line of <strong>m5 </strong>instances starting in <a href=\"https://aws.amazon.com/blogs/aws/m5-the-next-generation-of-general-purpose-ec2-instances/\">2017</a>. The family tree branched in <a href=\"https://aws.amazon.com/blogs/aws/ec2-instance-update-m5-instances-with-local-nvme-storage-m5d/\">2018</a> with the addition of the <strong>m5d</strong> instances with local NVMe storage.</p> \n<p>And that brings us to today, and to the new <strong>m6in</strong> and <strong>m6idn</strong> instances, both available in 9 sizes:</p> \n<table> \n <tbody> \n  <tr> \n   <td><strong>Name</strong></td> \n   <td><strong>vCPUs</strong></td> \n   <td><strong>Memory</strong></td> \n   <td><strong>Local Storage<br> (m6idn only)<br> </strong></td> \n   <td><strong>Network Bandwidth</strong></td> \n   <td><strong>EBS Bandwidth</strong></td> \n   <td><strong>EBS IOPS</strong></td> \n  </tr> \n  <tr> \n   <td><strong>m6in.large<br> m6idn.large<br> </strong></td> \n   <td>2</td> \n   <td>8 GiB</td> \n   <td>118 GB</td> \n   <td>Up to 25 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.xlarge<br> m6idn.xlarge<br> </strong></td> \n   <td>4</td> \n   <td>16 GiB</td> \n   <td>237 GB</td> \n   <td>Up to 30 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.2xlarge<br> m6idn.2xlarge<br> </strong></td> \n   <td>8</td> \n   <td>32 GiB</td> \n   <td>474 GB</td> \n   <td>Up to 40 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.4xlarge<br> m6idn.4xlarge<br> </strong></td> \n   <td>16</td> \n   <td>64 GiB</td> \n   <td>950 GB</td> \n   <td>Up to 50 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.8xlarge<br> m6idn.8xlarge<br> </strong></td> \n   <td>32</td> \n   <td>128 GiB</td> \n   <td>1900 GB</td> \n   <td>50 Gbps</td> \n   <td>20 Gbps</td> \n   <td>87,500</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.12xlarge<br> m6idn.12xlarge<br> </strong></td> \n   <td>48</td> \n   <td>192 GiB</td> \n   <td>2950 GB<br> (2 x 1425)</td> \n   <td>75 Gbps</td> \n   <td>30 Gbps</td> \n   <td>131,250</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.16xlarge<br> m6idn.16xlarge<br> </strong></td> \n   <td>64</td> \n   <td>256 GiB</td> \n   <td>3800 GB<br> (2 x 1900)</td> \n   <td>100 Gbps</td> \n   <td>40 Gbps</td> \n   <td>175,000</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.24xlarge<br> m6idn.24xlarge<br> </strong></td> \n   <td>96</td> \n   <td>384 GiB</td> \n   <td>5700 GB<br> (4 x 1425)</td> \n   <td>150 Gbps</td> \n   <td>60 Gbps</td> \n   <td>262,500</td> \n  </tr> \n  <tr> \n   <td><strong>m6in.32xlarge<br> m6idn.32xlarge<br> </strong></td> \n   <td>128</td> \n   <td>512 GiB</td> \n   <td>7600 GB<br> (4 x 1900)</td> \n   <td>200 Gbps</td> \n   <td>80 Gbps</td> \n   <td>350,000</td> \n  </tr> \n </tbody> \n</table> \n<p>The <strong>m6in</strong> and <strong>m6idn</strong> instances are available in the US East (Ohio, N. Virginia) and Europe (Ireland) regions in On-Demand and Spot form. Savings Plans and Reserved Instances are available.</p> \n<p><span><strong>New C6in Instances</strong></span><br> Back in 2008 we <a href=\"https://aws.amazon.com/articles/feature-guide-amazon-ec2-high-cpu-instance-types/\">launched</a> the first in what would prove to be a very long line of <a href=\"https://aws.amazon.com/ec2/\">Amazon Elastic Compute Cloud (Amazon EC2)</a> instances designed to give you high compute performance and a higher ratio of CPU power to memory than the general purpose instances. Starting with those initial <strong>c1</strong> instances, we went on to launch cluster computing instances in <a href=\"https://aws.amazon.com/blogs/aws/the-new-amazon-ec2-instance-type-the-cluster-compute-instance/\">2010</a> (<strong>cc1</strong>) and <a href=\"https://aws.amazon.com/blogs/aws/next-generation-cluster-computing-on-amazon-ec2-the-cc2-instance-type/\">2011</a> (<strong>cc2</strong>), and then (once we got our naming figured out), multiple generations of compute-optimized instances powered by Intel processors: <strong>c3</strong> (<a href=\"https://aws.amazon.com/blogs/aws/a-generation-of-ec2-instances-for-compute-intensive-workloads/\">2013</a>), <strong>c4</strong> (<a href=\"https://aws.amazon.com/blogs/aws/ec2-instance-history/\">2015</a>), and <strong>c5</strong> (<a href=\"https://aws.amazon.com/blogs/aws/ec2-instance-type-update-t2-r4-f1-elastic-gpus-i3-c5/\">2016</a>). As our customers put these instances to use in environments where networking performance was starting to become a limiting factor, we introduced <strong>c5n</strong> instances with 100 Gbps networking in <a href=\"https://aws.amazon.com/blogs/aws/new-c5n-instances-with-100-gbps-networking/\">2018</a>. We also broadened the c5 instance lineup by adding additional sizes (including bare metal), and instances with blazing-fast local NVMe storage.</p> \n<p>Today I am happy to announce the latest in our lineup of Intel-powered compute-optimized instances, the <strong>c6in</strong>, available in 9 sizes:</p> \n<table> \n <tbody> \n  <tr> \n   <td><strong>Name</strong></td> \n   <td><strong>vCPUs</strong></td> \n   <td><strong>Memory<br> </strong></td> \n   <td><strong>Network Bandwidth</strong></td> \n   <td><strong>EBS Bandwidth<br> </strong></td> \n   <td><strong>EBS IOPS</strong></td> \n  </tr> \n  <tr> \n   <td><strong>c6in.large</strong></td> \n   <td>2</td> \n   <td>4 GiB</td> \n   <td>Up to 25 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.xlarge</strong></td> \n   <td>4</td> \n   <td>8 GiB</td> \n   <td>Up to 30 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.2xlarge</strong></td> \n   <td>8</td> \n   <td>16 GiB</td> \n   <td>Up to 40 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.4xlarge</strong></td> \n   <td>16</td> \n   <td>32 GiB</td> \n   <td>Up to 50 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.8xlarge</strong></td> \n   <td>32</td> \n   <td>64 GiB</td> \n   <td>50 Gbps</td> \n   <td>20 Gbps</td> \n   <td>87,500</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.12xlarge</strong></td> \n   <td>48</td> \n   <td>96 GiB</td> \n   <td>75 Gbps</td> \n   <td>30 Gbps</td> \n   <td>131,250</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.16xlarge</strong></td> \n   <td>64</td> \n   <td>128 GiB</td> \n   <td>100 Gbps</td> \n   <td>40 Gbps</td> \n   <td>175,000</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.24xlarge</strong></td> \n   <td>96</td> \n   <td>192 GiB</td> \n   <td>150 Gbps</td> \n   <td>60 Gbps</td> \n   <td>262,500</td> \n  </tr> \n  <tr> \n   <td><strong>c6in.32xlarge</strong></td> \n   <td>128</td> \n   <td>256 GiB</td> \n   <td>200 Gbps</td> \n   <td>80 Gbps</td> \n   <td>350,000</td> \n  </tr> \n </tbody> \n</table> \n<p>The <strong>c6in</strong> instances are available in the US East (Ohio, N. Virginia), US West (Oregon), and Europe (Ireland) Regions.</p> \n<p>As I noted earlier, these instances are designed to be able to handle up to twice as many packets per second (PPS) as their predecessors. This allows them to deliver increased performance in situations where they need to handle a large number of small-ish network packets, which will accelerate many applications and use cases includes network virtual appliances (firewalls, virtual routers, load balancers, and appliances that detect and protect against DDoS attacks), telecommunications (Voice over IP (VoIP) and 5G communication), build servers, caches, in-memory databases, and gaming hosts. With more network bandwidth and PPS on tap, heavy-duty analytics applications that retrieve and store massive amounts of data and objects from Amazon <a href=\"https://aws.amazon.com/s3/\">Amazon Simple Storage Service (Amazon S3)</a> or data lakes will benefit. For workloads that benefit from low latency local storage, the disk versions of the new instances offer twice as much instance storage versus previous generation.</p> \n<p><span><strong>New Memory-Optimized (R6in/R6idn) Instances</strong></span><br> The first memory-optimized instance was the <strong>m2</strong>, launched in 2009 with the now-quaint <strong>Double Extra Large</strong> and <strong>Quadruple Extra Large</strong> names, and a higher ration of memory to CPU power than the earlier <strong>m1</strong> instances. We had yet to learn our naming lesson and launched the <strong>High Memory Cluster Eight Extra Large</strong> (aka <strong>cr1.8xlarge</strong>) in 2013, before settling on the r prefix and launching <strong>r3</strong> instances in <a href=\"https://aws.amazon.com/blogs/aws/new-memory-optimized-ec2-instances/\">2013</a>, followed by <strong>r4</strong> instances in <a href=\"https://aws.amazon.com/blogs/aws/new-next-generation-r4-memory-optimized-ec2-instances/\">2014</a>, and <strong>r5</strong> instances in <a href=\"https://aws.amazon.com/blogs/aws/amazon-ec2-instance-update-faster-processors-and-more-memory/\">2018</a>.</p> \n<p>And again that brings us to today, and to the new <strong>r6in</strong> and <strong>r6idn</strong> instances, also available in 9 sizes:</p> \n<table> \n <tbody> \n  <tr> \n   <td><strong>Name</strong></td> \n   <td><strong>vCPUs</strong></td> \n   <td><strong>Memory</strong></td> \n   <td><strong>Local Storage<br> (r6idn only)<br> </strong></td> \n   <td><strong>Network Bandwidth</strong></td> \n   <td><strong>EBS Bandwidth</strong></td> \n   <td><strong>EBS IOPS</strong></td> \n  </tr> \n  <tr> \n   <td><strong>r6in.large<br> r6idn.large<br> </strong></td> \n   <td>2</td> \n   <td>16 GiB</td> \n   <td>118 GB</td> \n   <td>Up to 25 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.xlarge<br> r6idn.xlarge<br> </strong></td> \n   <td>4</td> \n   <td>32 GiB</td> \n   <td>237 GB</td> \n   <td>Up to 30 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.2xlarge<br> r6idn.2xlarge<br> </strong></td> \n   <td>8</td> \n   <td>64 GiB</td> \n   <td>474 GB</td> \n   <td>Up to 40 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.4xlarge<br> r6idn.4xlarge<br> </strong></td> \n   <td>16</td> \n   <td>128 GiB</td> \n   <td>950 GB</td> \n   <td>Up to 50 Gbps</td> \n   <td>Up to 20 Gbps</td> \n   <td>Up to 87,500</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.8xlarge<br> r6idn.8xlarge<br> </strong></td> \n   <td>32</td> \n   <td>256 GiB</td> \n   <td>1900 GB</td> \n   <td>50 Gbps</td> \n   <td>20 Gbps</td> \n   <td>87,500</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.12xlarge<br> r6idn.12xlarge<br> </strong></td> \n   <td>48</td> \n   <td>384 GiB</td> \n   <td>2950 GB<br> (2 x 1425)</td> \n   <td>75 Gbps</td> \n   <td>30 Gbps</td> \n   <td>131,250</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.16xlarge<br> r6idn.16xlarge<br> </strong></td> \n   <td>64</td> \n   <td>512 GiB</td> \n   <td>3800 GB<br> (2 x 1900)</td> \n   <td>100 Gbps</td> \n   <td>40 Gbps</td> \n   <td>175,000</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.24xlarge<br> r6idn.24xlarge<br> </strong></td> \n   <td>96</td> \n   <td>768 GiB</td> \n   <td>5700 GB<br> (4 x 1425)</td> \n   <td>150 Gbps</td> \n   <td>60 Gbps</td> \n   <td>262,500</td> \n  </tr> \n  <tr> \n   <td><strong>r6in.32xlarge<br> r6idn.32xlarge<br> </strong></td> \n   <td>128</td> \n   <td>1024 GiB</td> \n   <td>7600 GB<br> (4 x 1900)</td> \n   <td>200 Gbps</td> \n   <td>80 Gbps</td> \n   <td>350,000</td> \n  </tr> \n </tbody> \n</table> \n<p>The <strong>r6in</strong> and <strong>r6idn</strong> instances are available in the US East (Ohio, N. Virginia), US West (Oregon), and Europe (Ireland) regions in On-Demand and Spot form. Savings Plans and Reserved Instances are available.</p> \n<p><span><strong>Inside the Instances</strong></span><br> As you can probably guess from these specs and from the blog post that I wrote to launch the <strong>c6in</strong> instances, all of these new instance types have a lot in common. I’ll do a rare cut-and-paste from that post in order to reiterate all of the other cool features that are available to you:</p> \n<p><strong>Ice Lake Processors</strong> – The 3rd generation Intel Xeon Scalable processors run at 3.5 GHz, and (<a href=\"https://edc.intel.com/content/www/us/en/products/performance/benchmarks/3rd-generation-intel-xeon-scalable-processors/\">according to Inte</a>l) offer a 1.46x average performance gain over the prior generation. All-core <a href=\"https://www.intel.com/content/www/us/en/support/articles/000007359/processors/intel-core-processors.html\">Intel Turbo Boost</a> mode is enabled on all instance sizes up to and including the <strong>12xlarge</strong>. On the larger sizes, you can control the C-states. Intel <a href=\"https://www.intel.com/content/www/us/en/architecture-and-technology/total-memory-encryption-security-paper.html\">Total Memory Encryption</a> (TME) is enabled, protecting instance memory with a single, transient 128-bit key generated at boot time within the processor.</p> \n<p><strong>NUMA</strong> – Short for Non-Uniform Memory Access, this important architectural feature gives you the power to optimize for workloads where the majority of requests for a particular block of memory come from one of the processors, and that block is “closer” (architecturally speaking) to one of the processors. You can control processor affinity (and take advantage of NUMA) on the <strong>24xlarge</strong> and <strong>32xlarge</strong> instances.</p> \n<p><strong>Networking</strong> – <a href=\"https://aws.amazon.com/about-aws/whats-new/2016/06/introducing-elastic-network-adapter-ena-the-next-generation-network-interface-for-ec2-instances/\">Elastic Network Adapter</a> (ENA) is available on all sizes of <strong>m6in</strong>, <strong>m6idn</strong>, <strong>c6in</strong>, <strong>r6in</strong>, and <strong>r6idn</strong> instances, and <a href=\"https://aws.amazon.com/hpc/efa/\">Elastic Fabric Adapter</a> (EFA) is available on the <strong>32xlarge</strong> instances. In order to make use of these adapters, you will need to make sure that your AMI includes the latest NVMe and ENA drivers. You can also make use of <a href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html\">Cluster Placement Groups</a>.</p> \n<p><strong>io2 Block Express</strong> – You can use all types of EBS volumes with these instances, including the io2 Block Express volumes that we launched earlier this year. As Channy shared in his post (<a href=\"https://aws.amazon.com/blogs/aws/amazon-ebs-io2-block-express-volumes-with-amazon-ec2-r5b-instances-are-now-generally-available/\">Amazon EBS io2 Block Express Volumes with Amazon EC2 R5b Instances Are Now Generally Available</a>), these volumes can be as large as 64 TiB, and can deliver up to 256,000 IOPS. As you can see from the tables above, you can use a <strong>24xlarge</strong> or <strong>32xlarge</strong> instance to achieve this level of performance.</p> \n<p><span><strong>Choosing the Right Instance<br> </strong></span>Prior to today’s launch, you could choose a <strong>c5n</strong>, <strong>m5n</strong>, or <strong>r5n</strong> instance to get the highest network bandwidth on an EC2 instance, or an <strong>r5b</strong> instance to have access to the highest EBS IOPS performance and high EBS bandwidth. Now, customers who need high networking or EBS performance can choose from a full portfolio of instances with different memory to vCPU ratio and instance storage options available, by selecting one of <strong>c6in</strong>, <strong>m6in</strong>, <strong>m6idn</strong>, <strong>r6in</strong>, or <strong>r6idn</strong> instances.</p> \n<p>The higher performance of the <strong>c6in</strong> instances will allow you to scale your network intensive workloads that need a low memory to vCPU, such as network virtual appliances, caching servers, and gaming hosts.</p> \n<p>The higher performance of <strong>m6in</strong> instances will allow you to scale your network and/or EBS intensive workloads such as data analytics, and telco applications including 5G User Plane Functions (UPF). You have the option to use the <strong>m6idn</strong> instance for workloads that benefit from low-latency local storage, such as high-performance file systems, or distributed web-scale in-memory caches.</p> \n<p>Similarly, the higher network and EBS performance of the <strong>r6in</strong> instances will allow you to scale your network-intensive SQL, NoSQL, and in-memory database workloads, with the option to use the <strong>r6idn</strong> when you need low-latency local storage.</p> \n<p></p>\n<p>— <a href=\"https://twitter.com/jeffbarr\">Jeff</a>;</p>\n<p></p>",
            "url": "https://aws.amazon.com/blogs/aws/new-general-purpose-compute-optimized-and-memory-optimized-amazon-ec2-instances-with-higher-packet-processing-performance/",
            "title": "New General Purpose, Compute Optimized, and Memory-Optimized Amazon EC2 Instances with Higher Packet-Processing Performance",
            "date_modified": "2022-11-29T03:57:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33771445\">Comments</a>",
            "url": "https://img.ly/blog/ultimate-guide-to-ffmpeg/",
            "title": "The Ultimate Guide to FFmpeg",
            "date_modified": "2022-11-28T09:30:20.000Z"
        },
        {
            "content_html": "<p>AWS VP and Chief Evangelist Jeff Barr, plus a select group of AWS Developer Advocate colleagues, have personally chosen their picks for some of the most impactful and exciting product and service launches to debut at <a href=\"https://reinvent.awsevents.com/\">AWS re:Invent 2022</a>. From now through Dec. 1, we’ll update this page daily with links to their AWS News Blog posts (plus a few noteworthy preview posts) so you can dive deeper once the launches have been announced.</p> \n<p>As always, there’s simply too much for the team to cover and even if a launch doesn’t make this list, that doesn’t mean it’s not noteworthy. <strong>Make sure to check out <a href=\"https://aws.amazon.com/about-aws/whats-new/2022/\">What’s New</a> for a complete rundown of all the AWS re:Invent 2022 announcements.</strong></p> \n<p>Here are a few more resources to help you keep up with all the re:Invent news:</p> \n<ul> \n <li><a href=\"https://aws.amazon.com/podcasts/aws-podcast/\">The Official AWS Podcast</a> will have keynote recaps each day and more deep dive episodes in the coming weeks.</li> \n <li><a href=\"https://www.twitch.tv/awsonair\">AWS OnAir</a> is livestreaming directly from the show floor bringing you the latest news, announcements, launches and demos from AWS re:Invent.</li> \n</ul> \n<div id=\"Analytics\"></div> \n<em>(This post was updated: 8:31 p.m. PST, Nov. 28, 2022.)</em>\n<p></p> \n<hr> \n<p><a name=\"top\"></a></p> \n<div id=\"Analytics\"></div> \n<strong>Quick category links:</strong>\n<p></p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Analytics\">Analytics</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Business_Applications\">Business Applications</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Artificial_Intelligence\">Artificial Intelligence / Machine Learning</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Compute\">Compute</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Containers\">Containers</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Database\">Database</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Management_Tools\">Management Tools</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Migration_&amp;_Transfer_Services\">Migration &amp; Transfer Services</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Security,_Identity,_&amp;_Compliance\">Security, Identity, &amp; Compliance</a> | <a href=\"https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/#Storage\">Storage</a> |</p> \n<div id=\"Artificial_Intelligence\"></div> \n<h3>Analytics</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-create-and-share-operational-reports-at-scale-with-amazon-quicksight-paginated-reports/\"><strong>New — Create and Share Operational Reports at Scale with Amazon QuickSight Paginated Reports</strong></a><br> This feature allows customers to create and share highly formatted, personalized reports containing business-critical data to hundreds of thousands of end-users without any infrastructure setup or maintenance, up-front licensing, or long-term commitments.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-quicksight-api-capabilities-to-accelerate-your-bi-transformation/\"><strong>New Amazon QuickSight API Capabilities to Accelerate Your BI Transformation</strong></a><br> New QuickSight API capabilities allow programmatic creation and management of dashboards, analysis, and templates. </p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-aws-glue-4-0-new-and-updated-engines-more-data-formats-and-more/\"><strong>New AWS Glue 4.0 – New and Updated Engines, More Data Formats, and More</strong></a><br> This version of Glue includes Python 3.10 and Apache Spark 3.3.0, plus native support for the Cloud Shuffle Service Plugin for Spark. It also includes Pandas support, and more.</p> \n<p><a href=\"https://aws.amazon.com/about-aws/whats-new/2022/11/aws-glue-ray-preview/\"><strong>Announcing AWS Glue for Ray (Preview)</strong></a><br> Data engineers can use AWS Glue for Ray to process large datasets with Python and popular Python libraries.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-for-amazon-transcribe-real-time-analytics-during-live-calls/\"><strong>New for Amazon Transcribe – Real-Time Analytics During Live Calls</strong></a><br> Real-time call analytics provides APIs for developers to accurately transcribe live calls and at the same time identify customer experience issues and sentiment in real time.</p> \n<div id=\"Business_Applications\"></div> \n<h3>Artificial Intelligence / Machine Learning</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/classifying-and-extracting-mortgage-loan-data-with-amazon-textract/\"><strong>Classifying and Extracting Mortgage Loan Data with Amazon Textract</strong></a><br> The new API was created in response to requests from major lenders in the industry to help them process applications faster and reduce errors, which improves the end-customer experience and lowers operating costs.</p> \n<p><a href=\"https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-codewhisperer-enterprise-controls-sign-up-new-languages/\"><strong>Amazon CodeWhisperer Adds Enterprise Administrative Controls, Simple Sign-up, and Support for New Languages (Preview)</strong></a><br> Administrators can now easily integrate CodeWhisperer with their existing workforce identity solutions, provide access to users and groups, and configure organization-wide settings.</p> \n<div id=\"Compute\"></div> \n<h3>Business Applications</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/aws-wickr-a-secure-end-to-end-encrypted-communication-service-for-enterprises-with-auditing-and-regulatory-requirements/\"><strong>AWS Wickr – A Secure, End-to-End Encrypted Communication Service For Enterprises With Auditing And Regulatory Requirements</strong></a><br> Unlike many enterprise communication tools, Wickr uses end-to-end encryption mechanisms to ensure your messages, files, voice, or video calls are solely accessible to their intended recipients.</p> \n<div id=\"Containers\"></div> \n<h3>Compute</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-ena-express-improved-network-latency-and-per-flow-performance-on-ec2/\"><strong>New – ENA Express: Improved Network Latency and Per-Flow Performance on EC2</strong></a><br> Jeff Barr shares how ENA Express gives you a lot more per-flow bandwidth with a lot less variability.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-general-purpose-compute-optimized-and-memory-optimized-amazon-ec2-instances-with-higher-packet-processing-performance/\"><strong>New General Purpose, Compute Optimized, and Memory-Optimized Amazon EC2 Instances with Higher Packet-Processing Performance</strong></a><br> The new instance families are designed to support your data-intensive workloads with the highest EBS performance in EC2, and the ability to handle up to twice as many packets per second (PPS) as earlier instances.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-ec2-instance-types-in-the-works-c7gn-r7iz-and-hpc7g/\"><strong>New Amazon EC2 Instance Types In the Works – C7gn, R7iz, and Hpc7g</strong></a><br> Jeff Barr provides a look at three upcoming and exciting new instance types. </p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-ecs-service-connect-enabling-easy-communication-between-microservices/\"><strong> New – Amazon ECS Service Connect Enables Easy Communication Between Microservices</strong></a><br> This new capability simplifies building and operating resilient distributed applications. You can add a layer of resilience to your ECS service communication and get traffic insights with no changes to your application code.</p> \n<p><a href=\"https://aws.amazon.com/about-aws/whats-new/2022/11/microsoft-amazon-machine-images-aws-provided-licenses/\"><strong>Announcing the availability of Microsoft Office Amazon Machine Images (AMIs) on Amazon EC2 with AWS provided licenses</strong></a><br> With this offering, customers have the flexibility to run Microsoft Office dependent applications on EC2.</p> \n<div id=\"Database\"></div> \n<h3>Containers</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-aws-marketplace-for-containers-now-supports-direct-deployment-to-amazon-eks-clusters/\"><strong>New – AWS Marketplace for Containers Now Supports Direct Deployment to Amazon EKS Clusters</strong></a><br> This new launch makes it easier for you to find third-party Kubernetes operation software from the Amazon EKS console and deploy it to your EKS clusters using the same commands used to deploy EKS add-ons.</p> \n<div id=\"Management_Tools\"></div> \n<h3>Database</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-rds-optimized-reads-and-optimized-writes/\"><strong>New – Amazon RDS Optimized Reads and Optimized Writes</strong></a><br> These two new features will accelerate your Amazon RDS for MySQL workloads.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-fully-managed-blue-green-deployments-in-amazon-aurora-and-amazon-rds/\"><strong>New – Fully Managed Blue/Green Deployments in Amazon Aurora and Amazon RDS</strong></a><br> This new feature for Amazon Aurora with MySQL compatibility, Amazon RDS for MySQL, and Amazon RDS for MariaDB, enables you to make database updates safer, simpler, and faster.</p> \n<div id=\"#Security,_Identity,_&amp;_Compliance\"></div> \n<h3>Management Tools</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-aws-config-rules-now-support-proactive-compliance/\"><strong>New – AWS Config Rules Now Support Proactive Compliance</strong></a><br> This release extends AWS Conﬁg rules to support proactive mode so that they can be run at any time before provisioning and save time spent to implement custom pre-deployment validations.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-for-aws-control-tower-comprehensive-controls-management-preview/\"><strong>New for AWS Control Tower – Comprehensive Controls Management (Preview)</strong></a><br> You can use the new capability to apply managed preventative, detective, and proactive controls to accounts and organizational units by service, control objective, or compliance framework.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/protect-sensitive-data-with-amazon-cloudwatch-logs/\"><strong>Protect Sensitive Data with Amazon CloudWatch Logs</strong></a><br> This new set of capabilities for Amazon CloudWatch Logs leverages pattern matching and machine learning (ML) to detect and protect sensitive log data in transit.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-cross-account-observability/\"><strong>New – Amazon CloudWatch Cross-Account Observability</strong></a><br> This new capability lets you search, analyze, and correlate cross-account telemetry data stored in CloudWatch such as metrics, logs, and traces.</p> \n<div id=\"Migration_&amp;_Transfer_Services\"></div> \n<a href=\"https://aws.amazon.com/blogs/aws/cloudwatch-internet-monitor-end-to-end-visibility-into-internet-performance-for-your-applications/\"><strong>Amazon CloudWatch Internet Monitor Provides End-to-End Visibility into Internet Performance for your Applications (Preview)</strong></a>\n<br> This new capability gives visibility into how an internet issue might impact the performance and availability of your applications. It allows you to reduce the time it takes to diagnose internet issues from days to minutes.\n<p></p> \n<h3>Migration &amp; Transfer Services</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-a-fully-managed-schema-conversion-in-aws-database-migration-service/\"><strong>New – A Fully Managed Schema Conversion in AWS Database Migration Service</strong></a><br> AWS DMS Schema Conversion streamlines database migrations by making schema assessment and conversion available inside AWS DMS. You can now plan, assess, convert and migrate under one central DMS service.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/aws-application-migration-service-major-updates-new-migration-servers-grouping-updated-launch-and-post-launch-template/\"><strong>AWS Application Migration Service Major Updates – New Migration Servers Grouping, Updated Launch, and Post-Launch Template</strong></a><br> These three major updates will support your migration projects of any size.</p> \n<div id=\"Storage\"></div> \n<h3>Security, Identity &amp; Compliance</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/amazon-inspector-now-scans-aws-lambda-functions-for-vulnerabilities/\"><strong>Amazon Inspector Now Scans AWS Lambda Functions for Vulnerabilities</strong></a><br> Until now, customers who wanted to analyze their mixed workloads (including EC2 instances, container images, and Lambda functions) against common vulnerabilities needed to use AWS and third-party tools.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/automated-data-discovery-for-amazon-macie/\"><strong>Automated Data Discovery for Amazon Macie</strong></a><br> This new capability allows you to gain visibility into where your sensitive data resides on Amazon Simple Storage Service (Amazon S3) at a fraction of the cost of running a full data inspection across all your S3 buckets.</p> \n<p><a href=\"https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-verified-permissions-preview/\"><strong>AWS announces Amazon Verified Permissions (Preview)</strong></a><br> This central fine-grained permissions management system simplifies changing and updating permission rules in a single place without needing to change the code.</p> \n<h3>Storage</h3> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-failover-controls-for-amazon-s3-multi-region-access-points/\"><strong>New – Failover Controls for Amazon S3 Multi-Region Access Points</strong></a><br> These controls let you shift S3 data access request traffic routed through an Amazon S3 Multi-Region Access Point to an alternate AWS Region within minutes to test and build highly available applications for business continuity.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-announcing-amazon-efs-elastic-throughput/\"><strong>New – Announcing Amazon EFS Elastic Throughput</strong></a><br> This new throughput mode is designed to provide your applications with as much throughput as they need with pay-as-you-use pricing.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-for-aws-backup-protect-and-restore-your-cloudformation-stacks/\"><strong>New for AWS Backup – Protect and Restore Your CloudFormation Stacks</strong></a><br> You now have an automated solution to create and restore your applications with a simplified experience, eliminating the need to manage custom scripts.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/new-amazon-redshift-support-in-aws-backup/\"><strong>New – Amazon Redshift Support in AWS Backup</strong></a><br> AWS Backup allows you to define a central backup policy to manage data protection of your applications and can now also protect your Amazon Redshift clusters.</p> \n<p><a href=\"https://aws.amazon.com/blogs/aws/automated-in-aws-failback-for-aws-elastic-disaster-recovery/\"><strong>Announcing Automated in-AWS Failback for AWS Elastic Disaster Recovery</strong></a><br> The new automated support provides a simplified and expedited experience to fail back Amazon Elastic Compute Cloud (Amazon EC2) instances to the original Region, and both failover and failback processes (for on-premises or in-AWS recovery) can be conveniently started from the AWS Management Console.</p>",
            "url": "https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2022/",
            "title": "Top Announcements of AWS re:Invent 2022",
            "date_modified": "2022-11-28T06:08:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33758849\">Comments</a>",
            "url": "https://cyrilgrislain.substack.com/p/startup-restructuring-101",
            "title": "Startup Restructuring 101",
            "date_modified": "2022-11-27T01:00:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33750654\">Comments</a>",
            "url": "https://github.com/nektos/act",
            "title": "Act: Run your GitHub Actions locally",
            "date_modified": "2022-11-26T07:21:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33745815\">Comments</a>",
            "url": "https://aws.amazon.com/blogs/opensource/introducing-finch-an-open-source-client-for-container-development/",
            "title": "Finch: An open-source client for container development",
            "date_modified": "2022-11-25T19:16:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33738879\">Comments</a>",
            "url": "https://paulstamatiou.com/craft/",
            "title": "Craft",
            "date_modified": "2022-11-25T03:31:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33721853\">Comments</a>",
            "url": "https://github.com/getlago/lago/wiki/Per-Seat-pricing-is-off-the-table-now",
            "title": "Per-seat pricing is off the table now",
            "date_modified": "2022-11-23T17:15:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33721685\">Comments</a>",
            "url": "https://wasmer.io/posts/announcing-wasmer-3.0",
            "title": "Wasmer 3.0",
            "date_modified": "2022-11-23T17:01:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33695886\">Comments</a>",
            "url": "https://emily.id.au/tailscale",
            "title": "CVE-2022-41924 – tailscaled can be used to remotely execute code on Windows",
            "date_modified": "2022-11-21T18:17:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33685209\">Comments</a>",
            "url": "https://www.kite.com/blog/product/kite-is-saying-farewell/",
            "title": "Kite is saying farewell, and is open-sourcing all of its code.",
            "date_modified": "2022-11-20T20:57:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33685209\">Comments</a>",
            "url": "https://www.kite.com/blog/product/kite-is-saying-farewell/",
            "title": "Kite is saying farewell and open-sourcing its code",
            "date_modified": "2022-11-20T20:57:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33684255\">Comments</a>",
            "url": "https://www.heavybit.com/library/podcasts/the-kubelist-podcast/ep-33-tailscale-with-avery-pennarun",
            "title": "Tailscale with Avery Pennarun",
            "date_modified": "2022-11-20T19:23:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33682599\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=33682599",
            "title": "Ask HN: What is example of good documentation in your opinion?",
            "date_modified": "2022-11-20T16:50:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33680661\">Comments</a>",
            "url": "http://bradconte.com/files/misc/HackerNewsParodyThread/",
            "title": "Hacker News Parody Thread",
            "date_modified": "2022-11-20T13:14:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33660185\">Comments</a>",
            "url": "https://jay.gooby.org/2022/05/06/use-a-basic-gmail-account-to-send-mail-as-with-a-domain-that-uses-cloudflare-email-routing",
            "title": "Use a custom domain to send emails with Gmail using Cloudflare email routing",
            "date_modified": "2022-11-18T19:02:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33656767\">Comments</a>",
            "url": "https://blog.alexellis.io/blazing-fast-ci-with-microvms/",
            "title": "Blazing fast CI with MicroVMs",
            "date_modified": "2022-11-18T16:09:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33656767\">Comments</a>",
            "url": "https://blog.alexellis.io/blazing-fast-ci-with-microvms/",
            "title": "Fast CI with MicroVMs",
            "date_modified": "2022-11-18T16:09:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33655542\">Comments</a>",
            "url": "https://devenv.sh/",
            "title": "Devenv.sh: Fast and reproducible developer environments using Nix",
            "date_modified": "2022-11-18T14:52:34.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image3-38.png\" alt=\"Doubling down on local development with Workers: Miniflare meets workerd\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image3-37.png\" alt=\"Doubling down on local development with Workers: Miniflare meets workerd\"><p>Local development gives you a fully-controllable and easy-to-debug testing environment. At the start of this year, we brought this experience to Workers developers by <a href=\"http://blog.cloudflare.com/miniflare/\">launching Miniflare 2.0</a>: a local Cloudflare Workers simulator. Miniflare 2 came with features like step-through debugging support, detailed <code>console.log</code>s, pretty <a href=\"https://miniflare.dev/developing/source-maps\">source-mapped</a> error pages, <a href=\"https://miniflare.dev/developing/live-reload\">live reload</a> and a highly-configurable <a href=\"https://miniflare.dev/testing/jest\">unit testing environment</a>. Not only that, but we also incorporated Miniflare into Wrangler, our Workers CLI, to enable <code>wrangler dev</code>’s --<code>local</code> mode.</p><p>Today, we’re taking local development to the next level! In addition to introducing new support for migrating existing projects to your local development environment, we're making it easier to work with your remote data—locally! Most importantly, we're releasing a much more accurate Miniflare 3, powered by the <a href=\"http://blog.cloudflare.com/workerd-open-source-workers-runtime/\">recently open-sourced <code>workerd</code> runtime</a>—the same runtime used by Cloudflare Workers!</p><h3 id=\"enabling-local-development-with-workerd\">Enabling local development with workerd</h3><p>One of the superpowers of having a local development environment is that you can test changes without affecting users in production. A great local environment offers a level of fidelity on par with production.</p><p>The way we originally approached local development was with Miniflare 2, which reimplemented Workers runtime APIs in JavaScript. Unfortunately, there were <a href=\"https://github.com/cloudflare/miniflare/issues?page=1&amp;q=is%3Aissue+label%3A%22behaviour+mismatch%22\">subtle behavior mismatches</a> between these re-implementations and the real Workers runtime. These types of issues are really difficult for developers to debug, as they don’t appear locally, and step-through debugging of deployed Workers isn’t possible yet. For example, the following Worker returns responses successfully in Miniflare 2, so we might assume it’s safe to publish:</p><pre><code>let cachedResponsePromise;\nexport default {\n  async fetch(request, env, ctx) {\n    // Let's imagine this fetch takes a few seconds. To speed up our worker, we\n    // decide to only fetch on the first request, and reuse the result later.\n    // This works fine in Miniflare 2, so we must be good right?\n    cachedResponsePromise ??= fetch(\"https://example.com\");\n    return (await cachedResponsePromise).clone();\n  },\n};\n</code></pre>\n<p>However, as soon as we send multiple requests to our deployed Worker, it fails with <code>Error: Cannot perform I/O on behalf of a different request</code>. The problem here is that response bodies created in one request’s handler cannot be accessed from a different request's handler. This limitation allows Cloudflare to improve overall Worker performance, but it was almost impossible for Miniflare 2 to detect these types of issues locally. In this particular case, the best solution is to <a href=\"https://developers.cloudflare.com/workers/examples/cache-using-fetch/\">cache using <code>fetch</code> itself</a>.</p><p>Additionally, because the Workers runtime uses a very recent version of V8, it supports some JavaScript features that aren’t available in all versions of Node.js. This meant a few features implemented in Workers, like <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast\"><code>Array#findLast</code></a>, weren’t always available in Miniflare 2.</p><p>With the Workers runtime <a href=\"http://blog.cloudflare.com/workerd-open-source-workers-runtime/\">now open-sourced</a>, Miniflare 3 can leverage the same implementations that are deployed on Cloudflare’s network, giving bug-for-bug compatibility and practically eliminating behavior mismatches. 🎉 </p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image4-26.png\" alt=\"Doubling down on local development with Workers: Miniflare meets workerd\"><figcaption>Miniflare 3’s new simplified architecture using worked</figcaption></figure><p>This radically simplifies our implementation too. We were able to remove <strong>over 50,000</strong> lines of code from Miniflare 2. Of course, we still kept all the Miniflare special-sauce that makes development fun like live reload and detailed logging. 🙂</p><a href=\"https://github.com/cloudflare/miniflare/pull/392\"><img src=\"http://blog.cloudflare.com/content/images/2022/11/image5-15.png\" alt=\"Doubling down on local development with Workers: Miniflare meets workerd\"></a>\n<p></p><h3 id=\"local-development-with-real-data\">Local development with real data</h3><p>We know that many developers choose to test their Workers remotely on the Cloudflare network as it gives them the ability to test against real data. Testing against fake data in staging and local environments is sometimes difficult, as it never quite matches the real thing.</p><p>With Miniflare 3, we’re blurring the lines between local and remote development, by bringing real data to your machine as an experimental opt-in feature. If enabled, Miniflare will read and write data to namespaces on the Cloudflare network, as your Worker would when deployed. This is only supported with <a href=\"https://developers.cloudflare.com/workers/runtime-apis/kv/\">Workers KV</a> for now, but we’re exploring similar solutions for <a href=\"https://developers.cloudflare.com/r2/\">R2</a> and <a href=\"http://blog.cloudflare.com/introducing-d1/\">D1</a>.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image2-46.png\" alt=\"Doubling down on local development with Workers: Miniflare meets workerd\"><figcaption>Miniflare’s system for accessing real KV data, reads and writes are cached locally for future accesses</figcaption></figure><h3 id=\"a-new-default-for-wrangler\">A new default for Wrangler</h3><p>With Miniflare 3 now effectively as accurate as the real Workers environment, and the ability to access real data locally, we’re revisiting the decision to make remote development the initial Wrangler experience. In a future update, <strong><code>wrangler dev --local</code> will become the default</strong>. <code>--local</code> will no longer be required. Benchmarking suggests this will bring an approximate <strong>10x reduction to startup</strong> and a massive <strong>60x reduction to script reload</strong> times! Over the next few weeks, we’ll be focusing on further optimizing Wrangler’s performance to bring you the fastest Workers development experience yet!</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image1-63.png\" alt=\"Doubling down on local development with Workers: Miniflare meets workerd\" title=\"Chart\"></figure><h3 id=\"wrangler-init-from-dash\"><code>wrangler init --from-dash</code></h3><p>We want all developers to be able to take advantage of the improved local experience, so we’re making it easy to start a local Wrangler project from an existing Worker that’s been developed in the Cloudflare dashboard. With <a href=\"https://nodejs.org/\">Node.js</a> installed, run <code><code>npx wrangler init </code>--<code>from-dash &lt;your_worker_name&gt;</code></code>in your terminal to set up a new project with all your existing code and bindings such as KV namespaces configured. You can now seamlessly continue development of your application locally, taking advantage of all the developer experience improvements Wrangler and Miniflare provide. When you’re ready to deploy your worker, run <code>npx wrangler publish</code>.</p><h3 id=\"looking-to-the-future\">Looking to the future</h3><p>Over the next few months, the Workers team is planning to further improve the local development experience with a specific focus on automated testing. Already, we’ve released a <a href=\"https://developers.cloudflare.com/workers/wrangler/api/#unstable_dev\">preliminary API</a> for programmatic end-to-end tests with <code>wrangler dev</code>, but we’re also investigating ways of bringing <a href=\"https://miniflare.dev/testing/jest\">Miniflare 2’s Jest/Vitest environments</a> to <code>workerd</code>. We’re also considering creating extensions for popular IDEs to make developing workers even easier. 👀</p><p>Miniflare 3.0 is now included in Wrangler! Try it out by running <code>npx wrangler@latest dev --experimental-local</code>. Let us know what you think in the <code>#wrangler</code> channel on the <a href=\"https://discord.gg/aTsevRH3pG\">Cloudflare Developers Discord</a>, and please <a href=\"https://github.com/cloudflare/wrangler2/issues/new/choose\">open a GitHub issue</a> if you hit any unexpected behavior.</p>",
            "url": "https://blog.cloudflare.com/miniflare-and-workerd/",
            "title": "Doubling down on local development with Workers: Miniflare meets workerd",
            "date_modified": "2022-11-18T14:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33653540\">Comments</a>",
            "url": "https://lassebomh.github.io/box-breathing/",
            "title": "Show HN: A visual guide to Box Breathing",
            "date_modified": "2022-11-18T12:16:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33648341\">Comments</a>",
            "url": "https://tailscale.com/blog/introducing-tailscale-funnel/",
            "title": "Tailscale Funnel",
            "date_modified": "2022-11-18T00:57:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33639610\">Comments</a>",
            "url": "https://github.com/wbkd/awesome-node-based-uis",
            "title": "Awesome Node-Based UIs",
            "date_modified": "2022-11-17T14:51:01.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image1-44.png\" alt=\"The Cloudflare API now uses OpenAPI schemas\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/11/image1-42.png\" alt=\"The Cloudflare API now uses OpenAPI schemas\"><p>Today, we are announcing the general availability of <a href=\"https://github.com/cloudflare/api-schemas\">OpenAPI Schemas for the Cloudflare API</a>. These are published via GitHub and will be updated regularly as Cloudflare adds and updates APIs. OpenAPI is the widely adopted standard for defining APIs in a machine-readable format. OpenAPI Schemas allow for the ability to plug our API into a wide breadth of tooling to accelerate development for ourselves and customers. Internally, it will make it easier for us to maintain and update our APIs. Before getting into those benefits, let’s start with the basics.</p><h2 id=\"what-is-openapi\">What is OpenAPI?</h2><p>Much of the Internet is built upon APIs (Application Programming Interfaces) or provides them as services to clients all around the world. This allows computers to talk to each other in a standardized fashion. OpenAPI is a widely adopted standard for how to define APIs. This allows other machines to reliably parse those definitions and use them in interesting ways. Cloudflare’s own <a href=\"https://developers.cloudflare.com/api-shield/security/schema-validation/\">API Shield product</a> uses OpenAPI schemas to provide schema validation to ensure only well-formed API requests are sent to your origin.</p><p>Cloudflare itself has an API that customers can use to interface with our security and performance products from other places on the Internet. How do we define our own APIs? In the past we used a standard called <a href=\"https://github.com/json-schema-org/json-hyperschema-spec\">JSON Hyper-Schema</a>. That had served us well, but as time went on we wanted to adopt more tooling that could both benefit ourselves internally and make our customer’s lives easier. The OpenAPI community has flourished over the past few years providing many capabilities as we will discuss that were unavailable while we used JSON Hyper-Schema. As of today we now use OpenAPI.</p><p>You can learn more about OpenAPI itself <a href=\"https://oai.github.io/Documentation/start-here.html\">here</a>. Having an open, well-understood standard for defining our APIs allows for shared tooling and infrastructure to be used that can read these standard definitions. Let’s take a look at a few examples.</p><h2 id=\"uses-of-cloudflare-s-openapi-schemas\">Uses of Cloudflare’s OpenAPI schemas</h2><p>Most customers won’t need to use the schemas themselves to see value. The first system leveraging OpenAPI schemas is our <a href=\"https://developers.cloudflare.com/api/\">new API Docs</a> that were <a href=\"http://blog.cloudflare.com/building-a-better-developer-experience-through-api-documentation/\">announced today</a>. Because we now have OpenAPI schemas, we leverage the open source tool <a href=\"https://stoplight.io/open-source/elements\">Stoplight Elements</a> to aid in generating this new doc site. This allowed us to retire our previously custom-built site that was hard to maintain. Additionally, many engineers at Cloudflare are familiar with OpenAPI, so we gain teams can write new schemas more quickly and are less likely to make mistakes by using a standard that teams understand when defining new APIs.</p><p>There are ways to leverage the schemas directly, however. The OpenAPI community has a huge number of tools that only require a set of schemas to be able to use. Two such examples are mocking APIs and library generation.</p><h3 id=\"mocking-cloudflare-s-api\">Mocking Cloudflare’s API</h3><p>Say you have code that calls Cloudflare’s API and you want to be able to easily run unit tests locally or integration tests in your CI/CD pipeline. While you could just call Cloudflare’s API in each run, you may not want to for a few reasons. First, you may want to run tests frequently enough that managing the creation and tear down of resources becomes a pain. Also, in many of these tests you aren’t trying to validate logic in Cloudflare necessarily, but your own system’s behavior. In this case, mocking Cloudflare’s API would be ideal since you can gain confidence that you aren’t violating Cloudflare’s API contract, but without needing to worry about specifics of managing real resources. Additionally, mocking allows you to simulate different scenarios, like being rate limited or receiving 500 errors. This allows you to test your code for typically rare circumstances that can end up having a serious impact.</p><p>As an example, <a href=\"https://docs.stoplight.io/docs/prism/83dbbd75532cf-http-mocking\">Spotlight Prism</a> could be used to mock Cloudflare’s API for testing purposes. With a local copy of Cloudflare’s API Schemas you can run the following command to spin up a local mock server:</p><pre><code>$ docker run --init --rm \\\n  -v /home/user/git/api-schemas/openapi.yaml:/tmp/openapi.yaml \\\n  -p 4010:4010 stoplight/prism:4 \\\n  mock -h 0.0.0.0 /tmp/openapi.yaml\n</code></pre>\n<p>Then you can send requests to the mock server in order to validate that your use of Cloudflare’s API doesn’t violate the API contract locally:</p><pre><code>$ curl -sX PUT localhost:4010/zones/f00/activation_check \\\n  -Hx-auth-email:foo@bar.com -Hx-auth-key:foobarbaz | jq\n{\n  \"success\": true,\n  \"errors\": [],\n  \"messages\": [],\n  \"result\": {\n    \"id\": \"023e105f4ecef8ad9ca31a8372d0c353\"\n  }\n}\n</code></pre>\n<p>This means faster development and shorter test runs while still catching API contract issues early before they get merged or deployed.</p><h3 id=\"library-generation\">Library generation</h3><p>Cloudflare has libraries in many programming languages like <a href=\"https://registry.terraform.io/providers/cloudflare/cloudflare/latest\">Terraform</a> and <a href=\"https://github.com/cloudflare/cloudflare-go\">Go</a>, but we don’t support every possible programming language. Fortunately, using a tool like <a href=\"https://github.com/OpenAPITools/openapi-generator\">openapi generator</a>, you can feed in Cloudflare’s API schemas and generate a library in a wide range of languages to then use in your code to talk to Cloudflare’s API. For example, you could generate a Java library using the following commands:</p><pre><code>git clone https://github.com/openapitools/openapi-generator\ncd openapi-generator\nmvn clean package\njava -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate \\\n   -i https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.yaml \\\n   -g java \\\n   -o /var/tmp/java_api_client\n</code></pre>\n<p>And then start using that client in your Java code to talk to Cloudflare’s API.</p><h2 id=\"how-cloudflare-transitioned-to-openapi\">How Cloudflare transitioned to OpenAPI</h2><p>As mentioned earlier, we previously used JSON Hyper-Schema to define our APIs. We have roughly 600 endpoints that were already defined in the schemas. Here is a snippet of what one endpoint looks like in JSON Hyper-Schema:</p><pre><code>{\n      \"title\": \"List Zones\",\n      \"description\": \"List, search, sort, and filter your zones.\",\n      \"rel\": \"collection\",\n      \"href\": \"zones\",\n      \"method\": \"GET\",\n      \"schema\": {\n        \"$ref\": \"definitions/zone.json#/definitions/collection_query\"\n      },\n      \"targetSchema\": {\n        \"$ref\": \"#/definitions/response_collection\"\n      },\n      \"cfOwnership\": \"www\",\n      \"cfPlanAvailability\": {\n        \"free\": true,\n        \"pro\": true,\n        \"business\": true,\n        \"enterprise\": true\n      },\n      \"cfPermissionsRequired\": {\n        \"enum\": [\n          \"#zone:read\"\n        ]\n      }\n    }\n</code></pre>\n<p>Let’s look at the same endpoint in OpenAPI:</p><pre><code>/zones:\n    get:\n      description: List, search, sort, and filter your zones.\n      operationId: zone-list-zones\n      responses:\n        4xx:\n          content:\n            application/json:\n              schema:\n                allOf:\n                - $ref: '#/components/schemas/components-schemas-response_collection'\n                - $ref: '#/components/schemas/api-response-common-failure'\n          description: List Zones response failure\n        \"200\":\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/components-schemas-response_collection'\n          description: List Zones response\n      security:\n      - api_email: []\n        api_key: []\n      summary: List Zones\n      tags:\n      - Zone\n      x-cfPermissionsRequired:\n        enum:\n        - '#zone:read'\n      x-cfPlanAvailability:\n        business: true\n        enterprise: true\n        free: true\n        pro: true\n</code></pre>\n<p>You can see that the two look fairly similar and for the most part the same information is contained in each including method type, a description, and request and response definitions (although those are linked in $refs). The value of migrating from one to the other isn’t the change in how we define the schemas themselves, but in what we can do with these schemas. Numerous tools can parse the latter, the OpenAPI, while much fewer can parse the former, the JSON Hyper-Schema.</p><p>If this one API was all that made up the Cloudflare API, it would be easy to just convert the JSON Hyper-Schema into the OpenAPI Schema by hand and call it a day. Doing this 600 times, however, was going to be a huge undertaking. When considering that teams are constantly adding new endpoints, it would be impossible to keep up. It was also the case that our existing API docs used the existing JSON Hyper-Schema, so that meant that we would need to keep both schemas up to date during any transition period. There had to be a better way.</p><h3 id=\"auto-conversion\">Auto conversion</h3><p>Given both JSON Hyper-Schema and OpenAPI are standards, it reasons that it should be possible to take a file in one format and convert to the other, right? Luckily the answer is yes! We built a tool that took all existing JSON Hyper-Schema and output fully compliant OpenAPI schemas. This of course didn’t happen overnight, but because of existing OpenAPI tooling, we could iteratively improve the auto convertor and run OpenAPI validation tooling over the output schemas to see what issues the conversion tool still had.</p><p>After many iterations and improvements to the conversion tool, we finally had fully compliant OpenAPI Spec schemas being auto-generated from our existing JSON Hyper-Schema. While we were building this tool, teams kept adding and updating the existing schemas and our Product Content team was also updating text in the schemas to make our API docs easier to use. The benefit of this process is we didn’t have to slow any of that work down since anything that changed in the old schemas was automatically reflected in the new schemas!</p><p>Once the tool was ready, the remaining step was to decide when and how we would stop making updates to the JSON Hyper-Schemas and move all teams to the OpenAPI Schemas. The (now old) API docs were the biggest concern, given they only understood JSON Hyper-Schema. Thanks to the help of our Developer Experience and Product Content teams, we were able to launch the new API docs today and can officially cut over to OpenAPI today as well!</p><h2 id=\"what-s-next\">What’s next?</h2><p>Now that we have fully moved over to OpenAPI, more opportunities become available. Internally, we will be investigating what tooling we can adopt in order to help reduce the effort of individual teams and speed up API development. One idea we are exploring is automatically creating openAPI schemas from code notations. Externally, we now have the foundational tools necessary to begin exploring how to auto generate and support more programming language libraries for customers to use. We are also excited to see what you may do with the schemas yourself, so if you do something cool or have ideas, don’t hesitate to share them with us!</p>",
            "url": "https://blog.cloudflare.com/open-api-transition/",
            "title": "The Cloudflare API now uses OpenAPI schemas",
            "date_modified": "2022-11-16T14:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33611759\">Comments</a>",
            "url": "https://planetscale.com/blog/how-planetscale-boost-serves-your-sql-queries-instantly",
            "title": "How PlanetScale Boost serves SQL queries faster",
            "date_modified": "2022-11-15T16:58:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33591858\">Comments</a>",
            "url": "https://bob-cd.github.io/",
            "title": "Bob CD",
            "date_modified": "2022-11-14T09:38:25.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/2sw9no/taking_off_with_nix_at_flightaware\">Comments</a></p>",
            "url": "https://flightaware.engineering/taking-off-with-nix-at-flightaware/",
            "title": "Taking off with Nix at FlightAware",
            "date_modified": "2022-11-14T01:09:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33582687\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=33582687",
            "title": "Ask HN: What are your “scratch own itch” projects?",
            "date_modified": "2022-11-13T13:07:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33580350\">Comments</a>",
            "url": "https://pickard.cc/posts/why-does-zsh-start-slowly/",
            "title": "Why does zsh start so slowly?",
            "date_modified": "2022-11-13T05:28:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33566419\">Comments</a>",
            "url": "https://infosec.rodeo/posts/thoughts-on-aws-iam/",
            "title": "AWS IAM Roles, a tale of unnecessary complexity",
            "date_modified": "2022-11-11T20:34:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33553659\">Comments</a>",
            "url": "https://github.com/mona-sans",
            "title": "GitHub is releasing two open-source fonts: Mona and Hubot Sans",
            "date_modified": "2022-11-10T21:28:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33536978\">Comments</a>",
            "url": "https://podman-desktop.io",
            "title": "Podman Desktop: A Free OSS Alternative to Docker Desktop",
            "date_modified": "2022-11-09T19:55:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33530356\">Comments</a>",
            "url": "https://affinity.serif.com/en-gb/",
            "title": "Affinity 2",
            "date_modified": "2022-11-09T12:05:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33526668\">Comments</a>",
            "url": "https://earthly.dev/blog/chroot/",
            "title": "Let’s build a container runtime using only the chroot system call",
            "date_modified": "2022-11-09T01:42:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33506132\">Comments</a>",
            "url": "https://blessed.rs/crates",
            "title": "Blessed.rs – An unofficial guide to the Rust ecosystem",
            "date_modified": "2022-11-07T14:25:42.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/jnp8us/fast_builds_secure_builds_choose_two\">Comments</a></p>",
            "url": "https://stripe.com/blog/fast-secure-builds-choose-two",
            "title": "Fast builds, secure builds. Choose two",
            "date_modified": "2022-11-07T09:49:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33502567\">Comments</a>",
            "url": "https://www.principlesofpricing.com/",
            "title": "The Principles of Pricing",
            "date_modified": "2022-11-07T07:54:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33502567\">Comments</a>",
            "url": "https://www.principlesofpricing.com/",
            "title": "Principles of Pricing",
            "date_modified": "2022-11-07T07:54:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33498799\">Comments</a>",
            "url": "https://blakewatson.com/journal/almost-monospaced-the-perfect-fonts-for-writing/",
            "title": "Almost monospaced: the perfect fonts for writing",
            "date_modified": "2022-11-06T23:31:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33489935\">Comments</a>",
            "url": "https://ebpf.io/what-is-ebpf/",
            "title": "eBPF – Adding functionality to OS at runtime to achieve performance and security",
            "date_modified": "2022-11-06T06:55:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33463327\">Comments</a>",
            "url": "https://sysdig.com/blog/kernel-parameters-falco/",
            "title": "Tales from the Kernel Parameter Side",
            "date_modified": "2022-11-04T07:52:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33445178\">Comments</a>",
            "url": "https://github.com/apenwarr/blip",
            "title": "Blip: A tool for seeing your Internet latency",
            "date_modified": "2022-11-03T00:52:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33429972\">Comments</a>",
            "url": "https://github.com/tierrun",
            "title": "Show HN: Tier.run – Terraform for Stripe",
            "date_modified": "2022-11-02T00:40:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33421751\">Comments</a>",
            "url": "https://www.rewind.ai",
            "title": "Rewind: The Search Engine for Your Life",
            "date_modified": "2022-11-01T14:34:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33416898\">Comments</a>",
            "url": "https://ismypackagereproducibleyet.org/",
            "title": "Is My Package Reproducible Yet?",
            "date_modified": "2022-11-01T04:25:57.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--r1EKO_oA--/c_fit,fl_progressive,q_80,w_636/6303aba872725e53875cc2961dabab62.jpg\"><p><em>Duel Corp</em>. looks so pretty that you’d be forgiving for thinking that it’s an upcoming RPG from Square Enix. But it’s actually made by some indie developers who decided to make a Soulslike from retro pixel art. And the effect is incredible. </p><p><a href=\"https://kotaku.com/duel-corp-soulslike-rpg-octopath-traveler-dark-souls-1849724755\">Read more...</a></p>",
            "url": "https://kotaku.com/duel-corp-soulslike-rpg-octopath-traveler-dark-souls-1849724755",
            "title": "Gorgeous Retro Soulslike RPG Looks Just Like Octopath Traveler",
            "date_modified": "2022-10-31T20:00:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33383023\">Comments</a>",
            "url": "https://ffmpeg.guide",
            "title": "Show HN: FFmpeg Command Visualizer and Editor",
            "date_modified": "2022-10-29T11:20:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33381571\">Comments</a>",
            "url": "https://buf.build/blog/protobuf-es-the-protocol-buffers-typescript-javascript-runtime-we-all-deserve",
            "title": "Protobuf-ES – Implementation of Protocol Buffers for TypeScript and JavaScript",
            "date_modified": "2022-10-29T06:34:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33360776\">Comments</a>",
            "url": "https://tailscale.com/blog/ssh-console/",
            "title": "Making an SSH client the hard way",
            "date_modified": "2022-10-27T17:13:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33357956\">Comments</a>",
            "url": "https://github.com/charmbracelet/vhs",
            "title": "VHS: Your CLI home video recorder",
            "date_modified": "2022-10-27T14:27:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33357956\">Comments</a>",
            "url": "https://github.com/charmbracelet/vhs",
            "title": "VHS: CLI home video recorder",
            "date_modified": "2022-10-27T14:27:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33344573\">Comments</a>",
            "url": "https://www.influxdata.com/blog/influxdb-engine/",
            "title": "IOx: InfluxData’s New Storage Engine",
            "date_modified": "2022-10-26T14:51:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33333332\">Comments</a>",
            "url": "https://www.cloudflarestatus.com/incidents/kdpqngcbbn25",
            "title": "Cloudflare CDN Partial Outage",
            "date_modified": "2022-10-25T17:07:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33310207\">Comments</a>",
            "url": "https://taras.glek.net/post/curious-case-of-maintaining-sufficient-free-space-with-zfs/",
            "title": "Curious Case of Maintaining Sufficient Free Space with ZFS",
            "date_modified": "2022-10-23T20:59:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33310207\">Comments</a>",
            "url": "https://taras.glek.net/post/curious-case-of-maintaining-sufficient-free-space-with-zfs/",
            "title": "Maintaining sufficient free space with ZFS",
            "date_modified": "2022-10-23T20:59:16.000Z"
        },
        {
            "content_html": "<div><div><p><a href=\"http://grpc.io\" target=\"_blank\">gRPC</a> is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It plays a critical role in efficiently connecting microservices in and across data centers with pluggable support for load balancing, tracing, health checking, authentication and other cross-cutting features. It may also be applied in the last mile of distributed computing to connect devices, mobile applications and browsers to backend services hosted on the public cloud. This unique position in the software stack can provide a clear end-to-end view of the whole system. A new gRPC observability feature provides this clarity for workloads running on, and/or able to connect to, Google Cloud.</p><h3>The kinds of observability data provided</h3><p>gRPC observability provides three different types of data:</p><p>1. Logs for key RPC events, including:</p><ul><li><p>When the client/server sends or receives the metadata of an RPC</p></li><li><p>When the client/server sends or receives the message payload of an RPC</p></li><li><p>When the client/server finishes an RPC with a final status (OK, or errors)</p></li></ul><p>2. Metrics (or statistical data) for key RPC events, including:</p><ul><li><p>How many bytes the client/server sent or received</p></li><li><p>How many RPCs the client/server started or completed</p></li><li><p>How long RPCs take to complete between the client and server (known as&nbsp; round trip latency)</p></li></ul><p>3. Distributed traces for RPCs and their fanout RPCs across the system. For example, when serving an RPC from upstream, a server may need to create multiple RPCs to its own backends. The distributed trace helps the user understand the relationships between these RPCs, the latency for each of them, and key events happening throughout the system.</p><h3>How the observability data is produced and collected</h3><p>When developers enable the gRPC observability feature in their binaries, the gRPC library will report the logging, metrics, and tracing data to Google Cloud’s <a href=\"https://cloud.google.com/products/operations\">operations suite</a>. Once the observability data is collected, users can leverage the Google Cloud console to:</p><ul><li><p>Visualize the observability data&nbsp;</p></li><li><p>Export the observability data out of the operations tools for further analysis with other tools.</p></li></ul><p><b>Logging</b></p><p>gRPC observability provides logs for key RPC events with information to help developers understand the context when these events occur. This contextual information can include which gRPC service/method is being invoked, whether the events happen on the client side or server side, whether it’s sending metadata or payloads, the size of the corresponding data, and even the concrete content of the metadata and/or payloads. These log entries are then presented in Cloud Logging with helpers to filter and even customize the query to search related logs.</p></div></div><div><div><div><figure><img alt=\"1 logging.jpg\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/1_logging.max-1000x1000.jpg\"></figure></div></div></div><div><div><p><b>Metrics</b></p><p>gRPC observability provides several metrics: the round trip latency of RPCs, how many RPCs were started and finished during a specific period of time, and even the number of bytes sent/received over the wire. All these metrics can be grouped by a few important parameters, including service/method name and final status. Platform-specific metrics can be included as well, depending on the Google Cloud environment and the gRPC payload actually running. For example, on the Google Kubernetes Engine (GKE) platform, developers can group/filter by namespace, container, and pod information fields to dig into more granular statistical data. With these metrics, Cloud Monitoring enables users to identify problems including:</p><ul><li><p>Which container is having higher than normal latency</p></li><li><p>Which pod is having higher than normal error rates</p></li><li><p>And others.</p></li></ul></div></div><div><div><div><figure><img alt=\"2 metrics.jpg\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/2_metrics.max-1000x1000.jpg\"></figure></div></div></div><div><div><p><b>Tracing</b></p><p>gRPC observability also allows developers to configure the sampling rate of RPCs. The sampling decision is propagated across the whole system, thus no matter where the RPCs actually happen, developers can always see a complete, end-to-end distributed trace for their processing logic. Sampled RPCs and any further RPCs triggered by them are displayed in Cloud Trace as parent/children spans.</p></div></div><div><div><div><figure><img alt=\"3 trace.jpg\" src=\"https://storage.googleapis.com/gweb-cloudblog-publish/images/3_trace.max-1000x1000.jpg\"></figure></div></div></div><div><div><h3>Getting started</h3><p>With gRPC observability, telemetry data (logs, metrics, traces) of gRPC workloads can be collected and reported to the Google&nbsp; Cloud operations suite. It helps developers get a better understanding of their systems and enables them to diagnose problems such as:</p><ul><li><p>Which microservices have suddenly become abnormally slow (long processing latency on the server side)?</p></li><li><p>Which microservices suddenly process less QPS, and is there a pattern?</p></li><li><p>Whether there’s a potential network issue for a particular microservice, as high latency is measured on the client side, but normal latency on the server side? If so, can we locate the problem in a particular cluster, or even a particular node/pod?</p></li></ul><p>To get started with gRPC observability, see our <a href=\"https://cloud.google.com/stackdriver/docs/solutions/grpc\">user guide</a>.</p></div></div>",
            "url": "https://cloud.google.com/blog/products/networking/introducing-grpc-observability-for-microservices/",
            "title": "Introducing gRPC observability for microservices",
            "date_modified": "2022-10-21T01:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33261288\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/910766/7678f8c4ede60928/",
            "title": "Identity management for WireGuard networks",
            "date_modified": "2022-10-19T12:43:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33261288\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/910766/7678f8c4ede60928/",
            "title": "Identity management for WireGuard",
            "date_modified": "2022-10-19T12:43:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33248988\">Comments</a>",
            "url": "https://github.blog/2022-10-18-introducing-fine-grained-personal-access-tokens-for-github/",
            "title": "Fine-grained personal access tokens for GitHub",
            "date_modified": "2022-10-18T15:36:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33247155\">Comments</a>",
            "url": "https://github.com/e-dant/watcher",
            "title": "Show HN: Filesystem Watcher",
            "date_modified": "2022-10-18T13:54:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33245457\">Comments</a>",
            "url": "https://www.jetstack.io/blog/announcing-paranoia/",
            "title": "Jetstack Paranoia: A New Open-Source Tool for Container Image Security",
            "date_modified": "2022-10-18T10:58:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33245042\">Comments</a>",
            "url": "https://drewdevault.com/2022/10/18/TOTP-is-easy.html",
            "title": "TOTP for 2FA is incredibly easy to implement. So what's your excuse?",
            "date_modified": "2022-10-18T10:02:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33235434\">Comments</a>",
            "url": "https://blog.doyensec.com/2022/10/11/ebpf-bypass-security-monitoring.html",
            "title": "On Bypassing eBPF Security Monitoring",
            "date_modified": "2022-10-17T16:00:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33235042\">Comments</a>",
            "url": "https://notado.substack.com/p/how-flyio-and-tailscale-saved-notado",
            "title": "How Fly.io and Tailscale Saved Notado",
            "date_modified": "2022-10-17T15:37:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33234884\">Comments</a>",
            "url": "https://paulosman.me/2022/10/17/being-on-a-board/",
            "title": "Being on a Board as an Employee",
            "date_modified": "2022-10-17T15:28:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33224240\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=33224240",
            "title": "Ask HN: How to Learn to Sell?",
            "date_modified": "2022-10-16T15:19:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33216626\">Comments</a>",
            "url": "https://www.willmcgugan.com/blog/tech/post/ceo-just-wants-to-draw-boxes/",
            "title": "A possibly new way of drawing boxes in the terminal",
            "date_modified": "2022-10-15T17:15:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33159680\">Comments</a>",
            "url": "https://mdwdotla.medium.com/everything-i-wish-i-had-known-about-raising-a-seed-round-a615f8f7740b",
            "title": "Everything I wish I had known about raising a seed round",
            "date_modified": "2022-10-11T04:58:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33151474\">Comments</a>",
            "url": "https://stepci.com/",
            "title": "Show HN: We built a tool that automatically generates your API Tests",
            "date_modified": "2022-10-10T13:57:50.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/unnamed-3.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/unnamed-2.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><p>Early on when we learn to program, we get introduced to the concept of <a href=\"https://ocw.mit.edu/courses/6-00sc-introduction-to-computer-science-and-programming-spring-2011/resources/lecture-6-recursion/\">recursion</a>. And that it is handy for computing, among other things, sequences defined in terms of recurrences. Such as the famous <a href=\"https://en.wikipedia.org/wiki/Fibonacci_number\">Fibonnaci numbers</a> - <em>Fn = Fn-1 + Fn-2</em>.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/Screenshot-2022-10-10-at-10.13.32.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"></figure><p>Later on, perhaps when diving into multithreaded programming, we come to terms with the fact that <a href=\"https://textbook.cs161.org/memory-safety/x86.html#26-stack-pushing-and-popping\">the stack space</a> for call frames is finite. And that there is an “okay” way and a “cool” way to calculate the Fibonacci numbers using recursion:</p><pre><code>// fib_okay.c\n\n#include &lt;stdint.h&gt;\n\nuint64_t fib(uint64_t n)\n{\n        if (n == 0 || n == 1)\n                return 1;\n\n        return fib(n - 1) + fib(n - 2);\n}\n</code></pre>\n<p><small>Listing 1. An okay Fibonacci number generator implementation</small></p>\n<pre><code>// fib_cool.c\n\n#include &lt;stdint.h&gt;\n\nstatic uint64_t fib_tail(uint64_t n, uint64_t a, uint64_t b)\n{\n    if (n == 0)\n        return a;\n    if (n == 1)\n        return b;\n\n    return fib_tail(n - 1, b, a + b);\n}\n\nuint64_t fib(uint64_t n)\n{\n    return fib_tail(n, 1, 1);\n}\n</code></pre>\n<p><small>Listing 2. A better version of the same</small></p>\n<p>If we take a look at the machine code the compiler produces, the “cool” variant translates to a nice and tight sequence of instructions:</p><p>⚠ DISCLAIMER: This blog post is assembly-heavy. We will be looking at assembly code for x86-64, arm64 and BPF architectures. If you need an introduction or a refresher, I can recommend <a href=\"https://github.com/Apress/low-level-programming\">“Low-Level Programming”</a> by Igor Zhirkov for x86-64, and <a href=\"https://github.com/Apress/programming-with-64-bit-ARM-assembly-language\">“Programming with 64-Bit ARM Assembly Language”</a> by Stephen Smith for arm64. For BPF, see the <a href=\"https://docs.kernel.org/bpf/instruction-set.html\">Linux kernel documentation</a>.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/Screenshot-2022-10-10-at-10.25.23.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"></figure><p><small>Listing 3. <code>fib_cool.c</code> compiled for x86-64 and arm64</small></p>\n<p>The “okay” variant, disappointingly, leads to <a href=\"https://github.com/cloudflare/cloudflare-blog/blob/master/2022-10-bpf-tail-call/fib_okay.x86-64.disasm\">more instructions</a> than a listing can fit. It is a spaghetti of <a href=\"https://en.wikipedia.org/wiki/Basic_block\">basic blocks</a>.</p><p><a href=\"https://raw.githubusercontent.com/cloudflare/cloudflare-blog/master/2022-10-bpf-tail-call/fib_okay.dot.png\" target=\"_blank\"><img src=\"http://blog.cloudflare.com/content/images/2022/10/image6.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\" width=\"1000\" height=\"161\"></a></p><p>But more importantly, it is not free of <a href=\"https://textbook.cs161.org/memory-safety/x86.html#29-x86-function-call-in-assembly\">x86 call instructions</a>.</p><pre><code>$ objdump -d fib_okay.o | grep call\n 10c:   e8 00 00 00 00          call   111 &lt;fib+0x111&gt;\n$ objdump -d fib_cool.o | grep call\n$\n</code></pre>\n<p>This has an important consequence - as fib recursively calls itself, the stacks keep growing. We can observe it with a bit of <a href=\"https://github.com/cloudflare/cloudflare-blog/blob/master/2022-10-bpf-tail-call/trace_rsp.gdb\">help from the debugger</a>.</p><pre><code>$ gdb --quiet --batch --command=trace_rsp.gdb --args ./fib_okay 6\nBreakpoint 1 at 0x401188: file fib_okay.c, line 3.\n[Thread debugging using libthread_db enabled]\nUsing host libthread_db library \"/lib64/libthread_db.so.1\".\nn = 6, %rsp = 0xffffd920\nn = 5, %rsp = 0xffffd900\nn = 4, %rsp = 0xffffd8e0\nn = 3, %rsp = 0xffffd8c0\nn = 2, %rsp = 0xffffd8a0\nn = 1, %rsp = 0xffffd880\nn = 1, %rsp = 0xffffd8c0\nn = 2, %rsp = 0xffffd8e0\nn = 1, %rsp = 0xffffd8c0\nn = 3, %rsp = 0xffffd900\nn = 2, %rsp = 0xffffd8e0\nn = 1, %rsp = 0xffffd8c0\nn = 1, %rsp = 0xffffd900\n13\n[Inferior 1 (process 50904) exited normally]\n$\n</code></pre>\n<p>While the “cool” variant makes no use of the stack.</p><pre><code>$ gdb --quiet --batch --command=trace_rsp.gdb --args ./fib_cool 6\nBreakpoint 1 at 0x40118a: file fib_cool.c, line 13.\n[Thread debugging using libthread_db enabled]\nUsing host libthread_db library \"/lib64/libthread_db.so.1\".\nn = 6, %rsp = 0xffffd938\n13\n[Inferior 1 (process 50949) exited normally]\n$\n</code></pre>\n<h2 id=\"wheredidthecallsgo\">Where did the <code>calls</code> go?</h2>\n<p>The smart compiler turned the last function call in the body into a regular jump. Why was it allowed to do that?</p><p>It is the last instruction in the function body we are talking about. The caller stack frame is going to be destroyed right after we return anyway. So why keep it around when we can reuse it for the callee’s <a href=\"https://eli.thegreenplace.net/2011/02/04/where-the-top-of-the-stack-is-on-x86/\">stack frame</a>?</p><p>This optimization, known as <a href=\"https://en.wikipedia.org/wiki/Tail_call#In_assembly\">tail call elimination</a>, leaves us with no function calls in the “cool” variant of our fib implementation. There was only one call to eliminate - right at the end.</p><p>Once applied, the call becomes a jump (loop). If assembly is not your second language, decompiling the fib_cool.o object file with <a href=\"https://ghidra-sre.org/\">Ghidra</a> helps see the transformation:</p><pre><code>long fib(ulong param_1)\n\n{\n  long lVar1;\n  long lVar2;\n  long lVar3;\n  \n  if (param_1 &lt; 2) {\n    lVar3 = 1;\n  }\n  else {\n    lVar3 = 1;\n    lVar2 = 1;\n    do {\n      lVar1 = lVar3;\n      param_1 = param_1 - 1;\n      lVar3 = lVar2 + lVar1;\n      lVar2 = lVar1;\n    } while (param_1 != 1);\n  }\n  return lVar3;\n}\n</code></pre>\n<p><small>Listing 4. <code>fib_cool.o</code> decompiled by Ghidra</small></p>\n<p>This is very much desired. Not only is the generated machine code much shorter. It is also way faster due to lack of calls, which pop up on the <a href=\"https://github.com/cloudflare/cloudflare-blog/blob/master/2022-10-bpf-tail-call/fib_okay_50.perf.txt#L85\">profile</a> for fib_okay.</p><p>But I am no <a href=\"https://github.com/dendibakh/perf-ninja\">performance ninja</a> and this blog post is not about compiler optimizations. So why am I telling you about it?</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image5.jpg\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><figcaption><a href=\"https://commons.wikimedia.org/wiki/File:Lemur_catta_-_tail_length_01.jpg\">Alex Dunkel (Maky), CC BY-SA 3.0</a>, via <a href=\"https://creativecommons.org/licenses/by-sa/3.0\">Wikimedia Commons</a></figcaption></figure><h2 id=\"tail-calls-in-bpf\">Tail calls in BPF</h2><p>The concept of tail call elimination made its way into the BPF world. Although not in the way you might expect. Yes, the LLVM compiler does get rid of the trailing function calls when building for -target bpf. The transformation happens at the intermediate representation level, so it is backend agnostic. This can save you some <a href=\"https://docs.cilium.io/en/stable/bpf/#bpf-to-bpf-calls\">BPF-to-BPF function calls</a>, which you can spot by looking for call -N instructions in the BPF assembly.</p><p>However, when we talk about tail calls in the BPF context, we usually have something else in mind. And that is a mechanism, built into the BPF JIT compiler, for <a href=\"https://docs.cilium.io/en/stable/bpf/#tail-calls\">chaining BPF programs</a>.</p><p>We first adopted BPF tail calls when building our <a href=\"https://legacy.netdevconf.info/0x13/session.html?talk-XDP-based-DDoS-mitigation\">XDP-based packet processing pipeline</a>. Thanks to it, we were able to divide the processing logic into several XDP programs. Each responsible for doing one thing.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image8.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><figcaption>Slide from “<a href=\"https://legacy.netdevconf.info/0x13/session.html?talk-XDP-based-DDoS-mitigation\">XDP based DDoS Mitigation</a>” talk by <a href=\"http://blog.cloudflare.com/author/arthur/\">Arthur Fabre</a></figcaption></figure><p>BPF tail calls have served us well since then. But they do have their caveats. Until recently it was impossible to have both BPF tails calls and BPF-to-BPF function calls in the same XDP program on arm64, which is one of the supported architectures for us.</p><p>Why? Before we get to that, we have to clarify what a BPF tail call actually does.</p><h2 id=\"a-tail-call-is-a-tail-call-is-a-tail-call\">A tail call is a tail call is a tail call</h2><p>BPF exposes the tail call mechanism through the <a href=\"https://elixir.bootlin.com/linux/v5.15.63/source/include/uapi/linux/bpf.h#L1712\">bpf_tail_call helper</a>, which we can invoke from our BPF code. We don’t directly point out which BPF program we would like to call. Instead, we pass it a BPF map (a container) capable of holding references to BPF programs (BPF_MAP_TYPE_PROG_ARRAY), and an index into the map.</p><pre><code>long bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index)\n\n       Description\n              This  special  helper is used to trigger a \"tail call\", or\n              in other words, to jump into  another  eBPF  program.  The\n              same  stack frame is used (but values on stack and in reg‐\n              isters for the caller are not accessible to  the  callee).\n              This  mechanism  allows  for  program chaining, either for\n              raising the maximum number of available eBPF instructions,\n              or  to  execute  given programs in conditional blocks. For\n              security reasons, there is an upper limit to the number of\n              successive tail calls that can be performed.\n</code></pre>\n<p><small><a href=\"https://man7.org/linux/man-pages/man7/bpf-helpers.7.html\">bpf-helpers(7) man page</a></small></p>\n<p>At first glance, this looks somewhat similar to the <a href=\"https://man7.org/linux/man-pages/man2/execve.2.html\">execve(2) syscall</a>. It is easy to mistake it for a way to execute a new program from the current program context. To quote the excellent <a href=\"https://docs.cilium.io/en/stable/bpf/#tail-calls\">BPF and XDP Reference Guide</a> from the Cilium project documentation:</p><blockquote><em>Tail calls can be seen as a mechanism that allows one BPF program to call another, without returning to the old program. Such a call has minimal overhead as unlike function calls, it is implemented as a long jump, reusing the same stack frame.</em></blockquote><p>But once we add <a href=\"https://docs.cilium.io/en/stable/bpf/#bpf-to-bpf-calls\">BPF function calls</a> into the mix, it becomes clear that the BPF tail call mechanism is indeed an implementation of tail call elimination, rather than a way to replace one program with another:</p><blockquote><em>Tail calls, before the actual jump to the target program, will unwind only its current stack frame. As we can see in the example above, if a tail call occurs from within the sub-function, the function’s (func1) stack frame will be present on the stack when a program execution is at func2. Once the final function (func3) function terminates, all the previous stack frames will be unwinded and control will get back to the caller of BPF program caller.</em></blockquote><p>Alas, one with sometimes slightly surprising semantics. Consider the code <a href=\"https://github.com/cloudflare/cloudflare-blog/blob/master/2022-10-bpf-tail-call/tail_call_ex3.bpf.c\">like below</a>, where a BPF function calls the <code>bpf_tail_call()</code> helper:</p><pre><code>struct {\n    __uint(type, BPF_MAP_TYPE_PROG_ARRAY);\n    __uint(max_entries, 1);\n    __uint(key_size, sizeof(__u32));\n    __uint(value_size, sizeof(__u32));\n} bar SEC(\".maps\");\n\nSEC(\"tc\")\nint serve_drink(struct __sk_buff *skb __unused)\n{\n    return 0xcafe;\n}\n\nstatic __noinline\nint bring_order(struct __sk_buff *skb)\n{\n    bpf_tail_call(skb, &amp;bar, 0);\n    return 0xf00d;\n}\n\nSEC(\"tc\")\nint server1(struct __sk_buff *skb)\n{\n    return bring_order(skb);    \n}\n\nSEC(\"tc\")\nint server2(struct __sk_buff *skb)\n{\n    __attribute__((musttail)) return bring_order(skb);  \n}\n</code></pre>\n<p>We have two seemingly not so different BPF programs - <code>server1()</code> and <code>server2()</code>. They both call the same BPF function <code>bring_order()</code>. The function tail calls into the <code>serve_drink()</code> program, if the <code>bar[0]</code> map entry points to it (let’s assume that).</p><p>Do both <code>server1</code> and <code>server2</code> return the same value? Turns out that - no, they don’t. We get a hex 🍔 from <code>server1</code>, and a ☕ from <code>server2</code>. How so?</p><p>First thing to notice is that a BPF tail call unwinds just the current function stack frame. Code past the <code>bpf_tail_call()</code> invocation in the function body never executes, providing the tail call is successful (the map entry was set, and the tail call limit has not been reached).</p><p>When the tail call finishes, control returns to the caller of the function which made the tail call. Applying this to our example, the control flow is <code>serverX() --&gt; bring_order() --&gt; bpf_tail_call() --&gt; serve_drink() -return-&gt; serverX()</code> for both programs.</p><p>The second thing to keep in mind is that the compiler does not know that the bpf_tail_call() helper changes the control flow. Hence, the unsuspecting compiler optimizes the code as if the execution would continue past the BPF tail call.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image2-8.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><figcaption>The call graph for <code>server1()</code> and <code>server2()</code> is the same, but the return value differs due to build time optimizations.</figcaption></figure><p>In our case, the compiler thinks it is okay to propagate the constant which <code>bring_order()</code> returns to <code>server1()</code>. Possibly catching us by surprise, if we didn’t check the <a href=\"https://github.com/cloudflare/cloudflare-blog/blob/master/2022-10-bpf-tail-call/tail_call_ex3.bpf.disasm\">generated BPF assembly</a>.</p><p>We can prevent it by <a href=\"https://clang.llvm.org/docs/AttributeReference.html#musttail\">forcing the compiler</a> to make a tail call to <code>bring_order()</code>. This way we ensure that whatever <code>bring_order()</code> returns will be used as the <code>server2()</code> program result.</p><p>🛈 General rule - for least surprising results, use <a href=\"https://clang.llvm.org/docs/AttributeReference.html#musttail\"><code>musttail attribute</code></a> when calling a function that contain a BPF tail call.</p><p>How does the <code>bpf_tail_call()</code> work underneath then? And why the BPF verifier wouldn’t let us mix the function calls with tail calls on arm64? Time to dig deeper.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image7.jpg\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><figcaption>Public Domain <a href=\"https://www.rawpixel.com/image/3578996/free-photo-image-bulldozer-construction-site\">image</a></figcaption></figure><h2 id=\"bpf-tail-call-on-x86-64\">BPF tail call on x86-64</h2><p>What does a <code>bpf_tail_call()</code> helper call translate to after BPF JIT for x86-64 has compiled it? How does the implementation guarantee that we don’t end up in a tail call loop forever?</p><p>To find out we will need to piece together a few things.</p><p>First, there is the BPF JIT compiler source code, which lives in <a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c\"><code>arch/x86/net/bpf_jit_comp.c</code></a>. Its code is annotated with helpful comments. We will focus our attention on the following call chain within the JIT:</p><p><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c#L894\"><span>do_jit()</span><span> 🔗</span><span><br></span></a><span>   </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c#L292\"><span>emit_prologue()</span><span> 🔗</span><span><br></span></a><span>   </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c#L257\"><span>push_callee_regs()</span><span> 🔗</span><span><br></span></a><span>   </span><span>for (i = 1; i &lt;= insn_cnt; i++, insn++) {</span><span><br></span><span>     </span><span>switch (insn-&gt;code) {</span><span><br></span><span>     </span><span>case BPF_JMP | BPF_CALL:</span><span><br></span><span>       </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c#L1434\"><span>/* emit function call */</span><span> 🔗</span><span><br></span></a><span>     </span><span>case BPF_JMP | BPF_TAIL_CALL:</span><span><br></span><span>       </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c#L531\"><span>emit_bpf_tail_call_direct()</span><span> 🔗</span><span><br></span></a><span>     </span><span>case BPF_JMP | BPF_EXIT:</span><span><br></span><span>       </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/x86/net/bpf_jit_comp.c#L1693\"><span>/* emit epilogue */</span><span> 🔗</span><span><br></span></a><span>     </span><span>}</span> <span><br></span><span>  </span><span>}</span></p><p>It is sometimes hard to visualize the generated instruction stream just from reading the compiler code. Hence, we will also want to inspect the input - BPF instructions - and the output - x86-64 instructions - of the JIT compiler.</p><p>To inspect BPF and x86-64 instructions of a loaded BPF program, we can use <code>bpftool prog dump</code>. However, first we must populate the BPF map used as the tail call jump table. Otherwise, we might not be able to see the tail call jump!</p><p>This is due to <a href=\"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=428d5df1fa4f28daf622c48dd19da35585c9053c\">optimizations</a> that use instruction patching when the index into the program array is known at load time.</p><pre><code># bpftool prog loadall ./tail_call_ex1.o /sys/fs/bpf pinmaps /sys/fs/bpf\n# bpftool map update pinned /sys/fs/bpf/jmp_table key 0 0 0 0 value pinned /sys/fs/bpf/target_prog\n# bpftool prog dump xlated pinned /sys/fs/bpf/entry_prog\nint entry_prog(struct __sk_buff * skb):\n; bpf_tail_call(skb, &amp;jmp_table, 0);\n   0: (18) r2 = map[id:24]\n   2: (b7) r3 = 0\n   3: (85) call bpf_tail_call#12\n; return 0xf00d;\n   4: (b7) r0 = 61453\n   5: (95) exit\n# bpftool prog dump jited pinned /sys/fs/bpf/entry_prog\nint entry_prog(struct __sk_buff * skb):\nbpf_prog_4f697d723aa87765_entry_prog:\n; bpf_tail_call(skb, &amp;jmp_table, 0);\n   0:   nopl   0x0(%rax,%rax,1)\n   5:   xor    %eax,%eax\n   7:   push   %rbp\n   8:   mov    %rsp,%rbp\n   b:   push   %rax\n   c:   movabs $0xffff888102764800,%rsi\n  16:   xor    %edx,%edx\n  18:   mov    -0x4(%rbp),%eax\n  1e:   cmp    $0x21,%eax\n  21:   jae    0x0000000000000037\n  23:   add    $0x1,%eax\n  26:   mov    %eax,-0x4(%rbp)\n  2c:   nopl   0x0(%rax,%rax,1)\n  31:   pop    %rax\n  32:   jmp    0xffffffffffffffe3   // bug? 🤔\n; return 0xf00d;\n  37:   mov    $0xf00d,%eax\n  3c:   leave\n  3d:   ret\n</code></pre>\n<p>There is a caveat. The target addresses for tail call jumps in <code>bpftool prog dump jited</code> output will not make any sense. To discover the real jump targets, we have to peek into the kernel memory. That can be done with <code>gdb</code> after we find the address of our JIT’ed BPF programs in <code>/proc/kallsyms</code>:</p><pre><code># tail -2 /proc/kallsyms\nffffffffa0000720 t bpf_prog_f85b2547b00cbbe9_target_prog        [bpf]\nffffffffa0000748 t bpf_prog_4f697d723aa87765_entry_prog [bpf]\n# gdb -q -c /proc/kcore -ex 'x/18i 0xffffffffa0000748' -ex 'quit'\n[New process 1]\nCore was generated by `earlyprintk=serial,ttyS0,115200 console=ttyS0 psmouse.proto=exps \"virtme_stty_c'.\n#0  0x0000000000000000 in ?? ()\n   0xffffffffa0000748:  nopl   0x0(%rax,%rax,1)\n   0xffffffffa000074d:  xor    %eax,%eax\n   0xffffffffa000074f:  push   %rbp\n   0xffffffffa0000750:  mov    %rsp,%rbp\n   0xffffffffa0000753:  push   %rax\n   0xffffffffa0000754:  movabs $0xffff888102764800,%rsi\n   0xffffffffa000075e:  xor    %edx,%edx\n   0xffffffffa0000760:  mov    -0x4(%rbp),%eax\n   0xffffffffa0000766:  cmp    $0x21,%eax\n   0xffffffffa0000769:  jae    0xffffffffa000077f\n   0xffffffffa000076b:  add    $0x1,%eax\n   0xffffffffa000076e:  mov    %eax,-0x4(%rbp)\n   0xffffffffa0000774:  nopl   0x0(%rax,%rax,1)\n   0xffffffffa0000779:  pop    %rax\n   0xffffffffa000077a:  jmp    0xffffffffa000072b\n   0xffffffffa000077f:  mov    $0xf00d,%eax\n   0xffffffffa0000784:  leave\n   0xffffffffa0000785:  ret\n# gdb -q -c /proc/kcore -ex 'x/7i 0xffffffffa0000720' -ex 'quit'\n[New process 1]\nCore was generated by `earlyprintk=serial,ttyS0,115200 console=ttyS0 psmouse.proto=exps \"virtme_stty_c'.\n#0  0x0000000000000000 in ?? ()\n   0xffffffffa0000720:  nopl   0x0(%rax,%rax,1)\n   0xffffffffa0000725:  xchg   %ax,%ax\n   0xffffffffa0000727:  push   %rbp\n   0xffffffffa0000728:  mov    %rsp,%rbp\n   0xffffffffa000072b:  mov    $0xcafe,%eax\n   0xffffffffa0000730:  leave\n   0xffffffffa0000731:  ret\n#\n</code></pre>\n<p>Lastly, it will be handy to have a cheat sheet of <a href=\"https://elixir.bootlin.com/linux/v5.15.63/source/arch/x86/net/bpf_jit_comp.c#L104\">mapping</a> between BPF registers (<code>r0</code>, <code>r1</code>, …) to hardware registers (<code>rax</code>, <code>rdi</code>, …) that the JIT compiler uses.</p>\n<table width=\"100%\">\n<thead>\n  <tr>\n    <th>BPF</th>\n    <th>x86-64</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>r0</td>\n    <td>rax</td>\n  </tr>\n  <tr>\n    <td>r1</td>\n    <td>rdi</td>\n  </tr>\n  <tr>\n    <td>r2</td>\n    <td>rsi</td>\n  </tr>\n  <tr>\n    <td>r3</td>\n    <td>rdx</td>\n  </tr>\n  <tr>\n    <td>r4</td>\n    <td>rcx</td>\n  </tr>\n  <tr>\n    <td>r5</td>\n    <td>r8</td>\n  </tr>\n  <tr>\n    <td>r6</td>\n    <td>rbx</td>\n  </tr>\n  <tr>\n    <td>r7</td>\n    <td>r13</td>\n  </tr>\n  <tr>\n    <td>r8</td>\n    <td>r14</td>\n  </tr>\n  <tr>\n    <td>r9</td>\n    <td>r15</td>\n  </tr>\n  <tr>\n    <td>r10</td>\n    <td>rbp</td>\n  </tr>\n  <tr>\n    <td>internal</td>\n    <td>r9-r12</td>\n  </tr>\n</tbody>\n</table><p>Now we are prepared to work out what happens when we use a BPF tail call.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image9.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"></figure><p>In essence, <code>bpf_tail_call()</code> emits a jump into another function, reusing the current stack frame. It is just like a regular optimized tail call, but with a twist.</p><p>Because of the BPF security guarantees - execution terminates, no stack overflows - there is a limit on the number of tail calls we can have (<a href=\"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebf7f6f0a6cdcc17a3da52b81e4b3a98c4005028\"><code>MAX_TAIL_CALL_CNT = 33</code></a>).</p><p>Counting the tail calls across BPF programs is not something we can do at load-time. The jump table (BPF program array) contents can change after the program has been verified. Our only option is to keep track of tail calls at run-time. That is why the JIT’ed code for the <code>bpf_tail_call()</code> helper checks and updates the <code>tail_call_cnt</code> counter.</p><p>The updated count is then passed from one BPF program to another, and from one BPF function to another, as we will see, through the <code>rax register</code> (<code>r0</code> in BPF).</p><p>Luckily for us, the x86-64 calling convention dictates that the <code>rax</code> register does not partake in passing function arguments, but rather holds the function return value. The JIT can repurpose it to pass an additional - hidden - argument.</p><p>The function body is, however, free to make use of the <code>r0/rax</code> register in any way it pleases. This explains why we want to save the <code>tail_call_cnt</code> passed via <code>rax</code> onto stack right after we jump to another program. <code>bpf_tail_call()</code> can later load the value from a known location on the stack.</p><p>This way, the <a href=\"https://elixir.bootlin.com/linux/v5.15.63/source/arch/x86/net/bpf_jit_comp.c#L1437\">code emitted</a> for each <code>bpf_tail_call()</code> invocation, and the <a href=\"https://elixir.bootlin.com/linux/v5.15.63/source/arch/x86/net/bpf_jit_comp.c#L281\">BPF function prologue</a> work in tandem, keeping track of tail call count across BPF program boundaries.</p><p>But what if our BPF program is split up into several BPF functions, each with its own stack frame? What if these functions perform BPF tail calls? How is the tail call count tracked then?</p><h3 id=\"mixing-bpf-function-calls-with-bpf-tail-calls\">Mixing BPF function calls with BPF tail calls</h3><p>BPF has its own terminology when it comes to functions and calling them, which is influenced by the internal implementation. Function calls are referred to as <a href=\"https://docs.cilium.io/en/stable/bpf/#bpf-to-bpf-calls\">BPF to BPF calls</a>. Also, the main/entry function in your BPF code is called “the program”, while all other functions are known as “subprograms”.</p><p>Each call to subprogram allocates a stack frame for local state, which persists until the function returns. Naturally, BPF subprogram calls can be nested creating a call chain. Just like nested function calls in user-space.</p><p>BPF subprograms are also allowed to make BPF tail calls. This, effectively, is a mechanism for extending the call chain to another BPF program and its subprograms.</p><p>If we cannot track how long the call chain can be, and how much stack space each function uses, we put ourselves at risk of <a href=\"https://en.wikipedia.org/wiki/Stack_overflow\">overflowing the stack</a>. We cannot let this happen, so BPF enforces limitations on <a href=\"https://elixir.bootlin.com/linux/v5.15.62/source/kernel/bpf/verifier.c#L3600\">when and how many BPF tail calls can be done</a>:</p><pre><code>static int check_max_stack_depth(struct bpf_verifier_env *env)\n{\n        …\n        /* protect against potential stack overflow that might happen when\n         * bpf2bpf calls get combined with tailcalls. Limit the caller's stack\n         * depth for such case down to 256 so that the worst case scenario\n         * would result in 8k stack size (32 which is tailcall limit * 256 =\n         * 8k).\n         *\n         * To get the idea what might happen, see an example:\n         * func1 -&gt; sub rsp, 128\n         *  subfunc1 -&gt; sub rsp, 256\n         *  tailcall1 -&gt; add rsp, 256\n         *   func2 -&gt; sub rsp, 192 (total stack size = 128 + 192 = 320)\n         *   subfunc2 -&gt; sub rsp, 64\n         *   subfunc22 -&gt; sub rsp, 128\n         *   tailcall2 -&gt; add rsp, 128\n         *    func3 -&gt; sub rsp, 32 (total stack size 128 + 192 + 64 + 32 = 416)\n         *\n         * tailcall will unwind the current stack frame but it will not get rid\n         * of caller's stack as shown on the example above.\n         */\n        if (idx &amp;&amp; subprog[idx].has_tail_call &amp;&amp; depth &gt;= 256) {\n                verbose(env,\n                        \"tail_calls are not allowed when call stack of previous frames is %d bytes. Too large\\n\",\n                        depth);\n                return -EACCES;\n        }\n        …\n}\n</code></pre>\n<p>While the stack depth can be calculated by the BPF verifier at load-time, we still need to keep count of tail call jumps at run-time. Even when subprograms are involved.</p><p>This means that we have to pass the tail call count from one BPF subprogram to another, just like we did when making a BPF tail call, so we yet again turn to value passing through the <code>rax register</code>.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image1-11.png\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><figcaption>Control flow in a BPF program with a function call followed by a tail call.</figcaption></figure><p>🛈 To keep things simple, BPF code in our examples does not allocate anything on stack. I encourage you to check how the JIT’ed code changes when you <a href=\"https://elixir.bootlin.com/linux/v5.19.11/source/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf6.c#L37\">add some local variables</a>. Just make sure the compiler does not optimize them out.</p><p>To make it work, we need to:</p><p>① load the tail call count saved on stack into <code>rax</code> before <code>call</code>’ing the subprogram,<br>② adjust the subprogram prologue, so that it does not reset the <code>rax</code> like the main program does,<br>③ save the passed tail call count on subprogram’s stack for the <code>bpf_tail_call()</code> helper to consume it.</p><p>A <code>bpf_tail_call()</code> within our suprogram will then:</p><p>④ load the tail call count from stack,<br>⑤ unwind the BPF stack, but keep the current subprogram’s stack frame in tact, and<br>⑥ jump to the target BPF program.</p><p>Now we have seen how all the pieces of the puzzle fit together to make BPF tail work on x86-64 safely. The only open question is does it work the same way on other platforms like arm64? Time to shift gears and dive into a completely different BPF JIT implementation.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/10/image10.jpg\" alt=\"Assembly within! BPF tail calls on x86 and ARM\"><figcaption>Based on an image by <a href=\"https://www.flickr.com/photos/pockethifi/48303582037/\">Wutthichai Charoenburi</a>, <a href=\"https://creativecommons.org/licenses/by/2.0/\">CC BY 2.0</a></figcaption></figure><h2 id=\"tail-calls-on-arm64\">Tail calls on arm64</h2><p>If you try loading a BPF program that uses both BPF function calls (aka BPF to BPF calls) and BPF tail calls on an arm64 machine running the latest 5.15 LTS kernel, or even the latest 5.19 stable kernel, the BPF verifier will kindly ask you to reconsider your choice:</p><pre><code># uname -rm\n5.19.12 aarch64\n# bpftool prog loadall tail_call_ex2.o /sys/fs/bpf\nlibbpf: prog 'entry_prog': BPF program load failed: Invalid argument\nlibbpf: prog 'entry_prog': -- BEGIN PROG LOAD LOG --\n0: R1=ctx(off=0,imm=0) R10=fp0\n; __attribute__((musttail)) return sub_func(skb);\n0: (85) call pc+1\ncaller:\n R10=fp0\ncallee:\n frame1: R1=ctx(off=0,imm=0) R10=fp0\n; bpf_tail_call(skb, &amp;jmp_table, 0);\n2: (18) r2 = 0xffffff80c38c7200       ; frame1: R2_w=map_ptr(off=0,ks=4,vs=4,imm=0)\n4: (b7) r3 = 0                        ; frame1: R3_w=P0\n5: (85) call bpf_tail_call#12\ntail_calls are not allowed in non-JITed programs with bpf-to-bpf calls\nprocessed 4 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0\n-- END PROG LOAD LOG --\n…\n#\n</code></pre>\n<p>That is a pity! We have been looking forward to reaping the benefits of code sharing with BPF to BPF calls in our lengthy machine generated BPF programs. So we asked - how hard could it be to make it work?</p><p>After all, BPF<a href=\"https://elixir.bootlin.com/linux/v5.15.70/source/arch/arm64/net/bpf_jit_comp.c\"> JIT for arm64</a> already can handle BPF tail calls and BPF to BPF calls, when used in isolation.</p><p>It is “just” a matter of understanding the existing JIT implementation, which lives in <code>arch/arm64/net/bpf_jit_comp.c</code>, and identifying the missing pieces.</p><p>To understand how BPF JIT for arm64 works, we will use the same method as before - look at its code together with sample input (BPF instructions) and output (arm64 instructions).</p><p>We don’t have to read the whole source code. It is enough to zero in on a few particular code paths:</p><p><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L1356\"><span>bpf_int_jit_compile()</span><span> 🔗</span><span><br></span></a><span>   </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L246\"><span>build_prologue()</span><span> 🔗</span><span><br></span></a><span>   </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L1287\"><span>build_body()</span><span> 🔗</span><span><br></span></a><span>     </span><span>for (i = 0; i &lt; prog-&gt;len; i++) {</span><span><br></span><span>        </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L663\"><span>build_insn()</span><span> 🔗</span><span><br></span></a><span>          </span><span>switch (code) {</span><span><br></span><span>          </span><span>case BPF_JMP | BPF_CALL:</span><span><br></span><span>            </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L983\"><span>/* emit function call */</span><span> 🔗</span><span><br></span></a><span>          </span><span>case BPF_JMP | BPF_TAIL_CALL:</span><span><br></span><span>            </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L329\"><span>emit_bpf_tail_call()</span><span> 🔗</span><span><br></span></a><span>          </span><span>}</span><span><br></span><span>     </span><span>}</span><span><br></span><span>   </span><a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L559\"><span>build_epilogue()</span><span> 🔗</span></a></p><p>One thing that the arm64 architecture, and RISC architectures in general, are known for is that it has a plethora of general purpose registers (<code>x0-x30</code>). This is a good thing. We have more registers to allocate to JIT internal state, like the tail call count. A cheat sheet of what <a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L42\">roles</a> the hardware registers play in the BPF JIT will be helpful:</p>\n<table width=\"100%\">\n<thead>\n  <tr>\n    <th>BPF</th>\n    <th>arm64</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>r0</td>\n    <td>x7</td>\n  </tr>\n  <tr>\n    <td>r1</td>\n    <td>x0</td>\n  </tr>\n  <tr>\n    <td>r2</td>\n    <td>x1</td>\n  </tr>\n  <tr>\n    <td>r3</td>\n    <td>x2</td>\n  </tr>\n  <tr>\n    <td>r4</td>\n    <td>x3</td>\n  </tr>\n  <tr>\n    <td>r5</td>\n    <td>x4</td>\n  </tr>\n  <tr>\n    <td>r6</td>\n    <td>x19</td>\n  </tr>\n  <tr>\n    <td>r7</td>\n    <td>x20</td>\n  </tr>\n  <tr>\n    <td>r8</td>\n    <td>x21</td>\n  </tr>\n  <tr>\n    <td>r9</td>\n    <td>x22</td>\n  </tr>\n  <tr>\n    <td>r10</td>\n    <td>x25</td>\n  </tr>\n  <tr>\n    <td>internal</td>\n    <td>x9-x12, x26 (tail_call_cnt), x27</td>\n  </tr>\n</tbody>\n</table><p>Now let’s try to understand the state of things by looking at the JIT’s input and output for two particular scenarios: (1) a BPF tail call, and (2) a BPF to BPF call.</p><p>It is hard to read assembly code selectively. We will have to go through all instructions one by one, and understand what each one is doing.</p><p>⚠ Brace yourself. Time to decipher a bit of ARM64 assembly. If this will be your first time reading ARM64 assembly, you might want to at least skim through this <a href=\"https://modexp.wordpress.com/2018/10/30/arm64-assembly/\">Guide to ARM64 / AArch64 Assembly on Linux</a> before diving in.</p><p>Scenario #1: A single BPF tail call - <a href=\"https://github.com/jsitnicki/cloudflare-blog/blob/jakub/2022-10-bpf-tail-calls/2022-10-bpf-tail-call/tail_call_ex1.bpf.c\"><code>tail_call_ex1.bpf.c</code></a></p><p>Input: BPF assembly (<code>bpftool prog dump xlated</code>)</p><pre><code>   0: (18) r2 = map[id:4]           // jmp_table map\n   2: (b7) r3 = 0\n   3: (85) call bpf_tail_call#12\n   4: (b7) r0 = 61453               // 0xf00d\n   5: (95) exit\n</code></pre>\n<p>Output: ARM64 assembly (<code>bpftool prog dump jited</code>)</p><pre><code> 0:   paciasp                            // Sign LR (ROP protection) ①\n 4:   stp     x29, x30, [sp, #-16]!      // Save FP and LR registers ②\n 8:   mov     x29, sp                    // Set up Frame Pointer\n c:   stp     x19, x20, [sp, #-16]!      // Save callee-saved registers ③\n10:   stp     x21, x22, [sp, #-16]!      // ⋮ \n14:   stp     x25, x26, [sp, #-16]!      // ⋮ \n18:   stp     x27, x28, [sp, #-16]!      // ⋮ \n1c:   mov     x25, sp                    // Set up BPF stack base register (r10)\n20:   mov     x26, #0x0                  // Initialize tail_call_cnt ④\n24:   sub     x27, x25, #0x0             // Calculate FP bottom ⑤\n28:   sub     sp, sp, #0x200             // Set up BPF program stack ⑥\n2c:   mov     x1, #0xffffff80ffffffff    // r2 = map[id:4] ⑦\n30:   movk    x1, #0xc38c, lsl #16       // ⋮ \n34:   movk    x1, #0x7200                // ⋮\n38:   mov     x2, #0x0                   // r3 = 0\n3c:   mov     w10, #0x24                 // = offsetof(struct bpf_array, map.max_entries) ⑧\n40:   ldr     w10, [x1, x10]             // Load array-&gt;map.max_entries\n44:   add     w2, w2, #0x0               // = index (0)\n48:   cmp     w2, w10                    // if (index &gt;= array-&gt;map.max_entries)\n4c:   b.cs    0x0000000000000088         //     goto out;\n50:   mov     w10, #0x21                 // = MAX_TAIL_CALL_CNT (33)\n54:   cmp     x26, x10                   // if (tail_call_cnt &gt;= MAX_TAIL_CALL_CNT)\n58:   b.cs    0x0000000000000088         //     goto out;\n5c:   add     x26, x26, #0x1             // tail_call_cnt++;\n60:   mov     w10, #0x110                // = offsetof(struct bpf_array, ptrs)\n64:   add     x10, x1, x10               // = &amp;array-&gt;ptrs\n68:   lsl     x11, x2, #3                // = index * sizeof(array-&gt;ptrs[0])\n6c:   ldr     x11, [x10, x11]            // prog = array-&gt;ptrs[index];\n70:   cbz     x11, 0x0000000000000088    // if (prog == NULL) goto out;\n74:   mov     w10, #0x30                 // = offsetof(struct bpf_prog, bpf_func)\n78:   ldr     x10, [x11, x10]            // Load prog-&gt;bpf_func\n7c:   add     x10, x10, #0x24            // += PROLOGUE_OFFSET * AARCH64_INSN_SIZE (4)\n80:   add     sp, sp, #0x200             // Unwind BPF stack\n84:   br      x10                        // goto *(prog-&gt;bpf_func + prologue_offset)\n88:   mov     x7, #0xf00d                // r0 = 0xf00d\n8c:   add     sp, sp, #0x200             // Unwind BPF stack ⑨\n90:   ldp     x27, x28, [sp], #16        // Restore used callee-saved registers\n94:   ldp     x25, x26, [sp], #16        // ⋮\n98:   ldp     x21, x22, [sp], #16        // ⋮\n9c:   ldp     x19, x20, [sp], #16        // ⋮\na0:   ldp     x29, x30, [sp], #16        // ⋮\na4:   add     x0, x7, #0x0               // Set return value\na8:   autiasp                            // Authenticate LR\nac:   ret                                // Return to caller\n</code></pre>\n<p>① BPF program prologue starts with Pointer Authentication Code (PAC), which protects against Return <a href=\"https://developer.arm.com/documentation/102433/0100/Return-oriented-programming\">Oriented Programming attacks</a>. PAC instructions are emitted by JIT only if CONFIG_ARM64_PTR_AUTH_KERNEL is enabled.</p><p>② <a href=\"https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst\">Arm 64 Architecture Procedure Call Standard</a> <a href=\"https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#the-frame-pointer\">mandates</a> that the Frame Pointer (register X29) and the Link Register (register X30), aka the return address, of the caller should be recorded onto the stack.</p><p>③ Registers X19 to X28, and X29 (FP) plus X30 (LR), are callee saved. ARM64 BPF JIT does not use registers X23 and X24 currently, so they are not saved.</p><p>④ We track the tail call depth in X26. No need to save it onto stack since we use a register dedicated just for this purpose.</p><p>⑤ FP bottom is an optimization that allows store/loads to BPF stack <a href=\"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5b3d19b9bd4080d7f5e260f91ce8f639e19eb499\">with a single instruction and an immediate offset value</a>.</p><p>⑥ Reserve space for the BPF program stack. The stack layout is now as shown in a <a href=\"https://elixir.bootlin.com/linux/v5.19.12/source/arch/arm64/net/bpf_jit_comp.c#L260\">diagram in <code>build_prologue()</code></a> source code.</p><p>⑦ The BPF function body starts here.</p><p>⑧ <code>bpf_tail_call()</code> instructions start here.</p><p>⑨ The epilogue starts here.</p><p>Whew! That was a handful 😅.</p><p>Notice that the BPF tail call implementation on arm64 is not as optimized as on x86-64. There is no code patching to make direct jumps when the target program index is known at the JIT-compilation time. Instead, the target address is always loaded from the BPF program array.</p><p>Ready for the second scenario? I promise it will be shorter. Function prologue and epilogue instructions will look familiar, so we are going to keep annotations down to a minimum.</p><p>Scenario #2: A BPF to BPF call - <a href=\"https://github.com/jsitnicki/cloudflare-blog/blob/jakub/2022-10-bpf-tail-calls/2022-10-bpf-tail-call/sub_call_ex1.bpf.c\"><code>sub_call_ex1.bpf.c</code></a></p><p>Input: BPF assembly (<code>bpftool prog dump xlated</code>)</p><pre><code>int entry_prog(struct __sk_buff * skb):\n   0: (85) call pc+1#bpf_prog_a84919ecd878b8f3_sub_func\n   1: (95) exit\nint sub_func(struct __sk_buff * skb):\n   2: (b7) r0 = 61453                   // 0xf00d\n   3: (95) exit\n</code></pre>\n<p>Output: ARM64 assembly</p><pre><code>int entry_prog(struct __sk_buff * skb):\nbpf_prog_163e74e7188910f2_entry_prog:\n   0:   paciasp                                 // Begin prologue\n   4:   stp     x29, x30, [sp, #-16]!           // ⋮\n   8:   mov     x29, sp                         // ⋮\n   c:   stp     x19, x20, [sp, #-16]!           // ⋮\n  10:   stp     x21, x22, [sp, #-16]!           // ⋮\n  14:   stp     x25, x26, [sp, #-16]!           // ⋮\n  18:   stp     x27, x28, [sp, #-16]!           // ⋮\n  1c:   mov     x25, sp                         // ⋮\n  20:   mov     x26, #0x0                       // ⋮\n  24:   sub     x27, x25, #0x0                  // ⋮\n  28:   sub     sp, sp, #0x0                    // End prologue\n  2c:   mov     x10, #0xffffffffffff5420        // Build sub_func()+0x0 address\n  30:   movk    x10, #0x8ff, lsl #16            // ⋮\n  34:   movk    x10, #0xffc0, lsl #32           // ⋮\n  38:   blr     x10 ------------------.         // Call sub_func()+0x0 \n  3c:   add     x7, x0, #0x0 &lt;----------.       // r0 = sub_func()\n  40:   mov     sp, sp                | |       // Begin epilogue\n  44:   ldp     x27, x28, [sp], #16   | |       // ⋮\n  48:   ldp     x25, x26, [sp], #16   | |       // ⋮\n  4c:   ldp     x21, x22, [sp], #16   | |       // ⋮\n  50:   ldp     x19, x20, [sp], #16   | |       // ⋮\n  54:   ldp     x29, x30, [sp], #16   | |       // ⋮\n  58:   add     x0, x7, #0x0          | |       // ⋮\n  5c:   autiasp                       | |       // ⋮\n  60:   ret                           | |       // End epilogue\n                                      | |\nint sub_func(struct __sk_buff * skb): | |\nbpf_prog_a84919ecd878b8f3_sub_func:   | |\n   0:   paciasp &lt;---------------------' |       // Begin prologue\n   4:   stp     x29, x30, [sp, #-16]!   |       // ⋮\n   8:   mov     x29, sp                 |       // ⋮\n   c:   stp     x19, x20, [sp, #-16]!   |       // ⋮\n  10:   stp     x21, x22, [sp, #-16]!   |       // ⋮\n  14:   stp     x25, x26, [sp, #-16]!   |       // ⋮\n  18:   stp     x27, x28, [sp, #-16]!   |       // ⋮\n  1c:   mov     x25, sp                 |       // ⋮\n  20:   mov     x26, #0x0               |       // ⋮\n  24:   sub     x27, x25, #0x0          |       // ⋮\n  28:   sub     sp, sp, #0x0            |       // End prologue\n  2c:   mov     x7, #0xf00d             |       // r0 = 0xf00d\n  30:   mov     sp, sp                  |       // Begin epilogue\n  34:   ldp     x27, x28, [sp], #16     |       // ⋮\n  38:   ldp     x25, x26, [sp], #16     |       // ⋮\n  3c:   ldp     x21, x22, [sp], #16     |       // ⋮\n  40:   ldp     x19, x20, [sp], #16     |       // ⋮\n  44:   ldp     x29, x30, [sp], #16     |       // ⋮\n  48:   add     x0, x7, #0x0            |       // ⋮\n  4c:   autiasp                         |       // ⋮\n  50:   ret ----------------------------'       // End epilogue\n</code></pre>\n<p>We have now seen what a BPF tail call and a BPF function/subprogram call compiles down to. Can you already spot what would go wrong if mixing the two was allowed?</p><p>That’s right! Every time we enter a BPF subprogram, we reset the X26 register, which holds the tail call count, to zero (<code>mov x26</code>, <code>#0x0</code>). This is bad. It would let users create program chains longer than the <code>MAX_TAIL_CALL_CNT</code> limit.</p><p>How about we just skip this step when emitting the prologue for BPF subprograms?</p><pre><code>@@ -246,6 +246,7 @@ static bool is_lsi_offset(int offset, int scale)\n static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)\n {\n        const struct bpf_prog *prog = ctx-&gt;prog;\n+       const bool is_main_prog = prog-&gt;aux-&gt;func_idx == 0;\n        const u8 r6 = bpf2a64[BPF_REG_6];\n        const u8 r7 = bpf2a64[BPF_REG_7];\n        const u8 r8 = bpf2a64[BPF_REG_8];\n@@ -299,7 +300,7 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)\n        /* Set up BPF prog stack base register */\n        emit(A64_MOV(1, fp, A64_SP), ctx);\n\n-       if (!ebpf_from_cbpf) {\n+       if (!ebpf_from_cbpf &amp;&amp; is_main_prog) {\n                /* Initialize tail_call_cnt */\n                emit(A64_MOVZ(1, tcc, 0, 0), ctx);\n</code></pre>\n<p>Believe it or not. This is everything that <a href=\"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d4609a5d8c70d21b4a3f801cf896a3c16c613fe1\">was missing</a> to get BPF tail calls working with function calls on arm64. The feature will be enabled in the upcoming Linux 6.0 release.</p><h2 id=\"outro\">Outro</h2><p>From recursion to tweaking the BPF JIT. How did we get here? Not important. It’s all about the journey.</p><p>Along the way we have unveiled a few secrets behind BPF tails calls, and hopefully quenched your thirst for low-level programming. At least for today.</p><p>All that is left is to sit back and watch the fruits of our work. With GDB hooked up to a VM, we can observe how a BPF program calls into a BPF function, and from there tail calls to another BPF program:</p><p><a href=\"https://demo-gdb-step-thru-bpf.pages.dev/\">demo-gdb-step-thru-bpf.pages.dev/</a></p><p>Until next time 🖖.</p>",
            "url": "https://blog.cloudflare.com/assembly-within-bpf-tail-calls-on-x86-and-arm/",
            "title": "Assembly within! BPF tail calls on x86 and ARM",
            "date_modified": "2022-10-10T13:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33134059\">Comments</a>",
            "url": "https://reflame.app/?source=show-hn-launch",
            "title": "Show HN: Reflame – Deploy your React web apps in milliseconds",
            "date_modified": "2022-10-08T17:10:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33110973\">Comments</a>",
            "url": "https://deno.com/blog/the-future-of-web-is-on-the-edge",
            "title": "The Future of the Web Is on the Edge",
            "date_modified": "2022-10-06T17:08:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33019286\">Comments</a>",
            "url": "https://blog.meadsteve.dev/docker/2022/09/29/docker-build-caching/",
            "title": "Using a Docker registry as a distributed layer cache for CI",
            "date_modified": "2022-09-29T11:49:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33011072\">Comments</a>",
            "url": "https://depot.dev",
            "title": "Show HN: Depot – fast, remote Docker container builds",
            "date_modified": "2022-09-28T18:01:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=33010960\">Comments</a>",
            "url": "https://www.inngest.com/blog/modern-serverless-job-scheduler",
            "title": "Modern Serverless Job Schedulers",
            "date_modified": "2022-09-28T17:53:24.000Z"
        },
        {
            "content_html": "<img src=\"http://blog.cloudflare.com/content/images/2022/09/image1-46.png\" alt=\"Leading venture capital firms to provide up to $1.25 BILLION to back startups built on Cloudflare Workers\"><p><em><small>This post is also available in <a href=\"http://blog.cloudflare.com/zh-cn/workers-launchpad-zh-cn/\">简体中文</a>, <a href=\"http://blog.cloudflare.com/ja-jp/workers-launchpad-ja-jp/\">日本語</a> and <a href=\"http://blog.cloudflare.com/de-de/workers-launchpad-de-de/\">Deutsch</a>.</small></em></p>\n<figure><img src=\"http://blog.cloudflare.com/content/images/2022/09/image1-47.png\" alt=\"Leading venture capital firms to provide up to $1.25 BILLION to back startups built on Cloudflare Workers\"></figure><p>From our earliest days, Cloudflare has stood for helping build a better Internet that’s accessible to all. It’s core to our mission that anyone who wants to start building on the Internet should be able to do so easily, and without the barriers of prohibitively expensive or difficult to use infrastructure.</p><p>Nowhere is this philosophy more important – and more impactful to the Internet – than with our developer platform, Cloudflare Workers. Workers is, quite simply, where developers and entrepreneurs start on Day 1. It’s a full developer platform that includes cloud storage; website hosting; SQL databases; and of course, the industry’s leading serverless product. The platform’s ease-of-use and accessible pricing (all the way down to free) are critical in advancing our mission. For startups, this translates into fast, easy deployment and iteration, that scales seamlessly with predictable, transparent and cost-effective pricing. Building a great business from scratch is hard enough – we ought to know! – and so we’re aiming to take all the complexity out of your application infrastructure.</p><h3 id=\"announcing-the-workers-launchpad-funding-program\">Announcing the Workers Launchpad funding program</h3><p>Today, we’re taking things a step further and making it easier for startups to build the business of their dreams. We’re announcing a <strong><strong>$1.25</strong> billion Workers Launchpad funding program</strong> in partnership with some of the world’s leading venture capital firms. Any startup built on Workers can apply. As is the case with the Workers Platform itself, we’ve tried to make applying dead simple: it should take you less than five minutes to submit your application through the Workers Launchpad <a href=\"http://cloudflare.com/lp/workers-launchpad\">portal</a>.</p><p><strong>How does it work? </strong>The only requirement for being eligible for the funding program is that you’ve built your core infrastructure on Workers. If you’re new to Cloudflare and Cloudflare Workers, check out our <a href=\"https://www.cloudflare.com/forstartups/\">Startup Plan</a> to get started. We hope these resources will be helpful to all startups and help level the playing field, no matter where in the world you might be.</p><p>Once you submit your application, it will be reviewed by our Launchpad team, several of whom are former entrepreneurs and venture capital folks themselves. They’ll match promising applicants with our VC partners who have the most expertise in your space (more on them below). Every quarter, we’ll announce the winners of our Launchpad program. Winners, our “Workers Founders”, will be guaranteed the opportunity to pitch the VC partner(s) that we’ve determined would be a good match for your business. It’s a win-win all around. VCs get the opportunity to invest in businesses they know are being built on a forward-looking, world-class, development platform. Entrepreneurs get connected to world-class VCs. And for the first class of winners, we’ll have a few added perks that we describe in more detail below.</p><h3 id=\"who-are-the-vcs-that-you-might-get-a-chance-to-pitch-to\">Who are the VCs that you might get a chance to pitch to?</h3><p>When we approached our friends in the venture community with our vision for the Workers Launchpad, we received incredibly positive feedback and excitement. Many have seen firsthand the competitive advantages of building on Workers through their own portfolio companies. Moreover, Cloudflare is home to one of the largest developer communities on Earth with approximately 20% of the world’s websites on our network. As such, we can play a unique role in matching great entrepreneurs with great VCs to further not only the Workers platform, but also the Internet ecosystem, for everyone.</p><p>We’re honored to announce a world-class group of VC Launch Partners supporting this program and the ecosystem of Workers-based startups:</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/09/VCs-logos.png\" alt=\"Leading venture capital firms to provide up to $1.25 BILLION to back startups built on Cloudflare Workers\"></figure><h3 id=\"more-on-why-we-re-doing-this\">More on why we’re doing this</h3><p>So why are we doing this? The simple answer is we’re proud of our Workers Developer Platform and think that everyone should be using it. Entrepreneurs who develop on Workers can ship <em>faster</em>; more <em>easily</em> and <em>cost-effectively</em>; and in a way that <em>future proofs your infrastructure</em>:</p><p><em><strong>Speed.</strong></em> Development velocity isn’t just a convenience for an entrepreneur. It’s a massive competitive advantage. In fact, development velocity is one of Cloudflare’s competitive advantages – we’re able to develop quickly <em>because</em> we build on Workers. When you develop on Workers, you don’t need to spend time configuring DNS records, maintaining certificates, scaling up clusters, or building complex deployment pipelines. Focus on developing your application, and Cloudflare will handle the rest.</p><p><em><strong>Ease of use</strong></em><strong>.</strong> Startup teams and founders are some of the busiest people on earth. You shouldn’t have to think about – or make complicated decisions about – IT infrastructure. Questions like: “Which availability zone should I choose?”, or “Will I be able to scale up my infrastructure in time for our next viral marketing campaign?” shouldn’t have to cross your mind! And on the Workers Platform, they don’t. The code you and your team writes automatically deploys quickly and consistently across Cloudflare’s global network in 275+ cities in over 100 countries. Cloudflare securely and scalably connects your users to your applications, regardless of where those applications are hosted or how many users suddenly sign up for your product. Developers can easily manage globally distributed applications with a programmable network that easily connects to whatever services they need to talk to.</p><p><em><strong>Future-proofing your infrastructure and your wallet. </strong></em>Cloudflare’s massive global network – that’s distributed across 275+ cities in over 100 countries – is able to scale with your business, no matter how large it grows to become. We also help you remain compliant with local laws and regulations as you expand around the world, with capabilities like Workers’ Jurisdictional Restrictions for Durable Objects. You can sleep soundly at night instead of worrying about how to level up your infrastructure in the midst of shifting regulations, and equally importantly, knowing that you will not wake up to any surprise bills. Many of us have had the experience of being charged unexpected and / or exorbitant fees from our cloud providers. For example, providers will often make it easy and free to onboard your application or data, but charge exorbitant rates when you want to move them out (i.e. egress fees). Cloudflare will never charge for egress. Our pricing is simple, and we constantly aim to be the low-cost provider, no matter how large your business grows to be.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/09/image3-31.png\" alt=\"Leading venture capital firms to provide up to $1.25 BILLION to back startups built on Cloudflare Workers\"></figure><h3 id=\"dogfooding-our-own-product\">Dogfooding our own product</h3><p>We’re excited about Workers not only because we’ve built our own infrastructure on it, but also because we’re seeing the incredible things others have built on it. In fact, we acquired a company built entirely on Workers at the end of last year, an Israeli start-up named Zaraz, which secures and accelerates third party web tools. Workers allowed Zaraz to replace the multiple network requests of each tool running on a website with one single request, effectively streamlining a messy web of extensions into a single lightweight application. This acquisition opened our eyes to the power of the global community that’s built on our platform, and left us motivated to help startups built on Workers find the funding, mentorship, and support needed to grow.</p><h3 id=\"but-wait-there-s-more-\">But wait, there’s more!</h3><p>To make it even easier for startups to take advantage of all the benefits that Workers has to offer, applicants to the Workers Launchpad program who have raised less than $3 million in total external funding will automatically have the option to receive Cloudflare’s <a href=\"https://www.cloudflare.com/forstartups/\">Startup Plan</a>. This plan includes all the elements of Cloudflare’s Pro and Business Plans ($2,400 annual value) plus higher tiers of our Stream video product, our Teams Zero Trust security suite and the Workers platform. To make sure the full range of our developer platform is accessible to startups, we recently more than tripled the number of products available in this plan, which now includes <a href=\"https://developers.cloudflare.com/email-security/\">email security</a>, <a href=\"https://www.cloudflare.com/products/r2/\">R2</a>, <a href=\"https://pages.cloudflare.com/\">Pages</a>, <a href=\"https://www.cloudflare.com/products/workers-kv/\">KV</a>, and many others.</p><p>Furthermore, all startups that apply by <strong>October 31, 2022</strong>, will be eligible to be selected for the Winter 2022 class of Workers Founders, which will unlock additional <em>support</em>, <em>mentorship</em>, and <em>marketing </em>opportunities. Being selected as a Workers Founder will get you a chance to practice your pitch with investors, engage with leaders from Cloudflare, and get advice on how to build a successful business from topics like recruiting to marketing, sales, and beyond during a virtual Workers Founders Bootcamp Week. The program will culminate in a <strong>virtual Demo Day, so</strong> you can show the world what you’ve been building. We’re leaning in to help promising entrepreneurs join us in our mission to help build a better Internet.</p><h3 id=\"helping-make-the-internet-better-for-all\">Helping make the Internet better for all</h3><p>Accessibility and ease of use are core to everything we do at Cloudflare. We will always make our products and platforms so easy to use that even the smallest business or hobbyist can easily use them. We hope the Workers Launchpad funding program encourages entrepreneurs from all around the world, and from all backgrounds, to start building on Workers, and makes it easier for you to find the funding you need to build the business of your dreams.</p><p>Head to the <a href=\"http://cloudflare.com/lp/workers-launchpad\">Workers Launchpad page</a> to apply and join the <a href=\"https://discord.gg/V3GEduuBjP\">Cloudflare Developer Discord</a> to engage with the Workers community. If you’re a VC that is interested in supporting the program, reach out to <a href=\"mailto:Workers-Launchpad@cloudflare.com\">Workers-Launchpad@cloudflare.com</a>.</p><p><small><em><strong>Cloudflare is not providing any funding or making any funding decisions, and there is no guarantee that any particular company will receive funding through the program. All funding decisions will be made by the venture capital firms that participate in the program. Cloudflare is not a registered broker-dealer, investment adviser, or other similar intermediary.</strong></em></small></p>",
            "url": "https://blog.cloudflare.com/workers-launchpad/",
            "title": "Leading venture capital firms to provide up to $1.25 BILLION to back startups built on Cloudflare Workers",
            "date_modified": "2022-09-27T13:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32992133\">Comments</a>",
            "url": "https://a16z.com/2014/05/30/selling-saas-products-dont-sell-themselves/",
            "title": "If SaaS Products Sell Themselves, Why Do We Need Sales?",
            "date_modified": "2022-09-27T06:36:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32990127\">Comments</a>",
            "url": "https://github.com/weaveworks/ignite",
            "title": "Ignite – Use Firecracker VMs with Docker images",
            "date_modified": "2022-09-26T23:50:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32987813\">Comments</a>",
            "url": "https://notes.crmarsh.com/isolates-microvms-and-webassembly",
            "title": "Isolates, microVMs, and WebAssembly",
            "date_modified": "2022-09-26T20:20:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32986713\">Comments</a>",
            "url": "https://nanovms.com/dev/tutorials/cross-region-unikernels-edge",
            "title": "Cross Region Unikernels at the Edge",
            "date_modified": "2022-09-26T18:46:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32982425\">Comments</a>",
            "url": "https://www.bytebase.com/blog/30saas-services-behind-startup",
            "title": "Bytebase: 20-Person Startup, 30 SaaS Services, and $1,183 Monthly Bill",
            "date_modified": "2022-09-26T12:47:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32982425\">Comments</a>",
            "url": "https://www.bytebase.com/blog/30saas-services-behind-startup",
            "title": "SaaS services behind a startup",
            "date_modified": "2022-09-26T12:47:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32980309\">Comments</a>",
            "url": "https://github.com/chatwoot/chatwoot",
            "title": "Show HN: Open-Source Intercom with Help Center",
            "date_modified": "2022-09-26T07:09:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32970198\">Comments</a>",
            "url": "https://fusectore.dev/2022/09/25/github-actions-pitfalls.html",
            "title": "GitHub Actions Pitfalls",
            "date_modified": "2022-09-25T10:32:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32967620\">Comments</a>",
            "url": "https://apisix.apache.org/blog/2022/08/12/arm-performance-google-aws-azure-with-apisix/",
            "title": "GCP, AWS, and Azure ARM-based server performance comparison",
            "date_modified": "2022-09-24T23:44:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32950500\">Comments</a>",
            "url": "https://kaleidawave.github.io/posts/introducing-ezno/",
            "title": "Ezno: a new TypeScript compiler",
            "date_modified": "2022-09-23T11:38:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32836661\">Comments</a>",
            "url": "https://blog.cloudflare.com/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet/",
            "title": "Pingora, the proxy that connects Cloudflare to the Internet",
            "date_modified": "2022-09-14T13:11:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32828584\">Comments</a>",
            "url": "https://earthly.dev/blog/bazel-build/",
            "title": "When to use Bazel?",
            "date_modified": "2022-09-13T18:46:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32816990\">Comments</a>",
            "url": "https://growing-products.paralect.com/a-development-process-startup-founders-should-use-to-ship-features-weirdly-fast",
            "title": "A development process startup founders should use to ship features weirdly fast",
            "date_modified": "2022-09-12T21:08:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32809239\">Comments</a>",
            "url": "https://xeiaso.net/talks/how-my-website-works",
            "title": "Serving a high-performance blog website from memory only, using Rust",
            "date_modified": "2022-09-12T12:40:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32780368\">Comments</a>",
            "url": "https://grahamc.com/blog/nix-and-layered-docker-images",
            "title": "Optimising Docker Layers for Better Caching with Nix (2018)",
            "date_modified": "2022-09-09T15:26:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32771235\">Comments</a>",
            "url": "https://www.relate.so/blog/six-applications-y-combinator/",
            "title": "Our five failed YC applications and one successful one",
            "date_modified": "2022-09-08T20:00:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32768143\">Comments</a>",
            "url": "https://blog.quarkslab.com/defeating-ebpf-uprobe-monitoring.html",
            "title": "Defeating eBPF Uprobe Monitoring",
            "date_modified": "2022-09-08T16:44:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32767784\">Comments</a>",
            "url": "https://www.graplsecurity.com/post/attacking-firecracker",
            "title": "Attacking Firecracker: AWS' MicroVM Monitor Written in Rust",
            "date_modified": "2022-09-08T16:20:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32765200\">Comments</a>",
            "url": "https://deno.com/blog/fresh-1.1",
            "title": "Fresh 1.1 – automatic JSX, plugins, DevTools, and more",
            "date_modified": "2022-09-08T13:28:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32764501\">Comments</a>",
            "url": "https://dl.acm.org/doi/10.1145/3132747.3132763",
            "title": "My VM is lighter (and safer) than your container (2017)",
            "date_modified": "2022-09-08T12:26:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32751538\">Comments</a>",
            "url": "https://www.ycombinator.com/blog/meet-the-yc-summer-2022-batch/",
            "title": "The YC Summer 2022 Batch",
            "date_modified": "2022-09-07T15:01:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32739954\">Comments</a>",
            "url": "https://determinate.systems/posts/introducing-riff",
            "title": "Riff, automatically provide external dependencies for Rust projects",
            "date_modified": "2022-09-06T17:05:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32726679\">Comments</a>",
            "url": "https://www.drawanything.app/",
            "title": "Show HN: Draw Anything – A Simple Stable Diffusion Playground",
            "date_modified": "2022-09-05T17:16:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32693107\">Comments</a>",
            "url": "https://determinate.systems/posts/we-want-to-make-nix-better",
            "title": "We want to make Nix better",
            "date_modified": "2022-09-02T17:03:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32688863\">Comments</a>",
            "url": "https://drewdevault.com/2022/09/02/2022-09-02-In-praise-of-qemu.html",
            "title": "In Praise of QEMU",
            "date_modified": "2022-09-02T10:08:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32661638\">Comments</a>",
            "url": "https://blog.cloudflare.com/cloudflares-abuse-policies-and-approach/",
            "title": "Cloudflare's abuse policies & approach",
            "date_modified": "2022-08-31T13:13:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32650996\">Comments</a>",
            "url": "https://thenewstack.io/javascript-hydration-is-a-workaround-not-a-solution/",
            "title": "JavaScript hydration is a workaround, not a solution",
            "date_modified": "2022-08-30T14:34:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32624138\">Comments</a>",
            "url": "https://acorn.io/",
            "title": "Acorn: A simple application deployment framework for Kubernetes",
            "date_modified": "2022-08-27T23:08:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32623081\">Comments</a>",
            "url": "https://devblogs.microsoft.com/dotnet/announcing-builtin-container-support-for-the-dotnet-sdk/",
            "title": "Built-in container support for the .NET SDK",
            "date_modified": "2022-08-27T20:53:15.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yefutu/developer_experience_infrastructure\">Comments</a></p>",
            "url": "https://kenneth.io/post/developer-experience-infrastructure-dxi",
            "title": "Developer Experience Infrastructure",
            "date_modified": "2022-08-27T14:40:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32619199\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=32619199",
            "title": "Ask HN: Should I checkmate my employer?",
            "date_modified": "2022-08-27T14:09:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32604313\">Comments</a>",
            "url": "https://blog.chainguard.dev/a-toolbox-for-a-secure-software-supply-chain/",
            "title": "A toolbox for a secure software supply chain",
            "date_modified": "2022-08-26T07:40:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32600821\">Comments</a>",
            "url": "https://github.com/jetpack-io/devbox",
            "title": "Devbox: Instant, easy, and predictable shells and containers",
            "date_modified": "2022-08-25T22:35:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32600821\">Comments</a>",
            "url": "https://github.com/jetpack-io/devbox",
            "title": "Show HN: Devbox – Easy, predictable shells and containers",
            "date_modified": "2022-08-25T22:35:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32567874\">Comments</a>",
            "url": "https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/",
            "title": "SSH commit verification now supported",
            "date_modified": "2022-08-23T17:07:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32561127\">Comments</a>",
            "url": "https://crawlee.dev/",
            "title": "Show HN: Crawlee – The web scraping and browser automation library for Node.js",
            "date_modified": "2022-08-23T06:25:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32560039\">Comments</a>",
            "url": "https://tomassetti.me/parsing-sql/",
            "title": "Parsing SQL",
            "date_modified": "2022-08-23T02:58:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32553758\">Comments</a>",
            "url": "https://fundingfyre.com/",
            "title": "Show HN: Largest collection of pitch deck, videos and memos to fund my education",
            "date_modified": "2022-08-22T16:32:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32535491\">Comments</a>",
            "url": "https://www.matteason.co.uk/scotbeats/",
            "title": "Ambient Scotrail Beats – Relax to Scottish train announcements over low-fi beats",
            "date_modified": "2022-08-20T21:56:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32511577\">Comments</a>",
            "url": "https://planetscale.com/blog/introducing-the-planetscale-serverless-driver-for-javascript",
            "title": "The PlanetScale serverless driver for JavaScript",
            "date_modified": "2022-08-18T16:13:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32507172\">Comments</a>",
            "url": "https://electronicmaterialsoffice.com/",
            "title": "Show HN: I spent a year designing a low profile, minimal mechanical keyboard",
            "date_modified": "2022-08-18T09:24:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32507172\">Comments</a>",
            "url": "https://electronicmaterialsoffice.com/",
            "title": "I spent a year designing a low profile, minimal mechanical keyboard",
            "date_modified": "2022-08-18T09:24:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32501448\">Comments</a>",
            "url": "https://nixpacks.com/docs/getting-started",
            "title": "Nixpacks takes a source directory and produces an OCI compliant image",
            "date_modified": "2022-08-17T20:43:26.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/f6oupd/hooking_go_from_rust_hitchhiker_s_guide_go\">Comments</a></p>",
            "url": "https://metalbear.co/blog/hooking-go-from-rust-hitchhikers-guide-to-the-go-laxy/",
            "title": "Hooking Go from Rust - Hitchhiker’s Guide to the Go-laxy",
            "date_modified": "2022-08-17T16:25:12.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/asgxrz/when_docker_images_stop_being_portable\">Comments</a></p>",
            "url": "https://mental-reverb.com/blog.php?id=37",
            "title": "When docker images stop being portable",
            "date_modified": "2022-08-16T21:38:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32477212\">Comments</a>",
            "url": "https://gittup.org/tup/",
            "title": "Tup – an instrumenting file-based build system",
            "date_modified": "2022-08-15T23:44:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32475237\">Comments</a>",
            "url": "https://github.com/drand/tlock",
            "title": "Timelock Encryption made possible and easy to use",
            "date_modified": "2022-08-15T20:54:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32474093\">Comments</a>",
            "url": "https://discord.com/blog/how-discord-supercharges-network-disks-for-extreme-low-latency",
            "title": "How Discord supercharges network disks for extreme low latency",
            "date_modified": "2022-08-15T19:24:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32468613\">Comments</a>",
            "url": "https://deno.com/blog/changes",
            "title": "Big Changes Ahead for Deno",
            "date_modified": "2022-08-15T12:06:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32467962\">Comments</a>",
            "url": "https://neon.tech/blog/paxos/",
            "title": "Why use Paxos instead of Raft?",
            "date_modified": "2022-08-15T10:32:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32467438\">Comments</a>",
            "url": "https://microfounder.com/5k",
            "title": "Solo developer startups to $5k MRR",
            "date_modified": "2022-08-15T09:11:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32456998\">Comments</a>",
            "url": "https://blog.najaryan.net/posts/partial-protobuf-encoding/",
            "title": "Faster Protocol Buffers",
            "date_modified": "2022-08-14T06:44:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32448309\">Comments</a>",
            "url": "https://dragonflydb.io/blog/2022/06/23/cache_design/",
            "title": "DragonFlydb: Cache Design",
            "date_modified": "2022-08-13T09:47:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32447995\">Comments</a>",
            "url": "https://github.com/containers/krunvm",
            "title": "Krunvm – Create MicroVMs from OCI Images",
            "date_modified": "2022-08-13T08:40:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32447636\">Comments</a>",
            "url": "https://tonsky.me/blog/datascript-2/",
            "title": "Ideas for DataScript 2",
            "date_modified": "2022-08-13T07:19:07.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/hqkdxi/direct_host_system_calls_from_kvm\">Comments</a></p>",
            "url": "https://lwn.net/Articles/902585/",
            "title": "Direct host system calls from KVM",
            "date_modified": "2022-08-12T22:22:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32439255\">Comments</a>",
            "url": "https://www.plural.sh/blog/kubernetes-statefulsets-are-broken/",
            "title": "Kubernetes Statefulsets Are Broken",
            "date_modified": "2022-08-12T14:31:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32423813\">Comments</a>",
            "url": "https://github.com/0xricksanchez/like-dbg",
            "title": "Fully Dockerized Linux kernel debugging environment",
            "date_modified": "2022-08-11T10:42:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32421793\">Comments</a>",
            "url": "https://github.com/containers/podman/releases/tag/v4.2.0",
            "title": "Podman 4.2.0",
            "date_modified": "2022-08-11T05:06:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32416193\">Comments</a>",
            "url": "https://www.usenix.org/conference/atc20/presentation/rebello",
            "title": "Can Applications Recover from Fsync Failures?",
            "date_modified": "2022-08-10T18:10:51.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/21ebky/developer_experience_funnel\">Comments</a></p>",
            "url": "https://sluongng.hashnode.dev/bazel-in-ci-part-0-the-developer-experience-funnel",
            "title": "Developer Experience Funnel",
            "date_modified": "2022-08-08T17:06:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32361558\">Comments</a>",
            "url": "http://_.0xffff.me/dynamodb2022.html",
            "title": "Some notes on DynamoDB 2022 paper",
            "date_modified": "2022-08-05T20:24:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32354294\">Comments</a>",
            "url": "https://john-millikin.com/stateless-kubernetes-overlay-networks-with-ipv6",
            "title": "Stateless Kubernetes overlay networks with IPv6",
            "date_modified": "2022-08-05T10:53:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32334741\">Comments</a>",
            "url": "https://github.com/docker-slim",
            "title": "Minify your container by up to 30x to be more secure (free and open source)",
            "date_modified": "2022-08-03T17:42:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32333661\">Comments</a>",
            "url": "https://www.svix.com/blog/guaranteeing-webhook-ordering/",
            "title": "You Can't Guarantee Webhook Ordering",
            "date_modified": "2022-08-03T16:17:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32331989\">Comments</a>",
            "url": "https://github.com/modus-continens/modus",
            "title": "Modus: A language for building Docker/OCI container images",
            "date_modified": "2022-08-03T14:16:45.000Z"
        },
        {
            "content_html": "<p>Startups are often filled with ambitious ideas, growth-hungry team members, and excellent positioning. But most share one common problem: they lack funding. In fact, nearly <a href=\"https://www.cbinsights.com/research/biggest-startup-failures/\" target=\"_blank\" rel=\"noopener\">30 percent</a> of startups fail due to inadequate funding. This problem isn't isolated to startups. <a href=\"https://www.nfib.com/content/resources/start-a-business/why-do-small-businesses-fail/\" target=\"_blank\" rel=\"noopener\">Eighty-two percent</a> of business closures are directly tied to cash flow struggles. Luckily, there are a wide variety of resources startups can leverage to get funding during the early stages.</p><p>One way to get started is to raise seed funding.</p><h2>Understanding seed funding</h2><p>Startups looking for funding rarely have positive net cash flow, and many only have a few founders and a great plan. Only <a href=\"https://www.embroker.com/blog/startup-statistics/#:~:text=95%25%20of%20entrepreneurs%20have%20at%20least%20a%20bachelor's%20degree.&amp;text=Only%202%20in%205%20startups,money%20(1%20in%203).&amp;text=67%25%20of%20Series%20A%20funded,generating%20revenue%20before%20being%20funded.\" target=\"_blank\" rel=\"noopener\">2 in 5 startups</a> are profitable at any point. And most startups struggle to find profits early. According to some sources, it takes around <a href=\"https://www.freshbooks.com/hub/startup/how-long-does-it-take-business-to-be-profitable#:~:text=Two%20to%20three%20years%20is,or%20longer%20to%20make%20money.\" target=\"_blank\" rel=\"noopener\">2 to 3 years</a> for the average startup to start generating profits. Many founders do not have the personal wealth to bootstrap their startups for years.</p><p>Because traditional funding levers (e.g., private equity firms, investment banks, hedge funds, etc.) aren't willing to fund unproven businesses, startups often look for a special type of funding called \"seed\" funding. As the name suggests, investors provide a seed (funding) in the hopes that the startup nurtures that seed money into a healthy tree (a successful startup). In return, the startup provides equity (usually between 5 to 20 percent) or convertible debt.</p><p>So, where do you find seed funding?</p><h2>The \"who's who\" of seed funding</h2><p>Generally, seed funding comes from one of the following sources:</p><ul><li><strong>Friends &amp; family</strong>: The most common method of seed funding is family and friends. Many startup founders have friends or family members who also own businesses or invest. Also, many startups have former founders on the team, who may have friends or colleagues looking for seed opportunities. However, not all founders are well-connected. Luckily, there are multiple other avenues for these founders to secure funding.</li><li><strong>Angels</strong>: Some investors prefer to work with startups. These people (who usually have a high personal net worth) are called \"angels.\" Many angels advertise themselves, but there are also a few websites that help connect people directly with angels (see the resource section below).</li><li><strong>Incubators</strong>: Founders with an idea (but without an actionable product or service) can join an incubator. These are essentially mentorship programs that provide a workspace, funding, networking, training, and resources to help startups succeed. You can expect to give away a percentage of your company to join an incubator. This is one of the best options for high-growth startups that need more than just funds.</li><li><strong>Accelerators</strong>: Unlike incubators (which focus on end-to-end growth), accelerators support growth-driven startups with financing, mentorship, and product development in a short burst. Most accelerators attempt to pack many years' worth of business experience into a few months. Generally, you give away some equity and join multiple businesses all accelerating at the same time.</li><li><strong>Venture capital</strong>: Traditional funding gateways like private equity firms prefer lower-risk investments. Venture capital firms thrive on high-risk, high-reward funding. And they almost solely fund startups. However, many startups save venture capital for later seed rounds. Incubators, angels, and accelerators provide unique benefits (i.e., networking, business support, education, etc.) that venture capital firms do not provide. However, startups with experienced founders often leverage venture capital as a hands-off way to raise initial funding.</li></ul><p>Each of these funding levers provides unique benefits. Friends and family are often the easiest way to get quick funding. But at the risk of damaged relationships. Angels, incubators, and accelerators all provide a wealth of intangible benefits, but they can be challenging to get into, depending on the startup's business plan. And venture capital usually works best when your startup already has a well-established framework.</p><h2>Resources to help you raise capital for your startup</h2><p>Understanding who provides seed funding isn't the same as actually getting seed funding. Many startups exist outside of massive cities, and not all founders are well-connected. Thankfully, a significant amount of funding is now done digitally. There are many websites and services aimed at providing startups with the resources and experience they need to grow a successful company. These include:</p><p><strong>Angel-finding websites</strong>: There are a wide variety of angel funding websites that directly connect founders with angels across the globe. Some of the most popular include:</p><ul><li><a href=\"https://angel.co/\" target=\"_blank\" rel=\"noopener\">Angellist</a></li><li><a href=\"https://www.angelinvestmentnetwork.us/\" target=\"_blank\" rel=\"noopener\">Angel Investment Network</a></li><li><a href=\"https://www.angelforum.org\" target=\"_blank\" rel=\"noopener\">Angel Forum</a></li><li><a href=\"https://gust.com/\" target=\"_blank\" rel=\"noopener\">Gust</a></li></ul><p><strong>Incubators</strong>: There are thousands of incubators across the United States alone, but some stand out more than others. The most successful incubators include:</p><ul><li><a href=\"https://www.techstars.com/\" target=\"_blank\" rel=\"noopener\">TechStars</a></li><li><a href=\"https://www.ycombinator.com/\" target=\"_blank\" rel=\"noopener\">Y Combinator</a></li><li><a href=\"https://masschallenge.org/\" target=\"_blank\" rel=\"noopener\">Mass Challenge</a></li><li><a href=\"https://sosv.com/\" target=\"_blank\" rel=\"noopener\">SOSV</a></li><li><a href=\"https://www.alchemistaccelerator.com/\" target=\"_blank\" rel=\"noopener\">Alchemist Accelerator</a></li></ul><p><strong>Crowdfunding websites: </strong>Crowdfunding is a great way to quickly raise funds for your business. You essentially get funded by many different anonymous people in return for equity. While <a href=\"https://www.gofundme.com/\" target=\"_blank\" rel=\"noopener\">GoFundMe</a> and <a href=\"https://www.kickstarter.com/\" target=\"_blank\" rel=\"noopener\">Kickstarter</a> are great for product-based startups, most new startups go through seed-based crowdfunding rounds on websites like:&nbsp;</p><ul><li><a href=\"https://www.seedinvest.com/\" target=\"_blank\" rel=\"noopener\">SeedInvest</a></li><li><a href=\"https://www.startengine.com/\" target=\"_blank\" rel=\"noopener\">StartEngine</a></li><li><a href=\"https://www.seedrs.com/\" target=\"_blank\" rel=\"noopener\">Seedrs</a></li><li><a href=\"https://microventures.com/\" target=\"_blank\" rel=\"noopener\">MicroVentures</a></li></ul><p><strong>Loans:</strong> You always have the option to take out loans and microloans. Small businesses can use <a href=\"https://www.sba.gov/funding-programs/loans\" target=\"_blank\" rel=\"noopener\">SBA loans</a> to get some quick capital, and there are a wide variety of unique loan options for startups. For example, solutions like <a href=\"https://www.pipe.com/\" target=\"_blank\" rel=\"noopener\">Pipe</a> give you up-front capital in return for recurring revenue (which may be ideal for some SaaS companies looking for hands-off funding). You can find loan options at your bank or through a variety of different websites.</p><p>In addition to these funding options, there are many resources for startups that aren't related to capital. Startup events, networking websites, forums, social media, and a wide variety of websites exist to help founders connect to other founders and business leaders. Growing a successful business takes more than money. You should always look to build connections and gain insights from industry leaders along the way.</p><h2>Partner with us</h2><p>DigitalOcean provides founders a variety of resources through <a href=\"https://www.digitalocean.com/hatch\">Hatch</a>, our global startup program. <a href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a> to learn how we can help you build and grow your startup through intelligent infrastructure solutions.</p>",
            "url": "https://www.digitalocean.com/blog/learn-the-basics-of-seed-funding",
            "title": "How to raise seed capital for your startup | DigitalOcean",
            "date_modified": "2022-08-03T12:22:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32329052\">Comments</a>",
            "url": "https://github.com/ValdikSS/nat-traversal-github-actions-openvpn-wireguard",
            "title": "OpenVPN & WireGuard server at GitHub Actions: representative NAT traversal case",
            "date_modified": "2022-08-03T08:23:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32322329\">Comments</a>",
            "url": "http://dallery.gallery/wp-content/uploads/2022/07/The-DALL%C2%B7E-2-prompt-book-v1.02.pdf",
            "title": "DALL·E 2 prompt book [pdf]",
            "date_modified": "2022-08-02T18:14:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32317520\">Comments</a>",
            "url": "https://stanislas.blog/2021/08/firecracker/",
            "title": "Using Firecracker and Go to run short-lived, untrusted code execution jobs",
            "date_modified": "2022-08-02T12:01:53.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/zo0nzb/using_firecracker_go_run_short_lived\">Comments</a></p>",
            "url": "https://stanislas.blog/2021/08/firecracker/",
            "title": "Using Firecracker and Go to run short-lived, untrusted code execution jobs",
            "date_modified": "2022-08-01T14:59:06.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/okw2pg/bazel_repository_cache\">Comments</a></p>",
            "url": "https://sluongng.hashnode.dev/bazel-caching-explained-pt-3-repository-cache",
            "title": "Bazel Repository Cache",
            "date_modified": "2022-08-01T13:45:32.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/a7ndkr/docker_oci_container_ecosystem\">Comments</a></p>",
            "url": "https://lwn.net/SubscriberLink/902049/374614a66c0367f3/",
            "title": "Docker and the OCI container ecosystem",
            "date_modified": "2022-08-01T01:17:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32269287\">Comments</a>",
            "url": "https://github.com/losfair/mvsqlite",
            "title": "Show HN: Distributed SQLite on FoundationDB",
            "date_modified": "2022-07-28T19:49:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32267862\">Comments</a>",
            "url": "https://chunk.run",
            "title": "Show HN: Chunk – Code sandbox for back-end devs",
            "date_modified": "2022-07-28T17:49:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32267287\">Comments</a>",
            "url": "https://www.edgedb.com/blog/edgedb-2-0",
            "title": "EdgeDB 2.0",
            "date_modified": "2022-07-28T17:12:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32266926\">Comments</a>",
            "url": "https://deepfence.io/aya-your-trusty-ebpf-companion/",
            "title": "Aya: your tRusty eBPF companion",
            "date_modified": "2022-07-28T16:51:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32266414\">Comments</a>",
            "url": "https://matt-rickard.com/10-years-and-s3-isnt-getting-cheaper/",
            "title": "S3 isn't getting cheaper",
            "date_modified": "2022-07-28T16:17:51.000Z"
        },
        {
            "content_html": "<p>Hello! The other day we talked about <a href=\"https://jvns.ca/blog/2022/07/20/pseudoterminals/\">what happened when you press a key in your terminal</a>.</p>\n\n<p>As a followup, I thought it might be fun to implement a program that’s like a\ntiny ssh server, but without the security. You can find it <a href=\"https://github.com/jvns/tiny-remote-login/blob/main/server.go\">on github here</a>, and I’ll explain how it works in this blog post.</p>\n\n<h3 id=\"the-goal-ssh-to-a-remote-computer\">the goal: “ssh” to a remote computer</h3>\n\n<p>Our goal is to be able to login to a remote computer and run commands, like you\ndo with SSH or telnet.</p>\n\n<p>The biggest difference between this program and SSH is that there’s literally\nno security (not even a password) – anyone who can make a TCP connection to\nthe server can get a shell and run commands.</p>\n\n<p>Obviously this is not a useful program in real life, but our goal is to learn a\nlittle more about how terminals works, not to write a useful program.</p>\n\n<p>(I will run a version of it on the public internet for the next week though,\nyou can see how to connect to it at the end of this blog post)</p>\n\n<h3 id=\"let-s-start-with-the-server\">let’s start with the server!</h3>\n\n<p>We’re also going to write a client, but the server is the interesting part, so\nlet’s start there. We’re going to write a server that listens on a TCP port (I\npicked 7777) and creates remote terminals for any client that connects to it to\nuse.</p>\n\n<p>When the server receives a new connection it needs to:</p>\n\n<ol>\n<li>create a pseudoterminal for the client to use</li>\n<li>start a <code>bash</code> shell process for the client to use</li>\n<li>connect <code>bash</code> to the pseudoterminal</li>\n<li>continuously copy information back and forth between the TCP connection and\nthe pseudoterminal</li>\n</ol>\n\n<p>I just said the word “pseudoterminal” a lot, so let’s talk about what that\nmeans.</p>\n\n<h3 id=\"what-s-a-pseudoterminal\">what’s a pseudoterminal?</h3>\n\n<p>Okay, what the heck is a pseudoterminal?</p>\n\n<p>A pseudoterminal is a lot like a bidirectional pipe or a socket – you have two\nends, and they can both send and receive information. You can read more about\nthe information being sent and received in <a href=\"https://jvns.ca/blog/2022/07/20/pseudoterminals/\">what happens if you press a key in your terminal</a></p>\n\n<p>Basically the idea is that on one end, we have a TCP connection, and on the\nother end, we have a <code>bash</code> shell. So we need to hook one part of the\npseudoterminal up to the TCP connection and the other end to bash.</p>\n\n<p>The two parts of the pseudoterminal are called:</p>\n\n<ul>\n<li>the “pseudoterminal master”. This is the end we’re going to hook up to the TCP connection.</li>\n<li>the “slave pseudoterminal device”. We’re going to set our bash shell’s <code>stdout</code>, <code>stderr</code>, and <code>stdin</code> to this.</li>\n</ul>\n\n<p>Once they’re conected, we can communicate with <code>bash</code> over our TCP connection\nand we’ll have a remote shell!</p>\n\n<h3 id=\"why-do-we-need-this-pseudoterminal-thing-anyway\">why do we need this “pseudoterminal” thing anyway?</h3>\n\n<p>You might be wondering – Julia, if a pseudoterminal is kind of like a socket,\nwhy can’t we just set our bash shell’s <code>stdout</code> / <code>stderr</code> / <code>stdin</code> to the TCP\nsocket?</p>\n\n<p>And you can! We could write a TCP connection handler like this that does exactly that, it’s not a lot of code (<a href=\"https://github.com/jvns/tiny-remote-login/blob/main/server-notty.go\">server-notty.go</a>).</p>\n\n<pre><code>\nfunc handle(conn net.Conn) {\n\ttty, _ := conn.(*net.TCPConn).File()\n\t// start bash with tcp connection as stdin/stdout/stderr\n\tcmd := exec.Command(\"bash\")\n\tcmd.Stdin = tty\n\tcmd.Stdout = tty\n\tcmd.Stderr = tty\n\tcmd.Start()\n}\n\n</code></pre>\n\n<p>It even kind of works – if we connect to it with <code>nc localhost 7778</code>, we can\nrun commands and look at their output.</p>\n\n<p>But there are a few problems. I’m not going to list all of them, just two.</p>\n\n<p><strong>problem 1: Ctrl + C doesn’t work</strong></p>\n\n<p>The way Ctrl + C works in a remote login session is</p>\n\n<ul>\n<li>you press ctrl + c</li>\n<li>That gets translated to <code>0x03</code> and sent through the TCP connection</li>\n<li>The terminal receives it</li>\n<li>the Linux kernel on the other end notes “hey, that was a Ctrl + C!”</li>\n<li>Linux sends a <code>SIGINT</code> to the appropriate process (more on what the “appropriate process” is exactly later)</li>\n</ul>\n\n<p>If the “terminal” is just a TCP connection, this doesn’t work, because when you\nsend <code>0x04</code> to a TCP connection, Linux won’t magically send <code>SIGINT</code> to any\nprocess.</p>\n\n<p><strong>problem 2: <code>top</code> doesn’t work</strong></p>\n\n<p>When I try to run <code>top</code> in this shell, I get the error message <code>top: failed tty get</code>. If we strace it, we see this system call:</p>\n\n<pre><code>ioctl(2, TCGETS, 0x7ffec4e68d60)        = -1 ENOTTY (Inappropriate ioctl for device)\n</code></pre>\n\n<p>So <code>top</code> is running an <code>ioctl</code> on its output file descriptor (2) to get some\ninformation about the terminal. But Linux is like “hey, this isn’t a terminal!”\nand returns an error.</p>\n\n<p>There are a bunch of other things that go wrong, but hopefully at this point\nyou’re convinced that we actually need to set bash’s stdout/stderr to be a\nterminal, not some other thing like a socket.</p>\n\n<p>So let’s start looking at the server code and see what creating a\npseudoterminal actually looks like.</p>\n\n<h3 id=\"step-1-create-a-pseudoterminal\">step 1: create a pseudoterminal</h3>\n\n<p>Here’s some Go code to create a pseudoterminal on Linux. This is copied from <a href=\"https://github.com/creack/pty/blob/7de28cee0d53510e719c1aeb1850af0fa647c343/pty_linux.go\">github.com/creack/pty</a>,\nbut I removed some of the error handling to make the logic a bit easier to follow:</p>\n\n<pre><code>pty, _ := os.OpenFile(\"/dev/ptmx\", os.O_RDWR, 0)\nsname := ptsname(p)\nunlockpt(p)\ntty, _ := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)\n</code></pre>\n\n<p>In English, what we’re doing is:</p>\n\n<ul>\n<li>open <code>/dev/ptmx</code> to get the “pseudoterminal master” Again, that’s the part we’re going to hook up to the TCP connection</li>\n<li>get the filename of the “slave pseudoterminal device”, which is going to be <code>/dev/pts/13</code> or something.</li>\n<li>“unlock” the pseudoterminal so that we can use it. I have no idea what the point of this is (why is it locked to begin with?) but you have to do it for some reason</li>\n<li>open <code>/dev/pts/13</code> (or whatever number we got from <code>ptsname</code>) to get the “slave pseudoterminal device”</li>\n</ul>\n\n<p>What do those <code>ptsname</code> and <code>unlockpt</code> functions do? They just make some\n<code>ioctl</code> system calls to the Linux kernel. All of the communication with the\nLinux kernel about terminals seems to be through various <code>ioctl</code> system calls.</p>\n\n<p>Here’s the code, it’s pretty short: (again, I just copied it from <a href=\"https://github.com/creack/pty/blob/7de28cee0d53510e719c1aeb1850af0fa647c343/pty_linux.go#L41-L54\">creack/pty</a>)</p>\n\n<pre><code>func ptsname(f *os.File) string {\n\tvar n uint32\n\tioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&amp;n)))\n\treturn \"/dev/pts/\" + strconv.Itoa(int(n))\n}\n\nfunc unlockpt(f *os.File) {\n\tvar u int32\n\t// use TIOCSPTLCK with a pointer to zero to clear the lock\n\tioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&amp;u)))\n}\n</code></pre>\n\n<h3 id=\"step-2-hook-the-pseudoterminal-up-to-bash\">step 2: hook the pseudoterminal up to <code>bash</code></h3>\n\n<p>The next thing we have to do is connect the pseudoterminal to <code>bash</code>. Luckily,\nthat’s really easy – here’s the Go code for it! We just need to start a new\nprocess and set the stdin, stdout, and stderr to <code>tty</code>.</p>\n\n<pre><code>cmd := exec.Command(\"bash\")\ncmd.Stdin = tty\ncmd.Stdout = tty\ncmd.Stderr = tty\ncmd.SysProcAttr = &amp;syscall.SysProcAttr{\n  Setsid: true,\n}\ncmd.Start()\n</code></pre>\n\n<p>Easy! Though – why do we need this <code>Setsid: true</code> thing, you might ask? Well,\nI tried commenting out that code to see what went wrong. It turns out that what\ngoes wrong is – Ctrl + C doesn’t work anymore!</p>\n\n<p><code>Setsid: true</code> creates a new <strong>session</strong> for the new bash process. But why does\nthat make <code>Ctrl + C</code> work? How does Linux know which process to send <code>SIGINT</code>\nto when you press <code>Ctrl + C</code>, and what does that have to do with sessions?</p>\n\n<h3 id=\"how-does-linux-know-which-process-to-send-ctrl-c-to\">how does Linux know which process to send Ctrl + C to?</h3>\n\n<p>I found this pretty confusing, so I reached for my favourite book for learning\nabout this kind of thing: <a href=\"https://man7.org/tlpi/\">the linux programming interface</a>, specifically chapter 34 on process groups\nand sessions.</p>\n\n<p>That chapter contains a few key facts: (#3, #4, and #5 are direct quotes from the book)</p>\n\n<ol>\n<li>Every process has a <strong>session id</strong> and a <strong>process group id</strong> (which may or may not be the same as its PID)</li>\n<li>A session is made up of multiple process groups</li>\n<li>All of the processes in a session share a single controlling terminal.</li>\n<li>A terminal may be the controlling terminal of at most one session.</li>\n<li>At any point in time, one of the process groups in a session is the\n<strong>foreground process group</strong> for the terminal, and the others are background\nprocess groups.</li>\n<li>When you press <code>Ctrl+C</code> in a terminal, SIGINT gets sent to all the processes in the foreground process group</li>\n</ol>\n\n<p>What’s a process group? Well, my understanding is that:</p>\n\n<ul>\n<li>processes in the same pipe <code>x | y | z</code> are in the same process group</li>\n<li>processes you start on the same shell line (<code>x &amp;&amp; y &amp;&amp; z</code>) are in the same process group</li>\n<li>child processes are by default in the same process group, unless you explicitly decide otherwise</li>\n</ul>\n\n<p>I didn’t know most of this (I had no idea processes had a session ID!) so this\nwas kind of a lot to absorb. I tried to draw a sketchy ASCII art diagram of the\nsituation</p>\n\n<pre><code>(maybe)  terminal --- session --- process group --- process\n                               |                 |- process\n                               |                 |- process\n                               |- process group \n                               |\n                               |- process group \n</code></pre>\n\n<p>So when we press Ctrl+C in a terminal, here’s what I think happens:</p>\n\n<ul>\n<li><code>\\x04</code> gets written to the “pseudoterminal master” of a terminal</li>\n<li>Linux finds the <strong>session</strong> for that terminal (if it exists)</li>\n<li>Linux find the <strong>foreground process group</strong> for that session</li>\n<li>Linux sends <code>SIGINT</code></li>\n</ul>\n\n<p>If we don’t create a new session for our new bash process, our new pseudoterminal\nactually won’t have <strong>any</strong> session associated with it, so nothing happens when\nwe press <code>Ctrl+C</code>. But if we do create a new session, then the new\npseudoterminal will have the new session associated with it.</p>\n\n<h3 id=\"how-to-get-a-list-of-all-your-sessions\">how to get a list of all your sessions</h3>\n\n<p>As a quick aside, if you want to get a list of all the sessions on your Linux\nmachine, grouped by session, you can run:</p>\n\n<pre><code>$ ps -eo user,pid,pgid,sess,cmd | sort -k3\n</code></pre>\n\n<p>This includes the PID, process group ID, and session ID. As an example of the output, here are the two processes in the pipeline:</p>\n\n<pre><code>bork       58080   58080   57922 ps -eo user,pid,pgid,sess,cmd\nbork       58081   58080   57922 sort -k3\n</code></pre>\n\n<p>You can see that they share the same process group ID and session ID, but of\ncourse they have different PIDs.</p>\n\n<p>That was kind of a lot but that’s all we’re going to say about sessions and\nprocess groups in this post. Let’s keep going!</p>\n\n<h3 id=\"step-3-set-the-window-size\">step 3: set the window size</h3>\n\n<p>We need to tell the terminal how big to be!</p>\n\n<p>Again, I just copied this from <code>creack/pty</code>. I decided to hardcode the size to 80x24.</p>\n\n<pre><code>Setsize(tty, &amp;Winsize{\n\t\tCols: 80,\n\t\tRows: 24,\n\t})\n</code></pre>\n\n<p>Like with getting the terminal’s pts filename and unlocking it, setting the\nsize is just one <code>ioctl</code> system call:</p>\n\n<pre><code>func Setsize(t *os.File, ws *Winsize) {\n\tioctl(t.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))\n}\n</code></pre>\n\n<p>Pretty simple! We could do something smarter and get the real window size, but\nI’m too lazy.</p>\n\n<h3 id=\"step-4-copy-information-between-the-tcp-connection-and-the-pseudoterminal\">step 4: copy information between the TCP connection and the pseudoterminal</h3>\n\n<p>As a reminder, our rough steps to set up this remote login server were:</p>\n\n<ol>\n<li>create a pseudoterminal for the client to use</li>\n<li>start a <code>bash</code> shell process</li>\n<li>connect <code>bash</code> to the pseudoterminal</li>\n<li>continuously copy information back and forth between the TCP connection and\nthe pseudoterminal</li>\n</ol>\n\n<p>We’ve done 1, 2, and 3, now we just need to ferry information between the TCP\nconnection and the pseudoterminal.</p>\n\n<p>There are two <code>io.Copy</code> calls, one to copy the input <em>from</em> the tcp connection, and one to copy the output <em>to</em> the TCP connection. Here’s what the code looks like:</p>\n\n<pre><code>\tgo func() {\n\t\t\tio.Copy(pty, conn)\n\t}()\n  io.Copy(conn, pty)\n</code></pre>\n\n<p>The first one is in a goroutine just so they can both run in parallel.</p>\n\n<p>Pretty simple!</p>\n\n<h3 id=\"step-5-exit-when-we-re-done\">step 5: exit when we’re done</h3>\n\n<p>I also added a little bit of code to close the TCP connection when the command exits</p>\n\n<pre><code>go func() {\n  cmd.Wait()\n  conn.Close()\n}()\n\n</code></pre>\n\n<p>And that’s it for the server!  You can see all of the Go code here: <a href=\"https://github.com/jvns/tiny-remote-login/blob/main/server.go\">server.go</a>.</p>\n\n<h3 id=\"next-write-a-client\">next: write a client</h3>\n\n<p>Next, we have to write a client. This is a lot easier than the server because we don’t need to do quite as much terminal setup. There are just 3 steps:</p>\n\n<ol>\n<li>Put the terminal into raw mode</li>\n<li>copy stdin/stdout to the TCP connection</li>\n<li>reset the terminal</li>\n</ol>\n\n<h3 id=\"client-step-1-put-the-terminal-into-raw-mode\">client step 1: put the terminal into “raw” mode</h3>\n\n<p>We need to put the client terminal into “raw” mode so that every time you press\na key, it gets sent to the TCP connection immediately. If we don’t do this,\neverything will only get sent when you press enter.</p>\n\n<p>“Raw mode” isn’t actually a single thing, it’s a bunch of flags that you want\nto turn off. There’s a good tutorial explaining all the flags we have to turn\noff called <a href=\"https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html\">Entering raw mode</a>.</p>\n\n<p>Like everything else with terminals, this requires <code>ioctl</code> system calls. In\nthis case we get the terminal’s current settings, modify them, and save the old\nsettings so that we can restore them later.</p>\n\n<p>I figured out how to do this in Go by going to <a href=\"https://grep.app\">grep.app</a> and typing in\n<code>syscall.TCSETS</code> to find some other Go code that was doing the same thing.</p>\n\n<pre><code>func MakeRaw(fd uintptr) syscall.Termios {\n\t// from https://github.com/getlantern/lantern/blob/devel/archive/src/golang.org/x/crypto/ssh/terminal/util.go\n\tvar oldState syscall.Termios\n\tioctl(fd, syscall.TCGETS, uintptr(unsafe.Pointer(&amp;oldState)))\n\n\tnewState := oldState\n\tnewState.Iflag &amp;^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF\n\tnewState.Lflag &amp;^= syscall.ECHO | syscall.ICANON | syscall.ISIG\n\tioctl(fd, syscall.TCSETS, uintptr(unsafe.Pointer(&amp;newState)))\n\treturn oldState\n}\n</code></pre>\n\n<h3 id=\"client-step-2-copy-stdin-stdout-to-the-tcp-connection\">client step 2: copy stdin/stdout to the TCP connection</h3>\n\n<p>This is exactly like what we did with the server. It’s very little code:</p>\n\n<pre><code>go func() {\n\t\tio.Copy(conn, os.Stdin)\n\t}()\n\tio.Copy(os.Stdout, conn)\n</code></pre>\n\n<h3 id=\"client-step-3-restore-the-terminal-s-state\">client step 3: restore the terminal’s state</h3>\n\n<p>We can put the terminal back into the mode it started in like this (another <code>ioctl</code>!):</p>\n\n<pre><code>func Restore(fd uintptr, oldState syscall.Termios) {\n\tioctl(fd, syscall.TCSETS, uintptr(unsafe.Pointer(&amp;oldState)))\n}\n</code></pre>\n\n<h3 id=\"we-did-it\">we did it!</h3>\n\n<p>We have written a tiny remote login server that lets anyone log in! Hooray!</p>\n\n<p>Obviously this has zero security so I’m not going to talk about that aspect.</p>\n\n<h3 id=\"it-s-running-on-the-public-internet-you-can-try-it-out\">it’s running on the public internet! you can try it out!</h3>\n\n<p>For the next week or so I’m going to run a demo of this on the internet at\n<code>tetris.jvns.ca</code>. It runs tetris instead of a shell because I wanted to avoid\nabuse, but if you want to try it with a shell you can run it on your own\ncomputer :).</p>\n\n<p>If you want to try it out, you can use <code>netcat</code> as a client instead of the\ncustom Go client program we wrote, because copying information to/from a TCP\nconnection is what netcat does. Here’s how:</p>\n\n<pre><code>stty raw -echo &amp;&amp; nc tetris.jvns.ca 7777 &amp;&amp; stty sane\n</code></pre>\n\n<p>This will let you play a terminal tetris game called <code>tint</code>.</p>\n\n<p>You can also use the <a href=\"https://github.com/jvns/tiny-remote-login/blob/main/client.go\">client.go program</a> and run <code>go run client.go tetris.jvns.ca 7777</code>.</p>\n\n<h3 id=\"this-is-not-a-good-protocol\">this is not a good protocol</h3>\n\n<p>This protocol where we just copy bytes from the TCP connection to the terminal\nand nothing else is not good because it doesn’t allow us to send over\ninformation information like the terminal or the actual window size of the\nterminal.</p>\n\n<p>I thought about implementing telnet’s protocol so that we could use telnet as a\nclient, but I didn’t feel like figuring out how telnet works so I didn’t. (the\nserver 30% works with telnet as is, but a lot of things are broken, I don’t\nquite know why, and I didn’t feel like figuring it out)</p>\n\n<h3 id=\"it-ll-mess-up-your-terminal-a-bit\">it’ll mess up your terminal a bit</h3>\n\n<p>As a warning: using this server to play tetris will probably mess up your\nterminal a bit because it sets the window size to 80x24. To fix that I just\nclosed the terminal tab after running that command.</p>\n\n<p>If we wanted to fix this for real, we’d need to restore the window size after\nwe’re done, but then we’d need a slightly more real protocol  than “just\nblindly copy bytes back and forth with TCP” and I didn’t feel like doing that.</p>\n\n<p>Also it sometimes takes a second to disconnect after the program exits for some\nreason, I’m not sure why that is.</p>\n\n<h3 id=\"other-tiny-projects\">other tiny projects</h3>\n\n<p>That’s all! There are a couple of other similar toy implementations of programs\nI’ve written here:</p>\n\n<ul>\n<li><a href=\"https://jvns.ca/blog/2022/03/23/a-toy-version-of-tls/\">toy tls 1.3 implementation</a></li>\n<li><a href=\"https://jvns.ca/blog/2022/02/01/a-dns-resolver-in-80-lines-of-go/\">toy dns resolver</a></li>\n</ul>",
            "url": "https://jvns.ca/blog/2022/07/28/toy-remote-login-server/",
            "title": "A toy remote login server",
            "date_modified": "2022-07-28T08:00:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32241820\">Comments</a>",
            "url": "https://www.thenile.dev/blog/multi-tenant-rls",
            "title": "Shipping Multi-Tenant SaaS Using Postgres Row-Level Security",
            "date_modified": "2022-07-26T18:16:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32213950\">Comments</a>",
            "url": "https://matt-rickard.com/non-obvious-docker-uses/",
            "title": "Non-Obvious Docker Uses",
            "date_modified": "2022-07-24T14:44:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32212862\">Comments</a>",
            "url": "https://dr-emann.github.io/squashfs/squashfs.html",
            "title": "Squashfs binary format",
            "date_modified": "2022-07-24T12:39:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32210438\">Comments</a>",
            "url": "https://www.nongnu.org/lzip/xz_inadequate.html",
            "title": "Xz format considered inadequate for long-term archiving (2016)",
            "date_modified": "2022-07-24T04:52:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32208430\">Comments</a>",
            "url": "https://www.bbc.com/news/science-environment-62225696",
            "title": "The audacious PR plot that seeded doubt about climate change",
            "date_modified": "2022-07-23T23:00:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32192498\">Comments</a>",
            "url": "https://vercel.com/blog/build-output-api",
            "title": "Vercel Build Output API: Infrastructure-as-Filesystem",
            "date_modified": "2022-07-22T14:53:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32179856\">Comments</a>",
            "url": "https://github.com/supabase/pg_jsonschema",
            "title": "Show HN: Pg_jsonschema – A Postgres extension for JSON validation",
            "date_modified": "2022-07-21T14:31:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32164247\">Comments</a>",
            "url": "https://www.hetzner.com/news/07-22-rx-line/",
            "title": "Hetzner to Offer First Arm-Based Dedicated Servers in Europe",
            "date_modified": "2022-07-20T11:37:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32156009\">Comments</a>",
            "url": "https://brandur.org/soft-deletion",
            "title": "Soft Deletion Probably Isn't Worth It",
            "date_modified": "2022-07-19T18:35:39.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image3-8.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image3-7.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"><p>Cloudflare has been using Kafka in production since 2014. We have come a long way since then, and currently run 14 distinct Kafka clusters, across multiple data centers, with roughly 330 nodes. Between them, over a trillion messages have been processed over the last eight years.</p><p>Cloudflare uses Kafka to decouple microservices and communicate the creation, change or deletion of various resources via a common data format in a fault-tolerant manner. This decoupling is one of many factors that enables Cloudflare engineering teams to work on multiple features and products concurrently.</p><p>We learnt a lot about Kafka on the way to one trillion messages, and built some interesting internal tools to ease adoption that will be explored in this blog post. The focus in this blog post is on inter-application communication use cases alone and not logging (we have other Kafka clusters that power the dashboards where customers view statistics that handle more than one trillion messages <em>each day</em>). I am an engineer on the Application Services team and our team has a charter to provide tools/services to product teams, so they can focus on their core competency which is delivering value to our customers.</p><p>In this blog I’d like to recount some of our experiences in the hope that it helps other engineering teams who are on a similar journey of adopting Kafka widely.</p><h3 id=\"tooling\">Tooling</h3><p>One of our Kafka clusters is creatively named Messagebus. It is the most general purpose cluster we run, and was created to:</p><ul><li>Prevent data silos;</li><li>Enable services to communicate more clearly with basically zero integration cost (more on how we achieved this below);</li><li>Encourage the use of a self-documenting communication format and therefore removing the problem of out of date documentation.</li></ul><p>To make it as easy to use as possible and to encourage adoption, the Application Services team created two internal projects. The first is unimaginatively named Messagebus-Client. Messagebus-Client is a Go library that wraps the fantastic <a href=\"https://github.com/Shopify/sarama\">Shopify Sarama</a> library with an opinionated set of configuration options and the ability to manage the rotation of mTLS certificates.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/unnamed1-2.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><p>The success of this project is also somewhat its downfall. By providing a ready-to-go Kafka client, we ensured teams got up and running quickly, but we also abstracted some core concepts of Kafka a little too much, meaning that small unassuming configuration changes could have a big impact.</p><p>One such example led to partition skew (a large portion of messages being directed towards a single partition, meaning we were not processing messages in real time; see the chart below). One drawback of Kafka is you can only have one consumer per partition, so when incidents do occur, you can’t trivially scale your way to faster throughput.</p><p>That also means before your service hits production it is wise to do some back of the napkin math to figure out what throughput might look like, otherwise you will need to add partitions later. We have since amended our library to make events like the below less likely.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image2-14.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><p>The reception for the Messagebus-Client has been largely positive. We spent time as a team to understand what the predominant use cases were, and took the concept one step further to build out what we call the connector framework.</p><h3 id=\"connectors\">Connectors</h3><p>The connector framework is based on Kafka-connectors and allows our engineers to easily spin up a service that can read from a system of record and push it somewhere else (such as Kafka, or even Cloudflare’s own <a href=\"http://blog.cloudflare.com/introducing-quicksilver-configuration-distribution-at-internet-scale/\">Quicksilver</a>). To make this as easy as possible, we use Cookiecutter templating to allow engineers to enter a few parameters into a CLI and in return receive a ready to deploy service.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/unnamed2-3.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><p>We provide the ability to configure data pipelines via environment variables. For simple use cases, we provide the functionality out of the box. However, extending the readers, writers and transformations is as simple as satisfying an interface and “registering” the new entry.</p><p>For example, adding the environment variables:</p><pre><code>READER=kafka\nTRANSFORMATIONS=topic_router:topic1,topic2|pf_edge\nWRITER=quicksilver\n</code></pre>\n<p>will:</p><ul><li>Read messages from Kafka topic “topic1” and “topic2”;</li><li>Transform the message using a transformation function called “pf_edge” which maps the request from a Kafka protobuf to a Quicksilver request;</li><li>Write the result to Quicksilver.</li></ul><p>Connectors come readily baked with basic metrics and alerts, so teams know they can move to production quickly but with confidence.</p><p>Below is a diagram of how one team used our connector framework to read from the Messagebus cluster and write to various other systems. This is orchestrated by a system the Application Service team runs called Communication Preferences Service (CPS). Whenever a user opts in/out of marketing emails or changes their language preferences on cloudflare.com, they are calling CPS which ensures those settings are reflected in all the relevant systems.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/unnamed3-2.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><h3 id=\"strict-schemas\">Strict Schemas</h3><p>Alongside the Messagebus-Client library, we also provide a repo called Messagebus Schema. This is a schema registry for all message types that will be sent over our Messagebus cluster. For message format, we use protobuf and have been very happy with that decision. Previously, our team had used JSON for some of our kafka schemas, but we found it much harder to enforce forward and backwards compatibility, as well as message sizes being substantially larger than the protobuf equivalent. Protobuf provides strict message schemas (including type safety), the forward and backwards compatibility we desired, the ability to generate code in multiple languages as well as the files being very human-readable.</p><p>We encourage heavy commentary before approving a merge. Once merged, we use prototool to do breaking change detection, enforce some stylistic rules and to generate code for various languages (at time of writing it's just Go and Rust, but it is trivial to add more).</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image6-8.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"><figcaption><em>An example Protobuf message in our schema</em></figcaption></figure><p>Furthermore, in Messagebus Schema we store a mapping of proto messages to a team, alongside that team’s chat room in our internal communication tool. This allows us to escalate issues to the correct team easily when necessary.</p><p>One important decision we made for the Messagebus cluster is to only allow one proto message per topic. This is configured in Messagebus Schema and enforced by the Messagebus-Client. This was a good decision to enable easy adoption, but it has led to numerous topics existing. When you consider that for each topic we create, we add numerous partitions and replicate them with a replication factor of at least three for resilience, there is a lot of potential to optimize compute for our lower throughput topics.</p><h3 id=\"observability\">Observability</h3><p>Making it easy for teams to observe Kafka is essential for our decoupled engineering model to be successful. We therefore have automated metrics and alert creation wherever we can to ensure that all the engineering teams have a wealth of information available to them to respond to any issues that arise in a timely manner.</p><p>We use Salt to manage our infrastructure configuration and follow a Gitops style model, where our repo holds the source of truth for the state of our infrastructure. To add a new Kafka topic, our engineers make a pull request into this repo and add a couple of lines of YAML. Upon merge, the topic and an alert for high lag (where lag is defined as the difference in time between the last committed offset being read and the last produced offset being produced) will be created. Other alerts can (and should) be created, but this is left to the discretion of application teams. The reason we automatically generate alerts for high lag is that this simple alert is a great proxy for catching a high amount of issues including:</p><ul><li>Your consumer isn’t running.</li><li>Your consumer cannot keep up with the amount of throughput or there is an anomalous amount of messages being produced to your topic at this time.</li><li>Your consumer is misbehaving and not acknowledging messages.</li></ul><p>For metrics, we use Prometheus and display them with Grafana. For each new topic created, we automatically provide a view into production rate, consumption rate and partition skew by producer/consumer. If an engineering team is called out, within the alert message is a link to this Grafana view.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image7-cropped.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><p>In our Messagebus-Client, we expose some metrics automatically and users get the ability to extend them further. The metrics we expose by default are:</p><p>For producers:</p><ul><li>Messages successfully delivered.</li><li>Message failed to deliver.</li></ul><p>For consumer:</p><ul><li>Messages successfully consumed.</li><li>Message consumption errors.</li></ul><p>Some teams use these for alerting on a significant change in throughput, others use them to alert if no messages are produced/consumed in a given time frame.</p><h3 id=\"a-practical-example\">A Practical Example</h3><p>As well as providing the Messagebus framework, the Application Services team looks for common concerns within Engineering and looks to solve them in a scalable, extensible way which means other engineering teams can utilize the system and not have to build their own (thus meaning we are not building lots of disparate systems that are only slightly different).</p><p>One example is the Alert Notification System (ANS). ANS is the backend service for the “Notifications” tab in the Cloudflare dashboard. You may have noticed over the past 12 months that new alert and policy types have been made available to customers very regularly. This is because we have made it very easy for other teams to do this. The approach is:</p><ul><li>Create a new entry into ANS’s configuration YAML (We use CUE lang to validate the configuration as part of our continuous integration process);</li><li>Import our Messagebus-Client into your code base;</li><li>Emit a message to our alert topic when an event of interest takes place.</li></ul><p>That’s it! The producer team now has a means for customers to configure granular alerting policies for their new alert that includes being able to dispatch them via Slack, Google Chat or a custom webhook, PagerDuty or email (by both API and dashboard). Retrying and dead letter messages are managed for them, and a whole host of metrics are made available, all by making some very small changes.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/unnamed4.png\" alt=\"Using Apache Kafka to process 1 trillion inter-service messages\"></figure><h3 id=\"what-s-next\">What’s Next?</h3><p>Usage of Kafka (and our Messagebus tools) is only going to increase at Cloudflare as we continue to grow, and as a team we are committed to making the tooling around Messagebus easy to use, customizable where necessary and (perhaps most importantly) easy to observe. We regularly take feedback from other engineers to help improve the Messagebus-Client (we are on the fifth version now) and are currently experimenting with abstracting the intricacies of Kafka away completely and allowing teams to use gRPC to stream messages to Kafka. Blog post on the success/failure of this to follow!</p><p>If you're interested in building scalable services and solving interesting technical problems, we are hiring engineers on our team in<em> <a href=\"https://boards.greenhouse.io/cloudflare/jobs/3252504?gh_jid=3252504\">Austin</a>, and <a href=\"https://boards.greenhouse.io/cloudflare/jobs/3252504?gh_jid=3252504\">Remote US</a>.</em></p>",
            "url": "https://blog.cloudflare.com/using-apache-kafka-to-process-1-trillion-messages/",
            "title": "Using Apache Kafka to process 1 trillion inter-service messages",
            "date_modified": "2022-07-19T13:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32134038\">Comments</a>",
            "url": "https://about.sourcegraph.com/blog/ex-googler-guide-dev-tools",
            "title": "An ex-Googler's guide to dev tools",
            "date_modified": "2022-07-18T02:50:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32129574\">Comments</a>",
            "url": "https://basicappleguy.com/basicappleblog/999month",
            "title": "$9.99/Month",
            "date_modified": "2022-07-17T18:28:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32128271\">Comments</a>",
            "url": "https://earthly.dev/blog/programming-language-improvements/",
            "title": "The slow march of progress in programming language tooling",
            "date_modified": "2022-07-17T16:32:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32116109\">Comments</a>",
            "url": "https://deno.com/blog/2022-07-13-outage-post-mortem",
            "title": "Deno’s July 13th incident update",
            "date_modified": "2022-07-16T08:07:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32116070\">Comments</a>",
            "url": "https://medium.com/@codervinod/i-deleted-78-of-my-redis-container-and-it-still-works-df8310a3a007",
            "title": "I deleted 78% of my Redis container and it still works",
            "date_modified": "2022-07-16T07:57:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32104805\">Comments</a>",
            "url": "https://devblogs.microsoft.com/engineering-at-microsoft/microsoft-open-sources-salus-software-bill-of-materials-sbom-generation-tool/",
            "title": "Microsoft open sources Salus software bill of materials (SBOM) generation tool",
            "date_modified": "2022-07-15T05:07:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32096801\">Comments</a>",
            "url": "https://justine.lol/pledge/",
            "title": "Show HN: Porting OpenBSD Pledge() to Linux",
            "date_modified": "2022-07-14T14:52:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32096610\">Comments</a>",
            "url": "https://github.com/Permify/permify",
            "title": "Show HN: Permify – Open-source authorization service based on Google Zanzibar",
            "date_modified": "2022-07-14T14:38:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32047535\">Comments</a>",
            "url": "https://github.com/Qovery/Replibyte",
            "title": "Replibyte – Seed your database with real data",
            "date_modified": "2022-07-10T18:39:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32037356\">Comments</a>",
            "url": "https://jvns.ca/blog/2022/07/09/monitoring-small-web-services/",
            "title": "Monitoring tiny web services",
            "date_modified": "2022-07-09T17:29:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32037207\">Comments</a>",
            "url": "https://changelog.com/podcast/496",
            "title": "Oxide Builds Servers",
            "date_modified": "2022-07-09T17:15:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32024882\">Comments</a>",
            "url": "https://code.visualstudio.com/blogs/2022/07/07/vscode-server",
            "title": "The Visual Studio Code Server",
            "date_modified": "2022-07-08T09:41:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32018066\">Comments</a>",
            "url": "https://fly.io/blog/soc2-the-screenshots-will-continue-until-security-improves/",
            "title": "SOC2: The Screenshots Will Continue Until Security Improves",
            "date_modified": "2022-07-07T19:14:19.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image1-OG-2.png\" alt=\"Announcing support for WASI on Cloudflare Workers\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/07/image1-OG-3.png\" alt=\"Announcing support for WASI on Cloudflare Workers\"><p>Today, we are announcing experimental support for WASI (the WebAssembly System Interface) on Cloudflare Workers and support within wrangler2 to make it a joy to work with. We continue to be incredibly excited about the entire WebAssembly ecosystem and are eager to adopt the standards as they are developed.</p><h3 id=\"a-quick-primer-on-webassembly\">A Quick Primer on WebAssembly</h3><p>So what is WASI anyway? To understand WASI, and why we’re excited about it, it’s worth a quick recap of WebAssembly, and the ecosystem around it.</p><p>WebAssembly promised us a future in which code written in compiled languages could be compiled to a common binary format and run in a secure sandbox, at near native speeds. While WebAssembly was designed with the browser in mind, the model rapidly extended to server-side platforms such as Cloudflare Workers (which <a href=\"http://blog.cloudflare.com/webassembly-on-cloudflare-workers/\">has supported WebAssembly</a> since 2017).</p><p>WebAssembly was originally designed to run <em>alongside </em>Javascript, and requires developers to interface directly with Javascript in order to access the world outside the sandbox. To put it another way, WebAssembly does not provide any standard interface for I/O tasks such as interacting with files, accessing the network, or reading the system clock. This means if you want to respond to an event from the outside world, it's up to the developer to handle that event in JavaScript, and directly call functions exported from the WebAssembly module. Similarly, if you want to perform I/O from within WebAssembly, you need to implement that logic in Javascript and import it into the WebAssembly module.</p><p>Custom toolchains such as Emscripten or libraries such as wasm-bindgen have emerged to make this easier, but they are language specific and add a tremendous amount of complexity and bloat. We've even built our own library, workers-rs, using wasm-bindgen that attempts to make writing applications in Rust feel native within a Worker – but this has proven not only difficult to maintain, but requires developers to write code that is Workers specific, and is not portable outside the Workers ecosystem.</p><p>We need more.</p><h3 id=\"the-webassembly-system-interface-wasi-\">The WebAssembly System Interface (WASI)</h3><p>WASI aims to provide a standard interface that any language compiling to WebAssembly can target. You can read the original post by Lin Clark <a href=\"https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/\">here</a>, which gives an excellent introduction – code cartoons and all. In a nutshell, Lin describes WebAssembly as an <em>assembly language</em> for a 'conceptual machine', whereas WASI is a<em> systems interface </em>for a ‘conceptual operating system.’</p><p>This standardization of the system interface has paved the way for existing toolchains to cross-compile existing codebases to the wasm32-wasi target. A tremendous amount of progress has already been made, specifically within Clang/LLVM via the <a href=\"https://github.com/WebAssembly/wasi-sdk\">wasi-sdk</a> and <a href=\"https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/spec/wasm32_wasi/index.html\">Rust</a> toolchains. These toolchains leverage a version of <a href=\"https://github.com/WebAssembly/wasi-libc\">Libc</a>, which provides POSIX standard API calls, that is built on top of WASI 'system calls.' There are even basic implementations in more fringe toolchains such as <a href=\"https://tinygo.org/docs/guides/webassembly/\">TinyGo</a> and <a href=\"https://swiftwasm.org/\">SwiftWasm</a>.</p><p>Practically speaking, this means that you can now write applications that not only interoperate with any WebAssembly runtime implementing the standard, but also any POSIX compliant system! This means the exact same 'Hello World!' that runs on your local Linux/Mac/Windows WSL machine.</p><h3 id=\"show-me-the-code\">Show me the code</h3><p>WASI sounds great, but does it actually make my life easier? You tell us. Let’s run through an example of how this would work in practice.<br></p><p>First, let’s generate a basic Rust “Hello, world!” application, compile, and run it.</p><pre><code>$ cargo new hello_world\n$ cd ./hello_world\n$ cargo build --release\n   Compiling hello_world v0.1.0 (/Users/benyule/hello_world)\n    Finished release [optimized] target(s) in 0.28s\n$ ./target/release/hello_world\nHello, world!\n</code></pre>\n<p>It doesn’t get much simpler than this. You’ll notice we only define a main() function followed by a println to stdout.</p><pre><code>fn main() {\n    println!(\"Hello, world!\");\n}\n</code></pre>\n<p>Now, let’s take the exact same program and compile against the wasm32-wasi target, and run it in an ‘off the shelf’ wasm runtime such as <a href=\"https://wasmtime.dev/\">Wasmtime</a>.</p><pre><code>$ cargo build --target wasm32-wasi --release\n$ wasmtime target/wasm32-wasi/release/hello_world.wasm\n\nHello, world!\n</code></pre>\n<p>Neat! The same code compiles and runs in multiple POSIX environments.</p><p>Finally, let’s take the binary we just generated for Wasmtime, but instead publish it to Workers using Wrangler2.</p><pre><code>$ npx wrangler@wasm dev target/wasm32-wasi/release/hello_world.wasm\n$ curl http://localhost:8787/\n\nHello, world!\n</code></pre>\n<p>Unsurprisingly, it works! The same code is compatible in multiple POSIX environments and the same binary is compatible across multiple WASM runtimes.</p><h3 id=\"running-your-cli-apps-in-the-cloud\">Running your CLI apps in the cloud</h3><p>The attentive reader may notice that we played a small trick with the HTTP request made via cURL. In this example, we actually stream stdin and stdout to/from the Worker using the HTTP request and response body respectively. This pattern enables some really interesting use cases, specifically, programs designed to run on the command line can be deployed as 'services' to the cloud.</p><p>‘Hexyl’ is an example that works completely out of the box. Here, we ‘cat’ a binary file on our local machine and ‘pipe’ the output to curl, which will then POST that output to our service and stream the result back. Following the steps we used to compile our 'Hello World!', we can compile hexyl.</p><pre><code>$ git clone git@github.com:sharkdp/hexyl.git\n$ cd ./hexyl\n$ cargo build --target wasm32-wasi --release\n</code></pre>\n<p>And without further modification we were able to take a real-world program and create something we can now run or deploy. Again, let's tell wrangler2 to preview hexyl, but this time give it some input.</p><pre><code>$ npx wrangler@wasm dev target/wasm32-wasi/release/hexyl.wasm\n$ echo \"Hello, world\\!\" | curl -X POST --data-binary @- http://localhost:8787\n\n┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n│00000000│ 48 65 6c 6c 6f 20 77 6f ┊ 72 6c 64 21 0a          │Hello wo┊rld!_   │\n└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n\n</code></pre>\n<p>Give it a try yourself by hitting <a href=\"https://hexly.examples.workers.dev\">https://hexyl.examples.workers.dev</a>.</p><pre><code>echo \"Hello world\\!\" | curl https://hexyl.examples.workers.dev/ -X POST --data-binary @- --output -\n</code></pre>\n<p>A more useful example, but requires a bit more work, would be to deploy a utility such as swc (swc.rs), to the cloud and use it as an on demand JavaScript/TypeScript transpilation service. Here, we have a few extra steps to ensure that the compiled output is as small as possible, but it otherwise runs out-of-the-box. Those steps are detailed in <a href=\"https://github.com/zebp/wasi-example-swc\">github.com/zebp/wasi-example-swc</a>, but for now let’s gloss over that and interact with the hosted example.</p><pre><code>$ echo \"const x = (x, y) =&gt; x * y;\" | curl -X POST --data-binary @- https://swc-wasi.examples.workers.dev/ --output -\n\nvar x=function(a,b){return a*b}\n</code></pre>\n<p>Finally, we can also do the same with C/C++, but requires a little more lifting to get our Makefile right. Here we show an example of compiling zstd and uploading it as a streaming compression service.</p><p><a href=\"https://github.com/zebp/wasi-example-zstd\">github.com/zebp/wasi-example-zstd</a></p><pre><code>$ echo \"Hello world\\!\" | curl https://zstd.examples.workers.dev/ -s -X POST --data-binary @- | file -\n</code></pre>\n<h3 id=\"what-if-i-want-to-use-wasi-from-within-a-javascript-worker\">What if I want to use WASI from within a JavaScript Worker?</h3><p>Wrangler can make it really easy to deploy code without having to worry about the Workers ecosystem, but in some cases you may actually want to invoke your WASI based WASM module from Javascript. This can be achieved with the following simple boilerplate. An updated README will be kept at <a href=\"https://github.com/cloudflare/workers-wasi\">github.com/cloudflare/workers-wasi</a>.</p><pre><code>import { WASI } from \"@cloudflare/workers-wasi\";\nimport demoWasm from \"./demo.wasm\";\n\nexport default {\n  async fetch(request, _env, ctx) {\n    // Creates a TransformStream we can use to pipe our stdout to our response body.\n    const stdout = new TransformStream();\n    const wasi = new WASI({\n      args: [],\n      stdin: request.body,\n      stdout: stdout.writable,\n    });\n\n    // Instantiate our WASM with our demo module and our configured WASI import.\n    const instance = new WebAssembly.Instance(demoWasm, {\n      wasi_snapshot_preview1: wasi.wasiImport,\n    });\n\n    // Keep our worker alive until the WASM has finished executing.\n    ctx.waitUntil(wasi.start(instance));\n\n    // Finally, let's reply with the WASM's output.\n    return new Response(stdout.readable);\n  },\n};\n</code></pre>\n<p>Now with our JavaScript boilerplate and wasm, we can easily deploy our worker with Wrangler’s WASM feature.</p><pre><code>$ npx wrangler publish\nTotal Upload: 473.89 KiB / gzip: 163.79 KiB\nUploaded wasi-javascript (2.75 sec)\nPublished wasi-javascript (0.30 sec)\n  wasi-javascript.zeb.workers.dev\n</code></pre>\n<h2 id=\"back-to-the-future\">Back to the future</h2><p>For those of you who have been around for the better part of the past couple of decades, you may notice this looks very similar to RFC3875, better known as CGI (The Common Gateway Interface). While our example here certainly does not conform to the specification, you can imagine how this can be extended to turn the stdin of a basic 'command line' application into a full-blown http handler.</p><p>We are thrilled to learn where developers take this from here. Share what you build with us on <a href=\"https://discord.com/invite/cloudflaredev\">Discord</a> or <a href=\"https://twitter.com/CloudflareDev\">Twitter</a>!</p><p>...<br>\n<em>We protect <a href=\"https://www.cloudflare.com/network-services/\">entire corporate networks</a>, help customers build <a href=\"https://workers.cloudflare.com/\">Internet-scale applications efficiently</a>, accelerate any <a href=\"https://www.cloudflare.com/performance/accelerate-internet-applications/\">website or Internet application</a>, ward off <a href=\"https://www.cloudflare.com/ddos/\">DDoS attacks</a>, keep <a href=\"https://www.cloudflare.com/application-security/\">hackers at bay</a>, and can help you on <a href=\"https://www.cloudflare.com/products/zero-trust/\">your journey to Zero Trust</a>.</em></p>\n<p><em>Visit <a href=\"https://1.1.1.1/\">1.1.1.1</a> from any device to get started with our free app that makes your Internet faster and safer.To learn more about our mission to help build a better Internet, start <a href=\"https://www.cloudflare.com/learning/what-is-cloudflare/\">here</a>. If you’re  looking for a new career direction, check out <a href=\"http://cloudflare.com/careers\">our open positions</a>.</em></p>",
            "url": "https://blog.cloudflare.com/announcing-wasi-on-workers/",
            "title": "Announcing support for WASI on Cloudflare Workers",
            "date_modified": "2022-07-07T16:09:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=32012175\">Comments</a>",
            "url": "https://www.smashingmagazine.com/2022/07/designing-better-pricing-page/",
            "title": "Designing a Better Pricing Page",
            "date_modified": "2022-07-07T10:27:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31993429\">Comments</a>",
            "url": "https://bun.sh/?launch",
            "title": "Bun: Fast JavaScript runtime, transpiler, and NPM client written in Zig",
            "date_modified": "2022-07-05T20:41:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31988820\">Comments</a>",
            "url": "https://github.com/citronneur/pamspy",
            "title": "Show HN: Credentials dumper for Linux using eBPF",
            "date_modified": "2022-07-05T14:44:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31974420\">Comments</a>",
            "url": "https://www.jeremybrown.tech/8-kubernetes-is-a-red-flag-signalling-premature-optimisation/",
            "title": "Kubernetes is a red flag signalling premature optimisation",
            "date_modified": "2022-07-04T07:27:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31973232\">Comments</a>",
            "url": "https://www.karltarvas.com/2020/10/25/macos-app-sandboxing-via-sandbox-exec.html",
            "title": "macOS: App sandboxing via sandbox-exec",
            "date_modified": "2022-07-04T03:32:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31973232\">Comments</a>",
            "url": "https://www.karltarvas.com/2020/10/25/macos-app-sandboxing-via-sandbox-exec.html",
            "title": "macOS: App sandboxing via sandbox-exec (2020)",
            "date_modified": "2022-07-04T03:32:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31965268\">Comments</a>",
            "url": "https://matt-rickard.com/the-end-of-ci/",
            "title": "The End of CI",
            "date_modified": "2022-07-03T05:14:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31958536\">Comments</a>",
            "url": "https://michael.stapelberg.ch/posts/2022-07-02-rsync-how-does-it-work/",
            "title": "rsync, article 3: How does rsync work?",
            "date_modified": "2022-07-02T12:41:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31957976\">Comments</a>",
            "url": "https://github.com/GauravDawra/Beast",
            "title": "Show HN: Beast – The Build System",
            "date_modified": "2022-07-02T11:04:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31954294\">Comments</a>",
            "url": "https://www.theatlantic.com/technology/archive/2022/07/us-sunscreen-ingredients-outdated-technology-better-eu-asia/661433/",
            "title": "You're Not Allowed to Have the Best Sunscreens in the World",
            "date_modified": "2022-07-01T23:19:01.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/BLOG-1004-header.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/BLOG-1004-header-1.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"><p>Here at Cloudflare we're constantly working on improving our service. Our engineers are looking at hundreds of parameters of our traffic, making sure that we get better all the time.</p><p>One of the core numbers we keep a close eye on is HTTP request latency, which is important for many of our products. We regard latency spikes as bugs to be fixed. One example is the 2017 story of <a href=\"http://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/\">\"Why does one NGINX worker take all the load?\"</a>, where we optimized our TCP Accept queues to improve overall latency of TCP sockets waiting for accept().</p><p>Performance tuning is a holistic endeavor, and we monitor and continuously improve a range of other performance metrics as well, including throughput. Sometimes, tradeoffs have to be made. Such a case occurred in 2015, when a latency spike was discovered in our processing of HTTP requests. The solution at the time was to set tcp_rmem to 4 MiB, which minimizes the amount of time the kernel spends on TCP collapse processing. It was this collapse processing that was causing the latency spikes. Later in this post we discuss TCP collapse processing in more detail.</p><p>The tradeoff is that using a low value for tcp_rmem limits TCP throughput over high latency links. The following graph shows the maximum throughput as a function of network latency for a window size of 2 MiB. Note that the 2 MiB corresponds to a tcp_rmem value of 4 MiB due to the tcp_adv_win_scale setting in effect at the time.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image10-5.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>For the Cloudflare products then in existence, this was not a major problem, as connections terminate and content is served from nearby servers due to our BGP anycast routing.</p><p>Since then, we have added new products, such as Magic WAN, WARP, Spectrum, Gateway, and others. These represent new types of use cases and traffic flows.</p><p>For example, imagine you're a typical Magic WAN customer. You have connected all of your worldwide offices together using the Cloudflare global network. While Time to First Byte still matters, Magic WAN office-to-office traffic also needs good throughput. For example, a lot of traffic over these corporate connections will be file sharing using protocols such as SMB. These are <a href=\"https://en.wikipedia.org/wiki/Elephant_flow\">elephant flows</a> over <a href=\"https://datatracker.ietf.org/doc/html/rfc1072\">long fat networks</a>. Throughput is the metric every eyeball watches as they are downloading files.</p><p>We need to continue to provide world-class low latency while simultaneously providing high throughput over high-latency connections.</p><p>Before we begin, let’s introduce the players in our game.</p><p><strong>TCP receive window </strong>is the maximum number of unacknowledged user payload bytes the sender should transmit (bytes-in-flight) at any point in time. The size of the receive window can and does go up and down during the course of a TCP session. It is a mechanism whereby the receiver can tell the sender to stop sending if the sent packets cannot be successfully received because the receive buffers are full. It is this receive window that often limits throughput over high-latency networks.</p><p><strong>net.ipv4.tcp_adv_win_scale</strong> is a (non-intuitive) number used to account for the overhead needed by Linux to process packets. The receive window is specified in terms of user payload bytes. Linux needs additional memory beyond that to track other data associated with packets it is processing.</p><p>The value of the receive window changes during the lifetime of a TCP session, depending on a number of factors. The maximum value that the receive window can be is limited by the amount of free memory available in the receive buffer, according to this table:</p><table>\n<thead>\n<tr>\n<th>tcp_adv_win_scale</th>\n<th>TCP window size</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>4</td>\n<td>15/16 * available memory in receive buffer</td>\n</tr>\n<tr>\n<td>3</td>\n<td>⅞ * available memory in receive buffer</td>\n</tr>\n<tr>\n<td>2</td>\n<td>¾ * available memory in receive buffer</td>\n</tr>\n<tr>\n<td>1</td>\n<td>½ * available memory in receive buffer</td>\n</tr>\n<tr>\n<td>0</td>\n<td>available memory in receive buffer</td>\n</tr>\n<tr>\n<td>-1</td>\n<td>½ * available memory in receive buffer</td>\n</tr>\n<tr>\n<td>-2</td>\n<td>¼ * available memory in receive buffer</td>\n</tr>\n<tr>\n<td>-3</td>\n<td>⅛ * available memory in receive buffer</td>\n</tr>\n</tbody>\n</table>\n<p>We can intuitively (and correctly) understand that the amount of available memory in the receive buffer is the difference between the used memory and the maximum limit. But what is the maximum size a receive buffer can be? The answer is sk_rcvbuf.</p><p><strong>sk_rcvbuf</strong> is a per-socket field that specifies the maximum amount of memory that a receive buffer can allocate. This can be set programmatically with the socket option SO_RCVBUF. This can sometimes be useful to do, for localhost TCP sessions, for example, but in general the use of SO_RCVBUF is not recommended.</p><p>So how is sk_rcvbuf set? The most appropriate value for that depends on the latency of the TCP session and other factors. This makes it difficult for L7 applications to know how to set these values correctly, as they will be different for every TCP session. The solution to this problem is Linux autotuning.</p><h2 id=\"linux-autotuning\">Linux autotuning</h2><p>Linux autotuning is logic in the Linux kernel that adjusts the buffer size limits and the receive window based on actual packet processing. It takes into consideration a number of things including TCP session RTT, L7 read rates, and the amount of available host memory.</p><p>Autotuning can sometimes seem mysterious, but it is actually fairly straightforward.</p><p>The central idea is that Linux can track the rate at which the local application is reading data off of the receive queue. It also knows the session RTT. Because Linux knows these things, it can automatically increase the buffers and receive window until it reaches the point at which the application layer or network bottleneck links are the constraint on throughput (and not host buffer settings). At the same time, autotuning prevents slow local readers from having excessively large receive queues. The way autotuning does that is by limiting the receive window and its corresponding receive buffer to an appropriate size for each socket.</p><p>The values set by autotuning can be seen via the Linux “<code>ss</code>” command from the <code>iproute</code> package (e.g. “<code>ss -tmi</code>”). &nbsp;The relevant output fields from that command are:</p><p><strong>Recv-Q</strong> is the number of user payload bytes not yet read by the local application.</p><p><strong>rcv_ssthresh</strong> is the window clamp, a.k.a. the maximum receive window size. This value is not known to the sender. The sender receives only the current window size, via the TCP header field. A closely-related field in the kernel, tp-&gt;window_clamp, is the maximum window size allowable based on the amount of available memory. rcv_ssthresh is the receiver-side slow-start threshold value.</p><p><strong>skmem_r</strong> is the actual amount of memory that is allocated, which includes not only user payload (Recv-Q) but also additional memory needed by Linux to process the packet (packet metadata). This is known within the kernel as sk_rmem_alloc.</p><p>Note that there are other buffers associated with a socket, so skmem_r does not represent the total memory that a socket might have allocated. Those other buffers are not involved in the issues presented in this post.</p><p><strong>skmem_rb</strong> is the maximum amount of memory that could be allocated by the socket for the receive buffer. This is higher than rcv_ssthresh to account for memory needed for packet processing that is not packet data. Autotuning can increase this value (up to tcp_rmem max) based on how fast the L7 application is able to read data from the socket and the RTT of the session. This is known within the kernel as sk_rcvbuf.</p><p><strong>rcv_space</strong> is the high water mark of the rate of the local application reading from the receive buffer during any RTT. This is used internally within the kernel to adjust sk_rcvbuf.</p><p>Earlier we mentioned a setting called tcp_rmem. <strong>net.ipv4.tcp_rmem</strong> consists of three values, but in this document we are always referring to the third value (except where noted). It is a global setting that specifies the maximum amount of memory that any TCP receive buffer can allocate, i.e. the maximum permissible value that autotuning can use for sk_rcvbuf. This is essentially just a failsafe for autotuning, and under normal circumstances should play only a minor role in TCP memory management.</p><p>It’s worth mentioning that receive buffer memory is not preallocated. Memory is allocated based on actual packets arriving and sitting in the receive queue. It’s also important to realize that filling up a receive queue is not one of the criteria that autotuning uses to increase sk_rcvbuf. Indeed, preventing this type of excessive buffering (<a href=\"https://en.wikipedia.org/wiki/Bufferbloat\">bufferbloat</a>) is one of the benefits of autotuning.</p><h2 id=\"what-s-the-problem\">What’s the problem?</h2><p>The problem is that we must have a large TCP receive window for high <a href=\"https://en.wikipedia.org/wiki/Bandwidth-delay_product\">BDP</a> sessions. This is directly at odds with the latency spike problem mentioned above.</p><p>Something has to give. The laws of physics (speed of light in glass, etc.) dictate that we must use large window sizes. There is no way to get around that. So we are forced to solve the latency spikes differently.</p><h2 id=\"a-brief-recap-of-the-latency-spike-problem\">A brief recap of the latency spike problem</h2><p>Sometimes a TCP session will fill up its receive buffers. When that happens, the Linux kernel will attempt to reduce the amount of memory the receive queue is using by performing what amounts to a “defragmentation” of memory. This is called collapsing the queue. Collapsing the queue takes time, which is what drives up HTTP request latency.</p><p>We do not want to spend time collapsing TCP queues.</p><p>Why do receive queues fill up to the point where they hit the maximum memory limit? The usual situation is when the local application starts out reading data from the receive queue at one rate (triggering autotuning to raise the max receive window), followed by the local application slowing down its reading from the receive queue. This is valid behavior, and we need to handle it correctly.</p><h2 id=\"selecting-sysctl-values\">Selecting sysctl values</h2><p>Before exploring solutions, let’s first decide what we need as the maximum TCP window size.</p><p>As we have seen above in the discussion about BDP, the window size is determined based upon the RTT and desired throughput of the connection.</p><p>Because Linux autotuning will adjust correctly for sessions with lower RTTs and bottleneck links with lower throughput, all we need to be concerned about are the maximums.</p><p>For latency, we have chosen 300 ms as the maximum expected latency, as that is the measured latency between our Zurich and Sydney facilities. It seems reasonable enough as a worst-case latency under normal circumstances.</p><p>For throughput, although we have very fast and modern hardware on the Cloudflare global network, we don’t expect a single TCP session to saturate the hardware. We have arbitrarily chosen 3500 mbps as the highest supported throughput for our highest latency TCP sessions.</p><p>The calculation for those numbers results in a BDP of 131MB, which we round to the more aesthetic value of 128 MiB.</p><p>Recall that allocation of TCP memory includes metadata overhead in addition to packet data. The ratio of actual amount of memory allocated to user payload size varies, depending on NIC driver settings, packet size, and other factors. For full-sized packets on some of our hardware, we have measured average allocations up to 3 times the packet data size. In order to reduce the frequency of TCP collapse on our servers, we set tcp_adv_win_scale to -2. From the table above, we know that the max window size will be ¼ of the max buffer space.</p><p>We end up with the following sysctl values:</p><pre><code>net.ipv4.tcp_rmem = 8192 262144 536870912\nnet.ipv4.tcp_wmem = 4096 16384 536870912\nnet.ipv4.tcp_adv_win_scale = -2\n</code></pre>\n<p>A tcp_rmem of 512MiB and tcp_adv_win_scale of -2 results in a maximum window size that autotuning can set of 128 MiB, our desired value.</p><h2 id=\"disabling-tcp-collapse\">Disabling TCP collapse</h2><p>Patient: Doctor, it hurts when we collapse the TCP receive queue.</p><p>Doctor: Then don’t do that!</p><p>Generally speaking, when a packet arrives at a buffer when the buffer is full, the packet gets dropped. In the case of these receive buffers, Linux tries to “save the packet” when the buffer is full by collapsing the receive queue. Frequently this is successful, but it is not guaranteed to be, and it takes time.</p><p>There are no problems created by immediately just dropping the packet instead of trying to save it. The receive queue is full anyway, so the local receiver application still has data to read. The sender’s congestion control will notice the drop and/or ZeroWindow and will respond appropriately. Everything will continue working as designed.</p><p>At present, there is no setting provided by Linux to disable the TCP collapse. We developed an in-house patch to the kernel to disable the TCP collapse logic.</p><h2 id=\"kernel-patch-attempt-1\">Kernel patch – Attempt #1</h2><p>The kernel patch for our first attempt was straightforward. At the top of tcp_try_rmem_schedule(), if the memory allocation fails, we simply return (after pred_flag = 0 and tcp_sack_reset()), thus completely skipping the tcp_collapse and related logic.</p><p>It didn’t work.</p><p>Although we eliminated the latency spikes while using large buffer limits, we did not observe the throughput we expected.</p><p>One of the realizations we made as we investigated the situation was that standard network benchmarking tools such as iperf3 and similar do not expose the problem we are trying to solve. iperf3 does not fill the receive queue. Linux autotuning does not open the TCP window large enough. Autotuning is working perfectly for our well-behaved benchmarking program.</p><p>We need application-layer software that is slightly less well-behaved, one that exercises the autotuning logic under test. So we wrote one.</p><h2 id=\"a-new-benchmarking-tool\">A new benchmarking tool</h2><p>Anomalies were seen during our “Attempt #1” that negatively impacted throughput. The anomalies were seen only under certain specific conditions, and we realized we needed a better benchmarking tool to detect and measure the performance impact of those anomalies.</p><p>This tool has turned into an invaluable resource during the development of this patch and raised confidence in our solution.</p><p>It consists of two Python programs. The reader opens a TCP session to the daemon, at which point the daemon starts sending user payload as fast as it can, and never stops sending.</p><p>The reader, on the other hand, starts and stops reading in a way to open up the TCP receive window wide open and then repeatedly causes the buffers to fill up completely. More specifically, the reader implemented this logic:</p><ol>\n<li>reads as fast as it can, for five seconds\n<ul>\n<li>this is called fast mode</li>\n<li>opens up the window</li>\n</ul>\n</li>\n<li>calculates 5% of the high watermark of the bytes reader during any previous one second</li>\n<li>for each second of the next 15 seconds:\n<ul>\n<li>this is called slow mode</li>\n<li>reads that 5% number of bytes, then stops reading</li>\n<li>sleeps for the remainder of that particular second</li>\n<li>most of the second consists of no reading at all</li>\n</ul>\n</li>\n<li>steps 1-3 are repeated in a loop three times, so the entire run is 60 seconds</li>\n</ol>\n<p>This has the effect of highlighting any issues in the handling of packets when the buffers repeatedly hit the limit.</p><h2 id=\"revisiting-default-linux-behavior\">Revisiting default Linux behavior</h2><p>Taking a step back, let’s look at the default Linux behavior. The following is kernel v5.15.16.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.12.11.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>The Linux kernel is effective at freeing up space in order to make room for incoming packets when the receive buffer memory limit is hit. As documented previously, the cost for saving these packets (i.e. not dropping them) is latency.</p><p>However, the latency spikes, in <em>milliseconds</em>, for tcp_try_rmem_schedule(), are:</p><p>tcp_rmem 170 MiB, tcp_adv_win_scale +2 (170p2):</p><pre><code>@ms:\n[0]       27093 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n[1]           0 |\n[2, 4)        0 |\n[4, 8)        0 |\n[8, 16)       0 |\n[16, 32)      0 |\n[32, 64)     16 |\n</code></pre>\n<p>tcp_rmem 146 MiB, tcp_adv_win_scale +3 (146p3):</p><pre><code>@ms:\n(..., 16)  25984 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n[16, 20)       0 |\n[20, 24)       0 |\n[24, 28)       0 |\n[28, 32)       0 |\n[32, 36)       0 |\n[36, 40)       0 |\n[40, 44)       1 |\n[44, 48)       6 |\n[48, 52)       6 |\n[52, 56)       3 |\n</code></pre>\n<p>tcp_rmem 137 MiB, tcp_adv_win_scale +4 (137p4):</p><pre><code>@ms:\n(..., 16)  37222 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n[16, 20)       0 |\n[20, 24)       0 |\n[24, 28)       0 |\n[28, 32)       0 |\n[32, 36)       0 |\n[36, 40)       1 |\n[40, 44)       8 |\n[44, 48)       2 |\n</code></pre>\n<p>These are the latency spikes we cannot have on the Cloudflare global network.</p><h2 id=\"kernel-patch-attempt-2\">Kernel patch – Attempt #2</h2><p>So the “something” that was not working in Attempt #1 was that the receive queue memory limit was hit early on as the flow was just ramping up (when the values for sk_rmem_alloc and sk_rcvbuf were small, ~800KB). This occurred at about the two second mark for 137p4 test (about 2.25 seconds for 170p2).</p><p>In hindsight, we should have noticed that tcp_prune_queue() actually raises sk_rcvbuf when it can. So we modified the patch in response to that, added a guard to allow the collapse to execute when sk_rmem_alloc is less than the threshold value.</p><p><code>net.ipv4.tcp_collapse_max_bytes = 6291456</code></p><p>The next section discusses how we arrived at this value for tcp_collapse_max_bytes.</p><p>The patch is available <a href=\"https://github.com/cloudflare/linux/blob/master/patches/0014-add-a-sysctl-to-enable-disable-tcp_collapse-logic.patch\">here</a>.</p><p>The results with the new patch are as follows:</p><p>oscil – 300ms tests</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.35.26.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>oscil – 20ms tests</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.35.38.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>oscil – 0ms tests</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.36.44.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>iperf3 – 300 ms tests</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.38.17.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>iperf3 – 20 ms tests</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.39.07.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>iperf3 – 0ms tests</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/Screenshot-2022-06-29-at-19.39.19.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>All tests are successful.</p><h2 id=\"setting-tcp_collapse_max_bytes\">Setting tcp_collapse_max_bytes</h2><p>In order to determine this setting, we need to understand what the biggest queue we <em>can</em> collapse without incurring unacceptable latency.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image8-12.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image7-13.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>Using 6 MiB should result in a maximum latency of no more than 2 ms.</p><h2 id=\"cloudflare-production-network-results\">Cloudflare production network results</h2><h3 id=\"current-production-settings-old-\">Current production settings (“Old”)</h3><pre><code>net.ipv4.tcp_rmem = 8192 2097152 16777216\nnet.ipv4.tcp_wmem = 4096 16384 33554432\nnet.ipv4.tcp_adv_win_scale = -2\nnet.ipv4.tcp_collapse_max_bytes = 0\nnet.ipv4.tcp_notsent_lowat = 4294967295\n</code></pre>\n<p>tcp_collapse_max_bytes of 0 means that the custom feature is disabled and that the vanilla kernel logic is used for TCP collapse processing.</p><h3 id=\"new-settings-under-test-new-\">New settings under test (“New”)</h3><pre><code>net.ipv4.tcp_rmem = 8192 262144 536870912\nnet.ipv4.tcp_wmem = 4096 16384 536870912\nnet.ipv4.tcp_adv_win_scale = -2\nnet.ipv4.tcp_collapse_max_bytes = 6291456\nnet.ipv4.tcp_notsent_lowat = 131072\n</code></pre>\n<p>The tcp_notsent_lowat setting is discussed in the last section of this post.</p><p>The middle value of tcp_rmem was changed as a result of separate work that found that Linux autotuning was setting receive buffers too high for localhost sessions. This updated setting reduces TCP memory usage for those sessions, but does not change anything about the type of TCP sessions that is the focus of this post.</p><p>For the following benchmarks, we used non-Cloudflare host machines in Iowa, US, and Melbourne, Australia performing data transfers to the Cloudflare data center in Marseille, France. In Marseille, we have some hosts configured with the existing production settings, and others with the system settings described in this post. Software used is perf3 version 3.9, kernel 5.15.32.</p><h3 id=\"throughput-results\">Throughput results</h3><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image3-36.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><table>\n<thead>\n<tr>\n<th></th>\n<th>RTT (ms)</th>\n<th>Throughput with Current Settings (mbps)</th>\n<th>Throughput with New Settings (mbps)</th>\n<th>Increase Factor</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Iowa to Marseille</td>\n<td>121</td>\n<td>276</td>\n<td>6600</td>\n<td>24x</td>\n</tr>\n<tr>\n<td>Melbourne to Marseille</td>\n<td>282</td>\n<td>120</td>\n<td>3800</td>\n<td>32x</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Iowa-Marseille throughput</strong></p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image6-16.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p><strong>Iowa-Marseille receive window and bytes-in-flight</strong></p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image2-51.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p><strong>Melbourne-Marseille throughput</strong></p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image9-10.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p><strong>Melbourne-Marseille receive window and bytes-in-flight</strong></p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image5-21.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>Even with the new settings in place, the Melbourne to Marseille performance is limited by the receive window on the Cloudflare host. This means that further adjustments to these settings yield even higher throughput.</p><h3 id=\"latency-results\">Latency results</h3><p>The Y-axis on these charts are the 99th percentile time for TCP collapse in seconds.</p><p><strong>Cloudflare hosts in Marseille running the current production settings</strong></p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image11-4.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p><strong>Cloudflare hosts in Marseille running the new settings</strong></p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image1-59.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>The takeaway in looking at these graphs is that maximum TCP collapse time for the new settings is no worse than with the current production settings. This is the desired result.</p><h3 id=\"send-buffers\">Send Buffers</h3><p>What we have shown so far is that the receiver side seems to be working well, but what about the sender side?</p><p>As part of this work, we are setting tcp_wmem max to 512 MiB. For oscillating reader flows, this can cause the send buffer to become quite large. This represents bufferbloat and wasted kernel memory, both things that nobody likes or wants.</p><p>Fortunately, there is already a solution: <strong>tcp_notsent_lowat</strong>. This setting limits the size of unsent bytes in the write queue. More details can be found at <a href=\"https://lwn.net/Articles/560082/\">https://lwn.net/Articles/560082</a>.</p><p>The results are significant:</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/06/image4-29.png\" alt=\"Optimizing TCP for high WAN throughput while preserving low latency\"></figure><p>The RTT for these tests was 466ms. Throughput is not negatively affected. Throughput is at full wire speed in all cases (1 Gbps). Memory usage is as reported by /proc/net/sockstat, TCP mem.</p><p>Our web servers already set tcp_notsent_lowat to 131072 for its sockets. All other senders are using 4 GiB, the default value. We are changing the sysctl so that 131072 is in effect for all senders running on the server.</p><h2 id=\"conclusion\">Conclusion</h2><p>The goal of this work is to open the throughput floodgates for high BDP connections while simultaneously ensuring very low HTTP request latency.</p><p>We have accomplished that goal.</p>",
            "url": "https://blog.cloudflare.com/optimizing-tcp-for-high-throughput-and-low-latency/",
            "title": "Optimizing TCP for high WAN throughput while preserving low latency",
            "date_modified": "2022-07-01T13:00:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31943016\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31943016",
            "title": "Ask HN: How do you ensure everyone on the team is heard on Slack?",
            "date_modified": "2022-07-01T06:17:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31930935\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31930935",
            "title": "Ask HN: Where and how do you find your early adoptors?",
            "date_modified": "2022-06-30T11:07:13.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/n5pgy4/nixpacks_beat_buildpacks_by_almost_two\">Comments</a></p>",
            "url": "https://twitter.com/JustJake/status/1542316666666614784",
            "title": "Nixpacks beat Buildpacks by almost two minutes",
            "date_modified": "2022-06-30T05:44:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31924554\">Comments</a>",
            "url": "https://github.blog/2022-06-29-improve-git-monorepo-performance-with-a-file-system-monitor/",
            "title": "Improve Git monorepo performance with a file system monitor",
            "date_modified": "2022-06-29T19:20:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31914087\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31914087",
            "title": "Ask HN: What tools are you a 10/10 on?",
            "date_modified": "2022-06-28T22:48:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31892384\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31892384",
            "title": "Ask HN: What is your Kubernetes nightmare?",
            "date_modified": "2022-06-27T09:39:57.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/5r1xht/picosnitch_monitor_linux_network\">Comments</a></p>",
            "url": "https://github.com/elesiuta/picosnitch",
            "title": "picosnitch: Monitor Linux network traffic per executable using BPF",
            "date_modified": "2022-06-25T19:41:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31872994\">Comments</a>",
            "url": "https://github.com/ronomon/pure",
            "title": "Pure: A static analysis file format checker",
            "date_modified": "2022-06-25T06:15:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31859040\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31859040",
            "title": "Ask HN: How to level up your technical writing?",
            "date_modified": "2022-06-24T08:23:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31857069\">Comments</a>",
            "url": "https://plausible.io/blog/open-source-saas",
            "title": "How We built a $1M ARR open source SaaS",
            "date_modified": "2022-06-24T01:54:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31851755\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/898522/9cf50ee3f96f90c1/",
            "title": "Whatever happened to SHA-256 support in Git?",
            "date_modified": "2022-06-23T16:47:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31843344\">Comments</a>",
            "url": "https://blog.pwego.com/why-users-sometimes-prefer-the-less-straightforward-ux/",
            "title": "Sometimes users prefer the less straightforward UX",
            "date_modified": "2022-06-22T23:41:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31839260\">Comments</a>",
            "url": "https://docs.thenile.dev/blog/infrastructure-saas",
            "title": "Infrastructure SaaS – a control plane first architecture",
            "date_modified": "2022-06-22T17:42:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31837115\">Comments</a>",
            "url": "https://tailscale.com/blog/tailscale-ssh/",
            "title": "Tailscale SSH",
            "date_modified": "2022-06-22T15:14:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31836974\">Comments</a>",
            "url": "https://survey.stackoverflow.co/2022/",
            "title": "Stack Overflow Developer Survey 2022",
            "date_modified": "2022-06-22T15:05:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31836922\">Comments</a>",
            "url": "https://webauthn.guide/",
            "title": "Guide to Web Authentication",
            "date_modified": "2022-06-22T15:02:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31813957\">Comments</a>",
            "url": "https://ifuckinghatejira.com/",
            "title": "I Fucking Hate Jira",
            "date_modified": "2022-06-20T18:40:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31812506\">Comments</a>",
            "url": "https://github.com/libfuse/sshfs",
            "title": "Sshfs Is Orphaned",
            "date_modified": "2022-06-20T16:45:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31810832\">Comments</a>",
            "url": "https://tuple.app/blog/sso-should-be-table-stakes",
            "title": "SSO should be table stakes",
            "date_modified": "2022-06-20T14:34:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31808227\">Comments</a>",
            "url": "https://blog.scottlogic.com/2022/06/20/state-of-wasm-2022.html",
            "title": "The State of WebAssembly 2022",
            "date_modified": "2022-06-20T09:42:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31795160\">Comments</a>",
            "url": "https://matt-rickard.com/dont-use-kubernetes-yet/",
            "title": "Don't use Kubernetes yet",
            "date_modified": "2022-06-19T00:49:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31793030\">Comments</a>",
            "url": "https://github.com/hwayne/awesome-cold-showers",
            "title": "Cold Showers",
            "date_modified": "2022-06-18T19:34:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31782200\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31782200",
            "title": "Ask HN: Best Dev Tool pitches of all time?",
            "date_modified": "2022-06-17T18:18:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31782200\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31782200",
            "title": "Ask HN: Best dev tool pitches of all time?",
            "date_modified": "2022-06-17T18:18:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31770618\">Comments</a>",
            "url": "https://www.cockroachlabs.com/blog/consistency-model/",
            "title": "CockroachDB's Consistency Model",
            "date_modified": "2022-06-16T20:30:14.000Z"
        },
        {
            "content_html": "<p><a href=\"https://streams.spec.whatwg.org/\"><em>Web streams</em></a> are a standard for <em>streams</em> that is now supported on all major web platforms: web browsers, Node.js, and Deno. (Streams are an abstraction for reading and writing data sequentially in small pieces from all kinds of sources – files, data hosted on servers, etc.)</p>\n<p>For example, <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/fetch\">the global function <code>fetch()</code></a> (which downloads online resources) asynchronously returns a Response which has a property <code>.body</code> with a web stream.</p>\n<p>This blog post covers web streams on Node.js, but most of what we learn applies to all web platforms that support them.</p>\n\n<div><a href=\"https://2ality.com/2022/06/web-streams-nodejs.html\">[Read rest of post]</a></div>",
            "url": "https://2ality.com/2022/06/web-streams-nodejs.html",
            "title": "Using web streams on Node.js",
            "date_modified": "2022-06-16T00:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31753345\">Comments</a>",
            "url": "https://neon.tech/blog/hello-world/",
            "title": "Select ’Hello, World’: Serverless Postgres Built for the Cloud",
            "date_modified": "2022-06-15T14:23:52.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--wNpvebk8--/c_fit,fl_progressive,q_80,w_636/463f087a13d2bac3afb65dd158e79ee9.jpg\"><p>In an interview published today, Bethesda’s creative director Todd Howard revealed the studio’s future plans, explaining that after the<em> Elder Scrolls 6</em> comes out, the studio’s next game will be <em>Fallout 5</em>, the next main entry in the company’s post-apocalyptic open-world RPG franchise. However, considering how long it…</p><p><a href=\"https://kotaku.com/fallout-5-v-bethesda-elder-scrolls-6-release-date-starf-1849062025\">Read more...</a></p>",
            "url": "https://kotaku.com/fallout-5-v-bethesda-elder-scrolls-6-release-date-starf-1849062025",
            "title": "Fallout 5 Is Bethesda’s Next Game After Elder Scrolls 6, Will Probably Be Out By 2050",
            "date_modified": "2022-06-14T22:17:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31743100\">Comments</a>",
            "url": "https://blog.heroku.com/april-2022-incident-review",
            "title": "Heroku April 2022 Incident Review",
            "date_modified": "2022-06-14T17:43:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31728693\">Comments</a>",
            "url": "https://www.courier.com/blog/the-developers-guide-to-saas-compliance/",
            "title": "The Developer's Guide to SaaS Compliance",
            "date_modified": "2022-06-13T17:32:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31720110\">Comments</a>",
            "url": "https://fresh.deno.dev/",
            "title": "Fresh – Next-gen web framework",
            "date_modified": "2022-06-13T01:50:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31719532\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/897435/3f00904f520a1956/",
            "title": "Vetting the Cargo",
            "date_modified": "2022-06-13T00:16:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31709317\">Comments</a>",
            "url": "https://seirdy.one/posts/2022/06/10/cli-best-practices/",
            "title": "Best Practices for Inclusive CLIs",
            "date_modified": "2022-06-11T23:01:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31686511\">Comments</a>",
            "url": "https://blog.sigstore.dev/introducing-gitsign-9fd3f1b682aa",
            "title": "Gitsign",
            "date_modified": "2022-06-09T20:36:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31669762\">Comments</a>",
            "url": "https://dx.tips/the-end-of-localhost",
            "title": "The End of Localhost",
            "date_modified": "2022-06-08T16:26:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31658638\">Comments</a>",
            "url": "https://www.saurabhnanda.in/2022/03/24/dhall-a-gateway-drug-to-haskell/",
            "title": "Dhall: A Gateway Drug to Haskell",
            "date_modified": "2022-06-07T18:59:15.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/av7f4o/ux_patterns_for_cli_tools\">Comments</a></p>",
            "url": "http://lucasfcosta.com/2022/06/01/ux-patterns-cli-tools.html",
            "title": "UX patterns for CLI tools",
            "date_modified": "2022-06-06T08:27:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31625086\">Comments</a>",
            "url": "https://dock.orion3.space/readme.htm",
            "title": "Painless Desktop containers for everyday development",
            "date_modified": "2022-06-04T21:07:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31615517\">Comments</a>",
            "url": "https://content.fme.de/en/blog/docker-is-dead-podman-an-alternative-tool",
            "title": "Docker is dead? Podman – an alternative tool?",
            "date_modified": "2022-06-04T00:22:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31576353\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31576353",
            "title": "Tell HN: Cloudflare prevents transfer-out of domains, sets to 'pendingdelete'",
            "date_modified": "2022-05-31T23:53:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31572031\">Comments</a>",
            "url": "https://somafm.com/",
            "title": "SomaFM",
            "date_modified": "2022-05-31T17:08:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31568138\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31568138",
            "title": "Ask HN: Docker vs simple DLLs?",
            "date_modified": "2022-05-31T11:02:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31560547\">Comments</a>",
            "url": "https://github.com/dragonflydb/dragonfly",
            "title": "Dragonflydb – A modern replacement for Redis and Memcached",
            "date_modified": "2022-05-30T16:18:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31559270\">Comments</a>",
            "url": "https://leerob.io/blog/heroku",
            "title": "The Story of Heroku",
            "date_modified": "2022-05-30T14:22:15.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZWqbblYY--/c_fit,fl_progressive,q_80,w_636/ce9518065c41fc05406878890d427289.jpg\"><p>I have found my happy place! <em>Escape Simulator</em> is such a lovely thing, a first-person simulacrum of escape rooms, built in 3D, with realistic physics. It is, as its title suggests, a simulation of attending a real-world escape room, in a way that almost all room-escape video games are not. Apart from when it’s in space.<br></p><p><a href=\"https://kotaku.com/escape-simulator-room-pine-studio-review-indie-pc-co-op-1848993246\">Read more...</a></p>",
            "url": "https://kotaku.com/escape-simulator-room-pine-studio-review-indie-pc-co-op-1848993246",
            "title": "This Is As Close To A Real-World Escape Room As You'll Find",
            "date_modified": "2022-05-30T13:30:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31549918\">Comments</a>",
            "url": "https://github.com/mgramin/awesome-db-tools",
            "title": "Everything that makes working with databases easier",
            "date_modified": "2022-05-29T15:31:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31543054\">Comments</a>",
            "url": "https://systemfontstack.com",
            "title": "System Font Stack",
            "date_modified": "2022-05-28T19:36:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31541070\">Comments</a>",
            "url": "https://ignaciochiazzo.medium.com/paginating-requests-in-apis-d4883d4c1c4c",
            "title": "Are all popular APIs moving to Cursor based pagination?",
            "date_modified": "2022-05-28T15:31:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31540521\">Comments</a>",
            "url": "https://github.com/richardartoul/tsdb-layer",
            "title": "FoundationDB Time Series Layer: Millions of writes/s in 2k lines of Go (2019)",
            "date_modified": "2022-05-28T14:19:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31536827\">Comments</a>",
            "url": "https://neon.tech",
            "title": "Neon – Serverless Postgres",
            "date_modified": "2022-05-28T01:37:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31530176\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31530176",
            "title": "Ask HN: How can you buy high quality healthcare?",
            "date_modified": "2022-05-27T14:38:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31520573\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/896267/6bac8e29d614ec66/",
            "title": "A Linux 5.10 patch has caused user-space regressions",
            "date_modified": "2022-05-26T16:48:47.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/wyqcev/learnings_from_5_years_tech_startup_code\">Comments</a></p>",
            "url": "https://kenkantzer.com/learnings-from-5-years-of-tech-startup-code-audits/",
            "title": "Learnings from 5 years of tech startup code audits",
            "date_modified": "2022-05-26T07:37:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31502186\">Comments</a>",
            "url": "https://www.smashingmagazine.com/2022/05/magical-svg-techniques/",
            "title": "Magical SVG Techniques",
            "date_modified": "2022-05-25T09:20:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31496744\">Comments</a>",
            "url": "https://fly.io/blog/fly-machines/",
            "title": "Fly Machines: An API for Fast-Booting VMs",
            "date_modified": "2022-05-24T19:48:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31494908\">Comments</a>",
            "url": "https://www.linen.dev/",
            "title": "Show HN: Linen – Make your Discord community Google-searchable",
            "date_modified": "2022-05-24T17:19:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31467182\">Comments</a>",
            "url": "https://flightcontrol.notion.site/Backend-Typescript-Engineer-at-Small-DevTools-Startup-12e52471539a4ab9878514e04c415da6",
            "title": "Flightcontrol (YC W22) Is hiring a back end TypeScript engineer [Own-cloud PaaS]",
            "date_modified": "2022-05-22T12:00:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31466885\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31466885",
            "title": "Ask HN: What developer tools would you like to see?",
            "date_modified": "2022-05-22T11:06:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31448148\">Comments</a>",
            "url": "https://github.com/sharkdp/fd",
            "title": "Fd: A simple, fast and user-friendly alternative to 'find'",
            "date_modified": "2022-05-20T14:44:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31431825\">Comments</a>",
            "url": "https://www.helloinbox.email",
            "title": "Show HN: HelloInbox – Ultimate email deliverability checklist and toolkit",
            "date_modified": "2022-05-19T07:40:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31425115\">Comments</a>",
            "url": "https://www.craigkerstiens.com/2022/05/18/unfinished-business-with-postgres/",
            "title": "Unfinished Business with Postgres",
            "date_modified": "2022-05-18T17:04:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31424450\">Comments</a>",
            "url": "https://www.getlago.com/blog/why-billing-systems-are-a-nightmare-for-engineers",
            "title": "Why billing systems are a nightmare for engineers",
            "date_modified": "2022-05-18T16:06:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31416843\">Comments</a>",
            "url": "https://www.datadoghq.com/blog/engineering/introducing-husky/",
            "title": "Husky, Datadog's Third-Generation Event Store",
            "date_modified": "2022-05-17T22:04:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31413483\">Comments</a>",
            "url": "https://blog.hartleybrody.com/thoughts-on-flyio/",
            "title": "My thoughts about Fly.io (so far) and other newish technology I'm getting into",
            "date_modified": "2022-05-17T16:59:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31402935\">Comments</a>",
            "url": "https://www.axiom.co/vercel",
            "title": "Request logging and web vitals for Vercel apps",
            "date_modified": "2022-05-16T20:45:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31401470\">Comments</a>",
            "url": "https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/",
            "title": "Supercharging GitHub Actions with Job Summaries",
            "date_modified": "2022-05-16T18:28:06.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/myb9qh/how_we_deploy_production_over_100_times\">Comments</a></p>",
            "url": "https://monzo.com/blog/2022/05/16/how-we-deploy-to-production-over-100-times-a-day/",
            "title": "How we deploy to production over 100 times a day",
            "date_modified": "2022-05-16T09:21:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31391272\">Comments</a>",
            "url": "https://brandur.org/nanoglyphs/033-heroku",
            "title": "Heroku: Core Impact",
            "date_modified": "2022-05-15T21:22:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31390506\">Comments</a>",
            "url": "https://christine.website/blog/fly.io-heroku-replacement",
            "title": "Fly.io: The reclaimer of Heroku's magic",
            "date_modified": "2022-05-15T19:56:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31372675\">Comments</a>",
            "url": "https://matt-rickard.com/why-did-heroku-fail/",
            "title": "Why Did Heroku Fail?",
            "date_modified": "2022-05-13T21:22:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31357221\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31357221",
            "title": "Show HN: I built a service to help companies reduce AWS spend by 50%",
            "date_modified": "2022-05-12T17:29:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31356730\">Comments</a>",
            "url": "https://twitter.com/benskuhn/status/1524791658381910023",
            "title": "Apple Maps location scan spikes WiFi latency every 60 seconds",
            "date_modified": "2022-05-12T16:56:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31351993\">Comments</a>",
            "url": "https://www.vladionescu.me/posts/scaling-containers-on-aws-in-2022/",
            "title": "Benchmarking Container Scaling on AWS",
            "date_modified": "2022-05-12T10:43:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31331438\">Comments</a>",
            "url": "https://m-m-pr.com/eurovision-diaries/",
            "title": "Eurovision Diaries",
            "date_modified": "2022-05-10T19:24:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31324641\">Comments</a>",
            "url": "https://arnon.dk/why-you-should-separate-your-billing-from-entitlement/",
            "title": "Separate Your Billing from Entitlements",
            "date_modified": "2022-05-10T08:00:21.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/yd4lmq/introducing_crane_composable_cacheable\">Comments</a></p>",
            "url": "https://ipetkov.dev/blog/introducing-crane/",
            "title": "Introducing Crane: Composable and Cacheable Builds with Cargo and Nix",
            "date_modified": "2022-05-06T21:40:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31287850\">Comments</a>",
            "url": "https://www.madrau.com/support/support/faq_files/ns_Why_isnt_there_any_HiDPI_resolu.html",
            "title": "Apple Intentionally Disabled HiDPI on M1 Macs to Push 4K Monitors Sales",
            "date_modified": "2022-05-06T17:39:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31271842\">Comments</a>",
            "url": "https://startuptrail.engine.is/",
            "title": "Startup Trail",
            "date_modified": "2022-05-05T11:06:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31269515\">Comments</a>",
            "url": "https://kiranrao.ca/2022/05/04/zero-downtime-migrations.html",
            "title": "Zero downtime migrations",
            "date_modified": "2022-05-05T04:33:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31261671\">Comments</a>",
            "url": "https://www.usenix.org/conference/nsdi20/presentation/agache",
            "title": "Firecracker: Lightweight Virtualization for Serverless Applications (2020)",
            "date_modified": "2022-05-04T15:16:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31247296\">Comments</a>",
            "url": "https://wildbit.com/blog/postmark-has-been-acquired-by-activecampaign",
            "title": "Postmark acquired by marketing firm ActiveCampaign",
            "date_modified": "2022-05-03T12:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31247296\">Comments</a>",
            "url": "https://wildbit.com/blog/postmark-has-been-acquired-by-activecampaign",
            "title": "Postmark has been acquired by ActiveCampaign",
            "date_modified": "2022-05-03T12:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31244626\">Comments</a>",
            "url": "https://plutoapp.xyz/blog/post/designing-a-command-palette/",
            "title": "Designing a Command Palette",
            "date_modified": "2022-05-03T04:29:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31236855\">Comments</a>",
            "url": "https://mail.openjdk.java.net/pipermail/jdk-dev/2022-April/006530.html",
            "title": "Project Loom preview ships in JDK 19",
            "date_modified": "2022-05-02T15:58:35.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/3xtx8w/fixing_mongodb_replication_protocol_bug\">Comments</a></p>",
            "url": "http://tla.msr-inria.inria.fr/kuppe/2019conf/06%20-%20William%20Schultz%20-%20Strangeloop%20TLA+%20Conference%202019%20Talk.pdf",
            "title": "Fixing a MongoDB Replication Protocol Bug with TLA+",
            "date_modified": "2022-05-02T15:33:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31236157\">Comments</a>",
            "url": "https://www.brendangregg.com/blog/2022-05-02/brendan-at-intel.html",
            "title": "Brendan at Intel.com",
            "date_modified": "2022-05-02T15:12:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31230903\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31230903",
            "title": "Ask HN: What are your favorite examples of elegant software?",
            "date_modified": "2022-05-02T02:47:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31230457\">Comments</a>",
            "url": "https://fasterthanli.me/articles/i-won-free-load-testing",
            "title": "I won free load testing",
            "date_modified": "2022-05-02T01:27:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31217977\">Comments</a>",
            "url": "https://gitbom.dev/resources/whitepaper/",
            "title": "GitBOM: Enabling universal artifact traceability in software supply chains",
            "date_modified": "2022-04-30T18:17:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31201528\">Comments</a>",
            "url": "https://letscooktime.com",
            "title": "Show HN: Recipes, Not Mommy Blogs",
            "date_modified": "2022-04-29T04:41:50.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/szzej7/wharf_protocol_quickly_transfer\">Comments</a></p>",
            "url": "https://github.com/itchio/wharf",
            "title": "wharf: A protocol to quickly transfer software builds (reference Go implementation)",
            "date_modified": "2022-04-29T00:03:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31183573\">Comments</a>",
            "url": "https://blog.porter.run/why-companies-move-off-heroku/",
            "title": "Why companies move off Heroku (besides the cost)",
            "date_modified": "2022-04-27T18:06:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31178577\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31178577",
            "title": "Ask HN: Do you find it challenging to talk to your users?",
            "date_modified": "2022-04-27T11:22:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31172645\">Comments</a>",
            "url": "https://webkit.org/blog/12578/non-interactive-elements-with-the-inert-attribute/",
            "title": "Non-interactive Elements with the inert attribute",
            "date_modified": "2022-04-26T20:19:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31167935\">Comments</a>",
            "url": "https://go.dev/doc/effective_go",
            "title": "Effective Go",
            "date_modified": "2022-04-26T14:31:48.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/0qifor/nix_flakes_how_convert_them\">Comments</a></p>",
            "url": "https://garnix.io/blog/converting-to-flakes",
            "title": "Nix flakes, and how to convert to them",
            "date_modified": "2022-04-25T18:34:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31156799\">Comments</a>",
            "url": "https://www.hyperui.dev/",
            "title": "Free Open Source Tailwind CSS Components",
            "date_modified": "2022-04-25T16:04:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31152511\">Comments</a>",
            "url": "https://m3o.com",
            "title": "Show HN: M3O – Universal Public API Interface",
            "date_modified": "2022-04-25T10:09:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31152490\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31152490",
            "title": "Ask HN: Have you used SQLite as a primary database?",
            "date_modified": "2022-04-25T10:06:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31149801\">Comments</a>",
            "url": "https://christine.website/blog/gonads-2022-04-24",
            "title": "Crimes with Go Generics",
            "date_modified": "2022-04-25T01:07:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31143327\">Comments</a>",
            "url": "https://www.textualize.io/",
            "title": "TUI in webapp design language(CSS) and pattern(check the demo, it’s next level)",
            "date_modified": "2022-04-24T12:15:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31141377\">Comments</a>",
            "url": "https://christine.website/talks/nixos-pain-2021-11-10",
            "title": "How Nix and NixOS Get So Close to Perfect",
            "date_modified": "2022-04-24T04:49:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31116984\">Comments</a>",
            "url": "https://ivov.dev/notes/typescript-and-set-theory",
            "title": "TypeScript and Set Theory",
            "date_modified": "2022-04-22T00:51:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31079144\">Comments</a>",
            "url": "https://nixery.dev/",
            "title": "Nixery – Docker images on the fly with Nix",
            "date_modified": "2022-04-19T02:13:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31072465\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=31072465",
            "title": "Ask HN: I burned out but I don't want to let my team down",
            "date_modified": "2022-04-18T15:59:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31062666\">Comments</a>",
            "url": "https://danielmangum.com/posts/the-missing-kubernetes-type-system/",
            "title": "The Missing Kubernetes Type System",
            "date_modified": "2022-04-17T17:45:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31048646\">Comments</a>",
            "url": "https://status.heroku.com/incidents/2413",
            "title": "Heroku Security Incident",
            "date_modified": "2022-04-16T02:20:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31038614\">Comments</a>",
            "url": "https://bytebase.com/blog/database-migration-sqlite-to-postgresql",
            "title": "Migrating from SQLite to PostgreSQL",
            "date_modified": "2022-04-15T10:42:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31037724\">Comments</a>",
            "url": "https://read.lukeburgis.com/p/cultural-anorexia",
            "title": "Cultural Anorexia: The Pursuit of Thicker Desires in a Thinning World",
            "date_modified": "2022-04-15T08:15:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31019778\">Comments</a>",
            "url": "https://lexical.dev/",
            "title": "Facebook open sources Lexical, an extensible text editor library",
            "date_modified": "2022-04-13T20:22:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31013829\">Comments</a>",
            "url": "https://www.newyorker.com/culture/rabbit-holes/the-most-popular-chess-streamer-on-twitch",
            "title": "The most popular chess streamer on Twitch",
            "date_modified": "2022-04-13T12:27:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31012685\">Comments</a>",
            "url": "https://grapee.jp/en/199026",
            "title": "Tokyo’s Manuscript Writing Cafe won't let writers leave until they are finished",
            "date_modified": "2022-04-13T09:32:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=31009313\">Comments</a>",
            "url": "https://jvns.ca/blog/2022/04/12/a-list-of-new-ish--command-line-tools/",
            "title": "A list of new(ish) command line tools – Julia Evans",
            "date_modified": "2022-04-12T23:08:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30988355\">Comments</a>",
            "url": "https://www.gov.uk/guidance/protect-domains-that-dont-send-email",
            "title": "Protect domains that do not send email",
            "date_modified": "2022-04-11T13:27:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30987770\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30987770",
            "title": "Ask HN: Editing remote code locally: Best practices?",
            "date_modified": "2022-04-11T12:28:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30976971\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30976971",
            "title": "Ask HN: Have you had any real benefits from apps like Headspace, Fabulous, etc.?",
            "date_modified": "2022-04-10T14:01:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30957156\">Comments</a>",
            "url": "https://mutagen.io/",
            "title": "Mutagen – Cloud-based development using your local tools",
            "date_modified": "2022-04-08T14:24:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30935709\">Comments</a>",
            "url": "https://missiveapp.com/blog/the-things-we-did-not-do",
            "title": "Things we did not do while reaching $2M ARR",
            "date_modified": "2022-04-06T18:27:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30899362\">Comments</a>",
            "url": "https://squeaky.ai/blog/development/why-we-dont-use-a-staging-environment",
            "title": "We don’t use a staging environment",
            "date_modified": "2022-04-03T18:28:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30883015\">Comments</a>",
            "url": "https://tailscale.com/blog/database-for-2022/",
            "title": "A Database for 2022",
            "date_modified": "2022-04-01T20:33:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30883015\">Comments</a>",
            "url": "https://tailscale.com/blog/database-for-2022/",
            "title": "A database for 2022",
            "date_modified": "2022-04-01T20:33:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30881441\">Comments</a>",
            "url": "https://github.com/dlvhdr/gh-dash",
            "title": "Show HN: gh-dash – GitHub CLI dashboard for pull requests and issues",
            "date_modified": "2022-04-01T18:12:14.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/qmropg/why_we_chose_nanoids_for_planetscale_s_api\">Comments</a></p>",
            "url": "https://planetscale.com/blog/why-we-chose-nanoids-for-planetscales-api",
            "title": "Why we chose NanoIDs for PlanetScale's API",
            "date_modified": "2022-04-01T18:00:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30876527\">Comments</a>",
            "url": "https://monzo.com/blog/2022/03/31/how-we-secure-monzos-banking-platform/",
            "title": "How we secure Monzo's banking platform",
            "date_modified": "2022-04-01T09:36:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30870148\">Comments</a>",
            "url": "https://amyunger.com/blog/2020/09/10/staff-engineer-at-heroku.html",
            "title": "How I operated as a Staff engineer at Heroku",
            "date_modified": "2022-03-31T17:47:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30857012\">Comments</a>",
            "url": "https://dagger.io/blog/public-launch-announcement",
            "title": "Dagger: a new way to build CI/CD pipelines",
            "date_modified": "2022-03-30T16:00:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30844414\">Comments</a>",
            "url": "https://reactjs.org/blog/2022/03/29/react-v18.html",
            "title": "React v18.0",
            "date_modified": "2022-03-29T16:09:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30827038\">Comments</a>",
            "url": "https://journal.plain.com/posts/2022-02-08-a-magical-aws-serverless-developer-experience/",
            "title": "A magical AWS serverless developer experience",
            "date_modified": "2022-03-28T04:05:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30812988\">Comments</a>",
            "url": "https://rubrikinc.github.io/wachy/",
            "title": "Show HN: Wachy – A UI for eBPF-based performance debugging",
            "date_modified": "2022-03-26T15:59:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30808945\">Comments</a>",
            "url": "https://blog.vamc19.dev/posts/dockerfile-copy-chmod/",
            "title": "`COPY –chmod` reduced the size of my container image by 35%",
            "date_modified": "2022-03-26T03:11:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30794764\">Comments</a>",
            "url": "https://styles.master.co/",
            "title": "Master Styles – The First Virtual CSS Engine",
            "date_modified": "2022-03-24T20:10:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30794332\">Comments</a>",
            "url": "https://github.com/porsager/postgres",
            "title": "Show HN: Postgres.js – Fastest Full-Featured PostgreSQL Client for Node and Deno",
            "date_modified": "2022-03-24T19:30:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30788768\">Comments</a>",
            "url": "https://planetscale.com/blog/its-fine-rewind-revert-a-migration-without-losing-data",
            "title": "It’s fine, Rewind: Revert a migration without losing data",
            "date_modified": "2022-03-24T12:16:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30778924\">Comments</a>",
            "url": "https://tea.xyz/",
            "title": "Tea – the toolkit that builds the Internet",
            "date_modified": "2022-03-23T14:42:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30778321\">Comments</a>",
            "url": "https://github.com/egoist/dum",
            "title": "Dum: An NPM scripts runner written in Rust",
            "date_modified": "2022-03-23T13:39:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30764592\">Comments</a>",
            "url": "https://show-picker.glitch.me/demo.html",
            "title": "HTMLInputElement ShowPicker()",
            "date_modified": "2022-03-22T10:46:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30747332\">Comments</a>",
            "url": "https://den.dev/blog/windows-priority-shuffle/",
            "title": "Windows needs a change in priorities",
            "date_modified": "2022-03-20T22:24:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30721299\">Comments</a>",
            "url": "https://www.seekret.io/blog/ebpf-nuances-on-minikube/",
            "title": "eBPF Nuances on Minikube",
            "date_modified": "2022-03-18T12:28:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30703202\">Comments</a>",
            "url": "https://symflower.com/en/company/blog/2022/complete-guide-on-shrinking-container-images/",
            "title": "How to make Docker images even smaller",
            "date_modified": "2022-03-16T19:51:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30701963\">Comments</a>",
            "url": "https://www.docker.com/blog/speed-boost-achievement-unlocked-on-docker-desktop-4-6-for-mac/",
            "title": "Speed boost achievement unlocked on Docker Desktop 4.6 for Mac",
            "date_modified": "2022-03-16T18:00:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30701451\">Comments</a>",
            "url": "https://tailscale.com/blog/free-plan/",
            "title": "How our free plan stays free",
            "date_modified": "2022-03-16T17:14:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30681182\">Comments</a>",
            "url": "https://ianthehenry.com/posts/how-to-learn-nix/introduction/",
            "title": "How to Learn Nix",
            "date_modified": "2022-03-15T02:45:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30678100\">Comments</a>",
            "url": "https://solito.dev/",
            "title": "Solito – React Native and Next.js unified",
            "date_modified": "2022-03-14T20:51:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30668137\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30668137",
            "title": "Show HN: HN Avatars in 357 bytes",
            "date_modified": "2022-03-14T03:08:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30660534\">Comments</a>",
            "url": "https://www.eficode.com/blog/the-future-of-kubernetes-and-why-developers-should-look-beyond-kubernetes-in-2022",
            "title": "The Future of Kubernetes",
            "date_modified": "2022-03-13T12:09:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30652155\">Comments</a>",
            "url": "https://jessicamayzwaan.medium.com/the-legal-implications-of-remote-working-cross-border-64904d274c0d",
            "title": "The Legal Implications of Remote Working Cross-Border",
            "date_modified": "2022-03-12T15:50:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30630170\">Comments</a>",
            "url": "https://contains.dev/blog/mastering-docker-cache",
            "title": "Mastering the Docker Cache",
            "date_modified": "2022-03-10T18:04:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30624579\">Comments</a>",
            "url": "https://frankzliu.com/blog/my-experience-living-and-working-in-china-part-i",
            "title": "My Experience Working and Living in China",
            "date_modified": "2022-03-10T08:31:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30612003\">Comments</a>",
            "url": "https://atlantic.money",
            "title": "Show HN: World’s first £3 flat fee (0% FX markup) money transfer service",
            "date_modified": "2022-03-09T07:58:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30557557\">Comments</a>",
            "url": "https://berkeleygraphics.com/typefaces/berkeley-mono",
            "title": "Show HN: Berkeley Mono Typeface",
            "date_modified": "2022-03-04T17:21:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30514209\">Comments</a>",
            "url": "https://sifted.eu/articles/startup-founders-salary/",
            "title": "How much do founders pay themselves? A European data set",
            "date_modified": "2022-03-01T14:01:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30497703\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30497703",
            "title": "Ask HN: Books you should read when you transform from SWE into SWE-Management",
            "date_modified": "2022-02-28T08:37:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30462695\">Comments</a>",
            "url": "https://github.com/orioledb/orioledb",
            "title": "OrioleDB – solving some PostgreSQL wicked problems",
            "date_modified": "2022-02-25T01:31:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30452662\">Comments</a>",
            "url": "https://bytebase.com/blog/how-bytebase-uses-render",
            "title": "How Bytebase uses Render – to improve dev workflow",
            "date_modified": "2022-02-24T09:24:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30442843\">Comments</a>",
            "url": "https://buf.build/blog/buf-cli-v1",
            "title": "The Buf CLI, an all-in-one tool for Protobuf development, has reached v1.0",
            "date_modified": "2022-02-23T16:30:46.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--S3-fEsSp--/c_fit,fl_progressive,q_80,w_636/669bbccfc0c4f4c77b67869ac4608cbd.jpg\"><p>Surviving in <a href=\"https://kotaku.com/horizon-forbidden-west-the-kotaku-review-1848524470\"><em>Horizon Forbidden West</em></a>, an open-world game about what happens when Elon Musk funds one too many projects, is only partially contingent on skill. But if you want to thrive in the robo-dino apocalypse, you’ll need to equip yourself with the best gear around.<br></p><p><a href=\"https://kotaku.com/horizon-forbidden-west-best-gear-elite-ropecaster-artif-1848578290\">Read more...</a></p>",
            "url": "https://kotaku.com/horizon-forbidden-west-best-gear-elite-ropecaster-artif-1848578290",
            "title": "The Best Gear In Horizon Forbidden West (And Where To Find It)",
            "date_modified": "2022-02-22T21:20:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30426163\">Comments</a>",
            "url": "https://onlineornot.com/what-learned-running-saas-for-year",
            "title": "What I learned running a SaaS for a year",
            "date_modified": "2022-02-22T10:30:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30392849\">Comments</a>",
            "url": "https://www.kopa.co/blog/posts/how-we-use-notion-as-a-startup",
            "title": "How we use Notion as a startup",
            "date_modified": "2022-02-18T23:54:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30383063\">Comments</a>",
            "url": "https://www.axios.com/qr-code-safety-coinbase-4b7f97d0-940c-45f4-9366-bf5d7f2f3c8f.html",
            "title": "FBI sounds alarm as QR code usage soars",
            "date_modified": "2022-02-18T07:32:42.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/02/tubular-1.png\" alt=\"Production ready eBPF, or how we fixed the BSD socket API\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/02/tubular.png\" alt=\"Production ready eBPF, or how we fixed the BSD socket API\"><p>As we develop new products, we often push our operating system - Linux - beyond what is commonly possible. A common theme has been relying on <a href=\"https://ebpf.io/what-is-ebpf/\">eBPF</a> to build technology that would otherwise have required modifying the kernel. For example, we’ve built <a href=\"http://blog.cloudflare.com/l4drop-xdp-ebpf-based-ddos-mitigations/\">DDoS mitigation</a> and a <a href=\"http://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer/\">load balancer</a> and use it to <a href=\"http://blog.cloudflare.com/introducing-ebpf_exporter/\">monitor our fleet of servers</a>.</p><p>This software usually consists of a small-ish eBPF program written in C, executed in the context of the kernel, and a larger user space component that loads the eBPF into the kernel and manages its lifecycle. We’ve found that the ratio of eBPF code to userspace code differs by an order of magnitude or more. We want to shed some light on the issues that a developer has to tackle when dealing with eBPF and present our solutions for building rock-solid production ready applications which contain eBPF.</p><p>For this purpose we are open sourcing the production tooling we’ve built for the <a href=\"https://www.kernel.org/doc/html/latest/bpf/prog_sk_lookup.html\">sk_lookup hook</a> we contributed to the Linux kernel, called <strong>tubular</strong>. It exists because <a href=\"http://blog.cloudflare.com/its-crowded-in-here/\">we’ve outgrown the BSD sockets API</a>. To deliver some products we need features that are just not possible using the standard API.</p><ul><li>Our services are available on millions of IPs.</li><li>Multiple services using the same port on different addresses have to coexist, e.g. <a href=\"https://1.1.1.1/\">1.1.1.1</a> resolver and our authoritative DNS.</li><li>Our Spectrum product <a href=\"http://blog.cloudflare.com/how-we-built-spectrum/\">needs to listen on all 2^16 ports</a>.</li></ul><p>The source code for tubular is at <a href=\"https://github.com/cloudflare/tubular\">github.com/cloudflare/tubular</a>, and it allows you to do all the things mentioned above. Maybe the most interesting feature is that you can change the addresses of a service on the fly:</p><div><iframe src=\"https://iframe.videodelivery.net/0c8aa9ba9144bbbb313fb9c865376e4e?preload=true&amp;poster=https%3A%2F%2Fvideodelivery.net%2F0c8aa9ba9144bbbb313fb9c865376e4e%2Fthumbnails%2Fthumbnail.jpg%3Ftime%3D48s%26height%3D600&amp;primaryColor=%23f7b018\"></iframe></div>\n<p></p><h2 id=\"how-tubular-works\">How tubular works</h2><p><code>tubular</code> sits at a critical point in the Cloudflare stack, since it has to inspect every connection terminated by a server and decide which application should receive it.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/02/unnamed.png\" alt=\"Production ready eBPF, or how we fixed the BSD socket API\"></figure><p>Failure to do so will drop or misdirect connections hundreds of times per second. So it has to be incredibly robust during day to day operations. We had the following goals for tubular:</p><ul><li><strong>Releases must be unattended and happen online. </strong>tubular runs on thousands of machines, so we can’t babysit the process or take servers out of production.</li><li><strong>Releases must fail safely.</strong> A failure in the process must leave the previous version of tubular running, otherwise we may drop connections.</li><li><strong>Reduce the impact of (userspace) crashes.</strong> When the inevitable bug comes along we want to minimise the blast radius.</li></ul><p>In the past we had built a proof-of-concept control plane for sk_lookup called <a href=\"https://github.com/majek/inet-tool\">inet-tool</a>, which proved that we could get away without a persistent service managing the eBPF. Similarly, tubular has <code>tubectl</code>: short-lived invocations make the necessary changes and persisting state is handled by the kernel in the form of <a href=\"https://www.kernel.org/doc/html/latest/bpf/maps.html\">eBPF maps</a>. Following this design gave us crash resiliency by default, but left us with the task of mapping the user interface we wanted to the tools available in the eBPF ecosystem.</p><h2 id=\"the-tubular-user-interface\">The tubular user interface</h2><p>tubular consists of a BPF program that attaches to the sk_lookup hook in the kernel and userspace Go code which manages the BPF program. The <code>tubectl</code> command wraps both in a way that is easy to distribute.</p><p><code>tubectl</code> manages two kinds of objects: bindings and sockets. A binding encodes a rule against which an incoming packet is matched. A socket is a reference to a TCP or UDP socket that can accept new connections or packets.</p><p>Bindings and sockets are \"glued\" together via arbitrary strings called labels. Conceptually, a binding assigns a label to some traffic. The label is then used to find the correct socket.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/02/unnamed--2-.png\" alt=\"Production ready eBPF, or how we fixed the BSD socket API\"></figure><h3 id=\"adding-bindings\">Adding bindings</h3><p>To create a binding that steers port 80 (aka HTTP) traffic destined for 127.0.0.1 to the label “foo” we use <code>tubectl bind</code>:</p><pre><code>$ sudo tubectl bind \"foo\" tcp 127.0.0.1 80\n</code></pre>\n<p>Due to the power of sk_lookup we can have much more powerful constructs than the BSD API. For example, we can redirect connections to all IPs in 127.0.0.0/24 to a single socket:</p><pre><code>$ sudo tubectl bind \"bar\" tcp 127.0.0.0/24 80\n</code></pre>\n<p>A side effect of this power is that it's possible to create bindings that \"overlap\":</p><pre><code>1: tcp 127.0.0.1/32 80 -&gt; \"foo\"\n2: tcp 127.0.0.0/24 80 -&gt; \"bar\"\n</code></pre>\n<p>The first binding says that HTTP traffic to localhost should go to “foo”, while the second asserts that HTTP traffic in the localhost subnet should go to “bar”. This creates a contradiction, which binding should we choose? tubular resolves this by defining precedence rules for bindings:</p><ol><li>A prefix with a longer mask is more specific, e.g. 127.0.0.1/32 wins over 127.0.0.0/24.</li><li>A port is more specific than the port wildcard, e.g. port 80 wins over \"all ports\" (0).</li></ol><p>Applying this to our example, HTTP traffic to all IPs in 127.0.0.0/24 will be directed to foo, except for 127.0.0.1 which goes to bar.</p><h3 id=\"getting-ahold-of-sockets\">Getting ahold of sockets</h3><p><code>sk_lookup</code> needs a reference to a TCP or a UDP socket to redirect traffic to it. However, a socket is usually accessible only by the process which created it with the socket syscall. For example, an HTTP server creates a TCP listening socket bound to port 80. How can we gain access to the listening socket?</p><p>A fairly well known solution is to make processes cooperate by passing socket file descriptors via<a href=\"http://blog.cloudflare.com/know-your-scm_rights/\"> SCM_RIGHTS</a> messages to a tubular daemon. That daemon can then take the necessary steps to hook up the socket with <code>sk_lookup</code>. This approach has several drawbacks:</p><ol><li>Requires modifying processes to send SCM_RIGHTS</li><li>Requires a tubular daemon, which may crash</li></ol><p>There is another way of getting at sockets by using systemd, provided<a href=\"https://www.freedesktop.org/software/systemd/man/systemd.socket.html\"> socket activation</a> is used. It works by creating an additional service unit with the correct<a href=\"https://www.freedesktop.org/software/systemd/man/systemd.service.html#Sockets=\"> Sockets</a> setting. In other words: we can leverage systemd oneshot action executed on creation of a systemd socket service, registering the socket into tubular. For example:</p><pre><code>[Unit]\nRequisite=foo.socket\n\n[Service]\nType=oneshot\nSockets=foo.socket\nExecStart=tubectl register \"foo\"\n</code></pre>\n<p>Since we can rely on systemd to execute <code>tubectl</code> at the correct times we don't need a daemon of any kind. However, the reality is that a lot of popular software doesn't use systemd socket activation. Dealing with systemd sockets is complicated and doesn't invite experimentation. Which brings us to the final trick:<a href=\"https://www.man7.org/linux/man-pages/man2/pidfd_getfd.2.html\"> pidfd_getfd</a>:</p><blockquote>The <code>pidfd_getfd()</code> system call allocates a new file descriptor in the calling process. This new file descriptor is a duplicate of an existing file descriptor, targetfd, in the process referred to by the PID file descriptor pidfd.</blockquote><p>We can use it to iterate all file descriptors of a foreign process, and pick the socket we are interested in. To return to our example, we can use the following command to find the TCP socket bound to 127.0.0.1 port 8080 in the httpd process and register it under the \"foo\" label:</p><pre><code>$ sudo tubectl register-pid \"foo\" $(pidof httpd) tcp 127.0.0.1 8080\n</code></pre>\n<p>It's easy to wire this up using systemd's<a href=\"https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStartPre=\"> ExecStartPost</a> if the need arises.</p><pre><code>[Service]\nType=forking # or notify\nExecStart=/path/to/some/command\nExecStartPost=tubectl register-pid $MAINPID foo tcp 127.0.0.1 8080\n</code></pre>\n<h2 id=\"storing-state-in-ebpf-maps\">Storing state in eBPF maps</h2><p>As mentioned previously, tubular relies on the kernel to store state, using<a href=\"https://prototype-kernel.readthedocs.io/en/latest/bpf/ebpf_maps.html\"> BPF key / value data structures also known as maps</a>. Using the <a href=\"https://www.kernel.org/doc/html/latest/userspace-api/ebpf/syscall.html\">BPF_OBJ_PIN syscall</a> we can persist them in /sys/fs/bpf:</p><pre><code>/sys/fs/bpf/4026532024_dispatcher\n├── bindings\n├── destination_metrics\n├── destinations\n├── sockets\n└── ...\n</code></pre>\n<p>The way the state is structured differs from how the command line interface presents it to users. Labels like “foo” are convenient for humans, but they are of variable length. Dealing with variable length data in BPF is cumbersome and slow, so the BPF program never references labels at all. Instead, the user space code allocates numeric IDs, which are then used in the BPF. Each ID represents a (<code>label</code>, <code>domain</code>, <code>protocol</code>) tuple, internally called <code>destination</code>.</p><p>For example, adding a binding for \"foo\" <code>tcp 127.0.0.1</code> ... allocates an ID for (\"<code>foo</code>\", <code>AF_INET</code>, <code>TCP</code>). Including domain and protocol in the destination allows simpler data structures in the BPF. Each allocation also tracks how many bindings reference a destination so that we can recycle unused IDs. This data is persisted into the destinations hash table, which is keyed by (Label, Domain, Protocol) and contains (ID, Count). Metrics for each destination are tracked in destination_metrics in the form of per-CPU counters.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/02/unnamed--1--5.png\" alt=\"Production ready eBPF, or how we fixed the BSD socket API\"></figure><p><code>bindings</code> is a<a href=\"https://en.wikipedia.org/wiki/Trie\"> longest prefix match (LPM) trie</a> which stores a mapping from (<code>protocol</code>, <code>port</code>, <code>prefix</code>) to (<code>ID</code>, <code>prefix length</code>). The ID is used as a key to the sockets map which contains pointers to kernel socket structures. IDs are allocated in a way that makes them suitable as an array index, which allows using the simpler BPF sockmap (an array) instead of a socket hash table. The prefix length is duplicated in the value to work around shortcomings in the BPF API.</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/02/unnamed--3-.png\" alt=\"Production ready eBPF, or how we fixed the BSD socket API\"></figure><h2 id=\"encoding-the-precedence-of-bindings\">Encoding the precedence of bindings</h2><p>As discussed, bindings have a precedence associated with them. To repeat the earlier example:</p><pre><code>1: tcp 127.0.0.1/32 80 -&gt; \"foo\"\n2: tcp 127.0.0.0/24 80 -&gt; \"bar\"\n</code></pre>\n<p>The first binding should be matched before the second one. We need to encode this in the BPF somehow. One idea is to generate some code that executes the bindings in order of specificity, a technique we’ve used to great effect in <a href=\"http://blog.cloudflare.com/l4drop-xdp-ebpf-based-ddos-mitigations/\">l4drop</a>:</p><pre><code>1: if (mask(ip, 32) == 127.0.0.1) return \"foo\"\n2: if (mask(ip, 24) == 127.0.0.0) return \"bar\"\n...\n</code></pre>\n<p>This has the downside that the program gets longer the more bindings are added, which slows down execution. It's also difficult to introspect and debug such long programs. Instead, we use a specialised BPF longest prefix match (LPM) map to do the hard work. This allows inspecting the contents from user space to figure out which bindings are active, which is very difficult if we had compiled bindings into BPF. The LPM map uses a trie behind the scenes, so <a href=\"https://en.wikipedia.org/wiki/Trie#Searching\">lookup has complexity proportional to the length of the key</a> instead of linear complexity for the “naive” solution.</p><p>However, using a map requires a trick for encoding the precedence of bindings into a key that we can look up. Here is a simplified version of this encoding, which ignores IPv6 and uses labels instead of IDs. To insert the binding <code>tcp 127.0.0.0/24 80</code> into a trie we first convert the IP address into a number.</p><pre><code>127.0.0.0    = 0x7f 00 00 00\n</code></pre>\n<p>Since we're only interested in the first 24 bits of the address we, can write the whole prefix as</p><pre><code>127.0.0.0/24 = 0x7f 00 00 ??\n</code></pre>\n<p>where “?” means that the value is not specified. We choose the number 0x01 to represent TCP and prepend it and the port number (80 decimal is 0x50 hex) to create the full key:</p><pre><code>tcp 127.0.0.0/24 80 = 0x01 50 7f 00 00 ??\n</code></pre>\n<p>Converting <code>tcp 127.0.0.1/32 80</code> happens in exactly the same way. Once the converted values are inserted into the trie, the LPM trie conceptually contains the following keys and values.</p><pre><code>LPM trie:\n        0x01 50 7f 00 00 ?? = \"bar\"\n        0x01 50 7f 00 00 01 = \"foo\"\n</code></pre>\n<p>To find the binding for a TCP packet destined for 127.0.0.1:80, we again encode a key and perform a lookup.</p><pre><code>input:  0x01 50 7f 00 00 01   TCP packet to 127.0.0.1:80\n---------------------------\nLPM trie:\n        0x01 50 7f 00 00 ?? = \"bar\"\n           y  y  y  y  y\n        0x01 50 7f 00 00 01 = \"foo\"\n           y  y  y  y  y  y\n---------------------------\nresult: \"foo\"\n\ny = byte matches\n</code></pre>\n<p>The trie returns “foo” since its key shares the longest prefix with the input. Note that we stop comparing keys once we reach unspecified “?” bytes, but conceptually “bar” is still a valid result. The distinction becomes clear when looking up the binding for a TCP packet to 127.0.0.255:80.</p><pre><code>input:  0x01 50 7f 00 00 ff   TCP packet to 127.0.0.255:80\n---------------------------\nLPM trie:\n        0x01 50 7f 00 00 ?? = \"bar\"\n           y  y  y  y  y\n        0x01 50 7f 00 00 01 = \"foo\"\n           y  y  y  y  y  n\n---------------------------\nresult: \"bar\"\n\nn = byte doesn't match\n</code></pre>\n<p>In this case \"foo\" is discarded since the last byte doesn't match the input. However, \"bar\" is returned since its last byte is unspecified and therefore considered to be a valid match.</p><h2 id=\"observability-with-minimal-privileges\">Observability with minimal privileges</h2><p>Linux has the powerful ss tool (part of iproute2) available to inspect socket state:</p><pre><code>$ ss -tl src 127.0.0.1\nState      Recv-Q      Send-Q           Local Address:Port           Peer Address:Port\nLISTEN     0           128                  127.0.0.1:ipp                 0.0.0.0:*\n</code></pre>\n<p>With tubular in the picture this output is not accurate anymore. <code>tubectl</code> bindings makes up for this shortcoming:</p><pre><code>$ sudo tubectl bindings tcp 127.0.0.1\nBindings:\n protocol       prefix port label\n      tcp 127.0.0.1/32   80   foo\n</code></pre>\n<p>Running this command requires super-user privileges, despite in theory being safe for any user to run. While this is acceptable for casual inspection by a human operator, it's a dealbreaker for observability via pull-based monitoring systems like Prometheus. The usual approach is to expose metrics via an HTTP server, which would have to run with elevated privileges and be accessible to the Prometheus server somehow. Instead, BPF gives us the tools to enable read-only access to tubular state with minimal privileges.</p><p>The key is to carefully set file ownership and mode for state in /sys/fs/bpf. Creating and opening files in /sys/fs/bpf uses<a href=\"https://www.kernel.org/doc/html/latest/userspace-api/ebpf/syscall.html#bpf-subcommand-reference\"> BPF_OBJ_PIN and BPF_OBJ_GET</a>. Calling BPF_OBJ_GET with BPF_F_RDONLY is roughly equivalent to open(O_RDONLY) and allows accessing state in a read-only fashion, provided the file permissions are correct. tubular gives the owner full access but restricts read-only access to the group:</p><pre><code>$ sudo ls -l /sys/fs/bpf/4026532024_dispatcher | head -n 3\ntotal 0\n-rw-r----- 1 root root 0 Feb  2 13:19 bindings\n-rw-r----- 1 root root 0 Feb  2 13:19 destination_metrics\n</code></pre>\n<p>It's easy to choose which user and group should own state when loading tubular:</p><pre><code>$ sudo -u root -g tubular tubectl load\ncreated dispatcher in /sys/fs/bpf/4026532024_dispatcher\nloaded dispatcher into /proc/self/ns/net\n$ sudo ls -l /sys/fs/bpf/4026532024_dispatcher | head -n 3\ntotal 0\n-rw-r----- 1 root tubular 0 Feb  2 13:42 bindings\n-rw-r----- 1 root tubular 0 Feb  2 13:42 destination_metrics\n</code></pre>\n<p>There is one more obstacle,<a href=\"https://github.com/systemd/systemd/blob/b049b48c4b6e60c3cbec9d2884f90fd4e7013219/src/shared/mount-setup.c#L111-L112\"> systemd mounts /sys/fs/bpf</a> in a way that makes it inaccessible to anyone but root. Adding the executable bit to the directory fixes this.</p><pre><code>$ sudo chmod -v o+x /sys/fs/bpf\nmode of '/sys/fs/bpf' changed from 0700 (rwx------) to 0701 (rwx-----x)\n</code></pre>\n<p>Finally, we can export metrics without privileges:</p><pre><code>$ sudo -u nobody -g tubular tubectl metrics 127.0.0.1 8080\nListening on 127.0.0.1:8080\n^C\n</code></pre>\n<p>There is a caveat, unfortunately: truly unprivileged access requires unprivileged BPF to be enabled. Many distros have taken to disabling it via the unprivileged_bpf_disabled sysctl, in which case scraping metrics does require CAP_BPF.</p><h2 id=\"safe-releases\">Safe releases</h2><p>tubular is distributed as a single binary, but really consists of two pieces of code with widely differing lifetimes. The BPF program is loaded into the kernel once and then may be active for weeks or months, until it is explicitly replaced. In fact, a reference to the program (and link, see below) is persisted into /sys/fs/bpf:</p><pre><code>/sys/fs/bpf/4026532024_dispatcher\n├── link\n├── program\n└── ...\n</code></pre>\n<p>The user space code is executed for seconds at a time and is replaced whenever the binary on disk changes. This means that user space has to be able to deal with an \"old\" BPF program in the kernel somehow. The simplest way to achieve this is to compare what is loaded into the kernel with the BPF shipped as part of tubectl. If the two don't match we return an error:</p><pre><code>$ sudo tubectl bind foo tcp 127.0.0.1 80\nError: bind: can't open dispatcher: loaded program #158 has differing tag: \"938c70b5a8956ff2\" doesn't match \"e007bfbbf37171f0\"\n</code></pre>\n<p><code>tag</code> is the truncated hash of the instructions making up a BPF program, which the kernel makes available for every loaded program:</p><pre><code>$ sudo bpftool prog list id 158\n158: sk_lookup  name dispatcher  tag 938c70b5a8956ff2\n...\n</code></pre>\n<p>By comparing the tag tubular asserts that it is dealing with a supported version of the BPF program. Of course, just returning an error isn't enough. There needs to be a way to update the kernel program so that it's once again safe to make changes. This is where the persisted link in /sys/fs/bpf comes into play. <code>bpf_links</code> are used to attach programs to various BPF hooks. \"Enabling\" a BPF program is a two-step process: first, load the BPF program, next attach it to a hook using a bpf_link. Afterwards the program will execute the next time the hook is executed. By updating the link we can change the program on the fly, in an atomic manner.</p><pre><code>$ sudo tubectl upgrade\nUpgraded dispatcher to 2022.1.0-dev, program ID #159\n$ sudo bpftool prog list id 159\n159: sk_lookup  name dispatcher  tag e007bfbbf37171f0\n…\n$ sudo tubectl bind foo tcp 127.0.0.1 80\nbound foo#tcp:[127.0.0.1/32]:80\n</code></pre>\n<p>Behind the scenes the upgrade procedure is slightly more complicated, since we have to update the pinned program reference in addition to the link. We pin the new program into /sys/fs/bpf:</p><pre><code>/sys/fs/bpf/4026532024_dispatcher\n├── link\n├── program\n├── program-upgrade\n└── ...\n</code></pre>\n<p>Once the link is updated we <a href=\"https://www.man7.org/linux/man-pages/man2/rename.2.html\">atomically rename</a> program-upgrade to replace program. In the future we may be able to <a href=\"https://lkml.kernel.org/netdev/20211028094724.59043-5-lmb@cloudflare.com/t/\">use RENAME_EXCHANGE</a> to make upgrades even safer.</p><h2 id=\"preventing-state-corruption\">Preventing state corruption</h2><p>So far we’ve completely neglected the fact that multiple invocations of <code>tubectl</code> could modify the state in /sys/fs/bpf at the same time. It’s very hard to reason about what would happen in this case, so in general it’s best to prevent this from ever occurring. A common solution to this is <a href=\"https://gavv.github.io/articles/file-locks/#differing-features\">advisory file locks</a>. Unfortunately it seems like BPF maps don't support locking.</p><pre><code>$ sudo flock /sys/fs/bpf/4026532024_dispatcher/bindings echo works!\nflock: cannot open lock file /sys/fs/bpf/4026532024_dispatcher/bindings: Input/output error\n</code></pre>\n<p>This led to a bit of head scratching on our part. Luckily it is possible to flock the directory instead of individual maps:</p><pre><code>$ sudo flock --exclusive /sys/fs/bpf/foo echo works!\nworks!\n</code></pre>\n<p>Each <code>tubectl</code> invocation likewise invokes <a href=\"https://www.man7.org/linux/man-pages//man2/flock.2.html\"><code>flock()</code></a>, thereby guaranteeing that only ever a single process is making changes.</p><h2 id=\"conclusion\">Conclusion</h2><p>tubular is in production at Cloudflare today and has simplified the deployment of Spectrum and our authoritative DNS. It allowed us to leave behind limitations of the BSD socket API. However, its most powerful feature is that <a href=\"https://research.cloudflare.com/publications/Fayed2021/\">the addresses a service is available on can be changed on the fly</a>. In fact, we have built tooling that automates this process across our global network. Need to listen on another million IPs on thousands of machines? No problem, it’s just an HTTP POST away.</p><p><em>Interested in working on tubular and our L4 load balancer </em><a href=\"http://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer/\"><em>unimog</em></a><em>? We are </em><a href=\"https://boards.greenhouse.io/cloudflare/jobs/3232234?gh_jid=3232234\"><em>hiring in our European offices</em></a><em>.</em></p>",
            "url": "https://blog.cloudflare.com/tubular-fixing-the-socket-api-with-ebpf/",
            "title": "Production ready eBPF, or how we fixed the BSD socket API",
            "date_modified": "2022-02-17T17:02:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30367781\">Comments</a>",
            "url": "https://future.a16z.com/software-development-building-for-99-developers/",
            "title": "Building for the 99% Developers",
            "date_modified": "2022-02-17T00:19:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30365301\">Comments</a>",
            "url": "https://docs.temporal.io/blog/series-b-announcement-open-letter/",
            "title": "An open letter to the Temporal user community",
            "date_modified": "2022-02-16T20:32:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30363537\">Comments</a>",
            "url": "https://www.gosecure.net/blog/2022/02/14/current-mfa-fatigue-attack-campaign-targeting-microsoft-office-365-users/",
            "title": "Current MFA fatigue attack campaign targeting Microsoft Office 365 users",
            "date_modified": "2022-02-16T18:17:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30359430\">Comments</a>",
            "url": "https://developer.1password.com/docs/ssh/",
            "title": "1Password for SSH and Git",
            "date_modified": "2022-02-16T13:01:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30329762\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30329762",
            "title": "Ask HN: What made your business take off that you wish you'd done much earlier?",
            "date_modified": "2022-02-14T08:47:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30296490\">Comments</a>",
            "url": "https://ardentperf.com/2022/02/10/a-hairy-postgresql-incident/",
            "title": "A Hairy PostgreSQL Incident",
            "date_modified": "2022-02-11T03:22:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30290225\">Comments</a>",
            "url": "https://www.edgedb.com/blog/edgedb-1-0",
            "title": "Show HN: EdgeDB 1.0",
            "date_modified": "2022-02-10T18:13:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30279427\">Comments</a>",
            "url": "https://blog.crunchydata.com/blog/postgres-constraints-for-newbies",
            "title": "Postgres Constraints for Newbies",
            "date_modified": "2022-02-09T22:25:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30275905\">Comments</a>",
            "url": "https://fly.io/blog/our-user-mode-wireguard-year/",
            "title": "Our User-Mode WireGuard Year",
            "date_modified": "2022-02-09T18:06:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30274886\">Comments</a>",
            "url": "https://brandur.org/fragments/single-dependency-stacks",
            "title": "Single dependency stacks",
            "date_modified": "2022-02-09T16:53:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30272101\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30272101",
            "title": "Ask HN: What's your solution for SSL on internal servers?",
            "date_modified": "2022-02-09T13:00:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30256256\">Comments</a>",
            "url": "https://github.com/ariga/atlas",
            "title": "Atlas – A Database Toolkit",
            "date_modified": "2022-02-08T09:00:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30190397\">Comments</a>",
            "url": "https://webonastick.com/fonts/routed-gothic/",
            "title": "Routed Gothic Font",
            "date_modified": "2022-02-03T09:16:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30178655\">Comments</a>",
            "url": "https://chown.me/blog/getting-my-own-asn",
            "title": "Why and How I Got My Own ASN",
            "date_modified": "2022-02-02T15:15:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30178571\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30178571",
            "title": "Show HN: Just Launched an App for Dads",
            "date_modified": "2022-02-02T15:09:27.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ydjy5d/why_how_i_got_my_own_asn\">Comments</a></p>",
            "url": "https://chown.me/blog/getting-my-own-asn",
            "title": "Why and how I got my own ASN",
            "date_modified": "2022-02-02T12:37:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30166077\">Comments</a>",
            "url": "https://databricks.com/blog/2022/01/26/creating-a-faster-tar-extractor.html",
            "title": "Speeding up LXC container pull by up to 3x",
            "date_modified": "2022-02-01T17:34:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30161853\">Comments</a>",
            "url": "https://northflank.com/",
            "title": "Northflank – Simplifying application deployment to cloud platforms",
            "date_modified": "2022-02-01T13:00:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30129572\">Comments</a>",
            "url": "https://twitter.com/theryanking/status/1487500943511932941",
            "title": "“Y Combinator is not worth it”",
            "date_modified": "2022-01-29T19:30:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30120590\">Comments</a>",
            "url": "https://www.quora.com/Why-do-tech-startups-still-hire-so-many-people-Given-the-well-documented-costs-both-direct-and-indirect-of-increasing-the-size-of-organizations-why-do-so-many-tech-companies-still-grow-so-quickly",
            "title": "Why do startups hire so many people?",
            "date_modified": "2022-01-28T21:27:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30102240\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=30102240",
            "title": "Ask HN: Hacker claimed ownership and then deleted my Facebook Page of 50k users",
            "date_modified": "2022-01-27T16:09:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30083764\">Comments</a>",
            "url": "https://fly.io/blog/run-ordinary-rails-apps-globally/",
            "title": "Run Ordinary Rails Apps Globally",
            "date_modified": "2022-01-26T10:19:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30067310\">Comments</a>",
            "url": "https://thewhyaxis.substack.com/p/how-paul-giamatti-broke-the-california",
            "title": "Paul Giamatti broke the California wine industry",
            "date_modified": "2022-01-25T02:53:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30055036\">Comments</a>",
            "url": "https://github.com/madler/zlib",
            "title": "Zlib – a spiffy yet delicately unobtrusive compression library",
            "date_modified": "2022-01-24T09:00:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30048332\">Comments</a>",
            "url": "https://charm.sh/",
            "title": "Charm – tools to make the command line glamorous",
            "date_modified": "2022-01-23T17:46:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30030991\">Comments</a>",
            "url": "https://github.com/khuedoan/homelab",
            "title": "My self-hosting infrastructure, fully automated",
            "date_modified": "2022-01-21T22:52:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=30020288\">Comments</a>",
            "url": "https://atlasgo.io/",
            "title": "Atlas – Terraform but for Database Migrations",
            "date_modified": "2022-01-21T07:23:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29981737\">Comments</a>",
            "url": "https://spacelift.io/blog/tricking-postgres-into-using-query-plan",
            "title": "Tricking PostgreSQL into using an insane, but faster,  query plan",
            "date_modified": "2022-01-18T16:42:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29977856\">Comments</a>",
            "url": "https://pawelurbanek.com/postgresql-query-bottleneck",
            "title": "PostgreSQL query performance bottlenecks",
            "date_modified": "2022-01-18T11:58:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29971720\">Comments</a>",
            "url": "https://www.shayon.dev/post/2022/17/why-i-enjoy-postgresql-infrastructure-engineers-perspective/",
            "title": "Why I Enjoy PostgreSQL – Infrastructure Engineer's Perspective",
            "date_modified": "2022-01-17T21:10:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29965381\">Comments</a>",
            "url": "https://research.nccgroup.com/2022/01/13/10-real-world-stories-of-how-weve-compromised-ci-cd-pipelines/",
            "title": "Real-world stories of how we’ve compromised CI/CD pipelines",
            "date_modified": "2022-01-17T11:08:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29959076\">Comments</a>",
            "url": "https://github.com/disposable-email-domains/disposable-email-domains/pull/298",
            "title": "Mozilla's Firefox Relay to be added to disposable-email-domains blacklist",
            "date_modified": "2022-01-16T19:28:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29943434\">Comments</a>",
            "url": "https://www.outseta.com/posts/five-years-a-founder",
            "title": "Five Years a [Bootstrapped] Founder",
            "date_modified": "2022-01-15T04:07:29.000Z"
        },
        {
            "content_html": "<figure><img src=\"http://blog.cloudflare.com/content/images/2022/01/image2-20.png\" alt=\"A Workers optimization\nthat reduces your bill\"></figure><img src=\"http://blog.cloudflare.com/content/images/2022/01/image2-19.png\" alt=\"A Workers optimization\nthat reduces your bill\"><p>Recently, we made an optimization to the Cloudflare Workers runtime which reduces the amount of time Workers need to spend in memory. We're passing the savings on to you for all your Unbound Workers.</p><h3 id=\"background\">Background</h3><p>Workers are often used to implement HTTP proxies, where JavaScript is used to rewrite an HTTP request before sending it on to an origin server, and then to rewrite the response before sending it back to the client. You can implement any kind of rewrite in a Worker, including both rewriting headers and bodies.</p><p>Many Workers, though, do not actually modify the response body, but instead simply allow the bytes to pass through from the origin to the client. In this case, the Worker's application code has finished executing as soon as the response headers are sent, before the body bytes have passed through. Historically, the Worker was nevertheless considered to be \"in use\" until the response body had fully finished streaming.</p><p>For billing purposes, under the Workers Unbound pricing model, we charge duration-memory (gigabyte-seconds) for the time in which the Worker is in use.</p><h3 id=\"the-change\">The change</h3><p>On December 15-16, we made a change to the way we handle requests that are streaming through the response without modifying the content. This change means that we can mark application code as “idle” as soon as the response headers are returned.</p><p>Since no further application code will execute on behalf of the request, the system does not need to keep the request state in memory – it only needs to track the low-level native sockets and pump the bytes through. So now, during this time, the Worker will be considered idle, and could even be evicted before the stream completes (though this would be unlikely unless the stream lasts for a very long time).</p><p>Visualized it looks something like this:</p><figure><img src=\"http://blog.cloudflare.com/content/images/2022/01/DES-4045-Diagram.png\" alt=\"A Workers optimization\nthat reduces your bill\"></figure><p>As a result of this change, we've seen that the time a Worker is considered \"in use\" by any particular request has dropped by an average of 70%. Of course, this number varies a lot depending on the details of each Worker. Some may see no benefit, others may see an even larger benefit.</p><p>This change is totally invisible to the application. To any external observer, everything behaves as it did before. But, since the system now considers a Worker to be idle during response streaming, the response streaming time will no longer be billed. So, if you saw a drop in your bill, this is why!</p><h3 id=\"but-it-doesn-t-stop-there-\">But it doesn’t stop there!</h3><p>The change also applies to a few other frequently used scenarios, namely Websocket proxying, reading from the cache and streaming from KV.</p><p><strong>WebSockets</strong>: once a Worker has arranged to proxy through a WebSocket, as long as it isn't handling individual messages in your Worker code, the Worker does not remain in use during the proxying. The change applies to regular stateless Workers, but not to Durable Objects, which are not usually used for proxying.</p><pre><code>export default {\n  async fetch(request: Request) {\n    //Do anything before\n    const upgradeHeader = request.headers.get('Upgrade')\n    if (upgradeHeader || upgradeHeader === 'websocket') {\n      return await fetch(request)\n    }\n    //Or with other requests\n  }\n}\n</code></pre>\n<p><strong>Reading from Cache</strong>: If you return the response from a <code>cache.match</code> call, the Worker is considered idle as soon as the response headers are returned.</p><pre><code>export default {\n  async fetch(request: Request) {\n    let response = await caches.default.match('https://example.com')\n    if (response) {\n      return response\n    }\n    // get/create response and put into cache\n  }\n}\n</code></pre>\n<p><strong>Streaming from KV</strong>: And lastly, when you stream from KV. This one is a bit trickier to get right, because often people retrieve the value from KV as a string, or JSON object and then create a response with that value. But if you fetch the value as a stream, as done in the example below, you can create a Response with the ReadableStream.</p><pre><code>interface Env {\n  MY_KV_NAME: KVNamespace\n}\n\nexport default {\n  async fetch(request: Request, env: Env) {\n    const readableStream = await env.MY_KV_NAME.get('hello_world.pdf', { type: 'stream' })\n    if (readableStream) {\n      return new Response(readableStream, { headers: { 'content-type': 'application/pdf' } })\n    }\n  },\n}\n</code></pre>\n<h3 id=\"interested-in-workers-unbound\">Interested in Workers Unbound?</h3><p>If you are already using Unbound, your bill will have automatically dropped already.</p><p>Now is a great time to check out Unbound if you haven’t already, especially since recently, we’ve also <a href=\"http://blog.cloudflare.com/workers-now-even-more-unbound/\">removed the egress fees</a>. Unbound allows you to build more complex workloads on our platform and only pay for what you use.</p><p>We are always looking for opportunities to make Workers better. Often that improvement takes the form of powerful new features such as the soon-to-be released <a href=\"http://blog.cloudflare.com/introducing-worker-services/\">Service Bindings</a> and, of course, <a href=\"http://blog.cloudflare.com/cloudflare-workers-the-fast-serverless-platform/\">performance enhancements</a>. This time, we are delighted to make Cloudflare Workers even cheaper than they already were.</p>",
            "url": "https://blog.cloudflare.com/workers-optimization-reduces-your-bill/",
            "title": "A Workers optimization\nthat reduces your bill",
            "date_modified": "2022-01-14T13:58:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29912450\">Comments</a>",
            "url": "https://arxiv.org/abs/2201.00223",
            "title": "They Still Haven't Told You",
            "date_modified": "2022-01-12T20:47:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29911965\">Comments</a>",
            "url": "https://olickel.com/button-syndrome",
            "title": "Losing our product to button syndrome",
            "date_modified": "2022-01-12T19:56:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29903695\">Comments</a>",
            "url": "https://mullvad.net/en/blog/2022/1/12/diskless-infrastructure-beta-system-transparency-stboot/",
            "title": "Mullvad: Diskless infrastructure using stboot in beta",
            "date_modified": "2022-01-12T08:10:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29892472\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29892472",
            "title": "Ask HN: Why isn't there a backlash around charging for security features?",
            "date_modified": "2022-01-11T15:20:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29878476\">Comments</a>",
            "url": "https://www.bolt.com/blog/switching-four-day-workweek/#",
            "title": "Switching to a four-day workweek (2021)",
            "date_modified": "2022-01-10T17:43:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29878365\">Comments</a>",
            "url": "https://blog.ycombinator.com/ycs-standard-deal/",
            "title": "YC’s $500k Standard Deal",
            "date_modified": "2022-01-10T17:36:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29847462\">Comments</a>",
            "url": "https://blog.sequin.io/events-not-webhooks",
            "title": "Give me /events, not webhooks",
            "date_modified": "2022-01-08T01:12:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29828386\">Comments</a>",
            "url": "https://contains.dev/blog/optimizing-docker-image-size",
            "title": "Optimizing Docker image size and why it matters",
            "date_modified": "2022-01-06T19:13:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29772927\">Comments</a>",
            "url": "https://jvns.ca/blog/2017/06/28/notes-on-bpf---ebpf/",
            "title": "Notes on BPF and eBPF",
            "date_modified": "2022-01-02T19:48:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29761728\">Comments</a>",
            "url": "https://joeldare.com/why-im-using-http-basic-auth-in-2022.html",
            "title": "Why I'm Using HTTP Basic Auth in 2022",
            "date_modified": "2022-01-01T19:25:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29752918\">Comments</a>",
            "url": "https://fusionauth.io/learn/expert-advice/oauth/modern-guide-to-oauth/",
            "title": "The Modern Guide to OAuth",
            "date_modified": "2021-12-31T21:54:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29751143\">Comments</a>",
            "url": "https://medium.com/bigpanda-engineering/what-is-engineering-enablement-b5293a5838ce",
            "title": "What Is Engineering Enablement",
            "date_modified": "2021-12-31T18:48:52.000Z"
        },
        {
            "content_html": "<p>Official project URL <a href=\"https://www.benthos.dev/\" rel=\"ugc\">www.benthos.dev/</a></p>\n\n            <p><a href=\"https://lobste.rs/s/lzd6i4/benthos_swiss_army_knife_stream\">Comments</a></p>",
            "url": "https://twitter.com/Jeffail/status/1476598958445285384",
            "title": "Benthos, the Swiss Army knife of stream processing, reached 100 contributors",
            "date_modified": "2021-12-31T13:10:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29733026\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29733026",
            "title": "Ask HN: What’s the Most Outrageous Belief You’re Confident Is True?",
            "date_modified": "2021-12-30T02:57:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29731065\">Comments</a>",
            "url": "https://coda.io/@mykola-bilokonsky/public-neurodiversity-support-center/how-to-talk-about-autism-respectfully-84",
            "title": "How to Talk About Autism Respectfully",
            "date_modified": "2021-12-29T22:40:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29723455\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29723455",
            "title": "Ask HN: What are some good rust code to read to learn the language?",
            "date_modified": "2021-12-29T10:08:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29690769\">Comments</a>",
            "url": "https://danluu.com/cgroup-throttling/",
            "title": "The Container Throttling Problem",
            "date_modified": "2021-12-26T08:42:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29687960\">Comments</a>",
            "url": "https://ptribble.blogspot.com/2021/12/the-cost-of-cloud.html",
            "title": "The Cost of Cloud",
            "date_modified": "2021-12-25T23:16:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29680698\">Comments</a>",
            "url": "https://github.com/galvez/typejuice",
            "title": "Typejuice: Docs generator for .d.ts files inspired by godoc",
            "date_modified": "2021-12-25T05:21:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29678970\">Comments</a>",
            "url": "https://adhdatwork.add.org/adhd-accommodation-guide/",
            "title": "ADHD Accommodations Guide",
            "date_modified": "2021-12-24T23:57:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29661170\">Comments</a>",
            "url": "https://tinyssh.org",
            "title": "Tinyssh",
            "date_modified": "2021-12-23T12:27:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29641907\">Comments</a>",
            "url": "https://tuple.app/",
            "title": "Tuple: Pair Programming Tool for macOS",
            "date_modified": "2021-12-21T20:46:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29634114\">Comments</a>",
            "url": "https://airbnb.io/lottie/",
            "title": "Lottie – Use after effects animations in web and native apps",
            "date_modified": "2021-12-21T05:08:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29633404\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29633404",
            "title": "Ask HN: Recommended Domain Registrars?",
            "date_modified": "2021-12-21T03:15:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29608967\">Comments</a>",
            "url": "https://github.com/haimgel/display-switch",
            "title": "Display-switch: Turn a $30 USB switch into a full-featured multi-monitor KVM",
            "date_modified": "2021-12-18T22:00:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29599132\">Comments</a>",
            "url": "https://webapp.io/blog/postgres-is-the-answer/",
            "title": "Postgres is a great pub/sub and job server (2019)",
            "date_modified": "2021-12-17T22:27:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29574750\">Comments</a>",
            "url": "https://help.sunsama.com/docs/pricing-manifesto",
            "title": "SaaS Pricing Manifesto",
            "date_modified": "2021-12-16T04:14:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29563226\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29563226",
            "title": "Ask HN: How do you manage direct updates to databases in a production system",
            "date_modified": "2021-12-15T08:07:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29554461\">Comments</a>",
            "url": "https://dynomight.net/plans/",
            "title": "Plans you're not supposed to talk about",
            "date_modified": "2021-12-14T17:34:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29523638\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29523638",
            "title": "Ask HN: Long-form info-dense videos on YouTube you would recommend to anyone?",
            "date_modified": "2021-12-11T19:44:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29489948\">Comments</a>",
            "url": "https://buf.build/blog/an-update-on-our-fundraising",
            "title": "Buf raises $93M to deprecate REST/JSON",
            "date_modified": "2021-12-08T20:20:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29482194\">Comments</a>",
            "url": "https://leejo.github.io/2021/12/08/curiosities-in-vinyl/",
            "title": "Curiosities in Vinyl",
            "date_modified": "2021-12-08T07:41:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29478375\">Comments</a>",
            "url": "https://martinfowler.com/articles/cant-buy-integration.html",
            "title": "You Can't Buy Integration",
            "date_modified": "2021-12-07T21:20:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29425650\">Comments</a>",
            "url": "https://www.flyertalk.com/forum/33719083-post2040.html",
            "title": "Google 20% time volunteers have been rewriting the ITA Matrix flight search app",
            "date_modified": "2021-12-03T01:47:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29391858\">Comments</a>",
            "url": "http://jpetazzo.github.io/2021/11/30/docker-build-container-images-antipatterns/",
            "title": "Anti-patterns when building container images",
            "date_modified": "2021-11-30T13:30:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29390483\">Comments</a>",
            "url": "https://chriskiehl.com/article/event-sourcing-is-hard",
            "title": "Event Sourcing Is Hard",
            "date_modified": "2021-11-30T09:41:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29389576\">Comments</a>",
            "url": "https://postgrest.org/en/v9.0/releases/v9.0.0.html",
            "title": "PostgREST v9.0.0",
            "date_modified": "2021-11-30T06:38:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29387761\">Comments</a>",
            "url": "https://blog.asciinema.org/post/smaller-faster/",
            "title": "4x Smaller, 50x Faster",
            "date_modified": "2021-11-30T01:40:22.000Z"
        },
        {
            "content_html": "<p>Today we are announcing that <a href=\"https://karpenter.sh/\">Karpenter</a> is ready for production. Karpenter is an open-source, flexible, high-performance Kubernetes cluster autoscaler built with AWS. It helps improve your application availability and cluster efficiency by rapidly launching right-sized compute resources in response to changing application load. Karpenter also provides just-in-time compute resources to meet your application’s needs and will soon automatically optimize a cluster’s compute resource footprint to reduce costs and improve performance.</p> \n<p>Before Karpenter, Kubernetes users needed to dynamically adjust the compute capacity of their clusters to support applications using <a href=\"https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html\">Amazon EC2 Auto Scaling groups</a> and the <a href=\"https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler\">Kubernetes Cluster Autoscaler</a>. Nearly half of Kubernetes customers on AWS report that configuring cluster auto scaling using the Kubernetes Cluster Autoscaler is challenging and restrictive.</p> \n<p>When Karpenter is installed in your cluster, Karpenter observes the aggregate resource requests of unscheduled pods and makes decisions to launch new nodes and terminate them to reduce scheduling latencies and infrastructure costs. Karpenter does this by observing events within the Kubernetes cluster and then sending commands to the underlying cloud provider’s compute service, such as Amazon EC2.</p> \n<p><img src=\"https://d2908q01vomqb2.cloudfront.net/da4b9237bacccdf19c0760cab7aec4a8359010b0/2021/11/23/2021-karpenter-diagram.png\" width=\"1052\" height=\"470\"></p> \n<p>Karpenter is an open-source project licensed under the <a href=\"https://github.com/awslabs/karpenter/blob/main/LICENSE\">Apache License 2.0</a>. It is designed to work with any Kubernetes cluster running in any environment, including all major cloud providers and on-premises environments. We welcome contributions to build additional cloud providers or to improve core project functionality. If you find a bug, have a suggestion, or have something to contribute, please engage with us on <a href=\"https://github.com/awslabs/karpenter/issues\">GitHub</a>.</p> \n<p><strong><u>Getting Started with Karpenter on AWS</u></strong><br> To get started with Karpenter in any Kubernetes cluster, ensure there is some compute capacity available, and install it using the Helm charts provided in the public repository. Karpenter also requires permissions to provision compute resources on the provider of your choice.</p> \n<p>Once installed in your cluster, the default Karpenter provisioner will observe incoming Kubernetes pods, which cannot be scheduled due to insufficient compute resources in the cluster and automatically launch new resources to meet their scheduling and resource requirements.</p> \n<p>I want to show a quick start using Karpenter in an Amazon EKS cluster based on <a href=\"https://karpenter.sh/docs/getting-started/\">Getting Started with Karpenter on AWS</a>. It requires the installation of <a href=\"https://aws.amazon.com/cli/\">AWS Command Line Interface</a> (AWS CLI), <a href=\"https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/\">kubectl</a>, <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html\">eksctl</a>, and <a href=\"https://helm.sh/docs/intro/install/\">Helm</a> (the package manager for Kubernetes). After setting up these tools, create a cluster with <code>eksctl</code>. This example configuration file specifies a basic cluster with one initial node.</p> \n<pre><code>cat &lt;&lt;EOF &gt; cluster.yaml\n---\napiVersion: eksctl.io/v1alpha5\nkind: ClusterConfig\nmetadata:\n  name: eks-karpenter-demo\n  region: us-east-1\n  version: \"1.20\"\nmanagedNodeGroups:\n  - instanceType: m5.large\n    amiFamily: AmazonLinux2\n    name: eks-kapenter-demo-ng\n    desiredCapacity: 1\n    minSize: 1\n    maxSize: 5\nEOF\n$ eksctl create cluster -f cluster.yaml</code></pre> \n<p>Karpenter itself can run anywhere, including on <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/worker.html\">self-managed node groups</a>, <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html\">managed node groups</a>, or <a href=\"https://aws.amazon.com/fargate/\">AWS Fargate</a>. Karpenter will provision EC2 instances in your account.</p> \n<p>Next, you need to create necessary <a href=\"https://aws.amazon.com/iam/\">AWS Identity and Access Management</a> (IAM) resources using the <a href=\"https://karpenter.sh/docs/getting-started/cloudformation.yaml\">AWS CloudFormation template</a> and <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html\">IAM Roles for Service Accounts</a> (IRSA) for the Karpenter controller to get permissions like launching instances following the <a href=\"https://karpenter.sh/docs/getting-started/\">documentation</a>. You also need to install the Helm chart to deploy Karpenter to your cluster.</p> \n<pre><code>$ helm repo add karpenter https://charts.karpenter.sh\n$ helm repo update\n$ helm upgrade --install --skip-crds karpenter karpenter/karpenter --namespace karpenter \\\n  --create-namespace --set serviceAccount.create=false --version 0.5.0 \\\n  --set controller.clusterName=eks-karpenter-demo\n  --set controller.clusterEndpoint=$(aws eks describe-cluster --name eks-karpenter-demo --query \"cluster.endpoint\" --output json) \\\n  --wait # for the defaulting webhook to install before creating a Provisioner</code></pre> \n<p>Karpenter provisioners are a Kubernetes resource that enables you to configure the behavior of Karpenter in your cluster. When you create a default provisioner, without further customization besides what is needed for Karpenter to provision compute resources in your cluster, Karpenter automatically discovers node properties such as instance types, zones, architectures, operating systems, and purchase types of instances. You don’t need to define these spec:requirements if there is no explicit business requirement.</p> \n<pre><code>cat &lt;&lt;EOF | kubectl apply -f -\napiVersion: karpenter.sh/v1alpha5\nkind: Provisioner\nmetadata:\nname: default\nspec:\n#Requirements that constrain the parameters of provisioned nodes. \n#Operators { In, NotIn } are supported to enable including or excluding values\n  requirements:\n    - key: node.k8s.aws/instance-type #If not included, all instance types are considered\n      operator: In\n      values: [\"m5.large\", \"m5.2xlarge\"]\n    - key: \"topology.kubernetes.io/zone\" #If not included, all zones are considered\n      operator: In\n      values: [\"us-east-1a\", \"us-east-1b\"]\n    - key: \"kubernetes.io/arch\" #If not included, all architectures are considered\n      values: [\"arm64\", \"amd64\"]\n    - key: \" karpenter.sh/capacity-type\" #If not included, the webhook for the AWS cloud provider will default to on-demand\n      operator: In\n      values: [\"spot\", \"on-demand\"]\n  provider:\n    instanceProfile: KarpenterNodeInstanceProfile-eks-karpenter-demo\n  ttlSecondsAfterEmpty: 30  \nEOF</code></pre> \n<p>The <code>ttlSecondsAfterEmpty</code> value configures Karpenter to terminate empty nodes. If this value is disabled, nodes will never scale down due to low utilization. To learn more, see <a href=\"https://karpenter.sh/docs/provisioner-crd/\">Provisioner custom resource definitions (CRDs)</a> on the Karpenter site.</p> \n<p>Karpenter is now active and ready to begin provisioning nodes in your cluster. Create some pods using a deployment, and watch Karpenter provision nodes in response.</p> \n<pre><code>$ kubectl create deployment --name inflate \\\n          --image=public.ecr.aws/eks-distro/kubernetes/pause:3.2</code></pre> \n<p>Let’s scale the deployment and check out the logs of the Karpenter controller.</p> \n<pre><code>$ kubectl scale deployment inflate --replicas 10\n$ kubectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -o name)\n2021-11-23T04:46:11.280Z        INFO    controller.allocation.provisioner/default       Starting provisioning loop      {\"commit\": \"abc12345\"}\n2021-11-23T04:46:11.280Z        INFO    controller.allocation.provisioner/default       Waiting to batch additional pods        {\"commit\": \"abc123456\"}\n2021-11-23T04:46:12.452Z        INFO    controller.allocation.provisioner/default       Found 9 provisionable pods      {\"commit\": \"abc12345\"}\n2021-11-23T04:46:13.689Z        INFO    controller.allocation.provisioner/default       Computed packing for 10 pod(s) with instance type option(s) [m5.large]  {\"commit\": \" abc123456\"}\n2021-11-23T04:46:16.228Z        INFO    controller.allocation.provisioner/default       Launched instance: i-01234abcdef, type: m5.large, zone: us-east-1a, hostname: ip-192-168-0-0.ec2.internal    {\"commit\": \"abc12345\"}\n2021-11-23T04:46:16.265Z        INFO    controller.allocation.provisioner/default       Bound 9 pod(s) to node ip-192-168-0-0.ec2.internal  {\"commit\": \"abc12345\"}\n2021-11-23T04:46:16.265Z        INFO    controller.allocation.provisioner/default       Watching for pod events {\"commit\": \"abc12345\"}</code></pre> \n<p>The provisioner’s controller listens for Pods changes, which launched a new instance and bound the provisionable Pods into the new nodes.</p> \n<p>Now, delete the deployment. After 30 seconds (<code>ttlSecondsAfterEmpty = 30</code>), Karpenter should terminate the empty nodes.</p> \n<pre><code>$ kubectl delete deployment inflate\n$ kubectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -o name)\n2021-11-23T04:46:18.953Z        INFO    controller.allocation.provisioner/default       Watching for pod events {\"commit\": \"abc12345\"}\n2021-11-23T04:49:05.805Z        INFO    controller.Node Added TTL to empty node ip-192-168-0-0.ec2.internal {\"commit\": \"abc12345\"}\n2021-11-23T04:49:35.823Z        INFO    controller.Node Triggering termination after 30s for empty node ip-192-168-0-0.ec2.internal {\"commit\": \"abc12345\"}\n2021-11-23T04:49:35.849Z        INFO    controller.Termination  Cordoned node ip-192-168-116-109.ec2.internal   {\"commit\": \"abc12345\"}\n2021-11-23T04:49:36.521Z        INFO    controller.Termination  Deleted node ip-192-168-0-0.ec2.internal    {\"commit\": \"abc12345\"}</code></pre> \n<p>If you delete a node with kubectl, Karpenter will gracefully cordon, drain, and shut down the corresponding instance. Under the hood, Karpenter adds a finalizer to the node object, which blocks deletion until all pods are drained, and the instance is terminated.</p> \n<p><strong><u>Things to Know</u></strong><br> Here are a couple of things to keep in mind about Kapenter features:</p> \n<p><strong>Accelerated Computing</strong>: Karpenter works with all kinds of Kubernetes applications, but it performs particularly well for use cases that require rapid provisioning and deprovisioning large numbers of diverse compute resources quickly. For example, this includes batch jobs to train machine learning models, run simulations, or perform complex financial calculations. You can leverage custom resources of nvidia.com/gpu, amd.com/gpu, and aws.amazon.com/neuron for use cases that require accelerated EC2 instances.</p> \n<p><strong>Provisioners Compatibility</strong>: Kapenter provisioners are designed to work alongside static capacity management solutions like Amazon EKS managed node groups and EC2 Auto Scaling groups. You may choose to manage the entirety of your capacity using provisioners, a mixed model with both dynamic and statically managed capacity, or a fully static approach. We recommend not using Kubernetes Cluster Autoscaler at the same time as Karpenter because both systems scale up nodes in response to unschedulable pods. If configured together, both systems will race to launch or terminate instances for these pods.</p> \n<p><strong><u>Join our Karpenter Community</u></strong><br> Karpenter’s community is open to everyone. Give it a try, and join our <a href=\"https://github.com/awslabs/karpenter/blob/main/WORKING_GROUP.md\">working group meeting</a>&nbsp;for future releases that interest you. As I said, we welcome <a href=\"https://github.com/awslabs/karpenter/blob/main/CONTRIBUTING.md\">any contributions</a> such as bug reports, new features, corrections, or additional documentation.</p> \n<p>To learn more about Karpenter, see the <a href=\"https://karpenter.sh/docs/\">documentation</a> and <a href=\"https://youtu.be/3f0Tv7IiQQw?t=19028\">demo video</a> from AWS Container Day.</p> \n<p>– <a href=\"https://twitter.com/channyun\">Channy</a></p><div>\n<a href=\"http://feeds.feedburner.com/~ff/AmazonWebServicesBlog?a=P7NIs4otsZk:-6QmXLVPeUY:yIl2AUoC8zA\"><img src=\"http://feeds.feedburner.com/~ff/AmazonWebServicesBlog?d=yIl2AUoC8zA\" border=\"0\"></a> <a href=\"http://feeds.feedburner.com/~ff/AmazonWebServicesBlog?a=P7NIs4otsZk:-6QmXLVPeUY:dnMXMwOfBR0\"><img src=\"http://feeds.feedburner.com/~ff/AmazonWebServicesBlog?d=dnMXMwOfBR0\" border=\"0\"></a> <a href=\"http://feeds.feedburner.com/~ff/AmazonWebServicesBlog?a=P7NIs4otsZk:-6QmXLVPeUY:7Q72WNTAKBA\"><img src=\"http://feeds.feedburner.com/~ff/AmazonWebServicesBlog?d=7Q72WNTAKBA\" border=\"0\"></a>\n</div>",
            "url": "https://aws.amazon.com/blogs/aws/introducing-karpenter-an-open-source-high-performance-kubernetes-cluster-autoscaler/",
            "title": "Introducing Karpenter – An Open-Source High-Performance Kubernetes Cluster Autoscaler",
            "date_modified": "2021-11-30T00:45:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29377515\">Comments</a>",
            "url": "https://blog.jetbrains.com/blog/2021/11/29/welcome-to-fleet/",
            "title": "Fleet, a Lightweight IDE from JetBrains",
            "date_modified": "2021-11-29T08:20:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29361004\">Comments</a>",
            "url": "https://microsoft.github.io/code-with-engineering-playbook/ENG-FUNDAMENTALS-CHECKLIST/",
            "title": "Code with Engineering Playbook",
            "date_modified": "2021-11-27T15:46:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29340015\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29340015",
            "title": "Ask HN: Tips on Sysadmin Job",
            "date_modified": "2021-11-25T10:24:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29329610\">Comments</a>",
            "url": "https://ory.dev/login-spa-react-nextjs-authentication-example-api-open-source/",
            "title": "Show HN: Open-Source Auth for NextJS / React",
            "date_modified": "2021-11-24T13:08:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29328761\">Comments</a>",
            "url": "https://www.mindtheproduct.com/overengineering-can-kill-your-product/",
            "title": "Overengineering can kill your product",
            "date_modified": "2021-11-24T10:53:18.000Z"
        },
        {
            "content_html": "<figure>\n      <img alt=\"the magic kingdom castle at disneyland\" src=\"https://cdn.vox-cdn.com/thumbor/ZgnDTr8mtoQBmWbGYMzxDObHCmw=/0x106:2040x1254/640x360/cdn.vox-cdn.com/uploads/chorus_image/image/70184640/jbareham_190531_ply0909_1334.0.jpg\">\n        <figcaption>Photo: James Bareham/Polygon</figcaption>\n    </figure>\n\n  <p>Defunctland’s new video goes deeper on crowd management than you ever thought possible</p>\n  <p>\n    <a href=\"https://www.polygon.com/22798637/disney-fastpass-2021-disneyland-disney-world-defunctland\">Continue reading…</a>\n  </p>",
            "url": "https://www.polygon.com/22798637/disney-fastpass-2021-disneyland-disney-world-defunctland",
            "title": "This is how Disney parks’ Fastpass spun completely out of control",
            "date_modified": "2021-11-23T22:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29323315\">Comments</a>",
            "url": "https://wasp-lang.dev/blog/2021/11/22/fundraising-learnings",
            "title": "Our fundraising learning post-YC",
            "date_modified": "2021-11-23T20:51:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29316357\">Comments</a>",
            "url": "https://sifted.eu/articles/brexit-london-new-york-leaving/",
            "title": "I’m leaving London for NYC and taking my tech startup",
            "date_modified": "2021-11-23T10:43:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29297229\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29297229",
            "title": "Ask HN: Great tools for solo SaaS founders?",
            "date_modified": "2021-11-21T16:08:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29264754\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29264754",
            "title": "Ask HN: What makes a good engineering blog?",
            "date_modified": "2021-11-18T12:42:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29264374\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29264374",
            "title": "Ask HN: What are you using for public documentation these days?",
            "date_modified": "2021-11-18T11:43:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29178710\">Comments</a>",
            "url": "https://github.com/Jarred-Sumner/hop",
            "title": "Hop: Faster than unzip and tar at reading individual files",
            "date_modified": "2021-11-10T18:50:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29175311\">Comments</a>",
            "url": "https://www.apple.com/business/essentials/",
            "title": "Apple Business Essentials",
            "date_modified": "2021-11-10T15:03:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29163319\">Comments</a>",
            "url": "https://hakibenita.com/postgresql-unknown-features",
            "title": "Lesser Known PostgreSQL Features",
            "date_modified": "2021-11-09T15:50:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29152008\">Comments</a>",
            "url": "https://rosefinchapp.com",
            "title": "Show HN: Insomnia-like client for SQLite",
            "date_modified": "2021-11-08T17:52:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29099746\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=29099746",
            "title": "Ask HN: Who's not sucky to work for?",
            "date_modified": "2021-11-03T20:19:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29068828\">Comments</a>",
            "url": "https://eisel.me/devtool-allocators",
            "title": "Faster Mac dev tools with custom allocators",
            "date_modified": "2021-11-01T16:18:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29018410\">Comments</a>",
            "url": "https://medium.com/swlh/the-codeless-cto-471ee6069288",
            "title": "The Codeless CTO",
            "date_modified": "2021-10-27T20:04:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=29003356\">Comments</a>",
            "url": "https://heap.io/blog/how-postgres-audit-tables-saved-us-from-taking-down-production",
            "title": "Postgres audit tables saved us from taking down production",
            "date_modified": "2021-10-26T17:32:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28992426\">Comments</a>",
            "url": "https://www.osohq.com/post/cross-platform-rust-libraries",
            "title": "We Built a Cross-Platform Library with Rust",
            "date_modified": "2021-10-25T20:02:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28987009\">Comments</a>",
            "url": "https://www.koyeb.com/blog/building-a-multi-region-service-mesh-with-kuma-envoy-anycast-bgp-and-mtls",
            "title": "Building a Multi-Region Service Mesh",
            "date_modified": "2021-10-25T12:29:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28881151\">Comments</a>",
            "url": "https://blog.battlefy.com/how-not-to-blow-up-the-production-database-424c162dccc6?gi=8ec446ad5e47",
            "title": "How not to blow up the production database",
            "date_modified": "2021-10-15T18:23:10.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/czhols/lynis_security_auditing_tool_for_linux\">Comments</a></p>",
            "url": "https://github.com/CISOfy/lynis",
            "title": "Lynis - Security auditing tool for Linux, macOS, and UNIX-based systems",
            "date_modified": "2021-10-13T21:56:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28854478\">Comments</a>",
            "url": "https://sysdig.com/blog/container-security-best-practices/",
            "title": "Container security best practices: Ultimate guide",
            "date_modified": "2021-10-13T16:47:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28853329\">Comments</a>",
            "url": "https://github.com/maxgoedjen/secretive",
            "title": "Secretive: An app for storing and managing SSH keys in the Secure Enclave",
            "date_modified": "2021-10-13T15:22:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28838132\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=28838132",
            "title": "Ask HN: Solo-preneurs, how do you DevOps to save time?",
            "date_modified": "2021-10-12T10:35:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28797485\">Comments</a>",
            "url": "https://www.simplethread.com/20-things-ive-learned-in-my-20-years-as-a-software-engineer/",
            "title": "Things I’ve learned in my 20 years as a software engineer",
            "date_modified": "2021-10-08T10:04:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28776786\">Comments</a>",
            "url": "https://www.notion.so/blog/sharding-postgres-at-notion",
            "title": "Lessons learned from sharding Postgres at Notion",
            "date_modified": "2021-10-06T18:50:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28709886\">Comments</a>",
            "url": "https://authzed.com/blog/spicedb-is-open-source/",
            "title": "SpiceDB Is Open Source",
            "date_modified": "2021-09-30T18:58:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28706570\">Comments</a>",
            "url": "https://boringseo.org/",
            "title": "Boring SEO guide for the non-lazy",
            "date_modified": "2021-09-30T14:15:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28682011\">Comments</a>",
            "url": "https://www.fastmail.com/1password/",
            "title": "Masked Email from Fastmail and 1Password",
            "date_modified": "2021-09-28T12:33:17.000Z"
        },
        {
            "content_html": "<figure>\n      <img alt=\"a collage of screenshots from inside job\" src=\"https://cdn.vox-cdn.com/thumbor/WSuPLo-IBTaXZZp9FpZkNmRw3nU=/0x312:3000x2000/640x360/cdn.vox-cdn.com/uploads/chorus_image/image/69915515/jbareham_210927_ecl1085_inside_job.0.jpeg\">\n        <figcaption>Graphic: James Bareham/Polygon | Source images: Netflix</figcaption>\n    </figure>\n\n\n  \t\t<p>‘[Adult animation] is ripe for new kinds of formats and new types of stories’</p>\n  <p>\n    <a href=\"https://www.polygon.com/animation-cartoons/22696218/inside-job-netflix-creator-shion-takeuchi-gravity-falls-interview\">Continue reading…</a>\n  </p>",
            "url": "https://www.polygon.com/animation-cartoons/22696218/inside-job-netflix-creator-shion-takeuchi-gravity-falls-interview",
            "title": "Gravity Falls writer Shion Takeuchi on her latest creation: Netflix’s Inside Job",
            "date_modified": "2021-09-27T16:40:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28643848\">Comments</a>",
            "url": "https://building.nubank.com.br/why-we-killed-our-end-to-end-test-suite/",
            "title": "We killed our end-to-end test suite",
            "date_modified": "2021-09-24T15:36:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28634436\">Comments</a>",
            "url": "https://shufflesharding.com/posts/aws-sigv4-and-sigv4a",
            "title": "AWS SIGv4 and SIGv4A – How AWS signs and verifies API requests",
            "date_modified": "2021-09-23T19:56:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28628615\">Comments</a>",
            "url": "https://github.com/gmpetrov/utlimate-saas-js",
            "title": "The ultimate SaaS template",
            "date_modified": "2021-09-23T13:14:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28616043\">Comments</a>",
            "url": "https://motion.dev/",
            "title": "Show HN: Motion One, the Web Animations API for Everyone",
            "date_modified": "2021-09-22T13:27:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28576617\">Comments</a>",
            "url": "https://rabexc.org/posts/pitfalls-of-ssh-agents",
            "title": "The pitfalls of using SSH-agent, or how to use an agent safely",
            "date_modified": "2021-09-18T14:40:49.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28563954\">Comments</a>",
            "url": "https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/",
            "title": "How to add ISSN metadata to a web page",
            "date_modified": "2021-09-17T11:13:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28554754\">Comments</a>",
            "url": "https://blog.px.dev/ebpf-openssl-tracing/",
            "title": "Tracing SSL/TLS connections using eBPF",
            "date_modified": "2021-09-16T17:44:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28553775\">Comments</a>",
            "url": "https://microfounder.com/blog/cofounder-in-marketing",
            "title": "Developer, you may need a co-founder in marketing",
            "date_modified": "2021-09-16T16:26:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28551568\">Comments</a>",
            "url": "https://linuxplumbersconf.org/event/11/contributions/954/",
            "title": "Use of eBPF in CPU Scheduler",
            "date_modified": "2021-09-16T13:34:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28535016\">Comments</a>",
            "url": "https://awsteele.com/blog/2021/09/15/aws-federation-comes-to-github-actions.html",
            "title": "AWS federation comes to GitHub Actions",
            "date_modified": "2021-09-15T03:44:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28428088\">Comments</a>",
            "url": "https://github.com/containerd/nerdctl",
            "title": "Docker compatible open source: Containerd nerdctl",
            "date_modified": "2021-09-05T21:41:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28425095\">Comments</a>",
            "url": "https://matklad.github.io/2021/09/04/fast-rust-builds.html",
            "title": "Fast Rust Builds",
            "date_modified": "2021-09-05T15:36:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28415035\">Comments</a>",
            "url": "https://psychedelicsociety.org.uk/news/bw06yvjnvblq47vwoocv5owchiyf8b",
            "title": "Do psychedelics make you liberal? Not always",
            "date_modified": "2021-09-04T13:46:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28299053\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=28299053",
            "title": "Ask HN: Companies of one, what is your tech stack?",
            "date_modified": "2021-08-25T07:54:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28295348\">Comments</a>",
            "url": "https://fly.io/blog/api-tokens-a-tedious-survey/",
            "title": "API Tokens: A Tedious Survey",
            "date_modified": "2021-08-24T21:41:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28291851\">Comments</a>",
            "url": "https://ramp.com/blog/ramp-finance-automation-platform",
            "title": "Ramp raises $300M",
            "date_modified": "2021-08-24T17:11:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28283030\">Comments</a>",
            "url": "https://sysprog21.github.io/lkmpg/",
            "title": "Linux Kernel Module Programming Guide",
            "date_modified": "2021-08-23T22:49:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28223515\">Comments</a>",
            "url": "https://pop.com/home",
            "title": "Show HN: Pop.com – pair programming with low-latency, Screenhero-style sharing",
            "date_modified": "2021-08-18T16:25:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28195746\">Comments</a>",
            "url": "https://www.theguardian.com/us-news/2021/aug/16/guantanamo-detainee-mansoor-adayfi",
            "title": "The US should be held accountable: Gitmo survivor on the war on terror’s failure",
            "date_modified": "2021-08-16T07:25:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28121060\">Comments</a>",
            "url": "https://sigstore.dev",
            "title": "Sigstore – A new standard for signing, verifying and protecting software",
            "date_modified": "2021-08-09T19:14:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28113034\">Comments</a>",
            "url": "https://www.brendangregg.com/blog/2019-08-19/bpftrace.html",
            "title": "A thorough introduction to bpftrace",
            "date_modified": "2021-08-09T05:33:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28063597\">Comments</a>",
            "url": "https://hookdeck.com?ref=hn",
            "title": "Show HN: Hookdeck – an infrastructure to consume webhooks",
            "date_modified": "2021-08-04T17:14:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=28024972\">Comments</a>",
            "url": "https://www.banterly.net/2021/07/31/new-in-git-switch-and-restore/",
            "title": "New in Git: switch and restore",
            "date_modified": "2021-08-01T09:11:57.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/lljjjb/verifiable_supply_chain_metadata_for\">Comments</a></p>",
            "url": "https://security.googleblog.com/2021/06/verifiable-supply-chain-metadata-for.html",
            "title": "Verifiable Supply Chain Metadata for Tekton",
            "date_modified": "2021-07-13T15:29:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27785323\">Comments</a>",
            "url": "https://earthly.dev/",
            "title": "Show HN: Earthly – Better Builds",
            "date_modified": "2021-07-09T16:21:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27766655\">Comments</a>",
            "url": "https://runwayml.com/",
            "title": "Runway – Create Impossible Video",
            "date_modified": "2021-07-07T22:49:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27722947\">Comments</a>",
            "url": "https://brendangregg.com/blog/2021-07-03/how-to-add-bpf-observability.html",
            "title": "How to add eBPF observability to your product",
            "date_modified": "2021-07-03T16:50:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27649378\">Comments</a>",
            "url": "https://jez.io/pandoc-markdown-css-theme/",
            "title": "Show HN: Pandoc Markdown CSS Theme",
            "date_modified": "2021-06-27T07:20:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27579693\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=27579693",
            "title": "Ask HN: I was hit with a patent troll lawsuit, how do I deal with it?",
            "date_modified": "2021-06-21T14:48:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27567313\">Comments</a>",
            "url": "https://www.lrb.co.uk/the-paper/v43/n12/sharon-marcus/more-husband-than-female",
            "title": "More Husband Than Female",
            "date_modified": "2021-06-20T05:16:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27390512\">Comments</a>",
            "url": "https://brendangregg.com/blog/2021-06-04/an-unbelievable-demo.html",
            "title": "An Unbelievable Demo",
            "date_modified": "2021-06-04T04:21:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27382299\">Comments</a>",
            "url": "https://lwn.net/SubscriberLink/858023/1caabaef50d4946b/",
            "title": "Auditing Io_uring",
            "date_modified": "2021-06-03T14:56:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27333260\">Comments</a>",
            "url": "https://old.reddit.com/r/ExperiencedDevs/comments/nmodyl/drunk_post_things_ive_learned_as_a_sr_engineer/",
            "title": "Drunk Post: Things I've Learned as a Sr Engineer",
            "date_modified": "2021-05-30T13:55:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27311964\">Comments</a>",
            "url": "https://boringavatars.com/",
            "title": "Boring Avatars – react library to generate custom avatars",
            "date_modified": "2021-05-28T05:15:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27213567\">Comments</a>",
            "url": "https://www.marcolancini.it/2021/blog-cloud-security-roadmap/",
            "title": "On Establishing a Cloud Security Program",
            "date_modified": "2021-05-19T19:49:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27197873\">Comments</a>",
            "url": "https://www.planetscale.com/blog/announcing-planetscale-the-database-for-developers",
            "title": "PlanetScale – Database for Developers",
            "date_modified": "2021-05-18T17:16:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27192884\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=27192884",
            "title": "Ask HN: Desperately need “sales for nerds” advice",
            "date_modified": "2021-05-18T10:04:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27182946\">Comments</a>",
            "url": "https://www.apple.com/newsroom/2021/05/apple-music-announces-spatial-audio-and-lossless-audio/",
            "title": "Apple Music Announces Spatial Audio and Lossless Audio",
            "date_modified": "2021-05-17T13:06:31.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/kjdyec/5_things_i_learned_while_developing\">Comments</a></p>",
            "url": "https://arnon.dk/5-things-i-learned-developing-billing-system/",
            "title": "5 things I learned while developing a billing system",
            "date_modified": "2021-05-15T17:22:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27166637\">Comments</a>",
            "url": "https://www.theguardian.com/lifeandstyle/2019/nov/06/i-was-an-astrologer-how-it-works-psychics",
            "title": "I was an astrologer – here's how it really works",
            "date_modified": "2021-05-15T16:31:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27081084\">Comments</a>",
            "url": "https://awslabs.github.io/smithy/",
            "title": "Smithy: A language for defining services and SDKs",
            "date_modified": "2021-05-07T21:31:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27063991\">Comments</a>",
            "url": "https://meta.stackexchange.com/questions/364048/were-switching-to-system-fonts-on-may-10-2021",
            "title": "Stack Overflow switches to system fonts on May 10, 2021",
            "date_modified": "2021-05-06T14:43:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=27022075\">Comments</a>",
            "url": "https://github.com/255kb/stack-on-a-budget",
            "title": "Stack on a Budget – A collection of services with free tiers",
            "date_modified": "2021-05-03T06:01:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26991505\">Comments</a>",
            "url": "https://github.blog/2021-04-29-scaling-monorepo-maintenance/",
            "title": "Scaling Monorepo Maintenance at GitHub",
            "date_modified": "2021-04-30T09:52:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26961013\">Comments</a>",
            "url": "https://medium.com/airbnb-engineering/our-journey-towards-cloud-efficiency-9c02ba04ade8",
            "title": "Our Journey Towards Cloud Efficiency",
            "date_modified": "2021-04-27T20:21:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26956846\">Comments</a>",
            "url": "https://console.dev/qa/keydb-john-sully/",
            "title": "KeyDB CEO Interview: Getting into YC with a Fork of Redis",
            "date_modified": "2021-04-27T15:01:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26872457\">Comments</a>",
            "url": "https://github.com/RobinCsl/awesome-js-tooling-not-in-js",
            "title": "JavaScript Tooling Not in JavaScript",
            "date_modified": "2021-04-20T09:24:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26860227\">Comments</a>",
            "url": "https://developers.google.com/style/word-list",
            "title": "Word usage guidance and alternative terms",
            "date_modified": "2021-04-19T08:15:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26856387\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=26856387",
            "title": "Ask HN: How do you run better meetings?",
            "date_modified": "2021-04-18T21:08:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26856053\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=26856053",
            "title": "Ask HN: Considering colocated hosting over cloud, what should I know?",
            "date_modified": "2021-04-18T20:32:54.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/opu18i/listing_contents_remote_zip_archive\">Comments</a></p>",
            "url": "https://rhardih.io/2021/04/listing-the-contents-of-a-remote-zip-archive-without-downloading-the-entire-file/",
            "title": "Listing the contents of a remote ZIP archive, without downloading the entire file",
            "date_modified": "2021-04-18T15:47:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26851433\">Comments</a>",
            "url": "https://itamargilad.com/product-led/",
            "title": "Product-Led Is Just as Bad as Sales-Driven",
            "date_modified": "2021-04-18T10:58:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26850682\">Comments</a>",
            "url": "https://jakedeichert.com/blog/reducing-rust-incremental-compilation-times-on-macos-by-70-percent/",
            "title": "Reducing Rust Incremental Compilation Times on macOS by 70%",
            "date_modified": "2021-04-18T07:59:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26847780\">Comments</a>",
            "url": "https://madned.substack.com/p/thanks-for-the-bonus-i-quit",
            "title": "Thanks for the Bonus, I Quit",
            "date_modified": "2021-04-17T21:55:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26746280\">Comments</a>",
            "url": "https://fly.io/blog/docker-without-docker/",
            "title": "Docker without Docker",
            "date_modified": "2021-04-09T03:06:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26738417\">Comments</a>",
            "url": "https://github.blog/2021-04-05-how-we-scaled-github-api-sharded-replicated-rate-limiter-redis/",
            "title": "We scaled the GitHub API with a sharded, replicated rate limiter in Redis",
            "date_modified": "2021-04-08T13:26:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26737771\">Comments</a>",
            "url": "https://anthonynsimon.com/blog/one-man-saas-architecture/",
            "title": "The Architecture of a One-Man SaaS",
            "date_modified": "2021-04-08T12:14:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26701149\">Comments</a>",
            "url": "https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/",
            "title": "Behind GitHub’s new authentication token formats",
            "date_modified": "2021-04-05T16:32:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26686378\">Comments</a>",
            "url": "https://mmartinfahy.medium.com/my-experience-releasing-3-failed-saas-products-44e61cbde424",
            "title": "My experience releasing 3 failed SaaS products",
            "date_modified": "2021-04-04T02:10:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26650493\">Comments</a>",
            "url": "https://airbyte.io/articles/our-story/the-deck-we-used-to-raise-our-seed-with-accel-in-13-days/",
            "title": "The deck we used to raise our seed",
            "date_modified": "2021-03-31T17:47:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26612894\">Comments</a>",
            "url": "http://www.pathsensitive.com/2021/03/developer-tools-can-be-magic-instead.html",
            "title": "Developer tools can be magic but instead collect dust",
            "date_modified": "2021-03-28T18:34:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26582263\">Comments</a>",
            "url": "https://github.blog/2021-03-25-how-github-actions-renders-large-scale-logs/",
            "title": "How GitHub Actions renders large-scale logs",
            "date_modified": "2021-03-25T16:30:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26563993\">Comments</a>",
            "url": "http://dan.bodar.com/2012/02/28/crazy-fast-build-times-or-when-10-seconds-starts-to-make-you-nervous/",
            "title": "Crazy fast build times, or when 10 seconds starts to make you nervous (2012)",
            "date_modified": "2021-03-24T05:44:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26553639\">Comments</a>",
            "url": "https://www.lennysnewsletter.com/p/saas-pricing-strategy",
            "title": "How to price your SaaS product",
            "date_modified": "2021-03-23T11:16:51.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26474331\">Comments</a>",
            "url": "https://magicbell.io/blog/the-ycombinator-experience-remote-edition",
            "title": "The Y Combinator Experience – Remote Edition",
            "date_modified": "2021-03-16T08:49:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26459184\">Comments</a>",
            "url": "https://superuser.com/questions/1633073/why-are-tar-xz-files-15x-smaller-when-using-pythons-tar-library-compared-to-mac",
            "title": "Why are tar.xz files 15x smaller when using Python's tar compared to macOS tar?",
            "date_modified": "2021-03-14T21:06:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26415558\">Comments</a>",
            "url": "https://github.com/gajus/slonik",
            "title": "Slonik: A PostgreSQL client with strict types, detailed logging, and assertions",
            "date_modified": "2021-03-10T20:12:23.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26312516\">Comments</a>",
            "url": "https://bdickason.com/posts/speed-is-the-killer-feature/",
            "title": "Speed Is the Killer Feature",
            "date_modified": "2021-03-02T06:40:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26284750\">Comments</a>",
            "url": "https://github.com/kuchin/awesome-cto",
            "title": "Awesome CTO",
            "date_modified": "2021-02-27T13:37:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26210179\">Comments</a>",
            "url": "https://www.lesswrong.com/posts/jHnFBHrwiNb5xvLBM/second-citizenships-residencies-and-or-temporary-relocation",
            "title": "Second Citizenships, Residencies, and/or Temporary Relocation",
            "date_modified": "2021-02-21T02:07:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26154095\">Comments</a>",
            "url": "https://lethain.com/managing-staff-plus-engineers/",
            "title": "Managing Staff-Plus Engineers",
            "date_modified": "2021-02-16T14:23:15.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26144706\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=26144706",
            "title": "Ask HN: Is “contact us for pricing” a dark pattern?",
            "date_modified": "2021-02-15T17:16:13.000Z"
        },
        {
            "content_html": "<figure>\n      <img alt=\"Austin Powers in a hot tub\" src=\"https://cdn.vox-cdn.com/thumbor/yzDJwcqzXyot_3P4hZRPXDU6x1Y=/0x0:2132x1199/640x360/cdn.vox-cdn.com/uploads/chorus_image/image/68814428/Screen_Shot_2021_02_12_at_10.32.38_PM.0.png\">\n        <figcaption>Image: New Line Cinema</figcaption>\n    </figure>\n\n  <p>A few shagedelic recommendations from across Netflix, HBO Max, and Amazon</p>\n  <p>\n    <a href=\"https://www.polygon.com/streaming/2021/2/13/22278710/best-new-movies-february-2021-netflix-amazon-hbo-disney-plus\">Continue reading…</a>\n  </p>",
            "url": "https://www.polygon.com/streaming/2021/2/13/22278710/best-new-movies-february-2021-netflix-amazon-hbo-disney-plus",
            "title": "11 best movies new to streaming to watch in February",
            "date_modified": "2021-02-13T15:32:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=26059517\">Comments</a>",
            "url": "https://nofreeplan.com",
            "title": "Don't Offer a Free Plan",
            "date_modified": "2021-02-08T00:26:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25887373\">Comments</a>",
            "url": "https://chriskiehl.com/article/thoughts-after-6-years",
            "title": "Software engineering topics I changed my mind on",
            "date_modified": "2021-01-24T00:02:48.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25686678\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=25686678",
            "title": "Ask HN: Which companies work like Gumroad?",
            "date_modified": "2021-01-08T16:56:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25610949\">Comments</a>",
            "url": "https://compiledcssinjs.com/",
            "title": "Show HN: Compiled, buildtime atomic CSS in JavaScript and all your favorite APIs",
            "date_modified": "2021-01-02T06:49:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25552342\">Comments</a>",
            "url": "https://francescodilorenzo.com/saas-we-pay-for",
            "title": "SaaS We Happily Pay For",
            "date_modified": "2020-12-27T16:59:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25544397\">Comments</a>",
            "url": "https://github.com/dpapathanasiou/simple-graph",
            "title": "Show HN: Simple-graph – a graph database in SQLite",
            "date_modified": "2020-12-26T16:31:47.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/dffvyt/tales_autistic_developer_expert\">Comments</a></p>",
            "url": "https://dev.to/baweaver/tales-of-the-autistic-developer-the-expert-pk2",
            "title": "Tales of the Autistic Developer - The Expert",
            "date_modified": "2020-12-26T03:47:07.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/dcgho5/toolchains_as_code\">Comments</a></p>",
            "url": "http://calculist.org/blog/2020/12/21/tac/",
            "title": "Toolchains as Code",
            "date_modified": "2020-12-21T17:12:00.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/nfhxbo/announcing_volta_1_0_0\">Comments</a></p>",
            "url": "https://blog.volta.sh/2020/12/21/announcing-volta-100/",
            "title": "Announcing Volta 1.0.0",
            "date_modified": "2020-12-21T17:11:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25481837\">Comments</a>",
            "url": "https://leanpub.com/implementing-ddd-cqrs-and-event-sourcing",
            "title": "Show HN: I wrote a book on implementing DDD, CQRS and Event Sourcing",
            "date_modified": "2020-12-19T22:24:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25466304\">Comments</a>",
            "url": "https://www.bloomberg.com/news/articles/2020-12-18/czech-startup-founders-turn-billionaires-without-vc-help",
            "title": "Jetbrains founders turn billionaires without VC help",
            "date_modified": "2020-12-18T12:06:06.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/virbxa/papers_i_love_gg\">Comments</a></p>",
            "url": "https://buttondown.email/nelhage/archive/papers-i-love-gg/",
            "title": "Papers I love: gg",
            "date_modified": "2020-12-06T17:22:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25304257\">Comments</a>",
            "url": "https://clig.dev/",
            "title": "CLI Guidelines – A guide to help you write better command-line programs",
            "date_modified": "2020-12-04T16:33:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25288188\">Comments</a>",
            "url": "https://github.com/turbo/pg-shortkey",
            "title": "pg-shortkey: YouTube-Like Short IDs as Postgres Primary Keys",
            "date_modified": "2020-12-03T12:26:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25271464\">Comments</a>",
            "url": "https://archive.org/details/attentionkmartshoppers",
            "title": "Attention K-Mart Shoppers: Piped Music Collection on the Internet Archive",
            "date_modified": "2020-12-01T23:31:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25217291\">Comments</a>",
            "url": "https://about.sourcegraph.com/blog/ex-googler-guide-dev-tools/",
            "title": "An ex-Googler’s guide to dev tools",
            "date_modified": "2020-11-26T05:59:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25213371\">Comments</a>",
            "url": "https://linkerd.io/2020/11/23/topology-aware-service-routing-on-kubernetes-with-linkerd/",
            "title": "Topology-Aware Service Routing on Kubernetes with Linkerd",
            "date_modified": "2020-11-25T19:41:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25205292\">Comments</a>",
            "url": "https://pingcap.com/blog/run-database-on-aws-with-better-performance-and-lower-cost/",
            "title": "How to Run a Database on AWS with Better Performance and Lower Cost",
            "date_modified": "2020-11-25T02:18:40.000Z"
        },
        {
            "content_html": "<div><div><p>Earlier this year, we announced <a href=\"https://cloud.google.com/blog/products/networking/better-load-balancing-for-app-engine-cloud-run-and-functions\">Cloud Load Balancer support for Cloud Run</a>. You might wonder, aren't Cloud Run services already load-balanced? Yes, each *.run.app endpoint load balances traffic between an autoscaling set of containers. However, with the Cloud Balancing integration for serverless platforms, you can now fine tune lower levels of your networking stack. In this article, we will explain the use cases for this type of set up and build an HTTPS load balancer from ground up for Cloud Run using <a href=\"https://www.terraform.io/\" target=\"_blank\">Terraform</a>.</p><h3>Why use a Load Balancer for Cloud Run?</h3><p>Every Cloud Run service comes with a load-balanced *.run.app endpoint that’s secured with HTTPS. Furthermore, Cloud Run also lets you <a href=\"https://cloud.google.com/run/docs/mapping-custom-domains\">map your custom domains</a> to your services. However, if you want to customize other details about how your load balancing works, you need to provision a <a href=\"https://cloud.google.com/load-balancing\">Cloud HTTP load balancer</a> yourself.</p><p>Here are a few reasons to run your Cloud Run service behind a Cloud Load Balancer:</p><a href=\"https://cloud.google.com/cdn/docs/setting-up-cdn-with-serverless\"></a><ul><a href=\"https://cloud.google.com/cdn/docs/setting-up-cdn-with-serverless\"></a><li><a href=\"https://cloud.google.com/cdn/docs/setting-up-cdn-with-serverless\"></a><a href=\"https://cloud.google.com/cdn/docs/setting-up-cdn-with-serverless\">Serving static assets with CDN</a> since Cloud CDN integrates with Cloud Load Balancing</li><li><a href=\"https://cloud.google.com/run/docs/multiple-regions\">Serving traffic from multiple regions</a> since Cloud Run is a regional service but you can provision a load balancer with a global anycast IP and route users to the closest available region.</li><li>Serve content from mixed backends, for example your /static path can be served from a storage bucket, /api can go to a Kubernetes cluster.</li><li>Bring your own TLS certificates, such as wildcard certificates you might have purchased.</li><li>Customize networking settings, such as TLS versions and ciphers supported.</li><li>Authenticating and enforcing authorization for specific users or groups with <a href=\"https://cloud.google.com/iap\">Cloud IAP</a> (this does not work yet with Cloud Run, however, stay tuned)</li><li>Configure WAF or DDoS protection with <a href=\"https://cloud.google.com/armor\">Cloud Armor</a>.</li></ul><p>The list goes on, Cloud HTTP Load Balancing has quite a lot of <a href=\"https://cloud.google.com/load-balancing#features\">features</a>.</p><h3>Why use Terraform for this?</h3><p>The short answer is that a Cloud HTTP Load Balancer consists of <a href=\"https://cloud.google.com/load-balancing/docs/https/ext-https-lb-simple\">many networking resources</a> that you need to create and connect to each other. There’s no single \"load balancer\" object in GCP APIs.</p><p>To understand the upcoming task, let's take a look at the resources involved:</p><a href=\"https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address\"></a><ul><a href=\"https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address\"></a><li><a href=\"https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address\"></a><a href=\"https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address\">global IP address</a> for your load balancer</li><li>Google-managed <a href=\"https://cloud.google.com/load-balancing/docs/ssl-certificates\">SSL certificate</a> (or bring your own)</li><li><a href=\"https://cloud.google.com/load-balancing/docs/using-forwarding-rules\">forwarding rules</a> to associate IP address with backends</li><li><a href=\"https://cloud.google.com/load-balancing/docs/target-proxies\">target HTTPS proxy</a> to terminate your HTTPS traffic</li><li><a href=\"https://cloud.google.com/load-balancing/docs/target-proxies\">target HTTP proxy</a> to receive HTTP traffic and redirect to HTTPS</li><li><a href=\"https://cloud.google.com/load-balancing/docs/url-map\">URL maps</a> to specify routing rules for URL path patterns.</li><li><a href=\"https://cloud.google.com/load-balancing/docs/backend-service\">backend service</a> to keep track of eligible backends</li><li><a href=\"https://cloud.google.com/load-balancing/docs/negs/serverless-neg-concepts\">network endpoint group</a> allowing you to register serverless apps as backends.</li></ul><p>As you might imagine, it is very tedious to provision and connect these resources just to achieve a simple task like enabling CDN.</p><p>You could write a bash script with the gcloud command-line tool to create these resources; however, it will be cumbersome to check corner cases like if a resource already exists, or modified manually later. You would also need to write a cleanup script to delete what you provisioned.</p><p>This is where Terraform shines. It lets you declaratively configure cloud resources and create/destroy your stack in different GCP projects efficiently with just a few commands.</p><h3>Building a load balancer: The hard way</h3><p>The goal of this article is to intentionally show you the hard way for each resource involved in creating a load balancer using Terraform configuration language.</p><p>We'll start with a few Terraform variables:</p><ul><li>var.name: used for naming the load balancer resources</li><li>var.project: GCP project ID</li><li>var.region: region to deploy the Cloud Run service</li><li>var.domain: a domain name for your managed SSL certificate</li></ul><p>First, let's define our Terraform providers:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>Then, let's deploy a new Cloud Run service named \"hello\" with the sample image, and allow unauthenticated access to it:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>If you manage your Cloud Run deployments outside Terraform, that’s perfectly fine: You can still import the equivalent <a href=\"https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/cloud_run_service\" target=\"_blank\">data source</a> to reference that service in your configuration file.</p><p>Next, we’ll reserve a global IPv4 address for our global load balancer:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>Next, let's create a managed SSL certificate that's issued and renewed by Google for you:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>If you want to bring your own SSL certificates, you can create your own <a href=\"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_ssl_certificate\" target=\"_blank\">google_compute_ssl_certificate</a> resource instead.</p><p>Then, make a network endpoint group (NEG) out of your serverless service:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>Now, let's create a backend service that'll keep track of these network endpoints:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>If you want to configure load balancing features such as CDN, Cloud Armor or custom headers, the <a href=\"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_backend_service\" target=\"_blank\">google_compute_backend_service</a> resource is the right place.</p><p>Then, create an empty URL map that doesn't have any routing rules and sends the traffic to this backend service we created earlier:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>Next, configure an HTTPS proxy to terminate the traffic with the Google-managed certificate and route it to the URL map:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>Finally, configure a global forwarding rule to route the HTTPS traffic on the IP address to the target HTTPS proxy:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>After writing this module, create an output variable that lists your IP address:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>When you apply these resources and set your domain’s DNS records to point to this IP address, a huge machinery starts rolling its wheels.</p><p>Soon, Google Cloud will verify your domain name ownership and start to issue a managed TLS certificate for your domain. After the certificate is issued, the load balancer configuration will propagate to all of Google’s edge locations around the globe. This might take a while, but once it starts working.</p><p>Astute readers will notice that so far this setup cannot handle the unencrypted HTTP traffic. Therefore, any requests that come over port 80 are dropped, which is not great for usability. To mitigate this, you need to create a new set of URL map, target HTTP proxy, and a forwarding rule with these:</p></div></div><div><div><div><div><pre><code></code></pre></div></div></div></div><div><div><p>As we are nearing 150 lines of Terraform configuration, you probably have realized by now, this is indeed the hard way to get a load balancer for your serverless applications.</p><p>If you like to try out this example, feel free to obtain a copy of this Terraform configuration file <a href=\"https://gist.github.com/ahmetb/506b829de37aa94632d79982a984e638\" target=\"_blank\">from this gist</a> and adopt it for your needs.</p><h3>Building a load balancer: The easy way</h3><p>To address the complexity in this experience, we have been designing a new Terraform module specifically to skip the hard parts of deploying serverless applications behind a Cloud HTTPS Load Balancer.</p><p>Stay tuned for the next article where we take a closer look at this new Terraform module and show you how easier this can get.</p></div></div><div><div><section><a data-analytics=\"{\n                       &quot;event&quot;: &quot;page interaction&quot;,\n                       &quot;category&quot;: &quot;article lead&quot;,\n                       &quot;action&quot;: &quot;related article - inline&quot;,\n                       &quot;label&quot;: &quot;article: {slug}&quot;\n                     }\" href=\"https://gweb-cloudblog-publish.appspot.com/products/networking/better-load-balancing-for-app-engine-cloud-run-and-functions/\"><div><p>Related Article</p><div><div><div></div></div><div><h4>Global HTTP(S) Load Balancing and CDN now support serverless compute</h4><p>Now, our App Engine, Cloud Run and Cloud Functions serverless compute offerings can take advantage of global load balancing and Cloud CDN.</p><div><span>Read Article<svg role=\"presentation\"><use xlink:href=\"#mi-arrow-forward\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"></use></svg></span></div></div></div></div></a></section></div></div>",
            "url": "https://cloud.google.com/blog/topics/developers-practitioners/serverless-load-balancing-terraform-hard-way/",
            "title": "Serverless load balancing with Terraform: The hard way",
            "date_modified": "2020-11-24T18:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25200324\">Comments</a>",
            "url": "https://aws.amazon.com/blogs/opensource/why-aws-loves-rust-and-how-wed-like-to-help/",
            "title": "Why AWS loves Rust, and how we’d like to help",
            "date_modified": "2020-11-24T17:00:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25199070\">Comments</a>",
            "url": "https://github.com/microsoft/TypeScript/wiki/Performance",
            "title": "Tips for Performant TypeScript",
            "date_modified": "2020-11-24T15:06:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25197041\">Comments</a>",
            "url": "https://adamfard.com/blog/enterprise-ux",
            "title": "Enterprise UX Design: Make me think",
            "date_modified": "2020-11-24T10:07:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25192733\">Comments</a>",
            "url": "https://www.netice9.com/blog/guide-to-oomkill-alerting-in-kubernetes-clusters",
            "title": "Guide to OOMKill Alerting in Kubernetes Clusters",
            "date_modified": "2020-11-23T22:31:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25186342\">Comments</a>",
            "url": "https://panelbear.com/blog/tech-stack/",
            "title": "The Tech Stack of a One-Man SaaS",
            "date_modified": "2020-11-23T13:06:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25184331\">Comments</a>",
            "url": "https://www.ox.ac.uk/news/2020-11-23-oxford-university-breakthrough-global-covid-19-vaccine",
            "title": "Oxford University breakthrough on global COVID-19 vaccine",
            "date_modified": "2020-11-23T07:25:28.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25171066\">Comments</a>",
            "url": "https://foundersatwork.posthaven.com/startups-the-very-beginning",
            "title": "Startups: The Beginning",
            "date_modified": "2020-11-21T17:27:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25160202\">Comments</a>",
            "url": "https://vale.dev/",
            "title": "The Vale Programming Language",
            "date_modified": "2020-11-20T13:25:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25159129\">Comments</a>",
            "url": "https://github.com/artipie/artipie",
            "title": "New binary artifact management tool",
            "date_modified": "2020-11-20T10:33:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25159097\">Comments</a>",
            "url": "https://github.com/PostgREST/postgrest",
            "title": "PostgREST: REST API for any Postgres database",
            "date_modified": "2020-11-20T10:25:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25159038\">Comments</a>",
            "url": "https://devfonts.gafi.dev/",
            "title": "Dev Fonts",
            "date_modified": "2020-11-20T10:14:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25155627\">Comments</a>",
            "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/",
            "title": "TypeScript 4.1",
            "date_modified": "2020-11-19T23:28:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25140604\">Comments</a>",
            "url": "https://blog.tailwindcss.com/tailwindcss-v2",
            "title": "TailwindCSS v2.0",
            "date_modified": "2020-11-18T18:42:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25124675\">Comments</a>",
            "url": "https://macosicons.com/",
            "title": "macOS Icons",
            "date_modified": "2020-11-17T15:17:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25121416\">Comments</a>",
            "url": "https://changelog.com/posts/git-is-simply-too-hard",
            "title": "Git is too hard",
            "date_modified": "2020-11-17T08:43:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25119033\">Comments</a>",
            "url": "https://www.userinterviewexchange.com/blog/user-interview-template",
            "title": "Show HN: I created a user interview template",
            "date_modified": "2020-11-17T00:39:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25114214\">Comments</a>",
            "url": "https://www.themvpsprint.com/p/how-and-when-to-acquire-saas-users",
            "title": "How and when to acquire SaaS users",
            "date_modified": "2020-11-16T17:15:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25113440\">Comments</a>",
            "url": "https://tomcam.github.io/postgres/",
            "title": "PostgreSQL psql command line tutorial and cheat sheet",
            "date_modified": "2020-11-16T16:18:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/qzdqit/how_do_you_write_simple_explanations\">Comments</a></p>",
            "url": "https://jvns.ca/blog/2020/11/15/simple-explanations-without-sounding-condescending/",
            "title": "How do you write simple explanations without sounding condescending?",
            "date_modified": "2020-11-16T02:52:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25084470\">Comments</a>",
            "url": "https://linc.sh/blog/durable-objects-in-production",
            "title": "Durable Objects in Production",
            "date_modified": "2020-11-13T17:09:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25081711\">Comments</a>",
            "url": "https://landshark.io/2020/11/13/saas-needs-a-single-point-of-purchase.html",
            "title": "SaaS Needs a Single Point of Purchase",
            "date_modified": "2020-11-13T13:03:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25076170\">Comments</a>",
            "url": "https://github.com/jemmaissroff/find_github_email",
            "title": "Show HN: Find GitHub Users' Email Address",
            "date_modified": "2020-11-12T22:38:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25075739\">Comments</a>",
            "url": "https://vectorized.io/redpanda",
            "title": "Redpanda – A Kafka-compatible streaming platform for mission-critical workloads",
            "date_modified": "2020-11-12T22:04:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25070416\">Comments</a>",
            "url": "http://koyeb.com/blog/announcing-the-koyeb-serverless-engine-docker-containers-and-continuous-deployment-of-functions",
            "title": "Koyeb Serverless Engine: Docker Containers, Continuous Deployment of Functions",
            "date_modified": "2020-11-12T14:47:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25068782\">Comments</a>",
            "url": "https://blog.m3o.com/2020/11/12/netlify-for-the-frontend-micro-for-the-backend.html",
            "title": "Netlify for the frontend, Micro for the backend",
            "date_modified": "2020-11-12T11:44:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25066587\">Comments</a>",
            "url": "https://www.quantamagazine.org/physicists-pin-down-nuclear-reaction-from-moments-after-the-big-bang-20201111/",
            "title": "Physicists Pin Down Nuclear Reaction from Moments After the Big Bang",
            "date_modified": "2020-11-12T04:55:47.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25058045\">Comments</a>",
            "url": "https://pgstats.dev/",
            "title": "Postgres Observability",
            "date_modified": "2020-11-11T13:12:51.000Z"
        },
        {
            "content_html": "<p><span>A few years ago I was preparing my talk for a conference and couldn’t find a good illustration of Postgres internals. This is how Postgres Observability was created – a diagram that gives a simplified view of all system views and functions for Postgres monitoring and tracking statistics.</span></p>\n<p><span>I’ve been updating the static version after each release and it got busier and busier overtime, so I decided that it is time to create a more in depth version of it.&nbsp;</span></p>\n<p><span>So here is the updated for PostgreSQL 13 interactive version of PostgreSQL Observability </span><a href=\"https://pgstats.dev\"><i><span>Pgstats.dev</span></i></a><i><span>.</span></i></p>\n<p><span>It now has additional information about each internal and each stat. Just follow the links and you can discover more about each one of Postgres components and ways they interact.</span></p>\n<p><span>Share it!&nbsp;</span></p>\n<p>The post <a href=\"https://dataegret.com/2020/11/interactive-new-look-for-postgresql-observability/\" rel=\"nofollow\">Interactive new look for PostgreSQL Observability</a> appeared first on <a href=\"https://dataegret.com\" rel=\"nofollow\">Data Egret</a>.</p>",
            "url": "https://postgr.es/p/4Xn",
            "title": "Alexey Lesovsky: Interactive new look for PostgreSQL Observability",
            "date_modified": "2020-11-10T10:11:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25036526\">Comments</a>",
            "url": "https://openstartups.listt.xyz",
            "title": "Startups that have hit more than $1K MRR in about 12-24 months",
            "date_modified": "2020-11-09T16:36:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25035752\">Comments</a>",
            "url": "https://nocomplexity.com/documents/arplaybook/index.html",
            "title": "Architecture Playbook",
            "date_modified": "2020-11-09T15:31:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=25024224\">Comments</a>",
            "url": "https://postgresqlco.nf/en/doc/param/",
            "title": "PostgreSQL Configuration for Humans",
            "date_modified": "2020-11-08T08:52:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24998203\">Comments</a>",
            "url": "https://slavovojacek.medium.com/grpc-on-node-js-with-buf-and-typescript-part-1-5aad61bab03b",
            "title": "gRPC on Node.js with Buf and TypeScript",
            "date_modified": "2020-11-05T13:55:10.000Z"
        },
        {
            "content_html": "<img alt=\"Over the Garden Wall: Wirt, Greg, and the frog band\" src=\"https://cdn.vox-cdn.com/thumbor/agYoHk-vC3A8wSEPHazqltvFWt8=/1x0:1366x768/640x360/cdn.vox-cdn.com/uploads/chorus_image/image/67730882/Over3.0.png\">\n\n  <small>Image: Cartoon Network</small>\n\n  <p>The tracks arrive on the 6th anniversary of the miniseries</p>\n  <p>\n    <a href=\"https://www.polygon.com/animation-cartoons/2020/11/3/21547498/over-the-garden-wall-new-songs\">Continue reading…</a>\n  </p>",
            "url": "https://www.polygon.com/animation-cartoons/2020/11/3/21547498/over-the-garden-wall-new-songs",
            "title": "Over the Garden Wall composers release 6 new songs that were cut from the series",
            "date_modified": "2020-11-03T14:41:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24974541\">Comments</a>",
            "url": "https://twitter.com/HammadH4/status/1323400864892022784",
            "title": "How not to SaaS – lessons from 2 years of building 3k MRR SaaS",
            "date_modified": "2020-11-02T23:07:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24965115\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=24965115",
            "title": "Ask HN: How to effectively get feedback from users?",
            "date_modified": "2020-11-02T05:49:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24963742\">Comments</a>",
            "url": "https://vlfig.me/posts/microservices",
            "title": "Microservices – architecture nihilism in minimalism's clothes",
            "date_modified": "2020-11-02T00:16:23.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/wifgmt/how_use_skopeo_migrate_off_docker_hub\">Comments</a></p>",
            "url": "https://jjasghar.github.io/blog/2020/10/30/how-to-use-skopeo-to-migrate-off-dockerhub/",
            "title": "How to use skopeo to migrate off Docker Hub",
            "date_modified": "2020-10-31T17:28:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24952234\">Comments</a>",
            "url": "https://dayzero.substack.com/p/product-market-fit-for-a-b2b-company",
            "title": "What does product market fit look for a B2B startup?",
            "date_modified": "2020-10-31T15:33:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24944852\">Comments</a>",
            "url": "https://changelog.com/podcast/417#transcript",
            "title": "What's so about Postgres? (transcript)",
            "date_modified": "2020-10-30T17:43:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24935880\">Comments</a>",
            "url": "https://one.google.com/about/vpn",
            "title": "VPN by Google One",
            "date_modified": "2020-10-29T21:05:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24925899\">Comments</a>",
            "url": "https://krebsonsecurity.com/2020/10/fbi-dhs-hhs-warn-of-imminent-credible-ransomware-threat-against-u-s-hospitals/",
            "title": "FBI, DHS, HHS Warn of Imminent,Credible Ransomware Threat Against U.S. Hospitals",
            "date_modified": "2020-10-29T00:49:22.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/cmxl7u/designing_for_web_by_mark_boulton\">Comments</a></p>",
            "url": "https://designingfortheweb.co.uk/",
            "title": "Designing for the Web, by Mark Boulton",
            "date_modified": "2020-10-28T23:50:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24916629\">Comments</a>",
            "url": "https://www.richardesigns.co.uk/2020/10/27/what-was-remote-yc-like.html",
            "title": "What Was Remote YC Like?",
            "date_modified": "2020-10-28T08:47:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24911033\">Comments</a>",
            "url": "https://redditblog.com/2020/10/27/evolving-reddits-workforce/",
            "title": "Evolving Reddit’s Workforce",
            "date_modified": "2020-10-27T19:46:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24882413\">Comments</a>",
            "url": "https://github.com/rome/tools",
            "title": "The Rome Toolchain: A linter, compiler, bundler, and more",
            "date_modified": "2020-10-24T22:29:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24876665\">Comments</a>",
            "url": "https://alexdanco.com/2020/10/23/six-lessons-from-six-months-at-shopify/",
            "title": "Six Lessons from Six Months at Shopify",
            "date_modified": "2020-10-24T03:16:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24872911\">Comments</a>",
            "url": "https://github.com/github/dmca/blob/master/2020/10/2020-10-23-RIAA.md",
            "title": "YouTube-dl has received a DMCA takedown from RIAA",
            "date_modified": "2020-10-23T19:26:35.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24836676\">Comments</a>",
            "url": "https://pitch.com/blog/pitch-launches",
            "title": "Pitch Launches",
            "date_modified": "2020-10-20T12:49:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24829635\">Comments</a>",
            "url": "https://ianvanagas.com/2020/10/19/how-discord-won/",
            "title": "How Discord Won",
            "date_modified": "2020-10-19T18:56:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24821141\">Comments</a>",
            "url": "https://timhutton.github.io/GravityIsNotAForce/",
            "title": "Gravity is not a force – free-fall parabolas are straight lines in spacetime",
            "date_modified": "2020-10-18T21:07:26.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24815640\">Comments</a>",
            "url": "https://temporal.io/",
            "title": "Temporal: Build Invincible Apps",
            "date_modified": "2020-10-18T04:57:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24813795\">Comments</a>",
            "url": "https://keepthescore.co/blog/posts/deleting_the_production_database/",
            "title": "I deleted the production database by accident",
            "date_modified": "2020-10-17T22:09:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24805748\">Comments</a>",
            "url": "https://www.atlassian.com/migration/journey-to-cloud?jobid=104830907&subid=1515944789",
            "title": "Atlassian moves to cloud, going to stop selling server licenses",
            "date_modified": "2020-10-16T22:47:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24790055\">Comments</a>",
            "url": "https://www.hashicorp.com/blog/announcing-waypoint",
            "title": "HashiCorp Waypoint",
            "date_modified": "2020-10-15T16:02:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/srzzv6/terraform_at_scale_modualized\">Comments</a></p>",
            "url": "https://medium.com/faun/terraform-at-scale-modualized-hierachical-layout-cb5dbe5a368d",
            "title": "Terraform at Scale — Modualized Hierachical Layout",
            "date_modified": "2020-10-15T11:25:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24777671\">Comments</a>",
            "url": "https://www.percona.com/blog/2020/10/14/announcing-pg_stat_monitor-tech-preview-get-better-insights-into-query-performance-in-postgresql/",
            "title": "Pg_stat_monitor – better insights into query performance in PostgreSQL",
            "date_modified": "2020-10-14T15:29:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24766900\">Comments</a>",
            "url": "https://quaderno.io/blog/is-hacker-news-a-good-platform-to-launch-your-product/",
            "title": "Is Hacker News a Good Platform to Launch Your Product?",
            "date_modified": "2020-10-13T16:11:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24754778\">Comments</a>",
            "url": "https://medium.com/faun/terraform-at-scale-modualized-hierachical-layout-cb5dbe5a368d",
            "title": "Terraform at Scale",
            "date_modified": "2020-10-12T14:40:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24753940\">Comments</a>",
            "url": "https://blog.cloudflare.com/introducing-cloudflare-one/",
            "title": "Cloudflare One",
            "date_modified": "2020-10-12T13:01:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24747492\">Comments</a>",
            "url": "http://dtrace.org/blogs/bmc/2020/10/11/rust-after-the-honeymoon/",
            "title": "Rust After the Honeymoon",
            "date_modified": "2020-10-11T17:31:14.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24738182\">Comments</a>",
            "url": "https://www.cncf.io/announcements/2020/10/07/cloud-native-computing-foundation-announces-rook-graduation/",
            "title": "Cloud Native Computing Foundation Announces Rook Graduation",
            "date_modified": "2020-10-10T11:24:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24735414\">Comments</a>",
            "url": "https://www.forbes.com/sites/alexkonrad/2020/10/09/twilio-to-acquire-cloud-startup-segment-for-3-billion/",
            "title": "Twilio Set to Acquire Cloud Customer Data Startup Segment for $3.2B",
            "date_modified": "2020-10-09T23:23:15.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ykk54d/amazon_s_build_system\">Comments</a></p>",
            "url": "https://gist.github.com/terabyte/15a2d3d407285b8b5a0a7964dd6283b0",
            "title": "Amazon's Build System",
            "date_modified": "2020-10-08T18:30:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24719525\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=24719525",
            "title": "Ask HN: What are the pros / cons of using monorepos?",
            "date_modified": "2020-10-08T14:46:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24703230\">Comments</a>",
            "url": "https://research.securitum.com/mutation-xss-via-mathml-mutation-dompurify-2-0-17-bypass/",
            "title": "DOMPurify bypass: XSS via HTML namespace confusion",
            "date_modified": "2020-10-06T22:43:10.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/nhi2cj/why_you_should_migrate_your_heroku\">Comments</a></p>",
            "url": "https://pawelurbanek.com/heroku-postgres-aws-rds",
            "title": "Why You Should Migrate your Heroku Postgres Database to AWS RDS",
            "date_modified": "2020-10-06T08:21:20.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24689339\">Comments</a>",
            "url": "https://canny.io/blog/how-we-built-a-1m-arr-saas-startup/",
            "title": "How we built a $1m ARR SaaS startup",
            "date_modified": "2020-10-05T16:39:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24673917\">Comments</a>",
            "url": "https://www.btao.org/2020/10/02/npm-trust.html",
            "title": "A Web of Trust for NPM",
            "date_modified": "2020-10-03T18:51:42.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24670252\">Comments</a>",
            "url": "https://cra.mr/an-honest-review-of-gatsby/",
            "title": "An Honest Review of Gatsby",
            "date_modified": "2020-10-03T07:18:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24649729\">Comments</a>",
            "url": "https://www.iqt.org/bewear-python-typosquatting-is-about-more-than-typos/",
            "title": "The Danger of a Typo: An Investigation of Python and Typosquatting",
            "date_modified": "2020-10-01T11:35:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24627363\">Comments</a>",
            "url": "https://www.socialcooling.com/",
            "title": "Social Cooling",
            "date_modified": "2020-09-29T13:12:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24621132\">Comments</a>",
            "url": "https://www.mikesonders.com/saas-website-content/",
            "title": "The SaaS website content you need to close sales",
            "date_modified": "2020-09-28T20:56:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24604687\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=24604687",
            "title": "Ask HN: Technical skills helpful for building a startup today",
            "date_modified": "2020-09-27T06:38:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24601579\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=24601579",
            "title": "Ask HN: How to learn sales?",
            "date_modified": "2020-09-26T19:44:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24588853\">Comments</a>",
            "url": "https://baremetrics.com/blog/saas-financial-model",
            "title": "SaaS Financial Model",
            "date_modified": "2020-09-25T11:59:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24557961\">Comments</a>",
            "url": "https://zachdaniel.dev/learning-how-to-learn-japanese/",
            "title": "Learning How to Learn Japanese",
            "date_modified": "2020-09-22T18:15:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24543433\">Comments</a>",
            "url": "https://stripe.com/en-in/atlas/guides/saas-pricing",
            "title": "Pricing Low-Touch SaaS",
            "date_modified": "2020-09-21T13:52:43.000Z"
        },
        {
            "content_html": "<img src=\"https://cdn.vox-cdn.com/thumbor/UtAyGXkcjyuVdyQKW35egRDXwAQ=/0x0:1100x733/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/67412194/49f6d15a3f028900802f14b75ebd134214_4572_01_HR_color.rhorizontal.w1100.0.jpg\">\n\n\n  <p>When does a model own her own image?</p>\n  <p>\n    <a href=\"https://www.thecut.com/article/emily-ratajkowski-owning-my-image-essay.html\">Continue reading…</a>\n  </p>",
            "url": "https://www.thecut.com/article/emily-ratajkowski-owning-my-image-essay.html",
            "title": "Buying myself back",
            "date_modified": "2020-09-15T21:48:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24467136\">Comments</a>",
            "url": "https://relinx.io/2020/09/14/old-good-database-design/",
            "title": "Old, Good Database Design",
            "date_modified": "2020-09-14T05:40:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24453721\">Comments</a>",
            "url": "https://about.gitlab.com/blog/2020/09/11/gitlab-pg-upgrade/",
            "title": "How we upgraded PostgreSQL",
            "date_modified": "2020-09-12T16:51:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24437715\">Comments</a>",
            "url": "http://amyunger.com/blog/2020/09/10/staff-engineer-at-heroku.html",
            "title": "How I operated as a staff engineer at Heroku",
            "date_modified": "2020-09-10T23:32:34.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24391612\">Comments</a>",
            "url": "https://www.polarsparc.com/xhtml/OAuth2-OIDC.html",
            "title": "Understanding OAuth2 and OpenID Connect",
            "date_modified": "2020-09-06T14:37:09.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24351566\">Comments</a>",
            "url": "https://erickhun.com/posts/kubernetes-faster-services-no-cpu-limits/",
            "title": "Kubernetes: Make your services faster by removing CPU limits",
            "date_modified": "2020-09-02T09:47:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24346317\">Comments</a>",
            "url": "https://www.howmanydayssinceajwtalgnonevuln.com",
            "title": "# of days since the last alg=none JWT vulnerability",
            "date_modified": "2020-09-01T19:59:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24324142\">Comments</a>",
            "url": "https://blog.codonomics.com/2020/08/multi-tenant-architectures.html",
            "title": "Multi-Tenant Architectures",
            "date_modified": "2020-08-30T17:06:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24282940\">Comments</a>",
            "url": "https://tryhexadecimal.com/journal/business-taxes",
            "title": "First tax year with Stripe Atlas",
            "date_modified": "2020-08-26T14:46:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24243190\">Comments</a>",
            "url": "https://cloudflaremirrors.com/",
            "title": "Cloudflare Package Repository Mirrors",
            "date_modified": "2020-08-22T10:38:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24184070\">Comments</a>",
            "url": "https://www.cybertec-postgresql.com/en/recursive-queries-postgresql/",
            "title": "Understanding Recursive Queries in PostgreSQL",
            "date_modified": "2020-08-17T04:46:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=24043987\">Comments</a>",
            "url": "https://abe-winter.github.io/2020/08/03/yr-of-git.html",
            "title": "One year of automatic DB migrations from Git",
            "date_modified": "2020-08-03T22:02:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23997998\">Comments</a>",
            "url": "https://heap.io/blog/engineering/postgresqls-powerful-new-join-type-lateral",
            "title": "PostgreSQL’s New Join Type: LATERAL (2014)",
            "date_modified": "2020-07-30T13:43:09.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/aweif8/resume_md_markdown_resume\">Comments</a></p>",
            "url": "https://mike.place/2020/resume.md/",
            "title": "resume.md: a Markdown resume",
            "date_modified": "2020-07-26T22:28:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23935943\">Comments</a>",
            "url": "https://github.com/hwayne/awesome-cold-showers",
            "title": "Cold Showers: For when people get too hyped up about things",
            "date_modified": "2020-07-24T05:08:17.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23919509\">Comments</a>",
            "url": "https://react-spectrum.adobe.com/react-spectrum/",
            "title": "A React implementation of Spectrum, Adobe’s design system",
            "date_modified": "2020-07-22T17:39:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23916842\">Comments</a>",
            "url": "https://uiplaybook.dev",
            "title": "Show HN: UI Playbook – A documented collection of UI components",
            "date_modified": "2020-07-22T13:42:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23903789\">Comments</a>",
            "url": "https://subvert.substack.com/p/stripe-building-a-developer-cult",
            "title": "Building a Developer Cult",
            "date_modified": "2020-07-21T02:19:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23839212\">Comments</a>",
            "url": "https://aws.amazon.com/blogs/containers/introducing-aws-copilot/",
            "title": "AWS Copilot",
            "date_modified": "2020-07-14T23:08:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23788003\">Comments</a>",
            "url": "https://arstechnica.com/gaming/2020/07/valve-secrets-spill-over-including-half-life-3-in-new-steam-documentary-app/",
            "title": "Valve secrets spill over, including Half-Life 3, in new Steam documentary app",
            "date_modified": "2020-07-10T03:57:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23778878\">Comments</a>",
            "url": "https://tyrrrz.me/blog/unit-testing-is-overrated",
            "title": "Unit Testing Is Overrated",
            "date_modified": "2020-07-09T11:06:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23759673\">Comments</a>",
            "url": "https://www.youtube.com/watch?v=g_ZdcHSFGv0&t=10s",
            "title": "Professor solves 240 computer science exam problems in 4 hours",
            "date_modified": "2020-07-07T14:34:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23693029\">Comments</a>",
            "url": "http://linear.app/",
            "title": "Linear – A fast issue tracker",
            "date_modified": "2020-06-30T18:16:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23682450\">Comments</a>",
            "url": "https://static.sched.com/hosted_files/ossna2020/fc/Running%20Postgres-as-a-Service%20in%20Kubernetes.pdf",
            "title": "Running Postgres in Kubernetes [pdf]",
            "date_modified": "2020-06-29T20:29:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23678409\">Comments</a>",
            "url": "https://sambleckley.com/writing/npm.html",
            "title": "Worrying about the NPM Ecosystem",
            "date_modified": "2020-06-29T15:21:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23677740\">Comments</a>",
            "url": "https://medium.com/@melissamcewen/a-guide-to-hacker-news-for-people-who-arent-men-5737bc3e68a",
            "title": "A Guide to Hacker News for People Who Aren’t Men – Melissa Mcewen",
            "date_modified": "2020-06-29T14:09:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23629438\">Comments</a>",
            "url": "https://blog.fleetsmith.com/fleetsmith-acquired-by-apple/",
            "title": "Apple Acquires Fleetsmith",
            "date_modified": "2020-06-24T15:55:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23591569\">Comments</a>",
            "url": "https://softwareengineeringdaily.com/2020/02/13/setting-the-stage-for-platform-engineering/",
            "title": "The Rise of Platform Engineering",
            "date_modified": "2020-06-21T12:07:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23573476\">Comments</a>",
            "url": "http://www.mpe.mpg.de/7461761/news20200619",
            "title": "Our deepest view of the X-ray sky",
            "date_modified": "2020-06-19T12:00:59.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23563823\">Comments</a>",
            "url": "https://github.blog/2020-06-18-introducing-github-super-linter-one-linter-to-rule-them-all/",
            "title": "GitHub Super Linter: one linter to rule them all",
            "date_modified": "2020-06-18T15:14:40.000Z"
        },
        {
            "content_html": "<img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--YgfgrC1r--/c_fit,fl_progressive,q_80,w_636/rcge2t8umevgcqb85qyo.png\"><p>It was May 2015, and the Hotel Surya in Varanasi, India, was hosting “Inner Awakening,” the flagship spiritual training program of Paramahamsa Nithyananda. To his followers, he is a god incarnate, “His Divine Holiness Bhagavan Sri Nithyananda Paramashivam,” the living avatar of Shiva, or, as one devotee put it, “the…</p><p><a href=\"https://gizmodo.com/she-built-a-shady-guru-s-youtube-army-now-she-s-his-fi-1843283794\">Read more...</a></p>",
            "url": "https://gizmodo.com/she-built-a-shady-guru-s-youtube-army-now-she-s-his-fi-1843283794",
            "title": "She Built a Shady Guru’s YouTube Army. Now She’s His Fiercest Critic—But Who Will Believe Her?",
            "date_modified": "2020-06-17T14:30:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23541863\">Comments</a>",
            "url": "https://jeffreymoro.com/blog/2020-02-13-against-cop-shit/",
            "title": "Against Cop Shit",
            "date_modified": "2020-06-16T17:42:54.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23536416\">Comments</a>",
            "url": "https://www.vice.com/en_us/article/v7gd9b/facebook-helped-fbi-hack-child-predator-buster-hernandez",
            "title": "Facebook Helped Develop a Tails Exploit",
            "date_modified": "2020-06-16T06:19:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23522602\">Comments</a>",
            "url": "https://docs.google.com/spreadsheets/u/1/d/1YmZeSxpz52qT-10tkCjWOwOGkQqle7Wd1P7ZM1wMW0E/htmlview?pru=AAABcql6DI8*mIHYeMnoj9XWUp3Svb_KZA",
            "title": "George Floyd Protest – police brutality videos on Twitter",
            "date_modified": "2020-06-15T00:12:40.000Z"
        },
        {
            "content_html": "<p><strong>Author:</strong> Zach Corleissen, Cloud Native Computing Foundation</p>\n<p><em>Editor's note: Zach is one of the chairs for the Kubernetes documentation special interest group (SIG Docs).</em></p>\n<p>I'm pleased to announce that the <a href=\"https://kubernetes.io\">Kubernetes website</a> now features the <a href=\"https://docsy.dev\">Docsy Hugo theme</a>.</p>\n<p>The Docsy theme improves the site's organization and navigability, and opens a path to improved API references. After over 4 years with few meaningful UX improvements, Docsy implements some best practices for technical content. The theme makes the Kubernetes site easier to read and makes individual pages easier to navigate. It gives the site a much-needed facelift.</p>\n<p>For example: adding a right-hand rail for navigating topics on the page. No more scrolling up to navigate!</p>\n<p>The theme opens a path for future improvements to the website. The Docsy functionality I'm most excited about is the theme's <a href=\"https://www.docsy.dev/docs/adding-content/shortcodes/#swaggerui\"><code>swaggerui</code> shortcode</a>, which provides native support for generating API references from an OpenAPI spec. The CNCF is partnering with <a href=\"https://developers.google.com/season-of-docs\">Google Season of Docs</a> (GSoD) for staffing to make better API references a reality in Q4 this year. We're hopeful to be chosen, and we're looking forward to Google's list of announced projects on August 16th. Better API references have been a personal goal since I first started working with SIG Docs in 2017. It's exciting to see the goal within reach.</p>\n<p>One of SIG Docs' tech leads, <a href=\"https://github.com/kbhawkey\">Karen Bradshaw</a> did a lot of heavy lifting to fix a wide range of site compatibility issues, including a fix to the last of our <a href=\"https://github.com/kubernetes/website/pull/21359\">legacy pieces</a> when we <a href=\"https://kubernetes.io/blog/2020/06/better-docs-ux-with-docsy/2018-05-05-hugo-migration/\">migrated from Jekyll to Hugo</a> in 2018. Our other tech leads, <a href=\"https://github.com/sftim\">Tim Bannister</a> and <a href=\"https://github.com/onlydole\">Taylor Dolezal</a> provided extensive reviews.</p>\n<p>Thanks also to <a href=\"https://bep.is/\">Björn-Erik Pedersen</a>, who provided invaluable advice about how to navigate a Hugo upgrade beyond <a href=\"https://gohugo.io/news/0.60.0-relnotes/\">version 0.60.0</a>.</p>\n<p>The CNCF contracted with <a href=\"https://gearboxbuilt.com/\">Gearbox</a> in Victoria, BC to apply the theme to the site. Thanks to Aidan, Troy, and the rest of the team for all their work!</p>",
            "url": "https://kubernetes.io/blog/2020/06/better-docs-ux-with-docsy/",
            "title": "Blog: A Better Docs UX With Docsy",
            "date_modified": "2020-06-15T00:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23515813\">Comments</a>",
            "url": "https://edition.cnn.com/2020/06/13/media/seattle-fox-news-autonomous-zone-protest/index.html",
            "title": "Fox News publishes altered and misleading images of Seattle demonstrations",
            "date_modified": "2020-06-14T07:38:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23370768\">Comments</a>",
            "url": "https://health.ucdavis.edu/health-news/contenthub/autism-severity-can-change-substantially-during-early-childhood/2020/05",
            "title": "Autism severity can change substantially during early childhood, study suggests",
            "date_modified": "2020-05-31T16:54:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23206566\">Comments</a>",
            "url": "https://www.michael-noll.com/blog/2020/01/16/what-every-software-engineer-should-know-about-apache-kafka-fundamentals/",
            "title": "What every software engineer should know about Apache Kafka",
            "date_modified": "2020-05-16T19:40:58.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23154846\">Comments</a>",
            "url": "https://diagrams.mingrammer.com/",
            "title": "Diagram as Code",
            "date_modified": "2020-05-12T15:13:46.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23056976\">Comments</a>",
            "url": "https://www.nytimes.com/2020/03/03/magazine/hideo-kojima-death-stranding-video-game.html",
            "title": "Hideo Kojima’s Strange, Unforgettable Video-Game Worlds",
            "date_modified": "2020-05-03T05:05:11.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23035094\">Comments</a>",
            "url": "https://layerci.com",
            "title": "Show HN: Layer – Get a dozen staging servers per developer",
            "date_modified": "2020-04-30T19:31:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=23032276\">Comments</a>",
            "url": "https://www.talentica.com/blogs/distributed-transactions-are-not-micro-services/",
            "title": "Distributed transactions are not microservices",
            "date_modified": "2020-04-30T15:57:27.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22942278\">Comments</a>",
            "url": "https://medium.com/@rakyll/things-i-wished-more-developers-knew-about-databases-2d0178464f78",
            "title": "Things I Wished More Developers Knew About Databases",
            "date_modified": "2020-04-22T04:45:04.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22802522\">Comments</a>",
            "url": "https://nipponcolors.com",
            "title": "Nippon Colors",
            "date_modified": "2020-04-07T12:25:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22778309\">Comments</a>",
            "url": "https://gds.blog.gov.uk/2020/04/03/how-gov-uk-notify-reliably-sends-text-messages-to-users/",
            "title": "How GOV.UK Notify reliably sends text messages to users",
            "date_modified": "2020-04-04T14:11:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22775330\">Comments</a>",
            "url": "https://medium.com/@rbranson/10-things-i-hate-about-postgresql-20dbab8c2791",
            "title": "PostgreSQL's Imperfections",
            "date_modified": "2020-04-04T00:57:41.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22715982\">Comments</a>",
            "url": "https://github.com/mbrt/gmailctl",
            "title": "Gmailctl – Declarative Configuration for Gmail Filters",
            "date_modified": "2020-03-29T02:56:12.000Z"
        },
        {
            "content_html": "<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-41-19.jpeg\" alt=\"Meta for Mac.\"><p>Meta for Mac.</p></div>\n<p id=\"p2\">For the past year, I’ve been using a high-res Sony music player to listen to my personal music collection. I detailed the entire story in the December 2019 episode of our <a href=\"https://club.macstories.net/\" rel=\"noopener noreferrer\">Club-exclusive MacStories Unplugged podcast</a>, but in short: I still use Apple Music to stream music every day and discover new artists; however, for those times when I want to more intentionally listen to music without doing anything else, I like to sit down, put on my good Sony <a href=\"https://www.sony.com/electronics/headband-headphones/mdr-z1r\" rel=\"noopener noreferrer\">headphones</a>, and try to enjoy all the sonic details of my favorite songs that wouldn’t normally be revealed by AirPods or my iPad Pro’s speakers. But this post isn’t about how I’ve been dipping my toes into the wild world of audiophiles and high-resolution music; rather, I want to highlight an excellent Mac app I’ve been using to organize and edit the metadata of the FLAC music library I’ve been assembling over the past year.</p>\n<p id=\"p3\"></p>\n<p id=\"p4\">These days, when I think of an old album I want to repurchase in <a href=\"https://www.technics.com/us/high-res-audio/what-is-high-resolution-audio.html\" rel=\"noopener noreferrer\">high resolution</a> (either 16-bit or 24-bit FLAC), or if I come across a new release I instantly fall in love with, I go ahead and buy it as a standalone FLAC digital download.<sup id=\"fnref-62661-websites\"><a href=\"https://www.macstories.net/mac/editing-flac-metadata-with-meta-for-mac/#fn-62661-websites\" rel=\"noopener noreferrer\">1</a></sup> I then organize albums with a standard Artist ⇾ Album folder hierarchy in the Mac’s Finder, as pictured below:</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-42-16.jpeg\" alt=\"My FLAC music collection.\"><p>My FLAC music collection.</p></div>\n<p id=\"p6\">Before you ask: yes, I <em>could</em> do this file organization with my iPad Pro alone because the Sony music player I use (this <a href=\"https://www.sony.com/electronics/walkman/nw-wm1a\" rel=\"noopener noreferrer\">Walkman model</a>) can be connected via USB to the iPad (with <a href=\"https://www.amazon.co.jp/gp/product/B009F2GCX8/\" rel=\"noopener noreferrer\">this adapter</a>) and comes with a standard SD card for expandable storage. However, I prefer to purchase and download FLAC music on my Mac mini because my music collection is also backed up and mirrored to <a href=\"https://www.plex.tv/\" rel=\"noopener noreferrer\">Plex</a>, and the Mac mini – as you might imagine – is running a <a href=\"https://support.plex.tv/articles/200375666-plex-media-server-requirements/\" rel=\"noopener noreferrer\">Plex media server</a> instance in the background at all times. The music library is stored on a <a href=\"https://www.samsung.com/semiconductor/minisite/ssd/product/portable/t5/\" rel=\"noopener noreferrer\">1&nbsp;TB Samsung T5 external SSD</a> that’s connected via USB-C to the Mac mini; whenever I purchase new music, I manually copy it into the T5 as well as the Sony Walkman’s SD card via the Finder.</p>\n<p id=\"p7\">Most of the time, FLAC music I purchase online comes with correct built-in metadata for fields such as track number, year, disc, and album artwork. But sometimes it doesn’t, which leads to the unfortunate situation of ending up with songs on my Walkman that lack album artwork or feature extra text in their titles such as “Remastered” or “Explicit”. It’s particularly annoying when artwork is missing because it ruins the experience of looking at the now playing screen while I’m focused on enjoying music. Plex doesn’t have this issue: by virtue of being an online service, Plex can <a href=\"https://support.plex.tv/articles/upgrade-music-libraries-new-metadata-system/\" rel=\"noopener noreferrer\">search various databases</a> for correct metadata and automatically fill missing fields in my library. One of the reasons I enjoy listening to my personal music library the old-school way is that the Walkman is completely offline, but that comes with the disadvantage of being unable to fix incorrect metadata via the web.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-18-12-59-53.jpeg\" alt=\"An example of songs with incorrect metadata on my Walkman. The word &quot;Remastered&quot; shouldn't be part of the song title field.\"><p>An example of songs with incorrect metadata on my Walkman. The word \"Remastered\" shouldn't be part of the song title field.</p></div>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-27-14.jpeg\" alt=\"No album artwork makes me sad.\"><p>No album artwork makes me sad.</p></div>\n<p id=\"p10\">This is where <a href=\"https://www.nightbirdsevolve.com/meta/\" rel=\"noopener noreferrer\">Meta</a>, an advanced music tag editor for Mac developed by French indie developer Benjamin Jaeger, comes in. I came across Meta a few months ago when, frustrated with the ugliness and bloated nature of other desktop metadata editors, I took it upon myself to find a polished, modern tag editor designed specifically with Mac users in mind. Meta is <em>exactly</em> what I was looking for: the app is a modern Mac utility that supports all popular audio formats (from standard MP3 and MP4 to FLAC, DSF, and AIFF) and can write metadata formats such as ID3v1, ID3v2, MP4, and APE tags. Unlike other cross-platform or open-source tag editors, Meta’s feature set is focused on one area – editing metadata for your digital music collection – and supports all the common interface paradigms you’d expect from a professional Mac app running on Catalina.</p>\n<p id=\"p11\">Meta can do a lot of different things, and this is by no means a comprehensive review of the app: after all, my needs are fairly basic – I drop an album into the app, fix the tags that are incorrect, and close the app. The <a href=\"https://www.nightbirdsevolve.com/meta/\" rel=\"noopener noreferrer\">Meta website</a> has details and screenshots that cover all of the app’s features, and there’s also a full 3-day free trial you can use without limitations to take Meta for a spin. Having used the app for a while, I would describe it as follows: Meta is based on a powerful tag engine (called <a href=\"https://taglib.org/\" rel=\"noopener noreferrer\">TagLib</a>) and customizable, native Mac UI that supports a resizable sidebar, popovers, dark mode, keyboard shortcuts, and multiple view options; in addition to this flexible UI, Meta sports an <em>incredible</em> text-matching engine that lets you perform tasks such as batch renaming multiple files, creating new tags by mixing text and metadata tokens, finding and replacing text, and even converting file paths or metadata to new tags using regex patterns.</p>\n<p id=\"p12\">As I noted above, that’s a lot to take in, so let me walk through my typical use of Meta. Once I’ve identified an album that needs some of its metadata fixed, I drop the entire folder from the Finder into Meta. If I want to add artwork to it, all I need to do is select all songs in Meta’s main list, then drag the artwork image I previously downloaded from Google Images into the artwork section of Meta’s sidebar. The artwork tag will be instantly written to all FLAC files at once, and that’s it.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-42-59.jpeg\" alt=\"You can drag and drop album covers from the Finder.\"><p>You can drag and drop album covers from the Finder.</p></div>\n<p id=\"p14\">But what if you don’t want to manually find and download a high-quality album artwork image beforehand? Meta has you covered there as well: by purchasing the additional, one-time-only Cover Finder feature, you’ll be able to select multiple tracks and hit ⌘⇧K to let Meta find artwork images for you. In my experience, results provided by Meta have been perfect (images fetched by Meta are high-definition covers with the correct color saturation and no watermarks); at least for me, they’re worth the extra purchase so I don’t have to manually search for each album artwork and clutter my desktop with (often low-res) images downloaded from random websites found via Google.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-25-16.jpeg\" alt=\"Meta can find album covers for you by searching online databases.\"><p>Meta can find album covers for you by searching online databases.</p></div>\n<p id=\"p16\">Besides album artwork, I’ve also been using Meta to fix text-based metadata by taking advantage of the app’s powerful text engine. I often come across remastered editions of albums where each song has the word “Remastered” included in the title field of its metadata – e.g. instead of “Wonderwall”, the song is called “Wonderwall Remastered”. Meta makes these kinds of batch edits extremely easy: after selecting all songs, I can select Edit ⇾ Find ⇾ Find &amp; Replace from the menu bar (or hit ⌥⌘F) to activate the app’s find and replace UI. From there, I can type the text I want to get rid of in the ‘Find’ field and select the ‘All’ button next to ‘Replace’ (a common UI element in Mac apps) to clear the unwanted text from all title fields at once. In a nice touch, Meta visually confirms text matches found in the selected items and even lets you add special tokens such as tab characters or white space if you want to further refine your searches.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-23-15.jpeg\" alt=\"Meta's find and replace UI.\"><p>Meta's find and replace UI.</p></div>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-24-12.jpeg\" alt=\"You can insert special characters in the find and replace UI with this menu.\"><p>You can insert special characters in the find and replace UI with this menu.</p></div>\n<p id=\"p19\">I could also edit each song’s metadata manually by clicking into the appropriate field in the sidebar (also pictured above), but for these batch operations, Meta’s find and replace system is ideal. If you don’t want to replace an existing tag’s value but compose a new one altogether, Meta can do that as well: after selecting an item (or multiple ones), hit the pencil button in the toolbar to open the ‘Compose Tag’ popup, which lets you use arbitrary plain text as well as built-in tokens to overwrite any existing tag value. In the screenshot below, you can see how I edited the ‘Year’ field for all songs contained in The Cure’s <a href=\"https://music.apple.com/us/album/greatest-hits/1288102355\" rel=\"noopener noreferrer\">Greatest Hits album</a> by manually typing “2001” into the Compose Tag box.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-21-47.jpeg\" alt=\"Composing a metadata tag in Meta.\"><p>Composing a metadata tag in Meta.</p></div>\n<p id=\"p21\">Meta can also rename files in the Finder: hit ⌘⇧R and, as with tags, you can mix and match existing tags and plain text to rename files however you see fit. In the example below, I used the track number tag, a hyphen, and the title tag to come up with a file name pattern I like to see in the Finder. Meta even remembers your preferences for file renaming, so once you’ve found a style you like, you won’t have to recreate the naming pattern every single time.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-02-20-53.jpeg\" alt=\"You can also let Meta rename files for you.\"><p>You can also let Meta rename files for you.</p></div>\n<p id=\"p23\">There are dozens of advanced features in Meta I haven’t used myself, but which contribute to making this app the premier utility for managing music collections. In the app’s preferences, you can set different options for album artwork including format, compression, and max size; you can let the app move and organize files in subfolders for you using the ‘Create Directory’ feature, which, again, uses patterns to let you craft a custom file path; there’s support for editing built-in lyrics, dates (with a visual date picker), and even track number sequences. The only thing Meta can’t do for me is add lyrics in the <a href=\"https://helpguide.sony.net/dmp/nwa40/v1/en/contents/TP0001449033.html\" rel=\"noopener noreferrer\">LRC format</a> Sony uses for the Walkman, but I can’t blame the developer for not supporting this particular aspect of Sony’s music ecosystem.</p>\n<div><img src=\"https://2672686a4cf38e8c2458-2712e00ea34e3076747650c92426bbb5.ssl.cf1.rackcdn.com/2020-03-19-16-43-55.jpeg\" alt=\"Meta offers several customization options.\"><p>Meta offers several customization options.</p></div>\n<p id=\"p25\">The greatest compliment I can pay to <a href=\"https://www.nightbirdsevolve.com/meta/\" rel=\"noopener noreferrer\">Meta</a> is that, despite its abundance of features, it never feels overwhelming, and every functionality is always where I expect it to be. Thanks to Meta, I’ve been able to clean up and reorganize my FLAC music collection so that every album now has correct metadata on my Walkman, and I’m happier because of it.</p>\n<p id=\"p26\">Meta is a remarkable example of the kind of thoughtful, powerful professional tools indie developers can still create on macOS these days, and it’s one of my favorite Mac app discoveries in a <em>long</em> time. If you also like to manage your music library the old-fashioned way and can’t stand incorrect metadata, I can’t recommend Meta enough.</p>\n<div id=\"div27\">\n<hr><ol><li id=\"fn-62661-websites\">\nWebsites I’ve been using to purchase FLAC music include <a href=\"https://uk.7digital.com/features/VUifKi0AAEM5KIpk/hi-resflac\" rel=\"noopener noreferrer\">7digital</a>, <a href=\"https://www.hdtracks.com/\" rel=\"noopener noreferrer\">HDtracks</a>, <a href=\"https://www.qobuz.com/us-en/shop\" rel=\"noopener noreferrer\">Qobuz</a>, and some artists’ official <a href=\"https://bandcamp.com/\" rel=\"noopener noreferrer\">Bandcamp</a> profiles.&nbsp;<a href=\"https://www.macstories.net/mac/editing-flac-metadata-with-meta-for-mac/#fnref-62661-websites\" rel=\"noopener noreferrer\">↩︎</a>\n</li>\n</ol></div>\n<hr><h3>Support MacStories Directly</h3><p></p><p>Club MacStories offers exclusive access to <strong>extra MacStories content</strong>, delivered every week;  it’s also a way to support us directly.</p>\n<p>Club MacStories will help you discover the best apps for your devices and get the most out of your iPhone, iPad, and Mac. Plus, it’s made in Italy.</p>\n<p></p><a href=\"https://club.macstories.net/?utm_source=ms&amp;utm_medium=rss\">Join Now</a>",
            "url": "https://www.macstories.net/mac/editing-flac-metadata-with-meta-for-mac/",
            "title": "Editing FLAC Metadata with Meta for Mac",
            "date_modified": "2020-03-19T16:24:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22541305\">Comments</a>",
            "url": "https://capiche.com/ama/patrick-mckenzie-stripe",
            "title": "Ask Patrick McKenzie Anything",
            "date_modified": "2020-03-10T23:46:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22505269\">Comments</a>",
            "url": "https://github.com/calebj0seph/spectro/blob/master/docs/making-of.md",
            "title": "Building Spectro: a Real-Time WebGL audio spectrogram visualizer",
            "date_modified": "2020-03-06T17:10:03.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22491598\">Comments</a>",
            "url": "http://radiooooo.com/",
            "title": "Radiooooo – The Music Time Machine",
            "date_modified": "2020-03-05T07:20:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22371629\">Comments</a>",
            "url": "https://poolside.fm/",
            "title": "Poolside.fm",
            "date_modified": "2020-02-20T00:22:30.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/w7eenl/postgresql_search_from_trenches\">Comments</a></p>",
            "url": "https://blog.soykaf.com/post/postgresl-front-report/",
            "title": "Postgresql Search: From the trenches",
            "date_modified": "2020-02-18T15:01:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22246615\">Comments</a>",
            "url": "https://www.mint-lang.com/",
            "title": "Mint: The programming language for writing single page applications",
            "date_modified": "2020-02-05T13:44:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22222137\">Comments</a>",
            "url": "https://blog.wesleyac.com/posts/engineering-beliefs",
            "title": "Things I Believe About Software Engineering",
            "date_modified": "2020-02-03T07:28:56.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/ii5og5/things_i_believe_about_software\">Comments</a></p>",
            "url": "https://blog.wesleyac.com/posts/engineering-beliefs",
            "title": "Things I Believe About Software Engineering",
            "date_modified": "2020-02-02T17:12:01.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22193383\">Comments</a>",
            "url": "https://changelog.com/posts/monoliths-are-the-future",
            "title": "Monoliths Are the Future",
            "date_modified": "2020-01-30T17:41:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22181437\">Comments</a>",
            "url": "https://github.com/typesense/typesense",
            "title": "Typesense: Open-Source Alternative to Algolia",
            "date_modified": "2020-01-29T15:20:39.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22157166\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=22157166",
            "title": "Ask HN: How do you currently solve authentication?",
            "date_modified": "2020-01-27T06:06:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22156370\">Comments</a>",
            "url": "https://www.unisonweb.org/docs/tour/",
            "title": "Unison: A Content-Addressable Programming Language",
            "date_modified": "2020-01-27T02:16:28.000Z"
        },
        {
            "content_html": "<p><a href=\"https://lobste.rs/s/v3s6vw/dhall_for_kubernetes\">Comments</a></p>",
            "url": "https://christine.website/blog/dhall-kubernetes-2020-01-25",
            "title": "Dhall for Kubernetes",
            "date_modified": "2020-01-25T21:26:45.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22144330\">Comments</a>",
            "url": "https://danluu.com/wat/",
            "title": "Normalization of Deviance (2015)",
            "date_modified": "2020-01-25T02:58:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22139975\">Comments</a>",
            "url": "https://www.ongres.com/blog/postgresqlconf-configuration-for-humans/",
            "title": "Show HN: Postgresqlco.nf: PostgreSQL Configuration for Humans",
            "date_modified": "2020-01-24T16:51:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22054565\">Comments</a>",
            "url": "https://www.ncsc.gov.uk/whitepaper/security-architecture-anti-patterns",
            "title": "Security Architecture Anti-Patterns",
            "date_modified": "2020-01-15T14:48:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22053998\">Comments</a>",
            "url": "https://www.jetbrains.com/lp/mono/",
            "title": "JetBrains Mono: A free and open-source typeface for developers",
            "date_modified": "2020-01-15T13:39:50.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22040400\">Comments</a>",
            "url": "https://cassandraxia.com/writing/police.html",
            "title": "What to do if you’re stopped by the police",
            "date_modified": "2020-01-14T00:25:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=22016212\">Comments</a>",
            "url": "https://www.issms2fasecure.com/",
            "title": "Is SMS 2FA Secure?",
            "date_modified": "2020-01-10T22:16:24.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21988924\">Comments</a>",
            "url": "http://www.overcomingbias.com/2020/01/how-bees-argue.html",
            "title": "Bees Argue",
            "date_modified": "2020-01-08T06:37:10.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21833718\">Comments</a>",
            "url": "https://www.nytimes.com/interactive/2019/12/19/opinion/location-tracking-cell-phone.html",
            "title": "An investigation into the smartphone tracking industry",
            "date_modified": "2019-12-19T10:46:37.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21831778\">Comments</a>",
            "url": "https://www.espn.com/esports/story/_/id/28319463/league-legends-quickly-gaining-traditional-sports-american-popularity",
            "title": "League of Legends quickly gaining on traditional sports in American popularity",
            "date_modified": "2019-12-19T03:21:52.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21810272\">Comments</a>",
            "url": "https://blog.eventide-project.org/articles/announcing-message-db/",
            "title": "Message DB: Event Store and Message Store for PostgreSQL",
            "date_modified": "2019-12-17T03:33:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21794981\">Comments</a>",
            "url": "https://www.inklestudios.com/ink/web-tutorial/",
            "title": "Writing web-based interactive fiction with Ink",
            "date_modified": "2019-12-15T07:29:02.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21717022\">Comments</a>",
            "url": "http://www.aidungeon.io/",
            "title": "Show HN: AI Dungeon 2, An AI generated text adventure built 1.5B param GPT-2",
            "date_modified": "2019-12-05T21:54:36.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21674599\">Comments</a>",
            "url": "https://twitter.com/patio11/status/1201003855770607618",
            "title": "Shibboleths that get you past the initial script stage",
            "date_modified": "2019-12-01T08:51:53.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21656891\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=21656891",
            "title": "Ask HN: How do you deal with atomicity in microservice environments?",
            "date_modified": "2019-11-28T12:48:40.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21623364\">Comments</a>",
            "url": "https://www.pika.dev/registry",
            "title": "Pika – A JavaScript package registry for the modern web",
            "date_modified": "2019-11-24T21:00:18.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21597585\">Comments</a>",
            "url": "https://half-life.com/en/alyx",
            "title": "Half-Life: Alyx",
            "date_modified": "2019-11-21T18:31:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21499771\">Comments</a>",
            "url": "https://www.eurogamer.net/articles/2019-08-06-the-creepy-corridors-of-video-games",
            "title": "The Creepy Corridors of Video Games",
            "date_modified": "2019-11-10T19:48:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21452643\">Comments</a>",
            "url": "https://monzo.com/blog/we-built-network-isolation-for-1-500-services",
            "title": "We built network isolation for 1500 services",
            "date_modified": "2019-11-05T14:31:30.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=21404694\">Comments</a>",
            "url": "https://paulstamatiou.com/getting-started-with-security-keys/",
            "title": "Getting Started with Security Keys",
            "date_modified": "2019-10-31T01:00:08.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=20859212\">Comments</a>",
            "url": "https://backgroundchecks.org/justdeleteme/",
            "title": "Just Delete Me – A directory of direct links to delete your account",
            "date_modified": "2019-09-02T14:14:55.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=20721736\">Comments</a>",
            "url": "http://japanesecomplete.com/777",
            "title": "Most Frequent 777 Kanji Give You 90.0% Coverage of Kanji in the Wild",
            "date_modified": "2019-08-17T03:12:06.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=20163500\">Comments</a>",
            "url": "https://github.com/hjacobs/kubernetes-failure-stories",
            "title": "Kubernetes Failure Stories",
            "date_modified": "2019-06-12T11:32:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=19813288\">Comments</a>",
            "url": "https://www.newyorker.com/magazine/2019/05/06/my-childhood-in-a-cult",
            "title": "My Childhood in a Cult",
            "date_modified": "2019-05-02T21:45:32.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=19787186\">Comments</a>",
            "url": "http://www.engineerbetter.com/blog/yubikey-all-the-things/",
            "title": "Show HN: Yubikey guide for Git Signing, SSH Auth, U2F 2FA, and 1Password",
            "date_modified": "2019-04-30T12:12:22.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=19402467\">Comments</a>",
            "url": "https://bittersoutherner.com/waffle-house-vistas",
            "title": "Waffle House Vistas",
            "date_modified": "2019-03-15T18:07:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=19224567\">Comments</a>",
            "url": "https://www.nytimes.com/interactive/2019/02/21/magazine/autism-office-design.html",
            "title": "An Office Designed for Workers with Autism",
            "date_modified": "2019-02-22T11:44:38.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=19130761\">Comments</a>",
            "url": "https://www.thedailybeast.com/inside-the-secret-facebook-war-for-mormon-hearts-and-minds?via=twitter_page",
            "title": "The Secret Facebook War for Mormon Hearts and Minds",
            "date_modified": "2019-02-10T23:15:21.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=19113139\">Comments</a>",
            "url": "http://www.nobadmemories.com/blog/2017/04/better-together-displaying-japanese-and-english-text-on-the-web/",
            "title": "Displaying Japanese and English Text on the Web",
            "date_modified": "2019-02-08T11:06:05.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=18953291\">Comments</a>",
            "url": "https://www.bbc.com/news/av/stories-46885707/rent-a-sister-coaxing-japan-s-young-men-out-of-their-rooms",
            "title": "Rent-a-sister: Coaxing Japan’s young men out of their rooms [video]",
            "date_modified": "2019-01-20T14:54:41.000Z"
        },
        {
            "content_html": "<div id=\"rss-wrap\">\n<figure><img src=\"https://cdn.arstechnica.net/wp-content/uploads/2019/01/GettyImages-167073081-800x527.jpg\" alt=\"A boy looks sad while other, out-of-focus boys appear to laugh at him.\"><p><a href=\"https://cdn.arstechnica.net/wp-content/uploads/2019/01/GettyImages-167073081.jpg\" data-height=\"2419\" data-width=\"3674\">Enlarge</a> (credit: <a rel=\"nofollow\" href=\"https://www.gettyimages.com/detail/news-photo/boy-left-out-uk-news-photo/167073081\">Getty | Photofusion</a>)</p>  </figure><div><a name=\"page-1\"></a></div>\n<p>The American Psychological Association is on the defensive over its newly released <a href=\"https://www.apa.org/about/policy/boys-men-practice-guidelines.pdf\">clinical guidance</a> (PDF) for treating boys and men, which links traditional masculinity ideology to a range of harms, including sexism, violence, mental health issues, suicide, and homophobia. Critics contend that the guidelines attack traditional values and innate characteristics of males.</p>\n<p>The APA’s 10-point guidance, released last week, is intended to help practicing psychologists address the varied yet gendered experience of men and boys with whom they work. It fits into the APA’s set of other clinical guidelines for working with specific groups, including older adults, people with disabilities, and one for girls and women, which was released in 2007. The association began working on the guidance for boys and men in 2005—well before the current #MeToo era—and drew from more than four decades of research for its framing and recommendations.</p>\n<p>That research showed that “some masculine social norms can have negative consequences for the health of boys and men,” the APA said in <a href=\"https://www.apa.org/news/apa/2019/boys-men-look.aspx\">a statement released January 14</a> amid backlash. Key among these harmful norms is pressure for boys to suppress their emotions (the “common ‘boys don’t cry’ refrain”), the APA said. This has been documented to lead to “increased negative risk-taking and inappropriate aggression among men and boys, factors that can put some males at greater risk for psychological and physical health problems.” It can also make males “less willing to seek help for psychological distress.”</p>\n</div><p><a href=\"https://arstechnica.com/?p=1441635#p3\">Read 8 remaining paragraphs</a> | <a href=\"https://arstechnica.com/?p=1441635&amp;comments=1\">Comments</a></p><div>\n<a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=jL3ytcIO_kU:5JdjV367j0I:V_sGLiPBpWU\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?i=jL3ytcIO_kU:5JdjV367j0I:V_sGLiPBpWU\" border=\"0\"></a> <a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=jL3ytcIO_kU:5JdjV367j0I:F7zBnMyn0Lo\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?i=jL3ytcIO_kU:5JdjV367j0I:F7zBnMyn0Lo\" border=\"0\"></a> <a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=jL3ytcIO_kU:5JdjV367j0I:qj6IDK7rITs\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?d=qj6IDK7rITs\" border=\"0\"></a> <a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=jL3ytcIO_kU:5JdjV367j0I:yIl2AUoC8zA\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?d=yIl2AUoC8zA\" border=\"0\"></a>\n</div>",
            "url": "https://arstechnica.com/?p=1441635",
            "title": "Psychologists defend claim of “destructive aspects” to masculinity",
            "date_modified": "2019-01-15T22:20:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=18770366\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=18770366",
            "title": "Ask HN: How do you store photos and videos?",
            "date_modified": "2018-12-27T16:19:31.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=18765529\">Comments</a>",
            "url": "https://www.ucsf.edu/news/2018/12/412916/sugars-sick-secrets-how-industry-forces-have-manipulated-science-downplay-harm",
            "title": "Sugar’s Sick Secrets: How Industry Forces Manipulated Science to Downplay Harm",
            "date_modified": "2018-12-26T22:40:56.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=18665595\">Comments</a>",
            "url": "https://optician-sans.com/",
            "title": "Optician Sans – A free font based on the historical eye charts",
            "date_modified": "2018-12-12T17:08:37.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NXFkmNi6--/c_scale,f_auto,fl_progressive,q_80,w_800/abhv7yumjvqq6i3iqnsv.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NXFkmNi6--/c_scale,f_auto,fl_progressive,q_80,w_800/abhv7yumjvqq6i3iqnsv.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NXFkmNi6--/c_scale,f_auto,fl_progressive,q_80,w_800/abhv7yumjvqq6i3iqnsv.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--NXFkmNi6--/c_scale,f_auto,fl_progressive,q_80,w_800/abhv7yumjvqq6i3iqnsv.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"abhv7yumjvqq6i3iqnsv\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p><em>Fallout: New California</em>, an enormously ambitious mod for <em>Fallout: New Vegas</em>, is now out, finished and ready to play.<br></p><p><em>New California—</em>built from the bones of an older mod called <em>Brazil—</em> is set in between the events of <em>Fallout 2</em> and <em>Fallout New Vegas</em>, and to simply call it a “mod” is to sell it short. This is practically a brand new, fan-made <em>Fallout </em>game, that’s even got voice acting and takes place on a new map, with the game set in the Black Bear Mountain National Forest in California.</p><div><div><div><p><small>Advertisement</small></p><div data-ad-unit=\"MOBILE_IN_POST\" data-targeting=\"{&quot;pos&quot;:&quot;mobile_inpost&quot;}\"></div><p></p></div></div></div><p>Its release is timed well; <em>Fallout 76</em> is out soon, and anyone disappointed that it’s a multiplayer affair, and not the traditional epic singleplayer RPG, can just try this out instead.</p><p><span><iframe data-chomp-id=\"nWkKzaplwbY\" data-recommend-id=\"youtube://nWkKzaplwbY\" height=\"450\" id=\"youtube-nWkKzaplwbY\" src=\"https://kinja.com/ajax/inset/iframe?id=youtube-video-nWkKzaplwbY&amp;start=0\" width=\"800\"></iframe></span></p><p>You can <a href=\"https://www.moddb.com/mods/falloutprojectbrazil\" target=\"_blank\" rel=\"noopener\">download the mod here</a>. As for how good it is, Nathan is playing it right now, and will have some impressions up on <em>Kotaku </em>soon!</p><div data-ad-unit=\"SPLASHYMID\" data-targeting=\"{&quot;pos&quot;:&quot;splashymid&quot;}\"></div><div><div></div><div><div><div><div id=\"ad-container-92954817\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-92954817\"></span></div></div></div></div></div><div><div><div><p><small>Advertisement</small></p><div data-ad-unit=\"MOBILE_IN_POST\" data-targeting=\"{&quot;pos&quot;:&quot;mobile_inpost&quot;}\"></div><p></p></div></div></div><p><strong>UPDATE</strong>:<em> This post’s headline earlier referred to the mod being nine years old. It’s actually been in active development since 2012.</em></p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/axH0MQbYthw\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/massive-fallout-mod-nine-years-in-the-making-is-out-tod-1829949486",
            "title": "Massive Fallout Mod New California Is Out Today",
            "date_modified": "2018-10-23T23:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=17530086\">Comments</a>",
            "url": "https://github.com/rtr7/router7",
            "title": "Show HN: Router7 – A pure-Go implementation of a small home internet router",
            "date_modified": "2018-07-14T12:35:16.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16999407\">Comments</a>",
            "url": "https://www.newyorker.com/culture/culture-desk/the-united-states-of-japan",
            "title": "The United States of Japan",
            "date_modified": "2018-05-05T00:03:37.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Pn1JhMFP--/c_scale,fl_progressive,q_80,w_800/tjuw6zwhy5z3gl1aqt5e.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Pn1JhMFP--/c_scale,fl_progressive,q_80,w_800/tjuw6zwhy5z3gl1aqt5e.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Pn1JhMFP--/c_scale,fl_progressive,q_80,w_800/tjuw6zwhy5z3gl1aqt5e.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--Pn1JhMFP--/c_scale,fl_progressive,q_80,w_800/tjuw6zwhy5z3gl1aqt5e.jpg\" data-sizes=\"auto\" data-width=\"2048\" data-chomp-id=\"tjuw6zwhy5z3gl1aqt5e\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p>The Environmental Working Group has released <a href=\"https://www.ewg.org/foodnews/summary.php\" target=\"_blank\" rel=\"noopener\">their latest “Dirty Dozen” list</a> of supposedly pesticide-laden fruits and vegetables. (<a href=\"https://vitals.lifehacker.com/why-you-shouldnt-buy-organic-based-on-the-dirty-dozen-1689190822\" target=\"_blank\" rel=\"noopener\">This is a misleading list</a>, as we’ve explained before.) You may be tempted to buy organic produce, as the EWG suggests, but guess what—organic produce is not pesticide-free.</p><p>Organic farmers may use pesticides, so long as they choose from <a href=\"https://www.omri.org/sites/default/files/opl_pdf/CompleteCompany-NOP.pdf\" target=\"_blank\" rel=\"noopener\">a list of approved options</a>. The USDA organic program <a href=\"https://www.ecfr.gov/cgi-bin/text-idx?c=ecfr&amp;SID=9874504b6f1025eb0e6b67cadf9d3b40&amp;rgn=div6&amp;view=text&amp;node=7:3.1.1.9.32.7&amp;idno=7\" target=\"_blank\" rel=\"noopener\">does not disallow <em>all</em> pesticides</a>, just “synthetic” ones. (By the way, the term “pesticides” includes both bug sprays and weed killers.) </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-62408824\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>So what remains on our vegetables? The USDA periodically tests produce for pesticide residues; this is the <a href=\"https://www.ams.usda.gov/datasets/pdp\" target=\"_blank\" rel=\"noopener\">Pesticide Data Program</a>. (The EWG repurposes this data to create their Dirty Dozen and Clean Fifteen lists.) But the USDA does <em>not</em> test for the presence of organic-allowed pesticides. So the EWG is reporting the stuff on conventional crops without considering what’s present on organic crops. </p><div><div><div id=\"ad-container-24155741\" data-zone-type=\"SPLASHYMID\"></div></div></div><div></div><p>So, will you lower your pesticide exposure by switching to organic? We don’t know, but the answer may very well be no. Even looking at the synthetic, non-organic pesticides in the USDA’s tests, conventional crops don’t always have the lowest amounts. Take strawberries, for example, the “dirtiest” item on the 2018 list: 75 percent of organic strawberries, and <em>76 percent</em> of conventional strawberries, had pesticide levels that were under 5 percent of the allowable levels. </p><p>In other words, buying organic strawberries might expose you to <em>more</em> pesticide residues than buying conventional. We recommend ignoring the Dirty Dozen list entirely, and buying whichever fruits and veggies work for your diet and your budget.</p><img src=\"http://feeds.feedburner.com/~r/lifehacker/vip/~4/_bO8FlNCZbQ\" height=\"1\" width=\"1\">",
            "url": "https://vitals.lifehacker.com/organic-food-has-pesticides-too-1825156951",
            "title": "Organic Food Has Pesticides, Too",
            "date_modified": "2018-04-10T21:30:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16638601\">Comments</a>",
            "url": "https://www.theguardian.com/world/2018/mar/21/christianity-non-christian-europe-young-people-survey-religion",
            "title": "'Christianity as default is gone': the rise of a non-Christian Europe",
            "date_modified": "2018-03-21T15:03:42.000Z"
        },
        {
            "content_html": "<img data-portal-copyright=\"Image: Adult Swim\" data-has-syndication-rights=\"1\" data-focal-region=\"x1:664,y1:256,x2:970,y2:562\" src=\"https://cdn.vox-cdn.com/thumbor/pMdzB7niuZmUffXfmcLTHDLPywM=/129x0:1505x917/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/59055509/Screen_Shot_2018_03_16_at_12.35.08_PM.0.png\">\n\n\n\n  <p id=\"B2jOoM\">Adult Swim’s hit animated show <em>Rick </em><em>and</em><em> Morty</em> finished its third season back in October, and we might not get season 4 until 2019, <a href=\"https://www.polygon.com/2018/1/3/16844814/rick-and-morty-season-4-release-date-2019\">according to one of the show’s writers</a>. To tide us over, Adult Swim just released a music video for Run The Jewels’ song “Oh Mama,” featuring the mad scientist and his grandson. </p>\n<p id=\"t1y3ve\">The video is directed by <em>Rick </em><em>and</em><em> Morty</em> director Juan Meza-León (he was responsible for episodes like “The Rickshank Rickdemption,” “The Whirly Dirly Conspiracy,” and “The ABC’s of Beth”), and it plays out a bit like a regular episode of the show: Rick and Morty fly off to a random planet, crash a club full of insectoid Gromflamites, and carnage ensues. They steal a briefcase and head out, only to encounter some additional problems,...</p>\n  <p>\n    <a href=\"https://www.theverge.com/2018/3/16/17130166/rick-and-morty-run-the-jewels-oh-mama-music-video-watch\">Continue reading…</a>\n  </p>",
            "url": "https://www.theverge.com/2018/3/16/17130166/rick-and-morty-run-the-jewels-oh-mama-music-video-watch",
            "title": "Adult Swim’s new music video for Run the Jewels’ Oh Mama is like a mini Rick and Morty episode",
            "date_modified": "2018-03-16T16:53:26.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ekxwl3dZ--/c_scale,fl_progressive,q_80,w_800/iisang85d4j8tbaitrqa.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ekxwl3dZ--/c_scale,fl_progressive,q_80,w_800/iisang85d4j8tbaitrqa.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ekxwl3dZ--/c_scale,fl_progressive,q_80,w_800/iisang85d4j8tbaitrqa.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--ekxwl3dZ--/c_scale,fl_progressive,q_80,w_800/iisang85d4j8tbaitrqa.jpg\" data-sizes=\"auto\" data-width=\"899\" data-chomp-id=\"iisang85d4j8tbaitrqa\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>Nintendo’s original headquarters in Shimokyo-ku, Kyoto, Japan, in 1889, the year of the company’s founding. Photo: <a href=\"https://meiji150.kyoto/photo/photo-2365/\" target=\"_blank\" rel=\"noopener\">City of Kyoto</a></figcaption></div></figure><p>It’s well-known that Nintendo was originally founded in Kyoto, Japan as a maker of playing cards in 1889. But a recent historical project by the city of Kyoto has turned up, for the first time, a photograph of what the company’s headquarters looked like in that year.</p><p>The <a href=\"https://meiji150.kyoto/photo/photo-2365/\" target=\"_blank\" rel=\"noopener\">photo</a>, and an accompanying <a href=\"https://meiji150.kyoto/feature/haiko-nintendo-origin/\" target=\"_blank\" rel=\"noopener\">blog post</a>, were published in December on “Memories of Kyoto, 150 Years After The Meiji Period,” an ongoing historical project documenting the city’s history during the reign of Emperor Meiji from 1868 to 1912. Nintendo historians authors Florent Gorges and Isao Yamazaki <a href=\"https://twitter.com/FlorentGorgesFR/status/955807400874971136\" target=\"_blank\" rel=\"noopener\">shared them around today</a>, noting that the blog post was full of fascinating little-known information about Nintendo’s founding.</p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-11735662\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Nintendo’s founder Fusajiro Yamauchi originally ran a company called Haiko, which at the time specialized in cement, the post says. His name was originally Fusajiro Fukui, but he was adopted as an adult by his boss Naoshichi Yamauchi. This is actually extremely common in Japan—government records show that the&nbsp;<a href=\"http://www.independent.co.uk/life-style/japanese-adoption-rates-majority-adult-men-a7524301.html\" target=\"_blank\" rel=\"noopener\">vast majority of adoptions</a> are adult men adopting other adult men, so that their companies can remain a “family business” even when there is no biological son to inherit the company. </p><p>Nintendo stayed a family business until the retirement of Fusajiro’s great-grandson Hiroshi Yamauchi in 2002, after which Satoru Iwata took the helm. Haiko is <a href=\"http://www.haiko.co.jp/\" target=\"_blank\" rel=\"noopener\">still in operation</a>  and is run by Kazumasa Yamauchi, who wrote the City of Kyoto’s blog post. </p><div><div></div><div><div><div><div id=\"ad-container-55962572\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-55962572\"></span></div></div></div></div></div><p>In 1889, Fusajiro Yamauchi struck out on his own to form Marufuku Nintendo Card Co., producing traditional Japanese playing cards called <em>hanafuda</em> and eventually Western playing cards as well. Here’s a map of where that original headquarters used to be located, in case you want to make a pilgrimage.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--6TMZCHwb--/c_scale,fl_progressive,q_80,w_800/voioik8t4kzjavj4xzjt.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--6TMZCHwb--/c_scale,fl_progressive,q_80,w_800/voioik8t4kzjavj4xzjt.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--6TMZCHwb--/c_scale,fl_progressive,q_80,w_800/voioik8t4kzjavj4xzjt.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--6TMZCHwb--/c_scale,fl_progressive,q_80,w_800/voioik8t4kzjavj4xzjt.jpg\" data-sizes=\"auto\" data-width=\"1619\" data-chomp-id=\"voioik8t4kzjavj4xzjt\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p>If you want to see the oldest Nintendo building that’s still actually standing, there’s one <a href=\"https://www.tripadvisor.com/Attraction_Review-g298564-d10664563-Reviews-Nintendo_Original_Building-Kyoto_Kyoto_Prefecture_Kinki.html\" target=\"_blank\" rel=\"noopener\">very close to downtown Kyoto</a>. According to Gorges and Yamazaki’s book, the building that housed the original headquarters was demolished in 2004, and replaced with a parking lot.</p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/Zf0s5hND8yY\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/heres-what-nintendos-hq-looked-like-in-1889-1822338950",
            "title": "Here's What Nintendo's HQ Looked Like In 1889",
            "date_modified": "2018-01-23T18:00:00.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pMJfYTik--/c_scale,fl_progressive,q_80,w_800/jblvhkljfmsco33zg39d.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pMJfYTik--/c_scale,fl_progressive,q_80,w_800/jblvhkljfmsco33zg39d.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pMJfYTik--/c_scale,fl_progressive,q_80,w_800/jblvhkljfmsco33zg39d.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--pMJfYTik--/c_scale,fl_progressive,q_80,w_800/jblvhkljfmsco33zg39d.png\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"jblvhkljfmsco33zg39d\" data-format=\"png\" width=\"600\"></picture></div></div></figure><p><em>The Red Strings Club</em> starts, and ends, with a character falling out of a high-rise window. There’s no indication that you could do anything to stop this from happening, or even that you’re supposed to. The game is more interested in how you get to that point—and the web of lies, manipulation, and tough choices you leave in your wake.<br></p><p>This cyberpunk point-and-click game, out today on PC, is by Deconstructeam, the developers behind 2014’s <em>Gods Will Be Watching</em>. <em>Gods</em> was a series of scenarios in which the player had to manage characters’ needs and unclear emotional states. <em>The Red Strings Club</em> is set up roughly the same way,  but it’s not the brutal tightrope walk of life and death its predecessor was. It’s more forgiving, but it’s also more complicated. Its decisions open up into possibilities, nuances, and outcomes that don’t have clear rights and wrongs.</p><p><span><iframe data-chomp-id=\"IKwKVukDsXQ\" data-recommend-id=\"youtube://IKwKVukDsXQ\" height=\"450\" id=\"youtube-IKwKVukDsXQ\" src=\"https://kinja.com/ajax/inset/iframe?id=youtube-video-IKwKVukDsXQ&amp;start=0\" width=\"800\"></iframe></span></p><p>The game mostly takes place in the titular Red Strings Club, a bar in a cyberpunk dystopia owned by a man named Donovan. In this future, people have cybernetic implants, but Donovan has a medical condition that prevents him from getting them. This gives him a fairly anti-implant view, in contrast to Brandeis, an implant-sporting hacker.<br></p><div><div></div><div><div><div><div id=\"ad-container-31096727\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-31096727\"></span></div></div></div></div></div><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-70222161\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>In addition to being a bartender, Donovan is also an information broker, luring secrets out of clients through mixing signature cocktails. Brandeis and Donovan stumble upon a plan by megacorporation Supercontinent that involves making secret changes to people’s implants—or rather, the plan stumbles upon them, in the form of an AI who crashes through the bar’s door one night. From there, the game becomes a tangle of hacking, social engineering, and heavy drinking.<br></p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Wsn-SycX--/c_scale,fl_progressive,q_80,w_800/baledyrkowahsbim8u7b.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Wsn-SycX--/c_scale,fl_progressive,q_80,w_800/baledyrkowahsbim8u7b.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Wsn-SycX--/c_scale,fl_progressive,q_80,w_800/baledyrkowahsbim8u7b.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--Wsn-SycX--/c_scale,fl_progressive,q_80,w_800/baledyrkowahsbim8u7b.png\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"baledyrkowahsbim8u7b\" data-format=\"png\" width=\"600\"></picture></div></div></figure><p>You spend most of your time in <em>The Red Strings Club</em> mixing cocktails. When a character comes into the bar, they’ll have several possible emotions—pride, depression, lust, euphoria—represented by icons in different positions on the screen. The player has to mix alcohol to move an indicator over the emotion they want to access. The labels on the bottles helpfully integrate arrows to remind you what moves where, and you later gain the ability to make the indicator move diagonally or to tilt its orientation. The controls are imprecise and uncomfortable, though it’s fun to spill booze everywhere. Nevertheless, it’s an unusual, enjoyable minigame, enriched with satisfying sound design. I regularly hurled ice cubes to the floor just to listen to them clatter.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--uIFdkOdA--/c_scale,fl_progressive,q_80,w_800/p4nbaxixmeawyspdokrs.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--uIFdkOdA--/c_scale,fl_progressive,q_80,w_800/p4nbaxixmeawyspdokrs.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--uIFdkOdA--/c_scale,fl_progressive,q_80,w_800/p4nbaxixmeawyspdokrs.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--uIFdkOdA--/c_scale,fl_progressive,q_80,w_800/p4nbaxixmeawyspdokrs.png\" data-sizes=\"auto\" data-width=\"1600\" data-chomp-id=\"p4nbaxixmeawyspdokrs\" data-format=\"png\" width=\"600\"></picture></div><figcaption>The mixology screen. The bottle design is really great.</figcaption></div></figure><p>Donovan uses these cocktails to manipulate characters into telling him what he wants to know. You have a series of objectives to uncover before taking on Supercontinent. Questions like who their CEO really is, or what role the android you found plays, can be teased out of a prideful inventor or scared out of a depressed marketing executive if you read their starting state right and adjust it accordingly. Manipulating characters through alcohol might seem deceitful, but everyone who comes into the bar <em>wants</em> to get drunk to change how they feel. I struggled with the idea of forcing patrons to feel unpleasant emotions, but Donovan’s intentionality felt less like selfishness and more like acknowledging the truth behind why we drink.<br></p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-34245306\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"mid\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>You’ll need whatever information you can get for the game’s climax, an epic tangle of social engineering done over a landline phone while the evil corporation closes in. It feels like the perfect combination of what you’ve been doing all along: teasing, lying, considering and exploiting the connections between people. <em>The Red Strings Club</em> humanizes each of its villains and protagonists, and as I called up one person pretending to be their crush in order to trick them into giving me their computer password, I felt ashamed of how clever I thought I was being, and guilty about how easy it was to get what I needed.<br></p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Hj0iWndF--/c_scale,fl_progressive,q_80,w_800/z5eahk2wgohkwgd2q6rr.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Hj0iWndF--/c_scale,fl_progressive,q_80,w_800/z5eahk2wgohkwgd2q6rr.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Hj0iWndF--/c_scale,fl_progressive,q_80,w_800/z5eahk2wgohkwgd2q6rr.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--Hj0iWndF--/c_scale,fl_progressive,q_80,w_800/z5eahk2wgohkwgd2q6rr.png\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"z5eahk2wgohkwgd2q6rr\" data-format=\"png\" width=\"600\"></picture></div></div></figure><p>Your choices are tracked by a screen that gets progressively more covered in red lines as the game goes. These “red strings” are another recurring theme: the web of secrets, lies, desires, and dreams that Donovan manipulates to get what he wants.<br></p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-94853737\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Things started fairly linear for me, though they soon became an intriguing mess. <em>The Red Strings Club</em> has a Telltale-style indicator for when a choice you’ve made has an impact, but what that impact would be seldom felt immediately apparent. Unlike <em>Gods Will Be Watching</em>, conversations never felt like they had a hard fail state. The game would tell me I’d done something impactful and a string would be added to my running tally, but it mostly felt like I was following my natural inclinations. With the exception of one moment, in which Brandeis has to approach another character in a methodical, clearly-laid out dance, <em>The Red Strings Club</em>’s choices are open-ended and vague, made in dialogue trees full of lines that run the gamut of options. It feels messy and mysterious in a very human way.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s---Jf2bvJq--/c_scale,fl_progressive,q_80,w_800/slgrlpm98yf4cygyxflc.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s---Jf2bvJq--/c_scale,fl_progressive,q_80,w_800/slgrlpm98yf4cygyxflc.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s---Jf2bvJq--/c_scale,fl_progressive,q_80,w_800/slgrlpm98yf4cygyxflc.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s---Jf2bvJq--/c_scale,fl_progressive,q_80,w_800/slgrlpm98yf4cygyxflc.png\" data-sizes=\"auto\" data-width=\"1600\" data-chomp-id=\"slgrlpm98yf4cygyxflc\" data-format=\"png\" width=\"600\"></picture></div><figcaption>Choices have been blurred out to avoid spoilers.</figcaption></div></figure><p><em>The Red Strings Club</em> deals with all the heavy issues you’d expect from cyberpunk: free will, humanity, technology, immortality. On occasion it felt a bit sophomoric—at one point two characters argue about how depression is vital to making good art—but it was just as quick to disagree with itself and come back down to earth. It’s a deeply emotional game, with a queer love story at its center. It’s also unexpectedly diverse, considering issues related to race, gender, and sexuality all as tacit parts of its future. One playthrough took me about three and a half hours, and I’m curious to see the consequences of different choices on another playthrough. A character will still probably fall out of a window, but it will mean something different depending how I get there.<br></p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/TxsRZCRU9mE\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/a-cyberpunk-game-where-you-manipulate-people-with-alcoh-1822305786",
            "title": "A Cyberpunk Game Where You Manipulate People With Alcohol",
            "date_modified": "2018-01-22T21:02:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16143810\">Comments</a>",
            "url": "https://www.nytimes.com/2018/01/10/science/dolphins-self-recognition.html",
            "title": "Dolphins Show Self-Recognition Earlier Than Children",
            "date_modified": "2018-01-14T11:28:25.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16138969\">Comments</a>",
            "url": "https://news.ycombinator.com/item?id=16138969",
            "title": "Ask HN: As an adult introvertish nerd what makes you happy?",
            "date_modified": "2018-01-13T09:58:12.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16121246\">Comments</a>",
            "url": "https://www.nature.com/articles/s41598-017-15736-4",
            "title": "Two genes in Chromosomes 13 and 14, linked to Homosexuality",
            "date_modified": "2018-01-11T03:36:53.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NfbX3NUU--/c_scale,fl_progressive,q_80,w_800/unekysuurpjwuilgcbvq.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NfbX3NUU--/c_scale,fl_progressive,q_80,w_800/unekysuurpjwuilgcbvq.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NfbX3NUU--/c_scale,fl_progressive,q_80,w_800/unekysuurpjwuilgcbvq.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--NfbX3NUU--/c_scale,fl_progressive,q_80,w_800/unekysuurpjwuilgcbvq.jpg\" data-sizes=\"auto\" data-width=\"2048\" data-chomp-id=\"unekysuurpjwuilgcbvq\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"https://twitter.com/naagaoshi/status/949970599199178753\" target=\"_blank\" rel=\"noopener\">naagaoshi</a>]</figcaption></div></figure><p>Quick question: During which season does Japan look most beautiful? Spring is probably the number one choice, followed by fall. But winter certainly is no slouch.<br></p><p>Twitter user <a href=\"https://twitter.com/naagaoshi\" target=\"_blank\" rel=\"noopener\">Naagaoshi</a> has been uploading a series of stunning Japanese winter photos, showing off just how lovely the country can look covered in snow and ice.</p><p>Have a look for yourself! </p><p><span><iframe height=\"159\" id=\"twitter-949213109356453889\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-949213109356453889&amp;autosize=1\" width=\"500\"></iframe></span></p><p><span><iframe height=\"159\" id=\"twitter-949970599199178753\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-949970599199178753&amp;autosize=1\" width=\"500\"></iframe></span></p><p><span><iframe height=\"159\" id=\"twitter-950302753099665408\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-950302753099665408&amp;autosize=1\" width=\"500\"></iframe></span></p><p><span><iframe height=\"159\" id=\"twitter-939806530487988224\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-939806530487988224&amp;autosize=1\" width=\"500\"></iframe></span></p><p><span><iframe height=\"159\" id=\"twitter-939481062765297665\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-939481062765297665&amp;autosize=1\" width=\"500\"></iframe></span></p><p><span><iframe height=\"159\" id=\"twitter-937256512795242496\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-937256512795242496&amp;autosize=1\" width=\"500\"></iframe></span></p><p><span><iframe height=\"159\" id=\"twitter-945629467656855552\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-945629467656855552&amp;autosize=1\" width=\"500\"></iframe></span></p><hr><p><small>Kotaku East is your slice of Asian internet culture, bringing you the latest talking points from Japan, Korea, China and beyond. Tune in every morning from 4am to 8am.</small></p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/IDSau-E7UTo\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/the-beauty-of-japan-in-winter-1821903686",
            "title": "The Beauty of Japan In Winter",
            "date_modified": "2018-01-09T12:30:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16094037\">Comments</a>",
            "url": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4522609/",
            "title": "Psychological and psychiatric terms to avoid",
            "date_modified": "2018-01-08T01:40:29.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=16092755\">Comments</a>",
            "url": "http://www.theage.com.au/federal-politics/political-news/advertising-banned-drinks-taxed-vending-machines-removed-doctors-plan-for-war-on-sugar-20180105-h0duw0.html",
            "title": "Doctors' plan for war on sugar",
            "date_modified": "2018-01-07T21:13:49.000Z"
        },
        {
            "content_html": "<img src=\"https://cdn.vox-cdn.com/thumbor/qF3kNB3vFf38J_9KvAL48UZ4l6U=/0x0:3000x2000/1310x873/cdn.vox-cdn.com/uploads/chorus_image/image/58118511/pxdmz2nabmyrehgtach4.0.0.jpg\">\n\n\n\n  <a href=\"https://www.racked.com/2017/12/27/16307098/bachelor-instagram-influencers-ads\">www.racked.com/2017/12/27/16307098/bach…</a>\n  <p>\n    <a href=\"https://www.racked.com/2017/12/27/16307098/bachelor-instagram-influencers-ads\">Continue reading…</a>\n  </p>",
            "url": "https://www.racked.com/2017/12/27/16307098/bachelor-instagram-influencers-ads",
            "title": "The sneaky allure of the Bachelor Instagram influencer",
            "date_modified": "2017-12-27T16:19:17.000Z"
        },
        {
            "content_html": "<div id=\"rss-wrap\">\n\n\n\n\n\n<div><a name=\"page-1\"></a></div>\n            <img src=\"https://cdn.arstechnica.net/wp-content/uploads/2017/12/Music-of-the-Spheres-980x980.jpeg\"><p>\n                                    <a href=\"https://www.reddit.com/r/DestinyTheGame/comments/7m2rmp/casually_leaking_music_of_the_spheres/\">Tlohtzin Espinosa and Owen Spence</a>\n                        </p>\n      \n\n<p>The original symphonic treatment for the game <em>Destiny</em> was long thought lost, thanks to it being shelved after a major staffing shake-up at developer Bungie. But <em>Destiny</em> fans received quite the Christmas miracle—albeit a legally dubious one—when fans discovered and posted an apparent rip of the album in question, titled <em>Destiny: Music of the Spheres</em>.</p>\n<p>The 8-track, 48-minute album leak, which is live as of press time at more than a few mirrors, was quickly confirmed as legitimate by two major contributors to the project: former Bungie composer Marty O'Donnell and former Bungie creative director Joe Staten. O'Donnell offered a \"I think this is it\" on Monday via Twitter, followed by <a href=\"https://twitter.com/MartyTheElder/status/945362982074060800\">an emphatic post</a> of \"Finally! #NeverForgotMotS.\" Staten followed up with <a href=\"https://twitter.com/MartyTheElder/status/945844413611126785\">acknowledgement that Sir Paul McCartney himself sang a lyric</a> Staten had suggested, then added, \"Glad #MOTS is finally out for all to hear.\"</p>\n</div><p><a href=\"https://arstechnica.com/?p=1237433#p3\">Read 5 remaining paragraphs</a> | <a href=\"https://arstechnica.com/?p=1237433&amp;comments=1\">Comments</a></p><div>\n<a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=-o4Cz9GGdM0:rJRJMAudTE0:V_sGLiPBpWU\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?i=-o4Cz9GGdM0:rJRJMAudTE0:V_sGLiPBpWU\" border=\"0\"></a> <a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=-o4Cz9GGdM0:rJRJMAudTE0:F7zBnMyn0Lo\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?i=-o4Cz9GGdM0:rJRJMAudTE0:F7zBnMyn0Lo\" border=\"0\"></a> <a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=-o4Cz9GGdM0:rJRJMAudTE0:qj6IDK7rITs\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?d=qj6IDK7rITs\" border=\"0\"></a> <a href=\"http://feeds.arstechnica.com/~ff/arstechnica/index?a=-o4Cz9GGdM0:rJRJMAudTE0:yIl2AUoC8zA\"><img src=\"http://feeds.feedburner.com/~ff/arstechnica/index?d=yIl2AUoC8zA\" border=\"0\"></a>\n</div>",
            "url": "https://arstechnica.com/?p=1237433",
            "title": "Lost Destiny symphonic album, complete with Paul McCartney, has totally leaked",
            "date_modified": "2017-12-27T10:30:52.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--vY-LisjN--/c_scale,fl_progressive,q_80,w_800/wdtpzgqnbtkbwoda6doy.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--vY-LisjN--/c_scale,fl_progressive,q_80,w_800/wdtpzgqnbtkbwoda6doy.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--vY-LisjN--/c_scale,fl_progressive,q_80,w_800/wdtpzgqnbtkbwoda6doy.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--vY-LisjN--/c_scale,fl_progressive,q_80,w_800/wdtpzgqnbtkbwoda6doy.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"wdtpzgqnbtkbwoda6doy\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p>In late 2013, veteran Bungie composer Marty O’Donnell finished <em>Music of the Spheres</em>, an eight-part musical work designed to be released alongside <em>Destiny</em>. It never came out. But today, thanks to an anonymous leaker, the elusive work is finally on the internet—at least until the copyright strikes hit.</p><p>Composed by O’Donnell, his partner Michael Salvatori, and former Beatle Paul McCartney, <em>Music of the Spheres</em> was envisioned as a musical companion to Bungie’s ambitious <em>Destiny</em>. But Bungie and O’Donnell spent nearly a year battling over, among other things, publisher Activision’s failure to use O’Donnell’s music in a trailer at E3 2013. In April 2014, Bungie fired O’Donnell, and despite O’Donnell’s hopes, the company indefinitely shelved <em>Music of the Spheres</em>. He has made several public comments on the work since, and last month, he implicitly encouraged people to share it.</p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-64760129\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>“Years ago, when I was Audio Director at Bungie, I gave away nearly 100 copies of <em>Music of the Spheres</em>,” O’Donnell tweeted on November 30, 2017. “I don’t have the authority to give you permission to share MotS. However, no one in the world can prevent me from giving you my blessing.”</p><p><span><iframe height=\"159\" id=\"twitter-936349365496459264\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-936349365496459264&amp;autosize=1\" width=\"500\"></iframe></span></p><p>In late 2016, teenager Owen Spence started his own independent project to recreate <em>Music of the Spheres</em> (as well-documented in <a href=\"http://www.eurogamer.net/articles/2017-06-05-the-teen-who-spent-over-a-year-piecing-together-destinys-unreleased-music\" target=\"_blank\" rel=\"noopener\">this <em>Eurogamer</em> piece</a>) using publicly available material. Spence has been in touch with me since then, and yesterday he told me via Twitter DM that he and a friend, Tlohtzin Espinosa, were contacted by someone with a copy of <em>Music of the Spheres</em> who wanted it to be public. </p><div><div></div><div><div><div><div id=\"ad-container-55308484\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-55308484\"></span></div></div></div></div></div><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-94730524\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"mid\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>So they put it on Soundcloud, where you can now <a href=\"https://soundcloud.com/tlohtzin123/sets/music-of-the-spheres-final\" target=\"_blank\" rel=\"noopener\">listen to it all </a>(until Activision takes it down).</p><p><span><iframe height=\"450\" id=\"soundcloud-playlist-406505084\" src=\"https://kinja.com/ajax/inset/iframe?id=soundcloud-playlist-406505084\" width=\"640\"></iframe></span></p><p>O’Donnell has not yet publicly commented on the leak—I’ve reached out for his thoughts—but it’s clear that he’s wanted this to happen for years now. Must be one hell of a Christmas gift.</p><p><span><iframe height=\"159\" id=\"twitter-661029720260128769\" src=\"https://kinja.com/ajax/inset/iframe?id=twitter-661029720260128769&amp;autosize=1\" width=\"500\"></iframe></span></p><p><strong>UPDATE (7:50pm):</strong> In an e-mail, O’Donnell told me that he’s thrilled about this development:</p><blockquote><p>I’m quite relieved and happy. This was the way it was supposed to have been heard 5 years ago. </p><p>My wife and I spent the afternoon with my now 93 year old father and we showed him that people were finally able to hear this work. It made our Christmas even better. My mother, his wife of over 60 years died a couple years ago and although she loved listening and shared it with some of her friends (she was a musician) she never understood why it wasn’t released.</p><p>I don’t know who actually did it but they have my blessing. I honestly don’t know how anyone could begrudge this any longer.</p></blockquote><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/y_LL7IvCp0A\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/four-years-later-destinys-music-of-the-spheres-has-lea-1821572335",
            "title": "Four Years Later, Destiny's Music Of The Spheres Has Leaked [UPDATE]",
            "date_modified": "2017-12-25T19:00:00.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--JckDBd7m--/c_scale,fl_progressive,q_80,w_800/iokbkgacwk1mblgjbjbm.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--JckDBd7m--/c_scale,fl_progressive,q_80,w_800/iokbkgacwk1mblgjbjbm.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--JckDBd7m--/c_scale,fl_progressive,q_80,w_800/iokbkgacwk1mblgjbjbm.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--JckDBd7m--/c_scale,fl_progressive,q_80,w_800/iokbkgacwk1mblgjbjbm.png\" data-sizes=\"auto\" data-width=\"3000\" data-chomp-id=\"iokbkgacwk1mblgjbjbm\" data-format=\"png\" width=\"600\"></picture></div><figcaption>Illustration by Chelsea Beck/GMG</figcaption></div></figure><p>Right after reinventing <a href=\"https://twitter.com/jacksmithiv/status/635925087640793088?lang=en\" target=\"_blank\" rel=\"noopener\">existing public services with private apps</a>, hacking death may be the ultimate  dream of SiliconValley’s elite. Death is truly the final boss for anyone who thinks enough money and lines of code can solve anything, and boy are they attacking it hard. In 2016, Mark Zuckerberg and his wife Priscilla Chan pledged $3 billion toward a plan to cure all diseases by the end of the century.<br></p><p>“By the time we get to the end of this century, it will be pretty normal for people to live past 100,” <a href=\"https://www.inverse.com/article/23603-zuckerberg-humans-100-normal-century\" target=\"_blank\" rel=\"noopener\">Zuckerberg said in 2016.</a></p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-81690812\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>And to be sure, science, medicine and unlocking more about how the body functions have already worked what would look like a miracle to someone living centuries ago: the average life expectancy for someone born in the United States <a href=\"https://ourworldindata.org/life-expectancy/\" target=\"_blank\" rel=\"noopener\">doubled in just 130 years</a>, from 39 years in 1880 to 78 years in 2011. So Zuckerberg’s prediction may actually be easier than ridding his platform of Russian bots. Longevity—and potential immortality—is a particularly popular obsession with the tech world and Silicon Valley billionaires, who seem to be offended that death would ever get the better of them, and that somehow future generations MUST be able to bask in their immortal wisdom, even if their bodies are just throbbing electric impulses in a jar sustained by regular infusions of monkey testicles (yes, a real thing people tried for awhile). </p><p>The ultimate problem is that human bodies, these sad, slumping, failure-prone products of evolution, just aren’t cut out for living forever. People throughout history have tried, but the garbage body always gets in the way. </p><div><div><div id=\"ad-container-51789433\" data-zone-type=\"SPLASHYMID\"></div></div></div><div></div><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-75319509\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"mid\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>“We humans, as we are now, messy bags of blood and bone, are not really fit for immortality,” Stephen Cave, a philosopher at the University of Cambridge and author of the book <em>Immortality: The Quest to Live Forever and How It Drives Civilization</em>, told me. “So some really profound thing has to happen if we’re going to [change that].”</p><p>But if you’re interested in trying, oligarchs, rich lunatics and scientists throughout history provide some a framework, and a lot more is in the works at this very moment. Below, a rundown of the different approaches that have been taken up in the never-ending quest for life to never end.</p><aside><span></span><span>﻿“We humans, as we are now, messy bags of blood and bone, are not really fit for immortality</span><span></span></aside><h3><strong>Hack all diseases out of existence</strong></h3><p>Zuckerberg, along with his Silicon Valley pals from Google and 23andme, set up the Breakthrough Prize in 2012 to celebrate and promote science innovations, including fighting disease and living longer. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-22767112\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>He also set up The Chan Zuckerberg Initiative, which will <a href=\"http://www.slate.com/articles/technology/future_tense/2016/09/why_mark_zuckerberg_and_priscilla_chan_s_3_billion_plan_to_end_disease_won.html\" target=\"_blank\" rel=\"noopener\">donate $3 billion over a decade</a> to basic medical research with the goal of curing disease. Some have argued this approach isn’t the most efficient and the money would be better spent targeting single diseases at a time instead of an across-the-board assault. For instance, eradicating smallpox cost just <a href=\"https://www.ncbi.nlm.nih.gov/pubmed/10681974\" target=\"_blank\" rel=\"noopener\">$300 million in less than 10 years</a>. </p><p>There is a problem with this approach, said Brian Kennedy, the director of the Center for Healthy Aging at the National University of Singapore: even if you treat diseases, you still haven’t cured aging itself.</p><p>“We don’t do healthcare [in the medical community], we do sick care,” he said, pointing out that the goal shouldn’t be just giving rich people access to cures for any disease but rather fundamentally attacking “aging” itself as a threat. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-87741000\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>“Aging is the biggest risk factor to all these diseases that go out of control,” he said. “This is not just about a few billionaires living longer. This is about a million people living longer.”</p><p>Aging itself creates risks, he said, because organs and body systems inevitably break down over time. His center is researching ways to halt aging at the enzyme level. One of the most promising is the TOR pathway, a kind of cellular signaling that tells a  cell to grow and divide or hunker down and turn up stress responses. Scientists believe that manipulating that pathway could slow down aging. </p><p>“It’s a really robust effect,” Kennedy said. </p><p>Once people realize that, he hopes his cause will be as flashy and imagination-capturing as Zuckerberg’s longevity quest. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-76052606\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>“The most important thing we can do right now is to validate [the idea that]<strong>&nbsp;</strong>we can affect the aging,” he said. “Once that happens, I think interest level will go way up.”</p><p>Biohacking will also open up new avenues—and intense ethical debates—about what lengths people can go to to change their genetic code. Scientists, for instance, are still <a href=\"https://gizmodo.com/genetically-engineering-yourself-sounds-like-a-horrible-1820189351#_ga=2.164994678.831180566.1512151706-1557875288.1506460947\" target=\"_blank\" rel=\"noopener\">carefully exploring CRISPR (</a>Clustered Regularly Interspaced Short Palindromic Repeats) technology, which acts like a homing missile that tracks down a specific DNA strand, then cuts and pastes a new strand in its place. It can be used to alter just about every aspect of DNA. In August, scientists  for the first time in the United States used the gene editing technology on a human embryo to <a href=\"https://www.washingtonpost.com/news/to-your-health/wp/2017/08/02/first-human-embryo-editing-experiment-in-u-s-corrects-gene-for-heart-condition/?utm_term=.4afe37acd749\" target=\"_blank\" rel=\"noopener\">erase a heritable heart condition.</a></p><h3><strong>Harvesting young body parts </strong></h3><p>Throughout history, people have seized on the idea that you can essentially patch or infuse the human body with parts of other bodies and cheat death, kinda like jailbreaking your iPhone so it can accept any software. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-11519691\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Take, for instance Serge Voronoff, a Russian-born scientist who in the early 20th century believed animal sex glands held the secret to prolonging life. In 1920, he tried it out, taking a piece of monkey testicle and sewing it to a human’s (although, it should be noted, not his own) scrotum. The idea seemed to catch on: by the mid-1920s, according to <a href=\"https://www.atlasobscura.com/articles/the-true-story-of-dr-voronoffs-plan-to-use-monkey-testicles-to-make-us-immortal\" target=\"_blank\" rel=\"noopener\">Atlas Obscura</a>, 300 people underwent his procedure; at least one woman received a graft of monkey ovary.</p><aside><span></span><span>﻿Monkey testicles have fallen out of style, but, unlike the good doctor Voronoff, the idea of harvesting body parts is still very much alive.</span><span></span></aside><p>“The sex gland stimulates cerebral activity as well as muscular energy and amorous passion,” Voronoff wrote in his 1920 book,<em> Life; a Study of the Means of Restoring Vital Energy and Prolonging Life</em>. “It pours into the stream of the blood a species of vital fluid which restores the energy of all the cells, and spreads happiness.” </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-77561601\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Voronoff eventually built his own monkey enclosure on his property and claimed he was able to restore 70 year olds to their youthful vigor. Some could live to 140, he claimed. He was able to charge as much as an average year’s salary at the time for the procedure. </p><p>Voronoff died in 1951, apparently never having rejuvenated himself. </p><p>Monkey testicles have fallen out of style, but, unlike the good doctor Voronoff, the idea of harvesting body parts is still very much alive. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-60905861\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Trump surrogate, Gawker killer and overall too-rich person Peter Thiel has talked about his <a href=\"https://gizmodo.com/someone-is-trying-to-discredit-the-story-of-peter-thiel-1796135794\" target=\"_blank\" rel=\"noopener\">interest in parabiosis</a>, the process of getting transfusions of blood from a younger person, to reverse aging. </p><p>“I’m looking into parabiosis stuff, which I think is really interesting. This is where they did the young blood into older mice and they found that had a massive rejuvenating effect,” he <a href=\"https://www.vanityfair.com/news/2016/08/peter-thiel-wants-to-inject-himself-with-young-peoples-blood\" target=\"_blank\" rel=\"noopener\">told Inc</a>. “It’s one of these very odd things where people had done these studies in the 1950s and then it got dropped altogether. I think there are a lot of these things that have been strangely under-explored.”</p><p>Studies have shown this <a href=\"https://www.theverge.com/2016/11/22/13699108/blood-transfer-parabiosis-aging-youth-study-peter-thiel\" target=\"_blank\" rel=\"noopener\">may just be the latest snake oil tactic,</a> though targeted to lunatic rich people who can’t help but be fascinated by the idea of literally feeding off the young. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-14745941\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>It certainly didn’t work out for Alexander Bogdanov, a science fiction writer, doctor, and pioneer of cybernetics who <a href=\"https://io9.gizmodo.com/the-genius-who-killed-himself-trying-to-become-immortal-1551484226\" target=\"_blank\" rel=\"noopener\">dabbled in blood transfusions in the 1920s</a>. He thought that if he ran a train of blood transfusions on himself, he could become functionally immortal. This thirst for blood met a hubristic end: he eventually took a blood transfusion from a malaria patient. The patient survived, but he did not.</p><h3><strong>Redefining the soul</strong></h3><p>Cave’s book breaks up immortality schemes throughout history into four classifications: the first one, staying alive in the body, involves all those life extending medicines and life hacking gene therapies discussed above. The second one involves resurrection, an idea that has fascinated people throughout history, from Luigi Galvani’s  18th century experiments running <a href=\"https://helix.northwestern.edu/article/experiment-shocked-world\" target=\"_blank\" rel=\"noopener\">electricity through a dead frog’s legs</a> to more recent efforts at cryonics, the process of freezing your body with the hope that future medicine or technology will be able to restore you to health. <a href=\"https://splinternews.com/silicon-valleys-young-tech-workers-are-betting-that-thi-1793855142#_ga=2.204617561.831180566.1512151706-1557875288.1506460947\" target=\"_blank\" rel=\"noopener\">Some in Silicon Valley are interested in new versions of cryonics</a>, but so far it doesn’t seem to be getting that much attention.</p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-69252490\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Cave’s third path involves finding immortality through the soul, something that has driven religious wars and controlled populations for eons. It takes as a fact that your physical body is a degrading mess that will one day betray you, but that doesn’t matter, since the soul is the real, eternal essence of who you are. But it’s best left to religious discussions nowadays, as science can’t seem to prove it exists.</p><p>“If bits of your brain are damaged, then bits of you, the fundamental deepest idea of who you are, have disappeared,” Cave said. He’s talking about the idea that if the soul is the indestructible essence of you that can survive eternity, why does our essence change when we suffer brain damage or other personality altering maladies? If your soul lets you live forever, which version of you exactly is the one that lives forever? </p><p>“That leads us to wonder if your soul is somehow supposed to be maintaining all these things, why can’t your soul do that for you? If it can do that when your whole brain is gone, why can’t that do that when a part of your brain is gone?” </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-86101278\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>But some techies argue the nature of these projects will redefine what a soul is entirely: not so much a ghostly essence of your being connected to a higher power, but more a specific set of brain signatures unique to you, a code that can be hacked like any other code.</p><aside><span></span><span>﻿“The challenge is to figure out ways to improve health span and get it to everybody as quickly as possible,” he said. “If it’s drugs, it’s achievable. If it’s a bunch of transfusions of young blood, that’s less achievable.”</span><span></span></aside><p>“Consider, then, the modern soul as the unique neuronal-synaptic signature integrating brain and body through a complex electrochemical flow of neurotransmitters. Each person has one, and they are all different,” Marcelo Gleiser, a theoretical physicist, writer, and a professor of natural philosophy, physics and astronomy at Dartmouth College, <a href=\"https://www.npr.org/sections/13.7/2017/04/05/522738015/is-neuroscience-rediscovering-the-soul\" target=\"_blank\" rel=\"noopener\">wrote for NPR in April</a>. “Can all this be reduced to information, such as to be replicated or uploaded into other-than-you substrates? That is, can we obtain sufficient information about this brain-body map so as to replicate it in other devices, be they machines or cloned biological replicas of your body?”</p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-38616287\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Google’s lifespan-extending project Calico launched in 2013 with a mission statement that calls aging “one of life’s greatest mysteries.” Also a great mystery is exactly what Calico has been up to: the company’s work <a href=\"https://www.vox.com/science-and-health/2017/4/27/15409672/google-calico-secretive-aging-mortality-research\" target=\"_blank\" rel=\"noopener\">has been shrouded in secrecy</a>, which has led to lots of curiosity and frustration  from the rest of people in the anti-aging field. So far, according to a <a href=\"https://www.newyorker.com/magazine/2017/04/03/silicon-valleys-quest-to-live-forever\" target=\"_blank\" rel=\"noopener\">New Yorker piece in April,</a> all that’s known is the company is tracking a thousand mice from birth to death to find “biomarkers” of aging, what can be described as biochemical substances whose levels predict death. The company has invested in drugs that may help fight diabetes and Alzheimer’s. </p><h2><strong>Creating a lasting legacy</strong></h2><p>The tech side of things brings us to Cave’s fourth path to immortality: legacy. For ancient civilizations, that meant creating monuments, having your living relatives chant your name after you’re gone or carving names on tomb walls. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-96397772\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>“If your name was spoken and your monuments still stood, they thought,” he wrote in his book, “then at least a part of you still lived.”</p><p>Today’s legacies look different than giant stone shrines, but the ego behind them is probably comparable. The idea of uploading consciousness to the cloud has crossed from science fiction into science possible: Russian web mogul Dmitry Itskov in 2011 launched the 2045 Initiative, an experiment to make himself immortal within the next 30 years by creating a robot that can store a human personality. </p><p>“Different scientists call it uploading or they call it mind transfer. I prefer to call it personality transfer,” Itskov <a href=\"http://www.telegraph.co.uk/business/2016/03/13/media-mogul-dmitry-itskov-plans-to-live-forever-by-uploading-his/\" target=\"_blank\" rel=\"noopener\">told the BBC last year.</a></p><h3><strong>Fears of an immortal tech bro planet</strong></h3><p>So here is one of the obvious main problems with Silicon Valley-led innovations, like many other tech-based lurches into the advanced future: it could be too expensive for everyone to afford. Which in turn could mean that we’ll have a class of near immortals, or cloud-based consciousnesses, ruling over people bound to their horrifying analog bodies. The meshing of human/computer/nantech parts will also open up a whole new thinkpiece industry about when someone stops becoming a “person” all together and is just lines of code.</p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-73434250\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Kennedy said opening these options up to everyone will depend on what avenue of research proves the most effective. If aging is treated as a disease (and healthcare in general somehow becomes affordable to everyone), there’s hope. </p><p>“The challenge is to figure out ways to improve health span and get it to everybody as quickly as possible,” he said. “If it’s drugs, it’s achievable. If it’s a bunch of transfusions of young blood, that’s less achievable.”</p><p>If all this has you bristling at the thought of techies creating their own super race of “disruptors” impervious to the torments of time and the limits of flesh, that’s understandable. But Cave said you may be encouraged by the entire history of people who’ve chased extended lifespans, from ancient Egypt to the people clinging to their diets and exercise throughout the 21st century. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-22746798\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"bottom\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>“The one thing that everyone has pursued immortality has in common,” he said, “is that they’re now six foot under, pushing up daisies.”</p><img src=\"http://feeds.feedburner.com/~r/lifehacker/vip/~4/U_GCR2gWr6c\" height=\"1\" width=\"1\">",
            "url": "https://lifehacker.com/how-to-outsmart-your-gross-body-and-live-forever-1820751558",
            "title": "How to Outsmart Your Gross Body and Live Forever",
            "date_modified": "2017-12-04T14:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15836698\">Comments</a>",
            "url": "http://fallibleideas.com/taking-children-seriously",
            "title": "Taking Children Seriously",
            "date_modified": "2017-12-03T08:50:33.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15534622\">Comments</a>",
            "url": "https://ventspace.wordpress.com/2017/10/20/games-look-bad-part-1-hdr-and-tone-mapping/",
            "title": "Games Look Bad: HDR and Tone Mapping",
            "date_modified": "2017-10-23T16:54:02.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--v5NEZOP8--/c_scale,fl_progressive,q_80,w_800/gm5gvjq5drku6lkwnstg.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--v5NEZOP8--/c_scale,fl_progressive,q_80,w_800/gm5gvjq5drku6lkwnstg.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--v5NEZOP8--/c_scale,fl_progressive,q_80,w_800/gm5gvjq5drku6lkwnstg.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--v5NEZOP8--/c_scale,fl_progressive,q_80,w_800/gm5gvjq5drku6lkwnstg.jpg\" data-sizes=\"auto\" data-width=\"4148\" data-chomp-id=\"gm5gvjq5drku6lkwnstg\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>Photo by<a href=\"https://unsplash.com/photos/215Fiqh6hRc\" target=\"_blank\" rel=\"noopener\"> Christopher Burns</a> </figcaption></div></figure><p>From an outsider’s perspective, what’s a cult and what’s not a cult can seem obvious. Not a cult: your new book group. Cult: that group your second cousin joined where all the women are renamed Meadow and are betrothed to Jeremy, their unshowered leader. Simple! </p><p>In reality, cults aren’t always so obvious; sometimes the cult-like aspects of an organization reveal themselves to you slowly, when you’re already fully invested. </p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-91859966\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>In our latest podcast episode, Rick Alan Ross outlined the three criteria of establishing whether or not a group is a cult—as identified by psychiatrist Robert Jay Lifton. </p><p>1. There is an authoritarian figure in charge of the group who is revered like a god. Everyone and every decision revolves around said figure. </p><div><div></div><div><div><div><div id=\"ad-container-72015008\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-72015008\"></span></div></div></div></div></div><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-95772466\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"mid\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>2. People in the group act against their own best interests, but in the best interest of the group (and the charismatic leader). This occurs through a process called “thought reform.” </p><p>3. The group exploits its members. The degree of harm inflicted on each member varies wildly depending on the group—some may take your money, others might inflict physical and sexual abuse.</p><p>Here’s the paper <a href=\"http://www.icsahome.com/articles/cult-formation-lifton-csj-8-1-1991\" target=\"_blank\" rel=\"noopener\">by Robert Jay Lifton</a> in its entirety—it’s fascinating and worth a read, especially if there are any organizations in your life you have doubts about. (Your book group leader <em>is </em>strangely charismatic, and everyone always loves her book suggestions...) </p><img src=\"http://feeds.feedburner.com/~r/lifehacker/vip/~4/lSxaFmQpVpg\" height=\"1\" width=\"1\">",
            "url": "https://lifehacker.com/how-to-figure-out-if-youre-in-a-cult-1819678625",
            "title": "How to Figure Out if You're in a Cult ",
            "date_modified": "2017-10-19T18:30:00.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--oKV3LiH7--/c_scale,fl_progressive,q_80,w_800/vvtdqgltqrhhauwhfxrp.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--oKV3LiH7--/c_scale,fl_progressive,q_80,w_800/vvtdqgltqrhhauwhfxrp.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--oKV3LiH7--/c_scale,fl_progressive,q_80,w_800/vvtdqgltqrhhauwhfxrp.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--oKV3LiH7--/c_scale,fl_progressive,q_80,w_800/vvtdqgltqrhhauwhfxrp.jpg\" data-sizes=\"auto\" data-width=\"3001\" data-chomp-id=\"vvtdqgltqrhhauwhfxrp\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>Image via <a href=\"http://www.apimages.com/metadata/Index/Manson-Follower-Parole/b88581a94b954eddbbb061d7ff070003/83/0\" target=\"_blank\" rel=\"noopener\">The Associated Press </a></figcaption></div></figure><p>In this episode we discussed cults: how they operate, how you identify one, what it’s like to be in one, and how to get out. To that end, we spoke with author Rebecca Stott, whose book <a rel=\"nofollow\" data-amazonasin=\"B01LY7FETJ\" data-amazonsubtag=\"[p|1819557884[a|B01LY7FETJ[au|5876237249237073298[b|lifehacker\" data-amazontag=\"lifehackeramzn-20\" href=\"https://www.amazon.com/Days-Rain-Daughter-Father-Cult-ebook/dp/B01LY7FETJ/ref=sr_1_1?ie=UTF8&amp;qid=1508186911&amp;sr=8-1&amp;keywords=In+the+Days+of+Rain%3A+A+Father%2C+a+Daughter%2C+a+Cult&amp;tag=lifehackeramzn-20&amp;ascsubtag=[t|link[p|1819557884[a|B01LY7FETJ[au|5876237249237073298[b|lifehacker\">In the Days of Rain: A Father, a Daughter, a Cult</a> details her childhood in the Exclusive Brethren, a cult that believed the world is ruled by Satan. We also talked to Rick Alan Ross, the founder and Executive Director of <a href=\"https://www.culteducation.com/\" target=\"_blank\" rel=\"noopener\">The Cult Education Institute</a>. And we talked with Elizabeth Yuko, a bioethicist and journalist who’s written extensively about cults.<br> </p><p><span><iframe height=\"150\" id=\"megaphone-PPY1139308045\" src=\"https://kinja.com/ajax/inset/iframe?id=megaphone-PPY1139308045\" width=\"640\"></iframe></span></p><p>Listen to The Upgrade above or find us in all the usual places where podcasts are served, including <a href=\"https://itunes.apple.com/us/podcast/lifehacker/id508117781?mt=2\" target=\"_blank\" rel=\"noopener\">Apple Podcasts</a>, <a href=\"https://play.google.com/music/listen?u=0#/ps/Illldmn6f4jkwb32lslhae3laru\" target=\"_blank\" rel=\"noopener\">Google Play</a>, <a href=\"https://open.spotify.com/show/43qzAEOZ861D4aXaBXaRKj\" target=\"_blank\" rel=\"noopener\">Spotify</a>, <a href=\"https://www.iheart.com/show/8-The-Upgrade-by-Lifehacker/\" target=\"_blank\" rel=\"noopener\">iHeartRadio</a>, <a href=\"http://www.stitcher.com/podcast/lifehacker-podcast\" target=\"_blank\" rel=\"noopener\">Stitcher</a>, and <a href=\"http://one.npr.org/i/522439906:522439908\" target=\"_blank\" rel=\"noopener\">NPR One</a>. Please subscribe, rate, and review!</p><h3><strong>Discussed in This Episode</strong></h3><ul><li>The Kristy McNichol/Jimmy McNichol vehicle <a href=\"http://www.imdb.com/title/tt0080444/\" target=\"_blank\" rel=\"noopener\">Blinded by The Light </a><br> </li><li><a href=\"http://www.bbc.co.uk/religion/religions/christianity/subdivisions/exclusivebrethren_1.shtml\" target=\"_blank\" rel=\"noopener\">The Exclusive Brethren</a></li><li>Why Rebecca Stott’s family left the Exclusive Brethren—the<a href=\"http://www.discourses.org.uk/History/TheAberdeenIncident.pdf\" target=\"_blank\" rel=\"noopener\"> “Aberdeen Incident</a>” </li><li>Rick Alan Ross’s <a href=\"https://www.culteducation.com/group.html\" target=\"_blank\" rel=\"noopener\">Cult Education Institute</a><br><a href=\"https://www.culteducation.com/group.html\" target=\"_blank\" rel=\"noopener\"> </a></li><li>Robert J. Lifton’s <a rel=\"nofollow\" data-amazonasin=\"B006M9RZQA\" data-amazonsubtag=\"[p|1819557884[a|B006M9RZQA[au|5876237249237073298[b|lifehacker\" data-amazontag=\"lifehackeramzn-20\" href=\"https://www.amazon.com/dp/B006M9RZQA/ref=dp-kindle-redirect?_encoding=UTF8&amp;btkr=1ascsubtag=33934ce4e14aa8f0babfbcee00c62e0327c8d20b&amp;rawdata=%5Bt%7Clink%5Bp%7C1802080666%5Ba%7CB006M9RZQA%5Bau%7C5876237249237073298%5Bb%7Clifehacker&amp;tag=lifehackeramzn-20&amp;ascsubtag=[t|link[p|1819557884[a|B006M9RZQA[au|5876237249237073298[b|lifehacker\">Thought Reform and the Psychology of Totalism</a></li><li><a href=\"https://en.wikipedia.org/wiki/Steven_Hassan\" target=\"_blank\" rel=\"noopener\">Steven Hassan</a>’s <a href=\"http://old.freedomofmind.com/Info/BITE/bitemodel.php\" target=\"_blank\" rel=\"noopener\">BITE model</a><br> </li><li>The <a href=\"https://freedomofmind.com/influence-continuum/\" target=\"_blank\" rel=\"noopener\">Influence Continuum</a><br> </li><li>Elizabeth Yuko’s Rolling Stone article, <a href=\"http://www.rollingstone.com/culture/features/cult-or-commune-how-utopian-communities-turn-dangerous-w449034\" target=\"_blank\" rel=\"noopener\">“Cult or Commune? How Utopian Communities Turn Dangerous</a></li><li>The documentary <a href=\"http://www.holyhellthedocumentary.com/\" target=\"_blank\" rel=\"noopener\">Holy Hell</a></li></ul><p>We reached out to any organizations mentioned by Rick Ross if he either referred to them as a cult or described having received complaints about them. We received responses from two organizations. Jehovah’s Witnesses declined to comment directly, but sent us these links: <a href=\"https://www.jw.org/en/jehovahs-witnesses/faq/are-jehovahs-witnesses-a-cult/#?insight%5Bsearch_id%5D=0bb9a655-e8ef-4010-9c16-1b23f572d60f&amp;insight%5Bsearch_result_index%5D=0\" target=\"_blank\" rel=\"noopener\">Are Jehovah’s Witnesses a Cult?</a>&nbsp;<a href=\"https://www.jw.org/en/jehovahs-witnesses/faq/not-a-sect/#?insight%5Bsearch_id%5D=6066507f-2db2-4b31-94dc-e64417665c14&amp;insight%5Bsearch_result_index%5D=1\" target=\"_blank\" rel=\"noopener\">Are Jehovah’s Witnesses an American Sect</a>?</p><p>We also heard from <a href=\"http://www.landmarkworldwide.com/\" target=\"_blank\" rel=\"noopener\">Landmark, </a>who commented, in part: <br></p><blockquote><p>Landmark is a global personal and professional growth, training, and development company that delivers programs and courses that empower and develop people to fulfill on what’s really important to them. Recognized for their leading-edge material and methodology, Landmark’s programs equip people to produce breakthrough results in areas such as career, relationships, productivity, and overall quality of life. More than 2.4 million people in more than 20 countries have taken Landmark’s programs and Landmark is considered one of the leading companies in its field. </p></blockquote><h3>Our Upgrades of the Week</h3><p>Every week we like to let you in on the upgrades we’ve made in our own lives. This week we talked about <a rel=\"nofollow\" data-amazonasin=\"B0012Y6PPM\" data-amazonsubtag=\"[p|1819557884[a|B0012Y6PPM[au|5876237249237073298[b|lifehacker\" data-amazontag=\"lifehackeramzn-20\" href=\"https://www.amazon.com/dp/B0012Y6PPM?psc=1ascsubtag=317dc6f0771f0b0628830791774bd4c48a202ea6&amp;rawdata=%5Bt%7Clink%5Bp%7C1802080666%5Ba%7CB0012Y6PPM%5Bau%7C5876237249237073298%5Bb%7Clifehacker&amp;tag=lifehackeramzn-20&amp;ascsubtag=[t|link[p|1819557884[a|B0012Y6PPM[au|5876237249237073298[b|lifehacker\">bloomers</a>, <a href=\"http://lifehacker.com/5635971/lifehacker-labs-capture-fruit-flies-with-a-cup-and-apple-cider-vinegar\" rel=\"nofollow\">killing fruit flies</a>, and <a href=\"https://www.permatemp.org/happening\" target=\"_blank\" rel=\"noopener\">this mesmerizing podcast.</a></p><h3>Want to Say Hi?</h3><p>There are two ways to reach out: </p><ul><li>Call (347) 687-8109 and record a question or comment. We love calls! We might just play yours on the show!</li><li>Email us at <a href=\"mailto:upgrade@lifehacker.com\" rel=\"nofollow\">upgrade@lifehacker.com</a>.</li></ul><p>We look forward to hearing from you!</p><img src=\"http://feeds.feedburner.com/~r/lifehacker/vip/~4/JBGzjD3G99g\" height=\"1\" width=\"1\">",
            "url": "https://lifehacker.com/how-to-identify-a-cult-with-rick-alan-ross-1819557884",
            "title": "How to Identify a Cult, With Rick Alan Ross ",
            "date_modified": "2017-10-17T12:30:00.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--KyBXcnk9--/c_scale,fl_progressive,q_80,w_800/imsdnuh0du1powclg3ex.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--KyBXcnk9--/c_scale,fl_progressive,q_80,w_800/imsdnuh0du1powclg3ex.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--KyBXcnk9--/c_scale,fl_progressive,q_80,w_800/imsdnuh0du1powclg3ex.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--KyBXcnk9--/c_scale,fl_progressive,q_80,w_800/imsdnuh0du1powclg3ex.jpg\" data-sizes=\"auto\" data-width=\"1600\" data-chomp-id=\"imsdnuh0du1powclg3ex\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://man326photogalley.blogspot.jp/2014/03/blog-post.html\" target=\"_blank\" rel=\"noopener\">Man-san’s Photo Gallery</a>]</figcaption></div></figure><p>Every business has  a starting point. For Japanese arcades, one of them was on department store rooftops. </p><p>Yes, Japanese arcades have their roots in the carnival type games often seen at local religious festivals. But way before coffee houses installed tabletop cabinets to capitalize on the late 70s <em>Space Invaders</em> craze, there were game machines atop department stores in cities like Osaka and Tokyo.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--zRqTkFuo--/c_scale,fl_progressive,q_80,w_800/pickrpwewxopbccoayot.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--zRqTkFuo--/c_scale,fl_progressive,q_80,w_800/pickrpwewxopbccoayot.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--zRqTkFuo--/c_scale,fl_progressive,q_80,w_800/pickrpwewxopbccoayot.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--zRqTkFuo--/c_scale,fl_progressive,q_80,w_800/pickrpwewxopbccoayot.jpg\" data-sizes=\"auto\" data-width=\"1600\" data-chomp-id=\"pickrpwewxopbccoayot\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://man326photogalley.blogspot.jp/2014/03/blog-post.html\" target=\"_blank\" rel=\"noopener\">Man-san’s Photo Gallery</a>]</figcaption></div></figure><p>Case in point: Namco, which still operates a large number of arcades in Japan. The company got its start making attractions, rides, and games for department store roofs. In 1955, when Namco was still Nakamura Manufacturing, the nascent company<a href=\"https://bandainamcoent.co.jp/corporate/history/namco/\" target=\"_blank\" rel=\"noopener\"> built two wooden horses</a> for a Yokohama department store. This was not the first of its kind. The maiden rooftop amusement area opened on a Tokyo department store in 1903, with wooden horses, a seesaw and an indoor play area. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--3A91U4T1--/c_scale,fl_progressive,q_80,w_800/gzhcdtsfnwnrvtputa3g.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--3A91U4T1--/c_scale,fl_progressive,q_80,w_800/gzhcdtsfnwnrvtputa3g.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--3A91U4T1--/c_scale,fl_progressive,q_80,w_800/gzhcdtsfnwnrvtputa3g.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--3A91U4T1--/c_scale,fl_progressive,q_80,w_800/gzhcdtsfnwnrvtputa3g.jpg\" data-sizes=\"auto\" data-width=\"800\" data-chomp-id=\"gzhcdtsfnwnrvtputa3g\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>This image dates from 1931. Check out the rooftop amusement park on top of this Tokyo department store. 「<a href=\"http://ameblo.jp/tokyo-skytree-blog/image-10819733234-11088337293.html\" target=\"_blank\" rel=\"noopener\">今日の東京スカイツリーと松屋浅草、あづち…</a> via Namazu-tron | <a href=\"https://ja.wikipedia.org/wiki/屋上遊園地#/media/File:Matsuya_Asakusa_1931.jpg\" target=\"_blank\" rel=\"noopener\">CC</a>]</figcaption></div></figure><p>By the time Nakamura Manufacturing entered the scene in the mid-1950s, this was a well established tradition in Japan, with a history of buildings covered with ropeways, Ferris Wheels and other attractions. No wonder they’re known as <em>okujou yuuenchi </em>(屋上遊園地), literally “rooftop amusement park.”</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--BgMVjt-E--/c_fit,fl_progressive,q_80,w_320/nwzqu40xkkhhtdfchc6v.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--GS_X26oG--/c_fit,fl_progressive,q_80,w_636/nwzqu40xkkhhtdfchc6v.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--GS_X26oG--/c_fit,fl_progressive,q_80,w_636/nwzqu40xkkhhtdfchc6v.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--BgMVjt-E--/c_fit,fl_progressive,q_80,w_320/nwzqu40xkkhhtdfchc6v.jpg\" data-sizes=\"auto\" data-width=\"580\" data-chomp-id=\"nwzqu40xkkhhtdfchc6v\" data-format=\"jpg\"></picture></div><figcaption>[Image: <a href=\"http://blog.livedoor.jp/nwknews/archives/5197116.html\" target=\"_blank\" rel=\"noopener\">Tetsugaku News</a>]</figcaption></div></figure><p>But with cities rebuilding after World War II, these rooftop play areas offered new business opportunities. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--o7v4eup5--/c_scale,fl_progressive,q_80,w_800/vicvru1tf3emjgen2vvu.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--o7v4eup5--/c_scale,fl_progressive,q_80,w_800/vicvru1tf3emjgen2vvu.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--o7v4eup5--/c_scale,fl_progressive,q_80,w_800/vicvru1tf3emjgen2vvu.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--o7v4eup5--/c_scale,fl_progressive,q_80,w_800/vicvru1tf3emjgen2vvu.jpg\" data-sizes=\"auto\" data-width=\"1259\" data-chomp-id=\"vicvru1tf3emjgen2vvu\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://blog.livedoor.jp/nwknews/archives/5197116.html\" target=\"_blank\" rel=\"noopener\">Tetsugaku News</a>]</figcaption></div></figure><p>In the early 1960s, Nakamura Manufacturing constructed a kiddy attraction called “Roadway Ride” on top of Mitsukoshi Department Store with children “driving” small cars on a railroad type track. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--PN1EC724--/c_fit,fl_progressive,q_80,w_320/nkdhg2ayndzpkhqiy7sz.png\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--6OtpU92X--/c_fit,fl_progressive,q_80,w_636/nkdhg2ayndzpkhqiy7sz.png\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--6OtpU92X--/c_fit,fl_progressive,q_80,w_636/nkdhg2ayndzpkhqiy7sz.png\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--PN1EC724--/c_fit,fl_progressive,q_80,w_320/nkdhg2ayndzpkhqiy7sz.png\" data-sizes=\"auto\" data-width=\"636\" data-chomp-id=\"nkdhg2ayndzpkhqiy7sz\" data-format=\"png\" width=\"600\"></picture></div><figcaption>[Image:<a href=\"http://www.namco.com/about/our-history\" target=\"_blank\" rel=\"noopener\"> Namco</a>]</figcaption></div></figure><p>Not everything was a ride, as there were also coin operated and carnival type games. Other companies, like Sega, also made  mechanical games that were enjoyed on these rooftops. <br></p><div><div></div><div><div><div><div id=\"ad-container-76539896\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-76539896\"></span></div></div></div></div></div><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-12339731\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>Website<a href=\"http://blog.livedoor.jp/nwknews/archives/5197116.html\" target=\"_blank\" rel=\"noopener\"><em> Tetsugaku News</em></a> recently published a series of old photos of Japanese department store roofs, showing the different attractions. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--uKz8Shon--/c_scale,fl_progressive,q_80,w_800/xwnh304dooxc0orx0nfz.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--uKz8Shon--/c_scale,fl_progressive,q_80,w_800/xwnh304dooxc0orx0nfz.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--uKz8Shon--/c_scale,fl_progressive,q_80,w_800/xwnh304dooxc0orx0nfz.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--uKz8Shon--/c_scale,fl_progressive,q_80,w_800/xwnh304dooxc0orx0nfz.jpg\" data-sizes=\"auto\" data-width=\"800\" data-chomp-id=\"xwnh304dooxc0orx0nfz\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://blog.livedoor.jp/nwknews/archives/5197116.html\" target=\"_blank\" rel=\"noopener\">Tetsugaku News</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--eHyz4G_f--/c_scale,fl_progressive,q_80,w_800/youpy428lc93l2d2pl56.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--eHyz4G_f--/c_scale,fl_progressive,q_80,w_800/youpy428lc93l2d2pl56.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--eHyz4G_f--/c_scale,fl_progressive,q_80,w_800/youpy428lc93l2d2pl56.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--eHyz4G_f--/c_scale,fl_progressive,q_80,w_800/youpy428lc93l2d2pl56.jpg\" data-sizes=\"auto\" data-width=\"1587\" data-chomp-id=\"youpy428lc93l2d2pl56\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>What a massive amusement area. [Image: <a href=\"http://blog.livedoor.jp/nwknews/archives/5197116.html\" target=\"_blank\" rel=\"noopener\">Tetsugaku News</a>] </figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--8QbTHygs--/c_scale,fl_progressive,q_80,w_800/xwxqzni66iwnpwabqter.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--8QbTHygs--/c_scale,fl_progressive,q_80,w_800/xwxqzni66iwnpwabqter.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--8QbTHygs--/c_scale,fl_progressive,q_80,w_800/xwxqzni66iwnpwabqter.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--8QbTHygs--/c_scale,fl_progressive,q_80,w_800/xwxqzni66iwnpwabqter.jpg\" data-sizes=\"auto\" data-width=\"800\" data-chomp-id=\"xwxqzni66iwnpwabqter\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://blog.livedoor.jp/nwknews/archives/5197116.html\" target=\"_blank\" rel=\"noopener\">Tetsugaku News</a>] </figcaption></div></figure><p>Via website <a href=\"http://nippon-sumizumi-kanko.com/b-hamaya-okujo.html\" target=\"_blank\" rel=\"noopener\"><em>Nippon Sumizumi Kanko</em></a>, here are some of the retro games found on the rooftop play area of Nagasaki’s Hamaya Department Store.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--P9hvZeBQ--/c_scale,fl_progressive,q_80,w_800/y9tzpxsrnpqpc1aro83z.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--P9hvZeBQ--/c_scale,fl_progressive,q_80,w_800/y9tzpxsrnpqpc1aro83z.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--P9hvZeBQ--/c_scale,fl_progressive,q_80,w_800/y9tzpxsrnpqpc1aro83z.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--P9hvZeBQ--/c_scale,fl_progressive,q_80,w_800/y9tzpxsrnpqpc1aro83z.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"y9tzpxsrnpqpc1aro83z\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://nippon-sumizumi-kanko.com/b-hamaya-okujo.html\" target=\"_blank\" rel=\"noopener\">Nippon Sumizumi Kanko</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--vyyrdDyv--/c_scale,fl_progressive,q_80,w_800/gvwy3hldarut3u3ct2st.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--vyyrdDyv--/c_scale,fl_progressive,q_80,w_800/gvwy3hldarut3u3ct2st.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--vyyrdDyv--/c_scale,fl_progressive,q_80,w_800/gvwy3hldarut3u3ct2st.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--vyyrdDyv--/c_scale,fl_progressive,q_80,w_800/gvwy3hldarut3u3ct2st.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"gvwy3hldarut3u3ct2st\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://nippon-sumizumi-kanko.com/b-hamaya-okujo.html\" target=\"_blank\" rel=\"noopener\">Nippon Sumizumi Kanko</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--2Z1Je8Kl--/c_scale,fl_progressive,q_80,w_800/xdbatszkzodqqwmll7hh.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--2Z1Je8Kl--/c_scale,fl_progressive,q_80,w_800/xdbatszkzodqqwmll7hh.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--2Z1Je8Kl--/c_scale,fl_progressive,q_80,w_800/xdbatszkzodqqwmll7hh.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--2Z1Je8Kl--/c_scale,fl_progressive,q_80,w_800/xdbatszkzodqqwmll7hh.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"xdbatszkzodqqwmll7hh\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://nippon-sumizumi-kanko.com/b-hamaya-okujo.html\" target=\"_blank\" rel=\"noopener\">Nippon Sumizumi Kanko</a>]</figcaption></div></figure><p>Around this same time, there were also bowling alleys with mechanical games, but everything radically changed with <em>Space Invaders</em>. Dedicated establishments, called “inbeedaa hausu” (“invader house”), starting popping up across the country, evolving into the Japanese gaming arcades of today.<br></p><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-84973302\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"mid\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>When I came to Japan in 2001, rooftop arcades were still fairly common. But these days, with more and more of them are being shuttered instead of getting needed updates and repairs.</p><p>For example, the previously mentioned Hamaya Department Store’s rooftop amusement park is no more, and after 56 years in operation, the okujou yuuenchi on Hanshin Department Store in Osaka was shut down, as documented by <a href=\"http://man326photogalley.blogspot.jp/2014/03/blog-post.html\" target=\"_blank\" rel=\"noopener\"><em>Man-san’s Photo Gallery</em></a>. These are just two of many, and it’s increasingly rare for department stores to have them. Sadly, the age of rooftop amusement parks is drawing to a close.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pQ00A1UX--/c_scale,fl_progressive,q_80,w_800/uwtvhnfcmv9qfopgoxnr.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pQ00A1UX--/c_scale,fl_progressive,q_80,w_800/uwtvhnfcmv9qfopgoxnr.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pQ00A1UX--/c_scale,fl_progressive,q_80,w_800/uwtvhnfcmv9qfopgoxnr.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--pQ00A1UX--/c_scale,fl_progressive,q_80,w_800/uwtvhnfcmv9qfopgoxnr.jpg\" data-sizes=\"auto\" data-width=\"1600\" data-chomp-id=\"uwtvhnfcmv9qfopgoxnr\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://man326photogalley.blogspot.jp/2014/03/blog-post.html\" target=\"_blank\" rel=\"noopener\">Man-san’s Photo Gallery</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--JkzLZ35F--/c_scale,fl_progressive,q_80,w_800/f0arazbc3qai1w0ai32f.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--JkzLZ35F--/c_scale,fl_progressive,q_80,w_800/f0arazbc3qai1w0ai32f.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--JkzLZ35F--/c_scale,fl_progressive,q_80,w_800/f0arazbc3qai1w0ai32f.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--JkzLZ35F--/c_scale,fl_progressive,q_80,w_800/f0arazbc3qai1w0ai32f.jpg\" data-sizes=\"auto\" data-width=\"1600\" data-chomp-id=\"f0arazbc3qai1w0ai32f\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Image: <a href=\"http://man326photogalley.blogspot.jp/2014/03/blog-post.html\" target=\"_blank\" rel=\"noopener\">Man-san’s Photo Gallery</a>]</figcaption></div></figure><p>But many modern Japanese arcades still have this rooftop amusement park DNA, offering small rides for children. Some, like Joypolis, are modern throwbacks to the okujou yuuenchi of yore.</p><p><span><iframe data-chomp-id=\"ulcPJBEUsUk\" data-recommend-id=\"youtube://ulcPJBEUsUk\" height=\"450\" id=\"youtube-ulcPJBEUsUk\" src=\"https://kinja.com/ajax/inset/iframe?id=youtube-video-ulcPJBEUsUk&amp;start=0\" width=\"800\"></iframe></span></p><p>The only thing that is missing is the rooftop setting. </p><p>For more photos, check out <a href=\"http://nippon-sumizumi-kanko.com/b-hamaya-okujo.html\" target=\"_blank\" rel=\"noopener\"><em>Nippon Sumizumi Kanko</em></a><em>&nbsp;</em>and <a href=\"http://man326photogalley.blogspot.jp/2014/03/blog-post.html\" target=\"_blank\" rel=\"noopener\"><em>Man-san’s Photo Gallery</em></a>.</p><p><small>This article was originally posted on April 6, 2017. </small></p><hr><p><small>Kotaku East is your slice of Asian internet culture, bringing you the latest talking points from Japan, Korea, China and beyond. Tune in every morning from 4am to 8am.</small></p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/IZ-REltAQvo\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/an-unlikely-origin-of-japanese-arcades-1794067285",
            "title": "The Birth Of Japanese Arcades ",
            "date_modified": "2017-10-10T11:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15412002\">Comments</a>",
            "url": "http://www.spoon-tamago.com/2017/10/04/japanese-vending-machines-at-night-juxtaposed-with-a-wintry-hokkaido-landscape/",
            "title": "Japanese Vending Machines at Night Juxtaposed with a Wintry Hokkaido Landscape",
            "date_modified": "2017-10-05T19:41:27.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--tig78TWv--/c_scale,fl_progressive,q_80,w_800/hpollnkgb0xenfkjoxou.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--VXORvLBD--/c_scale,fl_progressive,q_80,w_800/hpollnkgb0xenfkjoxou.gif\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--VXORvLBD--/c_scale,fl_progressive,q_80,w_800/hpollnkgb0xenfkjoxou.gif\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--tig78TWv--/c_scale,fl_progressive,q_80,w_800/hpollnkgb0xenfkjoxou.jpg\" data-sizes=\"auto\" data-poster-src=\"https://i.kinja-img.com/gawker-media/image/upload/s--tig78TWv--/c_scale,fl_progressive,q_80,w_800/hpollnkgb0xenfkjoxou.jpg\" data-anim-src=\"https://i.kinja-img.com/gawker-media/image/upload/s--gwNZA-bl--/c_fit,fl_progressive,q_80,w_320/hpollnkgb0xenfkjoxou.gif\" data-width=\"830\" data-chomp-id=\"hpollnkgb0xenfkjoxou\" data-format=\"gif\" width=\"600\"></picture><div>GIF <svg><use xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"#iconset-play\"></use></svg></div><div><div><div> </div><div> </div><div> </div><div> </div></div></div></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><p>A big part of <em>Your Name </em>takes place in Tokyo. Over on <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\"><em>Tofugu.com</em></a>, Kanae Nakamine visited the real-world locations depicted in the movie.&nbsp;<br></p><p>The resulting comparisons between the anime and real life are fascinating. </p><p>Nakamine also created a helpful itinerary in case you want to make a <em>Your Name</em> seichijunrei (聖地巡礼) or pilgrimage, visiting the places depicted in the hit anime.</p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--EQm94neS--/c_scale,fl_progressive,q_80,w_800/csbukxlqnxup0cizil7h.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--EQm94neS--/c_scale,fl_progressive,q_80,w_800/csbukxlqnxup0cizil7h.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--EQm94neS--/c_scale,fl_progressive,q_80,w_800/csbukxlqnxup0cizil7h.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--EQm94neS--/c_scale,fl_progressive,q_80,w_800/csbukxlqnxup0cizil7h.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"csbukxlqnxup0cizil7h\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--YTvs3hqW--/c_scale,fl_progressive,q_80,w_800/dt6bvbh9germ7phdwvol.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--YTvs3hqW--/c_scale,fl_progressive,q_80,w_800/dt6bvbh9germ7phdwvol.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--YTvs3hqW--/c_scale,fl_progressive,q_80,w_800/dt6bvbh9germ7phdwvol.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--YTvs3hqW--/c_scale,fl_progressive,q_80,w_800/dt6bvbh9germ7phdwvol.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"dt6bvbh9germ7phdwvol\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--A2CwfSmj--/c_scale,fl_progressive,q_80,w_800/qa4z83rronjnguqcqzry.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--A2CwfSmj--/c_scale,fl_progressive,q_80,w_800/qa4z83rronjnguqcqzry.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--A2CwfSmj--/c_scale,fl_progressive,q_80,w_800/qa4z83rronjnguqcqzry.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--A2CwfSmj--/c_scale,fl_progressive,q_80,w_800/qa4z83rronjnguqcqzry.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"qa4z83rronjnguqcqzry\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--LPAyzMXA--/c_scale,fl_progressive,q_80,w_800/nnlhw5tqcwasujq4diwl.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--LPAyzMXA--/c_scale,fl_progressive,q_80,w_800/nnlhw5tqcwasujq4diwl.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--LPAyzMXA--/c_scale,fl_progressive,q_80,w_800/nnlhw5tqcwasujq4diwl.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--LPAyzMXA--/c_scale,fl_progressive,q_80,w_800/nnlhw5tqcwasujq4diwl.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"nnlhw5tqcwasujq4diwl\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xFKMKVeB--/c_scale,fl_progressive,q_80,w_800/zmoy8lt2fd1logw8gjtr.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xFKMKVeB--/c_scale,fl_progressive,q_80,w_800/zmoy8lt2fd1logw8gjtr.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xFKMKVeB--/c_scale,fl_progressive,q_80,w_800/zmoy8lt2fd1logw8gjtr.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--xFKMKVeB--/c_scale,fl_progressive,q_80,w_800/zmoy8lt2fd1logw8gjtr.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"zmoy8lt2fd1logw8gjtr\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--V0V2eKxS--/c_scale,fl_progressive,q_80,w_800/dikbha00wzfvax6woqhk.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--V0V2eKxS--/c_scale,fl_progressive,q_80,w_800/dikbha00wzfvax6woqhk.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--V0V2eKxS--/c_scale,fl_progressive,q_80,w_800/dikbha00wzfvax6woqhk.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--V0V2eKxS--/c_scale,fl_progressive,q_80,w_800/dikbha00wzfvax6woqhk.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"dikbha00wzfvax6woqhk\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--_XBFcf5a--/c_scale,fl_progressive,q_80,w_800/bemj500yzfdlleefw6u3.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--_XBFcf5a--/c_scale,fl_progressive,q_80,w_800/bemj500yzfdlleefw6u3.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--_XBFcf5a--/c_scale,fl_progressive,q_80,w_800/bemj500yzfdlleefw6u3.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--_XBFcf5a--/c_scale,fl_progressive,q_80,w_800/bemj500yzfdlleefw6u3.jpg\" data-sizes=\"auto\" data-width=\"1280\" data-chomp-id=\"bemj500yzfdlleefw6u3\" data-format=\"jpg\" width=\"600\"></picture></div><figcaption>[Images: <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\">Tofugu</a>]</figcaption></div></figure><p>Be sure to check <a href=\"https://www.tofugu.com/japan/your-name-locations/\" target=\"_blank\" rel=\"noopener\"><em>Tofugu</em></a> for more comparisons and info regarding where these Tokyo spots are located.<br></p><p>In case you missed it, you can read <em>Kotaku</em>’s <em>Your Name</em> <a href=\"https://kotaku.com/brilliant-anime-movie-your-name-stands-toe-to-toe-with-1794083479\" rel=\"nofollow\">review right here</a>.&nbsp;</p><hr><p><small>Kotaku East is your slice of Asian internet culture, bringing you the latest talking points from Japan, Korea, China and beyond. Tune in every morning from 4am to 8am.</small></p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/NEgiBB2P1u0\" height=\"1\" width=\"1\">",
            "url": "https://kotaku.com/your-names-anime-locations-compared-with-the-real-world-1819130993",
            "title": "Your Name's Anime Locations Compared With The Real-World Ones",
            "date_modified": "2017-10-04T11:00:00.000Z"
        },
        {
            "content_html": "<p><span><iframe data-chomp-id=\"IMwi5E3qRnI\" data-recommend-id=\"youtube://IMwi5E3qRnI\" height=\"450\" id=\"youtube-IMwi5E3qRnI\" src=\"https://kinja.com/ajax/inset/iframe?id=youtube-video-IMwi5E3qRnI&amp;start=0\" width=\"800\"></iframe></span></p><p><strong>Here’s a cool video tribute to <a href=\"http://kotaku.com/former-valve-writer-posts-possible-half-life-2-episode-1798418160\" rel=\"nofollow\"><em>Half-Life 2: Episode 3</em></a> by <a href=\"https://www.youtube.com/channel/UCILaGtQaYAh3wnkvg4Rxzqg\" target=\"_blank\" rel=\"noopener\">Anomalous Materials</a>. </strong>Oh, what could have been. </p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/pOGzRUNHFJc\" height=\"1\" width=\"1\">",
            "url": "http://steamed.kotaku.com/1801256933",
            "title": "Here’s a cool video tribute to Half-Life 2: Episode 3 by Anomalous Materials.",
            "date_modified": "2017-09-07T01:10:40.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--5okJPbAE--/c_scale,fl_progressive,q_80,w_800/htrtkiqqrm8etvanfexb.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--5okJPbAE--/c_scale,fl_progressive,q_80,w_800/htrtkiqqrm8etvanfexb.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--5okJPbAE--/c_scale,fl_progressive,q_80,w_800/htrtkiqqrm8etvanfexb.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--5okJPbAE--/c_scale,fl_progressive,q_80,w_800/htrtkiqqrm8etvanfexb.jpg\" data-sizes=\"auto\" data-width=\"1170\" data-chomp-id=\"htrtkiqqrm8etvanfexb\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p>There’s an official <em>Evangelion </em>x New Balance line of sneakers coming out in Japan and China this weekend.<br></p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s---J8Gn1_W--/c_scale,fl_progressive,q_80,w_800/iau6f6t6poo5gq9xcmyw.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s---J8Gn1_W--/c_scale,fl_progressive,q_80,w_800/iau6f6t6poo5gq9xcmyw.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s---J8Gn1_W--/c_scale,fl_progressive,q_80,w_800/iau6f6t6poo5gq9xcmyw.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s---J8Gn1_W--/c_scale,fl_progressive,q_80,w_800/iau6f6t6poo5gq9xcmyw.jpg\" data-sizes=\"auto\" data-width=\"1170\" data-chomp-id=\"iau6f6t6poo5gq9xcmyw\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p>There’ll be four colours available, each in a very subtle scheme, while they’ll also come in nice padded zip-up boxes that feature <em>Evangelion </em>imagery.<br></p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--EEz6ZFE---/c_scale,fl_progressive,q_80,w_800/w9ty7ga55lykfpse7phz.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--EEz6ZFE---/c_scale,fl_progressive,q_80,w_800/w9ty7ga55lykfpse7phz.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--EEz6ZFE---/c_scale,fl_progressive,q_80,w_800/w9ty7ga55lykfpse7phz.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--EEz6ZFE---/c_scale,fl_progressive,q_80,w_800/w9ty7ga55lykfpse7phz.jpg\" data-sizes=\"auto\" data-width=\"1170\" data-chomp-id=\"w9ty7ga55lykfpse7phz\" data-format=\"jpg\" width=\"600\"></picture></div></div></figure><p>They’ll go on sale for around USD$100. More pics at <a href=\"https://hypebeast.com/2017/9/new-balance-evangelion-sneaker-collab\" target=\"_blank\" rel=\"noopener\">Hypebeast</a>.</p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/7BH_jLRoJpU\" height=\"1\" width=\"1\">",
            "url": "http://kotaku.com/evangelion-sneakers-are-surprisingly-tasteful-1800674421",
            "title": "Evangelion Sneakers Are Surprisingly Tasteful",
            "date_modified": "2017-09-07T00:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15156626\">Comments</a>",
            "url": "http://www.telegraph.co.uk/books/non-fiction/spot-psychopath/",
            "title": "How to spot a psychopath",
            "date_modified": "2017-09-02T15:59:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15124881\">Comments</a>",
            "url": "https://news.harvard.edu/gazette/story/2017/08/when-it-comes-to-internet-privacy-be-very-afraid-analyst-suggests/",
            "title": "When it comes to internet privacy, be afraid, analyst suggests",
            "date_modified": "2017-08-29T15:23:57.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15103085\">Comments</a>",
            "url": "https://www.activism.net/cypherpunk/manifesto.html",
            "title": "A Cypherpunk's Manifesto",
            "date_modified": "2017-08-25T23:32:43.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=15097015\">Comments</a>",
            "url": "http://www.marclaidlaw.com/epistle-3/",
            "title": "Half life 3 story released by Marc Laidlaw",
            "date_modified": "2017-08-25T08:35:35.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--jU8qCgf6--/c_scale,fl_progressive,q_80,w_800/cr8v7nrun15k30vk9gap.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--jU8qCgf6--/c_scale,fl_progressive,q_80,w_800/cr8v7nrun15k30vk9gap.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--jU8qCgf6--/c_scale,fl_progressive,q_80,w_800/cr8v7nrun15k30vk9gap.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--jU8qCgf6--/c_scale,fl_progressive,q_80,w_800/cr8v7nrun15k30vk9gap.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"cr8v7nrun15k30vk9gap\" data-format=\"jpg\"></picture></div><figcaption>My poor black Leviathan, so unhip. </figcaption></div></figure><p>I’ve been reviewing Razer accessories and hardware for ages, and aside from the odd license tie-in and <a href=\"http://kotaku.com/razers-figured-out-colors-everybody-1588596711\" rel=\"nofollow\">those rainbow headsets</a>, they’ve mostly had one thing in common—they’ve been black. Well now we’ve got the Mercury line, so bright and white they look like the restless spirits of real Razer products. </p><p>Razer’s actually got<a href=\"https://www.razerzone.com/campaigns/gunmetal-mercury\" target=\"_blank\" rel=\"noopener\"> two new color schemes going on</a>, Mercury (white) and Gunmetal (gray), but gray is really just light black, babysteps compared to the jarring contrast between Razer’s jet black lineup and these ivory beauties. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--gu1S6PLC--/c_scale,fl_progressive,q_80,w_800/ori8f3p72uurhwlw7s4l.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--gu1S6PLC--/c_scale,fl_progressive,q_80,w_800/ori8f3p72uurhwlw7s4l.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--gu1S6PLC--/c_scale,fl_progressive,q_80,w_800/ori8f3p72uurhwlw7s4l.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--gu1S6PLC--/c_scale,fl_progressive,q_80,w_800/ori8f3p72uurhwlw7s4l.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"ori8f3p72uurhwlw7s4l\" data-format=\"jpg\"></picture></div></div></figure><p>First off we have the Invicta “gaming surface.” I put gaming surface in quotes because it’s generally just a fancy term for mouse pad, but in this case it might be appropriate. For one, this has a case. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--XLf_KLyN--/c_scale,fl_progressive,q_80,w_800/wyzgasxibty7cgfduydv.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--XLf_KLyN--/c_scale,fl_progressive,q_80,w_800/wyzgasxibty7cgfduydv.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--XLf_KLyN--/c_scale,fl_progressive,q_80,w_800/wyzgasxibty7cgfduydv.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--XLf_KLyN--/c_scale,fl_progressive,q_80,w_800/wyzgasxibty7cgfduydv.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"wyzgasxibty7cgfduydv\" data-format=\"jpg\"></picture></div></div></figure><p>The Invicta is a dual-sided gaming surface (it’s growing on me). One side is smooth for speed. The other is rough, for control.  It costs $60 and weighs around 1.5 pounds, not counting the case. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pwbTVjjc--/c_scale,fl_progressive,q_80,w_800/livovgxy4qlgpj95exxt.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pwbTVjjc--/c_scale,fl_progressive,q_80,w_800/livovgxy4qlgpj95exxt.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--pwbTVjjc--/c_scale,fl_progressive,q_80,w_800/livovgxy4qlgpj95exxt.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--pwbTVjjc--/c_scale,fl_progressive,q_80,w_800/livovgxy4qlgpj95exxt.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"livovgxy4qlgpj95exxt\" data-format=\"jpg\"></picture></div></div></figure><p>The weight is because the Invicta pad comes with an aluminum base plate. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--8yhTr0iu--/c_scale,fl_progressive,q_80,w_800/k88n0ncmzyxljzwn2pj0.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--8yhTr0iu--/c_scale,fl_progressive,q_80,w_800/k88n0ncmzyxljzwn2pj0.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--8yhTr0iu--/c_scale,fl_progressive,q_80,w_800/k88n0ncmzyxljzwn2pj0.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--8yhTr0iu--/c_scale,fl_progressive,q_80,w_800/k88n0ncmzyxljzwn2pj0.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"k88n0ncmzyxljzwn2pj0\" data-format=\"jpg\"></picture></div></div></figure><p>That’s one serious mouse pad. And here’s a serious mouse for it. The Lancehead Tournament Edition is a mouse with a 16,000 DPI 5G optical sensor, tracking at 450 inches per second. As a man who generally gets by with a trackball, that’s ridiculous. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xb488cRG--/c_scale,fl_progressive,q_80,w_800/wujaqkvxam3revqxlhqj.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xb488cRG--/c_scale,fl_progressive,q_80,w_800/wujaqkvxam3revqxlhqj.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xb488cRG--/c_scale,fl_progressive,q_80,w_800/wujaqkvxam3revqxlhqj.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--xb488cRG--/c_scale,fl_progressive,q_80,w_800/wujaqkvxam3revqxlhqj.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"wujaqkvxam3revqxlhqj\" data-format=\"jpg\"></picture></div></div></figure><p>The $80 Lancehead is completely symmetrical, with side buttons on the left and right so anyone can use it, regardless of dominant hand. Plug it in and place it on top of the Invicta and it looks like a racing pod from some utopian future. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--gAsgwPg_--/c_scale,fl_progressive,q_80,w_800/yqwxnjazma7fupeohxoy.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--gAsgwPg_--/c_scale,fl_progressive,q_80,w_800/yqwxnjazma7fupeohxoy.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--gAsgwPg_--/c_scale,fl_progressive,q_80,w_800/yqwxnjazma7fupeohxoy.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--gAsgwPg_--/c_scale,fl_progressive,q_80,w_800/yqwxnjazma7fupeohxoy.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"yqwxnjazma7fupeohxoy\" data-format=\"jpg\"></picture></div></div></figure><p>The Kraken 7.1 v2 is Razer’s middle-of-the-road headset, priced at $99.99 (the $200 Razer Tiamat 7.1 V2 is their latest top-of-the-line wired set). It’s a great set of cans with a pretty good retractable mic. The switch to white isn’t as dramatic for this one, mainly because I never see it once it’s on my head. It’s still very pretty.  </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--m9KFWW_6--/c_scale,fl_progressive,q_80,w_800/cm12cgrbekuykw7yg88u.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--m9KFWW_6--/c_scale,fl_progressive,q_80,w_800/cm12cgrbekuykw7yg88u.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--m9KFWW_6--/c_scale,fl_progressive,q_80,w_800/cm12cgrbekuykw7yg88u.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--m9KFWW_6--/c_scale,fl_progressive,q_80,w_800/cm12cgrbekuykw7yg88u.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"cm12cgrbekuykw7yg88u\" data-format=\"jpg\"></picture></div></div></figure><p>The $150 Blackwidow X, on the other hand, barely looks like a Razer product anymore. Rather than go straight-up white everything, Razer made the exposed metal plate silver, giving the board a lovely contrast. The Razer logo on the front is barely visible when the unit’s LEDs are off. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZY14EqRs--/c_scale,fl_progressive,q_80,w_800/vfkhiodkt5000ipdlkze.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZY14EqRs--/c_scale,fl_progressive,q_80,w_800/vfkhiodkt5000ipdlkze.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZY14EqRs--/c_scale,fl_progressive,q_80,w_800/vfkhiodkt5000ipdlkze.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZY14EqRs--/c_scale,fl_progressive,q_80,w_800/vfkhiodkt5000ipdlkze.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"vfkhiodkt5000ipdlkze\" data-format=\"jpg\"></picture></div></div></figure><p>There is something otherworldly about a set of shine-through white keycaps with soft RGB lighting piping through. It’s dreamy. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ydmP0h5L--/c_scale,fl_progressive,q_80,w_800/es2ar3ticahxlnfs9fr6.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ydmP0h5L--/c_scale,fl_progressive,q_80,w_800/es2ar3ticahxlnfs9fr6.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--ydmP0h5L--/c_scale,fl_progressive,q_80,w_800/es2ar3ticahxlnfs9fr6.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--ydmP0h5L--/c_scale,fl_progressive,q_80,w_800/es2ar3ticahxlnfs9fr6.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"es2ar3ticahxlnfs9fr6\" data-format=\"jpg\"></picture></div></div></figure><p>Razer’s even gone as far as running a batch of their custom switches (this one has the clicky greens) with a pale gray housing as opposed to the normal black. The company’s made a real commitment here. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Zb0wlvdv--/c_scale,fl_progressive,q_80,w_800/m9iszxwubijo9ysueeq8.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Zb0wlvdv--/c_scale,fl_progressive,q_80,w_800/m9iszxwubijo9ysueeq8.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--Zb0wlvdv--/c_scale,fl_progressive,q_80,w_800/m9iszxwubijo9ysueeq8.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--Zb0wlvdv--/c_scale,fl_progressive,q_80,w_800/m9iszxwubijo9ysueeq8.jpg\" data-sizes=\"auto\" data-width=\"800\" data-chomp-id=\"m9iszxwubijo9ysueeq8\" data-format=\"jpg\"></picture></div></div></figure><p>The whole set comes together nicely. Razer is known for creating aggressive-looking gaming hardware with bold (some would say obnoxious) branding. The Mercury line softens those rough edges considerably. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--kvf0YJcV--/c_scale,fl_progressive,q_80,w_800/aluaxxzmceaenyruurxx.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--kvf0YJcV--/c_scale,fl_progressive,q_80,w_800/aluaxxzmceaenyruurxx.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--kvf0YJcV--/c_scale,fl_progressive,q_80,w_800/aluaxxzmceaenyruurxx.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--kvf0YJcV--/c_scale,fl_progressive,q_80,w_800/aluaxxzmceaenyruurxx.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"aluaxxzmceaenyruurxx\" data-format=\"jpg\"></picture></div><figcaption>Seriously looks like someone’s Razer desktop died and is haunting me. </figcaption></div></figure><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/6PkyIZnhkRY\" height=\"1\" width=\"1\">",
            "url": "http://kotaku.com/razer-gear-looks-really-nice-in-white-1797933112",
            "title": "Razer Gear Looks Really Nice In White",
            "date_modified": "2017-08-17T14:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14933640\">Comments</a>",
            "url": "https://www.l4sb.com/lpa/anonymous-llc/",
            "title": "Anonymous LLC",
            "date_modified": "2017-08-05T01:03:25.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--RoJioe6U--/c_scale,fl_progressive,q_80,w_800/ob5zk0hlmaa3vaynjqwa.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--RoJioe6U--/c_scale,fl_progressive,q_80,w_800/ob5zk0hlmaa3vaynjqwa.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--RoJioe6U--/c_scale,fl_progressive,q_80,w_800/ob5zk0hlmaa3vaynjqwa.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--RoJioe6U--/c_scale,fl_progressive,q_80,w_800/ob5zk0hlmaa3vaynjqwa.jpg\" data-sizes=\"auto\" data-width=\"2560\" data-chomp-id=\"ob5zk0hlmaa3vaynjqwa\" data-format=\"jpg\"></picture></div></div></figure><p><strong><em>Nier: Automata</em> audio engineer Masami Ueda has written <a href=\"https://www.platinumgames.com/official-blog/article/9581\" target=\"_blank\" rel=\"noopener\">a cool blog post</a></strong> detailing how he implemented composer Keiichi Okabe’s secondary hacking soundtrack. It’s easy to overlook how much work something like that takes, and fun to get a look at Ueda’s process.</p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/lj0yLwr9IvQ\" height=\"1\" width=\"1\">",
            "url": "http://kotaku.com/nier-automata-audio-engineer-masami-ueda-has-written-a-1797468934",
            "title": "Nier: Automata audio engineer Masami Ueda has written a cool blog post detailing how he implemented",
            "date_modified": "2017-08-02T16:45:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14785479\">Comments</a>",
            "url": "https://arstechnica.com/science/2017/07/defense-of-gwyneth-paltrows-goop-offers-case-study-on-how-to-sell-snake-oil/",
            "title": "Defense of Gwyneth Paltrow’s Goop offers case study on how to sell snake oil",
            "date_modified": "2017-07-17T03:34:19.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14778335\">Comments</a>",
            "url": "https://www.google.com/culturalinstitute/beta/exhibit/ogKCPmGdPtB7Iw",
            "title": "Tokyo street fashion and culture: 1980 – 2017",
            "date_modified": "2017-07-15T19:06:19.000Z"
        },
        {
            "content_html": "<figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--QmfmD86K--/c_scale,fl_progressive,q_80,w_800/hbxs7kmgztkafzwd89u2.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xDCWK-5N--/c_scale,fl_progressive,q_80,w_800/hbxs7kmgztkafzwd89u2.gif\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--xDCWK-5N--/c_scale,fl_progressive,q_80,w_800/hbxs7kmgztkafzwd89u2.gif\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--QmfmD86K--/c_scale,fl_progressive,q_80,w_800/hbxs7kmgztkafzwd89u2.jpg\" data-sizes=\"auto\" data-poster-src=\"https://i.kinja-img.com/gawker-media/image/upload/s--QmfmD86K--/c_scale,fl_progressive,q_80,w_800/hbxs7kmgztkafzwd89u2.jpg\" data-anim-src=\"https://i.kinja-img.com/gawker-media/image/upload/s--ZxwslWVe--/c_fit,fl_progressive,q_80,w_320/hbxs7kmgztkafzwd89u2.gif\" data-width=\"800\" data-chomp-id=\"hbxs7kmgztkafzwd89u2\" data-format=\"gif\"></picture><div>GIF <svg><use xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"#iconset-play\"></use></svg></div><div><div><div> </div><div> </div><div> </div><div> </div></div></div></div></div></figure><p><a href=\"http://store.steampowered.com/app/308060/Black_The_Fall/\" target=\"_blank\" rel=\"noopener\">Released this week on Steam</a> by Sand Sailor Studio, <em>Black The Fall</em> is a 2D side-scrolling puzzle platformer that shares many similarities with<a href=\"http://store.steampowered.com/app/308060/Black_The_Fall/\" target=\"_blank\" rel=\"noopener\"> last year’s indie hit, <em>Inside</em>.</a> Dark and moody atmosphere? Clever puzzles requiring plenty of trial and error? It’s got all that, plus a lil’ robot friend. </p><p>Comparisons between <em>Black The Fall</em> and  Playdead’s dark and thoughtful platformer are unavoidable. The game takes place in a ruined world ravaged by communism. The player is a tired worker trying to escape years of toil under the regime. Though eventually opening up to the brighter outside world, the opening moments of the game take place within the gray walls and black shadows of a vast factory. Early in the game the player gains access to a laser pointer, a tool that manipulates mechanical devices as well as the brains of mind-controlled workers. </p><p>It’s all very dark and hopeless, until the player makes a new friend. </p><figure><div><div><picture><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NkEmRwtK--/c_scale,fl_progressive,q_80,w_800/lmdzmh5aqxr2khaaeaos.jpg\" media=\"--small\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NkEmRwtK--/c_scale,fl_progressive,q_80,w_800/lmdzmh5aqxr2khaaeaos.jpg\" media=\"--xlarge\"><source data-srcset=\"https://i.kinja-img.com/gawker-media/image/upload/s--NkEmRwtK--/c_scale,fl_progressive,q_80,w_800/lmdzmh5aqxr2khaaeaos.jpg\"><img src=\"https://i.kinja-img.com/gawker-media/image/upload/s--NkEmRwtK--/c_scale,fl_progressive,q_80,w_800/lmdzmh5aqxr2khaaeaos.jpg\" data-sizes=\"auto\" data-width=\"1920\" data-chomp-id=\"lmdzmh5aqxr2khaaeaos\" data-format=\"jpg\"></picture></div></div></figure><p>The introduction of this little robot pupper makes a dark game a bit brighter. The robodog can help the player climb to higher heights, activate electronic switches and can even act as a brace, as seen in the GIF atop this post. </p><div><div></div><div><div><div><div id=\"ad-container-26464098\" data-media-type=\"INSTREAM_VIDEO\" data-zone-type=\"INSTREAM_VIDEO\" data-instream-position=\"instream_3\"><span id=\"js_instream_video-placeholder-26464098\"></span></div></div></div></div></div><div><div><div><p><small>Advertisement</small></p><div id=\"ad-container-48890307\" data-zone-type=\"MOBILE_IN_POST\" data-paragraph-position=\"top\" data-change-correlator=\"true\"></div><p></p></div></div></div><p>I streamed an hour and a half of <em>Black The Fall</em> earlier this week. Check out the stream archive below to see my escape attempt panned out. </p><p><span><iframe height=\"360\" id=\"fb-10155629086359040\" src=\"https://kinja.com/ajax/inset/iframe?id=fb-10155629086359040&amp;autosize=1\" width=\"640\"></iframe></span></p><img src=\"http://feeds.feedburner.com/~r/kotaku/vip/~4/3zdPdn9R8BM\" height=\"1\" width=\"1\">",
            "url": "http://kotaku.com/if-you-liked-inside-youll-like-black-the-fall-and-its-1796883369",
            "title": "If You Liked Inside, You'll Like Black The Fall And Its Little Robot Dog Too",
            "date_modified": "2017-07-13T16:00:00.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14646646\">Comments</a>",
            "url": "https://www.koipun.com/blog/how-i-learned-japanese-bryan-from-kuro-pixel",
            "title": "How I learned Japanese: Interview about habits and learning Japanese in Japan",
            "date_modified": "2017-06-27T16:04:44.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14629761\">Comments</a>",
            "url": "https://theoutline.com/post/1617/chiropractors-are-bullshit",
            "title": "Chiropractors are bullshit",
            "date_modified": "2017-06-25T12:49:07.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14612537\">Comments</a>",
            "url": "https://backlothelp.netflix.com/hc/en-us/articles/217237077-Production-and-Post-Production-Requirements-v2-1",
            "title": "Netflix Originals: Production and Post-Production Requirements v2.1",
            "date_modified": "2017-06-22T14:39:13.000Z"
        },
        {
            "content_html": "<a href=\"https://news.ycombinator.com/item?id=14574014\">Comments</a>",
            "url": "http://bombmagazine.org/article/665269/toshio-masumoto-s-em-funeral-parade-of-roses-em",
            "title": "A restored masterpiece unmasks Tokyo's underground gay subculture of the 1960s",
            "date_modified": "2017-06-17T02:44:58.000Z"
        }
    ]
}