<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Clay Smith</title>
    <subtitle>Software engineer, technology enthusiast, and occasional writer. Exploring Rust, web development, and creative solutions to technical challenges.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://clay.fyi/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://clay.fyi"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-06-02T00:00:00+00:00</updated>
    <id>https://clay.fyi/atom.xml</id>
    <entry xml:lang="en">
        <title>How to stream OpenTelemetry data to Iceberg and DuckLake with just DuckDB</title>
        <published>2026-06-02T00:00:00+00:00</published>
        <updated>2026-06-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/duckdb-is-all-you-need-for-opentelemetry-data/"/>
        <id>https://clay.fyi/blog/duckdb-is-all-you-need-for-opentelemetry-data/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/duckdb-is-all-you-need-for-opentelemetry-data/">&lt;h4 id=&quot;is-duckdb-all-you-need-for-storing-and-querying-otel&quot;&gt;Is duckdb all you need for storing and querying OTel?&lt;&#x2F;h4&gt;
&lt;p&gt;In a &lt;a href=&quot;&#x2F;blog&#x2F;cheap-opentelemetry-lakehouses-parquet-duckdb-iceberg&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; about OpenTelemetry, parquet, and data lakehouses, I mentioned difficulties when you want to push lots of metrics, logs, or traces to object storage with an Iceberg catalog like Amazon S3 Tables. A couple people messaged me, shared expletives, and said the same.&lt;&#x2F;p&gt;
&lt;p&gt;Some good news: there has been progress around this in the DuckDB ecosystem with the new &lt;a href=&quot;https:&#x2F;&#x2F;ducklake.select&#x2F;2026&#x2F;04&#x2F;13&#x2F;ducklake-10&#x2F;&quot;&gt;DuckLake 1.0&lt;&#x2F;a&gt; standard, &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;2026&#x2F;05&#x2F;29&#x2F;new-iceberg-features&quot;&gt;Iceberg extension features&lt;&#x2F;a&gt;, and the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;canardstack&quot;&gt;Quack Protocol&lt;&#x2F;a&gt;. To explore some of these ideas in action, I updated the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;duckdb-otlp&quot;&gt;DuckDB OpenTelemetry extension&lt;&#x2F;a&gt; to write metrics, logs and traces via the OpenTelemetry Protocol (OTLP&#x2F;HTTP) directly to duckdb. This makes it easy to store observability data in anything with existing duckdb support: lakehouses, normal and &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;community_extensions&#x2F;extensions&#x2F;gsheets&quot;&gt;not-normal&lt;&#x2F;a&gt; filesystems, or &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;docs&#x2F;current&#x2F;core_extensions&#x2F;aws&quot;&gt;object storage&lt;&#x2F;a&gt;. Everything runs in a single process: no extra dependencies or orchestration needed.&lt;&#x2F;p&gt;
&lt;p&gt;DuckDB is evolving into very capable data glue, even for streaming data. Below is some discussion of storing ~gigabytes to low terabytes of metrics, logs and traces streamed into a DuckLake using the new extension. Early benchmarks (see below) suggest if you&#x27;re working with a modest amount of OpenTelemetry data, it&#x27;s promising.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;streaming-observability-data-into-ducklake-and-other-catalogs&quot;&gt;Streaming observability data into DuckLake and other catalogs&lt;&#x2F;h3&gt;
&lt;p&gt;The idea was to do the following with a couple lines of SQL statements typed into duckdb on a mac:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Accept OpenTelemetry data streamed over HTTP (metrics, logs, traces)&lt;&#x2F;li&gt;
&lt;li&gt;Write compressed OTel data to object storage (Amazon S3, Cloudflare R2, etc)&lt;&#x2F;li&gt;
&lt;li&gt;Keep track of what data you write in a metadata catalog (DuckLake, Iceberg, etc)&lt;&#x2F;li&gt;
&lt;li&gt;Query the data in SQL with duckdb remotely (Quack Protocol)&lt;&#x2F;li&gt;
&lt;li&gt;Align to open standards&#x2F;open source&#x2F;vendor-neutral&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The main reason to do all of this is to get reasonable performance when you want to do SQL queries like &quot;show errors last Monday for the checkout service.&quot; If not: &lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;guides&#x2F;exporting-to-parquet&#x2F;&quot;&gt;just throw parquet files in a bucket&lt;&#x2F;a&gt;, which is &lt;a href=&quot;&#x2F;blog&#x2F;cheap-opentelemetry-lakehouses-parquet-duckdb-iceberg&#x2F;&quot;&gt;what the last post partly explored&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For #1-5, all you do is &lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;quickstart&#x2F;serve&#x2F;&quot;&gt;type a couple SQL commands&lt;&#x2F;a&gt; into the duckdb shell to load and configure a handful of extensions. That gets repetitive so I packaged it up in a docker image with duckdb embedded and pre-configured inside:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run --rm --name duckdb-otlp \
  -p 4318:4318 \
  -v &amp;quot;$(pwd):&amp;#x2F;data&amp;quot; \
  ghcr.io&amp;#x2F;smithclay&amp;#x2F;duckdb-otlp:latest
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;POST some OTel data to :4318, it gets added to local DuckLake, then you can query it with SQL (&lt;code&gt;SELECT * FROM otel_logs&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The trouble is what happens when you have hundreds of thousands of log lines arriving, which without optimizations means many small files being written to object storage. It kills query performance and costs more due to the intricacies of cloud object storage billing:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;a href=&quot;&#x2F;images&#x2F;ducklake-small-files-problem.png&quot;&gt;
    &lt;img src=&quot;&#x2F;images&#x2F;ducklake-small-files-problem.png&quot; alt=&quot;Diagram of the duckdb-otlp pipeline showing the small-file problem&quot;&gt;
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;The duckdb-otlp pipeline, and its catch.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Every telemetry pipeline that writes to object storage has ways to mitigate this by batching data to reduce the number of writes. This involves a tradeoff that varies widely depending on what you’re doing: how fresh do you need the data to be? You end up with configuration knobs that might dial the bill way down, but fresh data might not be queryable for a while.&lt;&#x2F;p&gt;
&lt;p&gt;DuckLake is building in automatic-ish maintenance features and making it easy to use &lt;a href=&quot;https:&#x2F;&#x2F;ducklake.select&#x2F;docs&#x2F;stable&#x2F;duckdb&#x2F;maintenance&#x2F;checkpoint&quot;&gt;via a single command&lt;&#x2F;a&gt; that can merge small files together (compaction) or reduce metadata. Iceberg catalogs like S3 Tables or R2 Data Catalog have had variations of this as well. If tuned correctly you might get reasonable query performance and a cheap object storage bill.&lt;&#x2F;p&gt;
&lt;p&gt;Until then, after some basic synthetic log ingest benchmarks and the official &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;docs&#x2F;current&#x2F;guides&#x2F;performance&#x2F;benchmarks&quot;&gt;duckdb benchmarks&lt;&#x2F;a&gt;: it seems promising. The duckdb-otlp extension was able to write ~100k log rows per second to remote cloud catalogs, which comes out to ~2.5 TB&#x2F;day of data &lt;em&gt;for a single duckdb process&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Catalog&lt;&#x2F;th&gt;&lt;th&gt;Durable rows&#x2F;s&lt;&#x2F;th&gt;&lt;th&gt;Accepted MiB&#x2F;s&lt;&#x2F;th&gt;&lt;th&gt;Drain Time&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;guides&#x2F;stream-to-local-ducklake&#x2F;&quot;&gt;Local DuckLake&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;97,545&lt;&#x2F;td&gt;&lt;td&gt;29.9&lt;&#x2F;td&gt;&lt;td&gt;0.21s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Local DuckLake, R2 Storage&lt;&#x2F;td&gt;&lt;td&gt;96,911&lt;&#x2F;td&gt;&lt;td&gt;30.2&lt;&#x2F;td&gt;&lt;td&gt;0.62s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;guides&#x2F;stream-to-remote-ducklake&#x2F;&quot;&gt;Neon DuckLake, R2 Storage&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;90,850&lt;&#x2F;td&gt;&lt;td&gt;30.1&lt;&#x2F;td&gt;&lt;td&gt;0.56s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;guides&#x2F;stream-to-s3-tables&#x2F;&quot;&gt;S3 Tables Catalog (Iceberg)&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;90,390&lt;&#x2F;td&gt;&lt;td&gt;29.5&lt;&#x2F;td&gt;&lt;td&gt;2.82s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;guides&#x2F;stream-to-r2-data-catalog&#x2F;&quot;&gt;R2 Data Catalog (Iceberg)&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;88,128&lt;&#x2F;td&gt;&lt;td&gt;30.0&lt;&#x2F;td&gt;&lt;td&gt;4.80s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Yes, more benchmarking is needed. That&#x27;s a future post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;grafana-goes-swimming-in-a-ducklake&quot;&gt;Grafana goes swimming in a DuckLake&lt;&#x2F;h3&gt;
&lt;p&gt;Performance aside, there&#x27;s a major UX problem: open-source observability tools use their own special-purpose databases and query languages (PromQL, etc)... not SQL. Visualization tools like Grafana can be &lt;a href=&quot;https:&#x2F;&#x2F;grafana.com&#x2F;grafana&#x2F;plugins&#x2F;mssql&#x2F;&quot;&gt;extended to support SQL&lt;&#x2F;a&gt; via plugins, but it&#x27;s not out of the box.&lt;&#x2F;p&gt;
&lt;p&gt;There are other people working on this, recently learned about &lt;a href=&quot;https:&#x2F;&#x2F;gigapipe.com&#x2F;&quot;&gt;Gigapipe&lt;&#x2F;a&gt; and vibe-weekend-coded a basic prototype &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;canardstack&quot;&gt;here&lt;&#x2F;a&gt;. For simple queries in PromQL&#x2F;Loki&#x2F;Tempo, you can get reasonable-looking dashboards in Grafana, powered by SQL queries in a duckdb instance fronted by compatibility APIs:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;a href=&quot;&#x2F;images&#x2F;grafana-ducklake-dashboard.png&quot;&gt;
    &lt;img src=&quot;&#x2F;images&#x2F;grafana-ducklake-dashboard.png&quot; alt=&quot;Grafana dashboard rendering logs, traces, and service metrics from a DuckLake&quot;&gt;
  &lt;&#x2F;a&gt;
  &lt;figcaption&gt;A weekend-prototype Grafana dashboard, activity totals, service memory, recent logs, and frontend traces, rendered entirely from SQL queries against a DuckLake behind PromQL&#x2F;Loki&#x2F;Tempo-compatible APIs.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;There&#x27;s also the parallel universe of visualization and analytics tools in the analytics world that work well with duckdb. Perspective is doing really cool things inside of browsers with wasm, &lt;a href=&quot;https:&#x2F;&#x2F;perspective-dev.github.io&#x2F;block.html?example=duckdb&quot;&gt;for example&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Quack is also opening up new integration pathways. As a standard, it&#x27;s just an official and fast way for duckdbs to talk to each other over HTTP (credit to extensions like &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;community_extensions&#x2F;extensions&#x2F;airport&quot;&gt;Airport&lt;&#x2F;a&gt; that have been exploring the idea for a while). &lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;duckdb-otlp&#x2F;guides&#x2F;query-with-quack&#x2F;&quot;&gt;Currently in the duckdb-otlp extension, it&#x27;s an easy way to connect to a remote DuckLake and query it&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;ATTACH &amp;#x27;quack:localhost:9494&amp;#x27; AS otel_writer (TYPE quack);
FROM otel_writer.query(
  $$
  SELECT * FROM lake.main.otlp_logs
  WHERE service_name = &amp;#x27;quack-local-ducklake-demo&amp;#x27;
  ORDER BY time_unix_nano DESC
  LIMIT 5
  $$
);
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What people start building with Quack and how that changes duckdb architecture will be interesting to watch. For example, there&#x27;s a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Query-farm&#x2F;quackscale&quot;&gt;new extension that runs Tailscale directly in duckdb&lt;&#x2F;a&gt;, making it easy to transfer data and queries across networks and firewalls. The tip of the iceberg (pun intended).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;it-s-promising-but-early-more-data-needed&quot;&gt;It&#x27;s promising but early, more data needed&lt;&#x2F;h3&gt;
&lt;p&gt;I think people are excited in the analytics space right now because there seems to be momentum in the direction of a vendor-neutral and cheap way to store and structured data, even if there&#x27;s a lot of it.&lt;&#x2F;p&gt;
&lt;p&gt;In many ways, if you consider &lt;a href=&quot;https:&#x2F;&#x2F;clickhouse.com&#x2F;docs&#x2F;use-cases&#x2F;observability&#x2F;clickstack&quot;&gt;ClickHouse&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;quickwit.io&#x2F;&quot;&gt;Quickwit&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;turbopuffer.com&#x2F;&quot;&gt;turbopuffer&lt;&#x2F;a&gt;, or &lt;a href=&quot;https:&#x2F;&#x2F;www.langchain.com&#x2F;blog&#x2F;introducing-smithdb&quot;&gt;LangChain&#x27;s new tracing database&lt;&#x2F;a&gt; it technically already exists already minus the vendor-neutral and open standard part. The promise of Iceberg, DuckLake, or Delta Lake getting traction is that almost any major closed-source or open-source analytics tool or AI agent can query and read the data with SQL, even if it doesn&#x27;t know what an OpenTelemetry trace is.&lt;&#x2F;p&gt;
&lt;p&gt;DuckLake and the new quack protocol have some emerging capabilities that could be compelling for ops teams stuck between running their own complex observability infrastructure (Kafka-esque) and paying huge monthly bills to vendors (canine-esque). More data and better benchmarks to validate that and understand how far you can scale up.&lt;&#x2F;p&gt;
&lt;p&gt;I hope this post gets people excited about working on open-source at the intersection of observability and analytics, which has the vibe right now of &quot;something is happening&quot;. If you aggree, say hi on &lt;a href=&quot;https:&#x2F;&#x2F;x.com&#x2F;duckdb&#x2F;status&#x2F;1471806748814106624?lang=en&quot;&gt;duckdb discord&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;opentelemetry.io&#x2F;community&#x2F;end-user&#x2F;#slack&quot;&gt;OpenTelemetry Slack&lt;&#x2F;a&gt; or the other channels.&lt;&#x2F;p&gt;
&lt;p&gt;PRs, feedback and benchmark ideas for &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;duckdb-otlp&quot;&gt;duckdb-otlp&lt;&#x2F;a&gt; are welcome.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading about my duck-pilled open-source project.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>New task orchestration in Claude Code 2.1.19 lets you build background agents in the cloud</title>
        <published>2026-01-27T00:00:00+00:00</published>
        <updated>2026-01-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/build-your-own-claude-code-cloud-swarm/"/>
        <id>https://clay.fyi/blog/build-your-own-claude-code-cloud-swarm/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/build-your-own-claude-code-cloud-swarm/">&lt;p&gt;Personal background agents in dangerous mode are a few hundred lines of code away with the 2.0.19 release of Claude Code. This matters because running 4+ coding agents at a time, &lt;a href=&quot;https:&#x2F;&#x2F;www.theregister.com&#x2F;2026&#x2F;01&#x2F;22&#x2F;cursor_ai_wrote_a_browser&#x2F;&quot;&gt;including fully autonomous agents that run for multiple days&lt;&#x2F;a&gt;, is quickly becoming a reality.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about some emerging patterns when you want to connect Claude Code sessions together in multiple cloud VMs &lt;a href=&quot;https:&#x2F;&#x2F;steve-yegge.medium.com&#x2F;welcome-to-gas-town-4f25ee16dd04&quot;&gt;without going full-on Gastown&lt;&#x2F;a&gt;. The urgency behind all of this is related to &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@peltomakiw&#x2F;how-a-single-email-turned-my-clawdbot-into-a-data-leak-1058792e783a&quot;&gt;security and prompt injection attacks when running locally&lt;&#x2F;a&gt;. As &lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;2025&#x2F;Oct&#x2F;22&#x2F;living-dangerously-with-claude&#x2F;&quot;&gt;Simon Willison said in October 2025&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;On the one hand I&#x27;m getting enormous value from running coding agents with as few restrictions as possible. On the other hand I&#x27;m &lt;a href=&quot;https:&#x2F;&#x2F;www.ibm.com&#x2F;think&#x2F;topics&#x2F;prompt-injection&quot;&gt;deeply concerned by the risks&lt;&#x2F;a&gt; that accompany that freedom.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The (imperfect) technical solution is a sandbox—ideally one that&#x27;s not on your computer or network. This is already built into the web interfaces of Claude Code for Web and ChatGPT, but let&#x27;s make three more assumptions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;You&#x27;re a developer using a coding agent on a command line.&lt;&#x2F;li&gt;
&lt;li&gt;You&#x27;re starting to run 4-10 coding sessions at a time, and it&#x27;s getting chaotic.&lt;&#x2F;li&gt;
&lt;li&gt;You want a way to centrally manage and monitor those sessions.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In other words, if you&#x27;re adopting sandboxes: you now get to manage a small fleet of cloud VMs or try to use some sort of container orchestration thing. This is a nightmare for non-experts using the major cloud providers as of early 2026, and even Opus 4.5 struggles with AWS IAM policies and the Azure CLI.&lt;&#x2F;p&gt;
&lt;p&gt;Enter a new category of &quot;sandbox-as-a-service&quot; products from &lt;a href=&quot;https:&#x2F;&#x2F;exe.dev&#x2F;&quot;&gt;exe.dev&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;sprites.dev&quot;&gt;sprites.dev&lt;&#x2F;a&gt; (fly.io), &lt;a href=&quot;https:&#x2F;&#x2F;modal.com&#x2F;&quot;&gt;Modal&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;sandbox&#x2F;&quot;&gt;Cloudflare Sandbox SDK&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;e2b.dev&#x2F;&quot;&gt;E2B&lt;&#x2F;a&gt;. You get a remote, fully stateful server that spins up in a few seconds: a perfect and isolated home for Claude Code, Gemini, or Codex.&lt;&#x2F;p&gt;
&lt;p&gt;My rough CLI implementation of doing this with sprites and exe.dev is at &lt;a href=&quot;http:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;ocaptain&quot;&gt;github.com&#x2F;smithclay&#x2F;ocaptain&lt;&#x2F;a&gt;, but expect there to be 10,000 orchestrators created in the next few months before Claude Code or Codex releases something that obsoletes them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;get-a-full-linux-vm-in-a-couple-seconds-seriously&quot;&gt;Get a full Linux VM in a couple seconds, seriously&lt;&#x2F;h3&gt;
&lt;p&gt;With exe.dev or sprites.dev, the idea is simple. You use a CLI (or ssh session, a cool twist on how exe.dev does it), and type something like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ ssh exe.dev new
$ sprite create your-sprite
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few seconds later you get a full Linux VM you can do whatever you want with: run Docker images, run Claude Code, run Codex, install services, whatever. It won&#x27;t go away. The pricing is reasonable (cost of a coffee and croissant in San Francisco per month) and it&#x27;s fast.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;living-dangerously-and-obscure-claude-code-token-settings&quot;&gt;Living dangerously and obscure Claude Code token settings&lt;&#x2F;h3&gt;
&lt;p&gt;Once you go multi-Claude Code, the immediate hurdle is logging into your subscription on multiple VMs or containers: a fresh start of Claude asks you to log in. If you&#x27;re an API user it&#x27;s easy to generate a new key, but for subscription users it&#x27;s a different story.&lt;&#x2F;p&gt;
&lt;p&gt;I lost four hours on a sunny and warm Saturday vibing elaborate token injection proxies before learning something critical: there&#x27;s just a poorly-documented command to get an OAuth token tied to your subscription that lives for one year. Run &lt;code&gt;claude get-token&lt;&#x2F;code&gt;, and set that token using a special environment variable (&lt;code&gt;CLAUDE_CODE_OAUTH_TOKEN&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;You can revoke or manage the token at &lt;a href=&quot;https:&#x2F;&#x2F;claude.ai&#x2F;settings&#x2F;claude-code&quot;&gt;https:&#x2F;&#x2F;claude.ai&#x2F;settings&#x2F;claude-code&lt;&#x2F;a&gt;. That&#x27;s it.&lt;&#x2F;p&gt;
&lt;p&gt;When launching an autonomous session in interactive mode, it&#x27;s also helpful to disable the onboarding flow:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;echo &amp;#x27;{&amp;quot;hasCompletedOnboarding&amp;quot;:true}&amp;#x27; &amp;gt; ~&amp;#x2F;.claude.json
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since you&#x27;re in an isolated VM with none of your data, it&#x27;s relatively okay to run Claude with &lt;code&gt;--dangerously-skip-permissions&lt;&#x2F;code&gt; – that means no prompts will pop up for permissions.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s also a good idea to set up some outbound firewall rules on the VM while you&#x27;re at it to specific domains only.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;task-orchestration-without-external-dependencies&quot;&gt;Task orchestration without external dependencies&lt;&#x2F;h3&gt;
&lt;p&gt;Before Friday, January 24—the release of Claude Code 2.1.19—task orchestration across sessions was largely a roll-your-own sort of thing, the most popular solution being Steve Yegge&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;steveyegge&#x2F;beads&quot;&gt;beads&lt;&#x2F;a&gt;. But now, with an &lt;a href=&quot;https:&#x2F;&#x2F;x.com&#x2F;trq212&#x2F;status&#x2F;2014480496013803643?s=20&quot;&gt;upgrade to the task management system in Claude&lt;&#x2F;a&gt;, it&#x27;s straightforward to generate and distribute tasks, dependencies between those tasks (e.g. write API, then write API integration tests) and status across sessions.&lt;&#x2F;p&gt;
&lt;p&gt;All you need are some JSON files that you can generate yourself &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;ocaptain&#x2F;blob&#x2F;main&#x2F;.claude&#x2F;skills&#x2F;voyage-plan&#x2F;SKILL.md&quot;&gt;or with a skill&lt;&#x2F;a&gt;, Claude Code handles the rest. Each instance of Claude Code gets passed a special environment variable called &lt;code&gt;CLAUDE_CODE_TASK_LIST_ID&lt;&#x2F;code&gt; that identifies a shared task list that lives in a collection of JSON files in &lt;code&gt;~&#x2F;.claude&#x2F;tasks&#x2F;&amp;lt;task id&amp;gt;&lt;&#x2F;code&gt;. After the session starts, the task list pops up and updates automatically in each session: just ask Claude to pick up an item that needs work and that isn&#x27;t blocked. You can set up a &lt;a href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;hooks&quot;&gt;hook&lt;&#x2F;a&gt; that fires to notify you when all sessions end and there are no tasks left.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what it looks like, note the task list below that has been picked up by multiple &quot;ships&quot; (separate VMs, my orchestration CLI has a nautical theme). You can attach via tmux to the live Claude Code session at any time to see what&#x27;s going on, but probably won&#x27;t:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;images&#x2F;ship-task-coordination.png&quot;&gt;&lt;img src=&quot;&#x2F;images&#x2F;ship-task-coordination.png&quot; alt=&quot;ohcaptain task orchestration&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In terms of sharing the &lt;code&gt;~&#x2F;.claude&#x2F;tasks&lt;&#x2F;code&gt; directory across VMs, I initially used &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;libfuse&#x2F;sshfs&quot;&gt;sshfs&lt;&#x2F;a&gt; to mount a shared directory across my exe.dev machines, but many approaches are possible: it&#x27;s just a regular Linux VM so choose your favorite network filesystem. Claude Code handles the file locks between sessions. For VMs without SSH access, creating your own Tailscale &lt;a href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;kb&#x2F;1136&#x2F;tailnet&quot;&gt;tailnet&lt;&#x2F;a&gt; is also compelling.&lt;&#x2F;p&gt;
&lt;p&gt;There are some sharp edges, but initial testing for semi-complex tasks is promising. The capabilities around this in Claude Code are likely to grow, &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=46743908&quot;&gt;as recent hidden feature flag investigations&lt;&#x2F;a&gt; have shown.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;trust-but-verify-with-logs-and-metrics-from-sessions&quot;&gt;Trust but verify with logs and metrics from sessions&lt;&#x2F;h3&gt;
&lt;p&gt;When you&#x27;re scaling up to even a modest number of VMs, what&#x27;s going on inside those sessions is a quasi-black box: you&#x27;re no longer babysitting, approving permissions, or seeing tool calls go by. All you see is the end result of the sessions. Some level of logging and analytics is needed, and fortunately Claude Code and Codex &lt;a href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;monitoring-usage&quot;&gt;can output relevant events and metrics in OpenTelemetry format&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Nowadays, it&#x27;s easy enough to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&quot;&gt;store those logs and metrics in parquet format&lt;&#x2F;a&gt; on disk or in cheap object storage (see the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&quot;&gt;otlp2parquet&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2pipeline&quot;&gt;otlp2pipeline&lt;&#x2F;a&gt; projects). Several weeks of heavy sessions for a single user take less than 100 MB of storage. It&#x27;s a low enough volume you can easily query and do some meaningful analytics on it with duckdb on a laptop or even in a web browser. Potential queries include: understanding cost per session, token count&#x2F;cached token count over time, tool call errors, tool calls to external domains, etc.&lt;&#x2F;p&gt;
&lt;p&gt;The OTel-to-parquet&#x2F;Iceberg workflow works nicely with AI-assisted notebook tools like &lt;a href=&quot;https:&#x2F;&#x2F;sidequery.dev&#x2F;&quot;&gt;Sidequery&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;marimo.io&#x2F;&quot;&gt;Marimo&lt;&#x2F;a&gt;: you don&#x27;t even have to write the SQL or pandas code and you get some nice visualizations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-we-wanted-serverless-to-be-in-the-late-2010s&quot;&gt;What we wanted serverless to be in the late 2010s&lt;&#x2F;h3&gt;
&lt;p&gt;Looking back: this is probably what we always wanted serverless compute to be in the first place… just took us a while to get here.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;clay.fyi&#x2F;blog&#x2F;ssh-ing-into-aws-lambda-functions&#x2F;&quot;&gt;In 2017, I wrote a small Go program&lt;&#x2F;a&gt; so I could ssh into an AWS Lambda function as a joke for a conference talk: back then, there were—and still are—many people who didn&#x27;t like functions-as-a-service architecture (&quot;it&#x27;s still a server!&quot;, &quot;containers are better&quot;, &quot;the cost model doesn&#x27;t make sense&quot;, etc).&lt;&#x2F;p&gt;
&lt;p&gt;This new wave of infrastructure providers settles the architecture debate: you get all the benefits of a serverless function (fast, ephemeral, cheap) but it&#x27;s &lt;em&gt;also&lt;&#x2F;em&gt; a full VM. This pattern is also more relevant than ever with millions of developers starting to use coding agents in more sophisticated ways that require remote and secure sandboxes.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a fun time to build stuff using cloud sandbox VMs, and it&#x27;s likely to evolve a lot in the next 6 months. Just don&#x27;t run YOLO mode on your personal laptop.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;links-and-acknowledgements&quot;&gt;Links and acknowledgements&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;exe.dev docs: &lt;a href=&quot;https:&#x2F;&#x2F;exe.dev&#x2F;docs&#x2F;what-is-exe&quot;&gt;https:&#x2F;&#x2F;exe.dev&#x2F;docs&#x2F;what-is-exe&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Sprites docs: &lt;a href=&quot;https:&#x2F;&#x2F;docs.sprites.dev&#x2F;&quot;&gt;https:&#x2F;&#x2F;docs.sprites.dev&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;My own CLI impementation of this post: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;ocaptain&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;ocaptain&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Simon&#x27;s blog is great and he has been convering sandboxes extensively: &lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;2026&#x2F;Jan&#x2F;9&#x2F;sprites-dev&#x2F;&quot;&gt;https:&#x2F;&#x2F;simonwillison.net&#x2F;2026&#x2F;Jan&#x2F;9&#x2F;sprites-dev&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;OTLP to parquet converter, useful for monitoring: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Cheap OpenTelemetry lakehouses with parquet, duckdb and Iceberg</title>
        <published>2025-12-02T00:00:00+00:00</published>
        <updated>2025-12-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/cheap-opentelemetry-lakehouses-parquet-duckdb-iceberg/"/>
        <id>https://clay.fyi/blog/cheap-opentelemetry-lakehouses-parquet-duckdb-iceberg/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/cheap-opentelemetry-lakehouses-parquet-duckdb-iceberg/">&lt;h4 id=&quot;what-if-all-your-observability-data-was-just-parquet-files&quot;&gt;What if all your observability data was just parquet files?&lt;&#x2F;h4&gt;
&lt;p&gt;If you work in SRE, DevOps, or observability it might be time to introduce yourself to the data engineers and understand what&#x27;s going on with lakehouses, columnar storage, and a &lt;a href=&quot;https:&#x2F;&#x2F;projects.apache.org&#x2F;projects.html?category#big-data&quot;&gt;bewildering number of projects&lt;&#x2F;a&gt; from the Apache Foundation. Some of the most interesting developments in observability in late 2025 are coming from analytics.&lt;&#x2F;p&gt;
&lt;p&gt;For people who have tuned out everything related to data warehouses since Hadoop:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Columnar storage formats, &lt;a href=&quot;https:&#x2F;&#x2F;arrow.apache.org&#x2F;&quot;&gt;Apache Arrow&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-telemetry&#x2F;otel-arrow&quot;&gt;emerging schemas for OpenTelemetry&lt;&#x2F;a&gt; are making it more realistic to do analytics on logs, metrics, and traces quickly and cheaply in object storage (S3, R2, &lt;a href=&quot;https:&#x2F;&#x2F;www.min.io&#x2F;&quot;&gt;MinIO&lt;&#x2F;a&gt;, etc).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;&quot;&gt;duckdb&lt;&#x2F;a&gt; lets you do analytics on all this data using regular SQL, including from &lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;otlp2parquet&#x2F;query-demo&#x2F;&quot;&gt;inside your browser&lt;&#x2F;a&gt; (feat. WebAssembly).&lt;&#x2F;li&gt;
&lt;li&gt;Iceberg-based lakehouse&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; architecture, now available via managed services like Cloudflare &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;r2&#x2F;data-catalog&#x2F;&quot;&gt;R2 Data Catalog&lt;&#x2F;a&gt; and AWS &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;s3&#x2F;features&#x2F;tables&#x2F;&quot;&gt;S3 Tables&lt;&#x2F;a&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; that implement the &lt;a href=&quot;https:&#x2F;&#x2F;iceberg.apache.org&#x2F;&quot;&gt;Apache Iceberg&lt;&#x2F;a&gt; spec, add convenient analytics, metadata and performance features on top of parquet files in object storage without needing to run a distributed compute cluster.&lt;&#x2F;li&gt;
&lt;li&gt;The monitoring world is currently obsessed with AI agents and anomaly detection. By having OTel data in a lakehouse, Python&#x2F;Pandas&#x2F;PyTorch workloads run directly against Parquet without moving data.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Observability isn&#x27;t a typical analytics workload: lots of small, highly concurrent writes present many technical challenges that have traditionally been left to specialized vendors ($$$). This is at odds with the overall lakehouse philosophy: stop shuttling data through multiple storage tiers and put it once into a managed table format on object storage so tools read from a single source of truth.&lt;&#x2F;p&gt;
&lt;p&gt;This post explores a prototype of how far you could get with cheap storage, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&quot;&gt;some glue code written in rust&lt;&#x2F;a&gt;, and open table formats for observability to understand if the big data ecosystem has arrived at the point where lakehouses could cost-effectively augment observability tools for longer-term analytics.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;In this post, &#x27;lakehouse&#x27; means what &lt;a href=&quot;https:&#x2F;&#x2F;www.cidrdb.org&#x2F;cidr2021&#x2F;papers&#x2F;cidr2021_paper17.pdf&quot;&gt;Armbrust et al. defined in their 2021 CIDR paper&lt;&#x2F;a&gt;: data on cheap object storage in open formats (Parquet), with a metadata layer that adds transactions, schema evolution, versioning, and query optimization.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;strong&gt;12&#x2F;3&#x2F;2025 update:&lt;&#x2F;strong&gt; The day after this post was published, &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;aws&#x2F;amazon-cloudwatch-introduces-unified-data-management-and-analytics-for-operations-security-and-compliance&#x2F;&quot;&gt;Amazon released an automatic integration for S3 Tables from CloudWatch Logs&lt;&#x2F;a&gt;. It makes all of the glue code below redundant for log sources in AWS.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;teaching-duckdb-to-speak-opentelemetry&quot;&gt;Teaching duckdb to speak OpenTelemetry&lt;&#x2F;h3&gt;
&lt;p&gt;A straightforward way to export OpenTelemetry data is to configure a collector with a &lt;a href=&quot;https:&#x2F;&#x2F;opentelemetry.io&#x2F;docs&#x2F;specs&#x2F;otel&#x2F;protocol&#x2F;file-exporter&#x2F;&quot;&gt;file exporter&lt;&#x2F;a&gt; and output a bunch of protobuf or JSON files to a local directory. See this &lt;a href=&quot;https:&#x2F;&#x2F;www.otelbin.io&#x2F;?#config=receivers%3A*N__otlp%3A*N____protocols%3A*N______grpc%3A*N________endpoint%3A_0.0.0.0%3A4317*N______http%3A*N________endpoint%3A_0.0.0.0%3A4318*N*Nprocessors%3A*N__batch%3A_%7B%7D*N*Nexporters%3A*N__file%2Fproto-logs%3A*N____path%3A_%2Fexport%2Fproto%2Flogs.proto*N____format%3A_proto*N____rotation%3A*N______max*_megabytes%3A_128*N______max*_days%3A_7*N______max*_backups%3A_100*N__file%2Fproto-metrics%3A*N____path%3A_%2Fexport%2Fproto%2Fmetrics.proto*N____format%3A_proto*N____rotation%3A*N______max*_megabytes%3A_128*N______max*_days%3A_7*N______max*_backups%3A_100*N__file%2Fproto-traces%3A*N____path%3A_%2Fexport%2Fproto%2Ftraces.proto*N____format%3A_proto*N____rotation%3A*N______max*_megabytes%3A_128*N______max*_days%3A_7*N______max*_backups%3A_100*N*Nextensions%3A*N__health*_check%3A*N__pprof%3A*N__zpages%3A*N*Nservice%3A*N__extensions%3A_%5Bhealth*_check%2C_pprof%2C_zpages%5D*N__pipelines%3A*N____traces%3A*N______receivers%3A_%5Botlp%5D*N______processors%3A_%5Bbatch%5D*N______exporters%3A_%5Bfile%2Fproto-traces%5D*N____metrics%3A*N______receivers%3A_%5Botlp%5D*N______processors%3A_%5Bbatch%5D*N______exporters%3A_%5Bfile%2Fproto-metrics%5D*N____logs%3A*N______receivers%3A_%5Botlp%5D*N______processors%3A_%5Bbatch%5D*N______exporters%3A_%5Bfile%2Fproto-logs%5D%7E&amp;amp;distro=otelcol-contrib%7E&amp;amp;distroVersion=v0.139.0%7E&quot;&gt;example on OTelBin&lt;&#x2F;a&gt; using the latest collector version (v0.139.0).&lt;&#x2F;p&gt;
&lt;p&gt;With JSON&#x2F;protobuf files, querying this data remains neither easy nor fast with standard analytics tools. This is what &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;docs&#x2F;stable&#x2F;extensions&#x2F;overview&quot;&gt;duckdb extensions&lt;&#x2F;a&gt; were designed for: taking external data and converting it into rows you can easily query with SQL.&lt;&#x2F;p&gt;
&lt;p&gt;I recently wrote and published a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;duckdb-otlp&quot;&gt;community extension&lt;&#x2F;a&gt; that imports telemetry data from JSON or protobuf files. It uses a schema inspired by the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-telemetry&#x2F;opentelemetry-collector-contrib&#x2F;tree&#x2F;main&#x2F;exporter&#x2F;clickhouseexporter&quot;&gt;Clickhouse OpenTelemetry exporter.&lt;&#x2F;a&gt; Here&#x27;s what it looks like in the duckdb CLI (v1.4.2 or later). duckdb makes reading multiple files or reading from HTTP&#x2F;S3&#x2F;cloud storage easy:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;INSTALL otlp FROM community;
LOAD otlp;
LOAD httpfs;

-- Query slow traces
SELECT
    TraceId,
    SpanName,
    Duration &amp;#x2F; 1000000 AS duration_ms
FROM read_otlp_traces(&amp;#x27;https:&amp;#x2F;&amp;#x2F;raw.githubusercontent.com&amp;#x2F;smithclay&amp;#x2F;otlp2parquet&amp;#x2F;refs&amp;#x2F;heads&amp;#x2F;main&amp;#x2F;testdata&amp;#x2F;traces.jsonl&amp;#x27;)
WHERE Duration &amp;gt; 1000000000  -- over 1 second
ORDER BY Duration DESC
LIMIT 5;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is enough to do some powerful analytics: it&#x27;s trivial to join across signal types or correlate with non-observability data. You can also save any data (errors, slow traces, interesting logs) to parquet format with a SQL one-liner:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;-- Query slow traces, write to Parquet
COPY (
    SELECT
        TraceId,
        SpanName,
        Duration &amp;#x2F; 1000000 AS duration_ms
    FROM read_otlp_traces(
        &amp;#x27;https:&amp;#x2F;&amp;#x2F;raw.githubusercontent.com&amp;#x2F;smithclay&amp;#x2F;otlp2parquet&amp;#x2F;refs&amp;#x2F;heads&amp;#x2F;main&amp;#x2F;testdata&amp;#x2F;traces.jsonl&amp;#x27;
    )
    WHERE Duration &amp;gt; 1000000000
    ORDER BY Duration DESC
    LIMIT 5
)
TO &amp;#x27;slow_traces.parquet&amp;#x27; (FORMAT PARQUET);
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So that&#x27;s cool. There are two significant problems:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Needs streaming support:&lt;&#x2F;strong&gt; OpenTelemetry data isn&#x27;t usually accessible from JSON&#x2F;protobuf files in production scenarios. It&#x27;s streamed in real time from instrumented applications through various collectors and pipelines to specialized databases like Elastic or Tempo.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Wrong formats:&lt;&#x2F;strong&gt; Naively writing a lot of metrics, logs and traces to small JSON&#x2F;protobuf files kills the price and performance benefits of a data lake: object storage gets more expensive and queries get slower.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Next is what happened when I tried to convert OpenTelemetry to parquet in a Lambda function&#x2F;Cloudflare worker and hoped for the best. Spoiler: object stores are great at cheap blobs, but less great at &quot;thousands of tiny writes per second, forever.&quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;streaming-opentelemetry-to-parquet-files&quot;&gt;Streaming OpenTelemetry to Parquet files&lt;&#x2F;h3&gt;
&lt;p&gt;No duckdb extension is needed if metrics, logs, and traces are stored in parquet instead of JSON or protobuf. The OTel schema, especially for metrics, is far from trivial, but there are emerging projects exploring what it could look like including &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-telemetry&#x2F;otel-arrow&quot;&gt;OpenTelemetry Arrow&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;www.rakirahman.me&#x2F;otel-arrow-delta-lake&#x2F;&quot;&gt;OpenTelemetry on Delta Lake&lt;&#x2F;a&gt;. These take advantage of the columnar, semi-structured, highly repetitive and compression-friendly aspects of OTel data: you could see file size reduction of up to 90%.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to the robust Arrow&#x2F;rust&#x2F;Apache ecosystem and Claude Code, I wrote a simple Rust library called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&quot;&gt;&lt;code&gt;otlp2parquet&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; that handles these boring parts: decoding OTLP, mapping it into Arrow, writing Parquet, and dealing with cloud storage. It&#x27;s profoundly cheap in terms of compute costs: log-to-parquet conversion using demo data came out to about $0.01&#x2F;per uncompressed GB. That&#x27;s a rounding error on cloud bills and doesn&#x27;t even account for free tiers on Workers or Lambda.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;images&#x2F;otlp2parquet-architecture.png&quot;&gt;&lt;img src=&quot;&#x2F;images&#x2F;otlp2parquet-architecture.png&quot; alt=&quot;otlp2parquet architecture diagram&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Parquet format means query engines benefit from only reading—and transferring over the wire—a significantly smaller amount of data when running a query like: &quot;give me auth service logs in us-east-2 for the past hour.&quot; Only portions of the file are scanned.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what a simple logs query looks like now. For maximum fun, you can even run this completely in your browser &lt;a href=&quot;https:&#x2F;&#x2F;smithclay.github.io&#x2F;otlp2parquet&#x2F;query-demo&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;LOAD httpfs;
SELECT Timestamp, ServiceName, SeverityText, Body
FROM read_parquet(&amp;#x27;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;smithclay&amp;#x2F;otlp2parquet&amp;#x2F;raw&amp;#x2F;refs&amp;#x2F;heads&amp;#x2F;main&amp;#x2F;testdata&amp;#x2F;parquet&amp;#x2F;logs.parquet&amp;#x27;)
ORDER BY Timestamp DESC
LIMIT 5;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So imagine you actually do this. A few months later, you&#x27;ve got 100k+ small Parquet files (a &quot;data swamp&quot;) sitting in a bucket. How do you query it, what happens when you want to change the schema, add partitions for faster queries, or consolidate (&lt;a href=&quot;https:&#x2F;&#x2F;www.dremio.com&#x2F;blog&#x2F;compaction-in-apache-iceberg-fine-tuning-your-iceberg-tables-data-files&#x2F;&quot;&gt;compact&lt;&#x2F;a&gt;) small files and metadata into larger files?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;writing-to-managed-apache-iceberg-s3-tables-and-r2-data-catalog&quot;&gt;Writing to managed Apache Iceberg: S3 Tables and R2 Data Catalog&lt;&#x2F;h3&gt;
&lt;p&gt;Enter catalogs like Apache Iceberg or Delta Lake, available as &lt;a href=&quot;https:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;AmazonS3&#x2F;latest&#x2F;userguide&#x2F;s3-tables.html&quot;&gt;fully managed service on AWS&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;r2&#x2F;data-catalog&#x2F;&quot;&gt;Cloudflare&lt;&#x2F;a&gt; (among &lt;a href=&quot;https:&#x2F;&#x2F;docs.cloud.google.com&#x2F;biglake&#x2F;docs&#x2F;about-blms&quot;&gt;others&lt;&#x2F;a&gt;). Data warehouses have traditionally been big, expensive, and complex. This is starting to change with more or less &quot;managed bucket&quot; cloud services based on Iceberg that provide both the cheap storage and catalog&#x2F;metadata management service in one.&lt;&#x2F;p&gt;
&lt;p&gt;Iceberg handles snapshots, partitions and schema changes so your query engine doesn&#x27;t have to crawl a bucket listing every time. These catalogs somewhat help wrangle the million tiny files problem, but they don&#x27;t magically erase it. Someone still has to pay for compaction and decide how aggressively to merge things.&lt;&#x2F;p&gt;
&lt;p&gt;(Fun fact: Cloudflare R2 Data Catalog has no additional cost for egress or using it over regular storage while in beta.)&lt;&#x2F;p&gt;
&lt;p&gt;With managed Iceberg services, you get lakehouse-like semantics for working with OpenTelemetry in addition to performance benefits, like more efficient reads. In lakehouse terms, otlp2parquet plus R2 Data Catalog&#x2F;S3 Tables are the metadata and transaction layer over Parquet. DuckDB is one of potentially many SQL&#x2F;ML engines on top.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what it looks like connected to a Cloudflare R2 Data Catalog in duckdb. Once the connection is made, you query the data using regular SQL.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;--- Credentials to read R2 buckets
CREATE SECRET r2_secret (
      TYPE S3,
      KEY_ID &amp;#x27;ACCESS_KEY_ID&amp;#x27;,
      SECRET &amp;#x27;SECRET_KEY&amp;#x27;,
      ENDPOINT &amp;#x27;ACCOUNT_ID.r2.cloudflarestorage.com&amp;#x27;,
      REGION &amp;#x27;auto&amp;#x27;);

--- Credentials to read catalog metadata
CREATE SECRET r2_catalog_secret (
      TYPE ICEBERG,
      TOKEN &amp;#x27;API_TOKEN&amp;#x27;);
ATTACH &amp;#x27;ACCOUNT_ID_BUCKET_NAME&amp;#x27; as iceberg_catalog (TYPE ICEBERG, ENDPOINT &amp;#x27;https:&amp;#x2F;&amp;#x2F;catalog.cloudflarestorage.com&amp;#x2F;ACCOUNT_ID&amp;#x2F;BUCKET_NAME&amp;#x27;);

--- Attach catalog
ATTACH &amp;#x27;account_bucket&amp;#x27; AS iceberg_catalog (
    TYPE ICEBERG,
    ENDPOINT &amp;#x27;https:&amp;#x2F;&amp;#x2F;catalog.cloudflarestorage.com&amp;#x2F;&amp;lt;account-id&amp;gt;&amp;#x2F;&amp;lt;bucket-name&amp;gt;&amp;#x27;
);

--- Show available metric&amp;#x2F;log&amp;#x2F;trace tables
SHOW tables FROM iceberg_catalog;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a big caveat: this pattern assumes batch commits, but observability forces you to confront what freshness means for streaming telemetry. Lakehouse systems assume a small number of writers doing big transactional commits... OTel looks more like a million tiny writers. A production scenario needs well-designed queues and aggregators (potentially just a well-tuned OTel collector) so that only a small number of writers update the Iceberg metadata.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks on a helpful pointer from a GitHub issue, am also exploring improved batching in &lt;code&gt;otlp2parquet&lt;&#x2F;code&gt; using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;otlp2parquet&#x2F;pull&#x2F;48&quot;&gt;Cloudflare Durable Objects&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are several open source projects and startups trying to solve the &quot;streaming database&quot; problem in a more production-ready way: see &lt;a href=&quot;https:&#x2F;&#x2F;fluss.apache.org&#x2F;&quot;&gt;Apache Fluss&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;risingwave.com&#x2F;&quot;&gt;Risingwave&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Mooncake-Labs&#x2F;moonlink&quot;&gt;moonlink&lt;&#x2F;a&gt;. An observability startup called Parsable, built on top of object storage and parquet, details their solution for this &lt;a href=&quot;https:&#x2F;&#x2F;www.parseable.com&#x2F;docs&#x2F;architecture&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;observability-s-data-engineering-moment&quot;&gt;Observability&#x27;s data engineering moment?&lt;&#x2F;h3&gt;
&lt;p&gt;The observability team and the data engineering team may see each other at the company holiday party this year but probably don&#x27;t eat lunch together. This is too bad: there&#x27;s genuinely interesting stuff happening in the big data ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t think a lakehouse replaces your existing observability stack, but may eat its cold storage. A cheap, analytics-friendly back-end for long-term retention, weird one-off questions, and joining telemetry with the rest of your data is where it gets interesting. Youn own data storage you own also simplifies complex and growing regulatory requirements (PII, GDPR, etc).&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s already shaking up how people do analysis on large amounts of &lt;a href=&quot;https:&#x2F;&#x2F;geoiceberg.org&#x2F;&quot;&gt;geospatial data&lt;&#x2F;a&gt;, maybe logs and traces will be next.&lt;&#x2F;p&gt;
&lt;p&gt;If a standard schema and &lt;a href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;2025&#x2F;10&#x2F;13&#x2F;duckdb-streaming-patterns&quot;&gt;streaming patterns can be figured out&lt;&#x2F;a&gt;, the combination of cheap and queryable observability data, located entirely within the cloud of your choice, might make data lakes for metrics, logs, and traces much more common and usable.&lt;&#x2F;p&gt;
&lt;p&gt;The data engineers might be using the components of your next observability stack.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;related-projects&quot;&gt;Related projects&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;open-telemetry&#x2F;otel-arrow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;open-telemetry&#x2F;otel-arrow&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Mooncake-Labs&#x2F;moonlink&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Mooncake-Labs&#x2F;moonlink&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.rakirahman.me&#x2F;otel-arrow-delta-lake&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.rakirahman.me&#x2F;otel-arrow-delta-lake&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The unexpected joy of Claude Code on your iPhone</title>
        <published>2025-07-29T00:00:00+00:00</published>
        <updated>2025-07-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/iphone-claude-code-context-coding/"/>
        <id>https://clay.fyi/blog/iphone-claude-code-context-coding/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/iphone-claude-code-context-coding/">&lt;p&gt;You might be surprised to learn that &lt;a href=&quot;https:&#x2F;&#x2F;docs.anthropic.com&#x2F;en&#x2F;docs&#x2F;claude-code&#x2F;overview&quot;&gt;Claude Code&lt;&#x2F;a&gt; on your phone is fun, not frustrating. After experimenting several weeks, including using it during a two hour flight delay to build this blog and deploy it on &lt;a href=&quot;https:&#x2F;&#x2F;vercel.com&#x2F;&quot;&gt;Vercel&lt;&#x2F;a&gt;: I&#x27;m now that person on the BART train &lt;del&gt;vibe&lt;&#x2F;del&gt; &lt;a href=&quot;https:&#x2F;&#x2F;x.com&#x2F;karpathy&#x2F;status&#x2F;1937902205765607626&quot;&gt;context coding&lt;&#x2F;a&gt; from their iPhone.&lt;&#x2F;p&gt;
&lt;p&gt;Claude Code, when combined with push notifications, some iOS apps, &lt;a href=&quot;https:&#x2F;&#x2F;docs.anthropic.com&#x2F;en&#x2F;docs&#x2F;claude-code&#x2F;slash-commands&quot;&gt;slash commands&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.anthropic.com&#x2F;en&#x2F;docs&#x2F;claude-code&#x2F;hooks&quot;&gt;hooks&lt;&#x2F;a&gt; is surprisingly mobile-friendly. Thanks to &lt;a href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;&quot;&gt;Tailscale&lt;&#x2F;a&gt; and some &lt;a href=&quot;https:&#x2F;&#x2F;www.wireguard.com&#x2F;&quot;&gt;mesh VPN crypto magic&lt;&#x2F;a&gt;, it now takes about 5 minutes to set up a private, always-on connection between your phone and home laptop or desktop.&lt;&#x2F;p&gt;
&lt;p&gt;If inspiration strikes in the subway: you get a new superpower. You can also go along with your regular commute and get pinged by a notification when Claude Code needs your input. It&#x27;s better for your brain than social media apps and you get to make cool stuff in unexpected places.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I used Claude Code on my phone and you can too (with caveats). The hooks, commands, containers, and terminal layouts I used are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;claudetainer&quot;&gt;on Github&lt;&#x2F;a&gt;, where I&#x27;ve tried to package them in an &quot;easy-mode&quot; CLI that automates this setup inside a &lt;a href=&quot;https:&#x2F;&#x2F;containers.dev&#x2F;&quot;&gt;devcontainer&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-minimal-mobile-app-stack&quot;&gt;The minimal mobile app stack&lt;&#x2F;h2&gt;
&lt;img src=&quot;&#x2F;images&#x2F;cc-iphone&#x2F;image1.png&quot; alt=&quot;Mobile setup diagram&quot; class=&quot;mobile-image&quot;&gt;
&lt;p&gt;The basic ingredients for iPhone are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;an iOS terminal app that supports mosh (recommend &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;us&#x2F;app&#x2F;blink-shell-build-code&#x2F;id1594898306&quot;&gt;Blink Shell&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;us&#x2F;app&#x2F;termius-modern-ssh-client&#x2F;id549039908&quot;&gt;Termimus&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;us&#x2F;app&#x2F;ntfy&#x2F;id1625396347&quot;&gt;ntfy&lt;&#x2F;a&gt; app for free push notifications&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;&quot;&gt;Tailscale&lt;&#x2F;a&gt; if connecting to your laptop&#x2F;desktop, otherwise any remote server&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zellij.dev&#x2F;&quot;&gt;zellij&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tmux&#x2F;tmux&#x2F;wiki&quot;&gt;tmux&lt;&#x2F;a&gt; for switching between your desktop and phone easily and nice UI&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;us&#x2F;app&#x2F;github&#x2F;id1477376905&quot;&gt;GitHub app&lt;&#x2F;a&gt; for merging PRs, seeing CI output, etc&lt;&#x2F;li&gt;
&lt;li&gt;a &lt;a href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;pricing&quot;&gt;Claude subscription&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The tech stack is pretty much identical for Android, but assume if you&#x27;re a developer and Android user you&#x27;ve been running a custom shell on your phone since the early Obama administration.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Caveat #1:&lt;&#x2F;strong&gt; I don&#x27;t recommend this workflow at all if you&#x27;re new to Claude Code, even if you&#x27;ve been using similar tools like Cursor for a while. The idea is to be able to keep ideas and inspiration going on your mobile devices once you find a groove on a real computer with a keyboard.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re past that stage, happy to report that this technical combination of apps and mobile-friendly remote protocols is surprisingly resilient. It works on a 3G connection. It also worked well on United&#x27;s wifi somewhere over the North Atlantic.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Caveat #2:&lt;&#x2F;strong&gt; You&#x27;re obviously not going to be doing much typing, and it&#x27;s a good idea to bootstrap your project on your laptop first. However, you can make a lot of typos and use terrible, shortened English. Claude always understands what you mean when you ask it to &quot;gut&quot; commit your files. Consider adding a keyboard shortcut that types &quot;&lt;a href=&quot;https:&#x2F;&#x2F;claudelog.com&#x2F;faqs&#x2F;what-is-ultrathink&#x2F;&quot;&gt;ultrathink&lt;&#x2F;a&gt;&quot; in the prompt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-your-phone-to-laptop-pipeline&quot;&gt;Setting up your phone-to-laptop pipeline&lt;&#x2F;h2&gt;
&lt;p&gt;Before we start context engineering, some prerequisites are needed. Let&#x27;s assume you have a personal macOS laptop with a reliable Wifi connection, and &lt;a href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;&quot;&gt;Tailscale&lt;&#x2F;a&gt; installed on both your phone and laptop. In my case, I&#x27;ve also connected my iPad and a cloud Linux server on DigitalOcean — everything can talk to everything else over the same private network.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;cc-iphone&#x2F;image4.png&quot; alt=&quot;Tailscale network diagram&quot; class=&quot;mobile-image&quot;&gt;
&lt;p&gt;This means, using an iPhone app like Blink Shell, you can run &lt;code&gt;mosh you@&amp;lt;private Tailscale IP&amp;gt;&lt;&#x2F;code&gt; (because &lt;a href=&quot;https:&#x2F;&#x2F;mosh.org&#x2F;&quot;&gt;mosh&lt;&#x2F;a&gt; is much better for mobile roaming) and get a shell on your laptop anywhere in the world. That by itself is pretty cool.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Caveat #3:&lt;&#x2F;strong&gt; Tailscale is optional if you have a development server somewhere... potentially even &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;features&#x2F;codespaces&quot;&gt;GitHub Codespaces&lt;&#x2F;a&gt; could be an interesting option.&lt;&#x2F;p&gt;
&lt;p&gt;With the network and server in place: let&#x27;s now build a fully functional Python app on our iPhone.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;context-coding-on-the-go&quot;&gt;Context coding on the go&lt;&#x2F;h2&gt;
&lt;p&gt;While it&#x27;s completely possible to do everything via your phone, it&#x27;s much less painful to do the initial bootstrapping with a real keyboard. In my case, I used an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fpgmaas&#x2F;cookiecutter-uv&quot;&gt;excellent python cookiecutter template&lt;&#x2F;a&gt; to create a modern python app scaffold with quality checks and a build system in a directory on my personal laptop. Good scaffolding with things like lint rules and code formatting saves hours (trust me on this). My example python project also has access to my systemwide collection of Claude Code hooks, commands and (soon) &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fpgmaas&#x2F;cookiecutter-uv&quot;&gt;sub-agents&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;On my iPhone, I then open up a MOSH session to my laptop over my private Tailscale network inside of the Blink Shell app:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;blink&amp;gt; mosh clay@&amp;lt;redacted-tailscale-ip-of-my-laptop&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point: a lot happens behind the scenes. My shell profile detects a remote session and spins up (or resumes) a &lt;a href=&quot;https:&#x2F;&#x2F;zellij.dev&#x2F;&quot;&gt;Zellij&lt;&#x2F;a&gt; layout that gives me a terminal-based Claude Code workspace optimized for mobile: I have my first tab to run Claude Code, then others to monitor usage of Claude Code itself using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ryoppippi&#x2F;ccusage&quot;&gt;ccusage&lt;&#x2F;a&gt;, a view of my git history, and system performance info. I can switch between tabs by tapping on them, and my primary input device is the iOS keyboard with many shortened shell aliases (gs = &quot;git status&quot;, etc).&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;cc-iphone&#x2F;image6.png&quot; alt=&quot;Mobile terminal interface&quot; class=&quot;mobile-image&quot;&gt;
&lt;p&gt;It&#x27;s normal&#x2F;regular Claude Code at this point, with one more critical mobile addition: push notifications when Claude Code has been waiting for your input more than 60 seconds, which is just a lightweight integration between a hook and the free ntfy app:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;cc-iphone&#x2F;image3.png&quot; alt=&quot;Push notification example&quot; class=&quot;mobile-image&quot;&gt;
&lt;p&gt;The push notifications are really nice because, for especially long running Claude Code tasks, you can switch to other tabs or tasks on your phone (or life).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Caveat #4:&lt;&#x2F;strong&gt; Figuring out ways to reduce typing is, well, key. You &lt;strong&gt;must&lt;&#x2F;strong&gt; have pre-defined Claude Code commands (like &#x2F;check and &#x2F;test) that do meaningful repetitive work and automated quality hooks that validate the output of Claude.&lt;&#x2F;p&gt;
&lt;p&gt;The other addition I&#x27;ll highlight is monitoring of Claude Code itself ... when it&#x27;s available on your phone, you may find yourself using it even more. I keep an eye on it in a separate tab:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;cc-iphone&#x2F;image5.png&quot; alt=&quot;Claude Code monitoring interface&quot; class=&quot;mobile-image&quot;&gt;
&lt;p&gt;If this seems like a lot to set up, it&#x27;s mostly copying files to your ~&#x2F;.claude directory and setting up some aliases: Claude Code can actually help you with both. Paste in your bash&#x2F;shell history for some recommendations. If you don&#x27;t want to use Tailscale, ask it to write some terraform and deploy a new server to the cloud of your choice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-this-matters-or-not&quot;&gt;Why this matters (or not)&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s a possible near-future where the morning commute is full of people typing, yelling, or whispering to their phones about the quality checks, PR approvals, and research reports that were running overnight. But who knows what the future workflow might be? As Scott Werner said, &lt;a href=&quot;https:&#x2F;&#x2F;worksonmymachine.substack.com&#x2F;p&#x2F;the-great-flood-of-adequate-software?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&quot;&gt;we&#x27;re all junior AI engineers now&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t use Claude Code for work, but for fun non-professional projects. Here&#x27;s what happened to my personal GitHub commit history after I got it working on my phone:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;cc-iphone&#x2F;image2.png&quot; alt=&quot;GitHub commit history showing increased activity&quot; class=&quot;mobile-image&quot;&gt;
&lt;p&gt;This is &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jevons_paradox&quot;&gt;Jevons&#x27; paradox&lt;&#x2F;a&gt; in action: I made this thing to use Claude Code on my iPhone, and I am now committing much more code for projects that I&#x27;ve wanted to build for a long time. The mobile constraint also had the effect of making me focus on what matters in this new workflow: good commands, well-defined &lt;a href=&quot;http:&#x2F;&#x2F;claude.md&quot;&gt;CLAUDE.md&lt;&#x2F;a&gt; files, and hooks to get the best results the first time without many keystrokes.&lt;&#x2F;p&gt;
&lt;p&gt;The barrier between ideas and actually building a prototype of it is dissolving faster than the time it takes to take BART from Embarcadero to 24th Street. This setup isn&#x27;t for everyone, but generally works for me and the types of projects I like to build for fun (python APIs, web apps, mobile apps, shell scripts).&lt;&#x2F;p&gt;
&lt;p&gt;With &lt;a href=&quot;https:&#x2F;&#x2F;support.anthropic.com&#x2F;en&#x2F;articles&#x2F;11145838-using-claude-code-with-your-pro-or-max-plan&quot;&gt;new Claude Code limits coming soon&lt;&#x2F;a&gt;, it&#x27;s a good time to be focused on context and prompt efficiency. And you know what? It&#x27;s way more fun than doom scrolling.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Check out &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;claudetainer&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;claudetainer&lt;&#x2F;a&gt; for the code.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rebooting San Francisco royalty: Emperor Norton meets GenAI</title>
        <published>2024-07-24T00:00:00+00:00</published>
        <updated>2024-07-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/emperor-norton-meets-genai/"/>
        <id>https://clay.fyi/blog/emperor-norton-meets-genai/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/emperor-norton-meets-genai/">&lt;blockquote&gt;
&lt;p&gt;Oyez, oyez! We, Norton I, Emperor of America and Chief Wrangler of Cantankerous Seagulls, do hereby proclaim: Our Royal persona shall be reborn in the realm of thinking machines! All citizens must converse with Our digital doppelgänger or face a tax of 37 pineapples.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_YLnC7GFt1FjaSwrKndmLUw.png&quot; alt=&quot;San Francisco Panorama circa 1880&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;San Francisco Panorama circa 1880&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The genteel alleys of Jackson Square north of San Francisco’s Transamerica Pyramid are full of boutique venture capital firms, design studios, and the occasional Michelin-starred restaurant. As one of the few neighborhoods not obliterated by the 1906 earthquake, it also has a direct connection to the city’s Barbary Coast origins. If you take a historical walking tour, you’ll hear about the gold and silver rushes, financial panics, political corruption, violent gangs of Australian convicts, and an eccentric named &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Emperor_Norton&quot;&gt;Joshua Norton&lt;&#x2F;a&gt; who proclaimed himself Emperor of the United States and Protector of Mexico.&lt;&#x2F;p&gt;
&lt;p&gt;The most recent San Francisco boom cycle involves generative artificial intelligence, not precious metals in the Sierras. The peculiar mix of local culture, technology, and money is the same. Developers are writing code on the same street where Emperor Norton wrote a proclamation with the then-absurd idea of a bridge across the Bay. He was the original mascot, progressive, oddball, and minor celebrity who helped generations of locals reflect on what kind of a place San Francisco was or could be.&lt;&#x2F;p&gt;
&lt;p&gt;Last year, I decided to use generative AI to recreate Norton’s voice and have it engage with people on social media. I was interested in how an AI model of Norton, which I’ll refer to as the Emperor Bot, could make us more creative. Much of what is written about AI seems to be about civilization-ending scenarios or startup pitches for new ways to summarize your emails. I wanted to know if generative AI could help people reflect on the past… and possibly laugh.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;building-an-emperor-bot-that-challenges-injustice&quot;&gt;&lt;strong&gt;Building an Emperor Bot that challenges injustice&lt;&#x2F;strong&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The first thing I had to do was build the bot, which was the most fun I’ve had on a technical project in a long time. How it works is straightforward, even for a non-programmer: you instruct a large language model (LLM), like OpenAI’s GPT-4, to reply as Emperor Norton. This instruction is called the prompt.&lt;&#x2F;p&gt;
&lt;p&gt;Some facts about Norton have been incorporated into the base LLM models. The result is cutting-edge technology approximating how to speak in the voice of someone from the 19th century. After I made dozens of small adjustments to the prompt — it was difficult to get the replies concise — here’s the bot’s reply to a tweet about seagulls and a locally famous Taco Bell:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Emperor Norton I has decreed that all seagulls must be removed from Pacifica &amp;amp; its environs. The Board of Supervisors must award damages to the owners of the desecrated Cathedral of Taco Bell.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The reply captures three things that are essential to the historical Norton: it challenges injustice (albeit of a Taco Bell), holds politicians accountable, and does so with the authority of someone with absolute political power.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s another reply to a tweet from a local supervisor proposing renovation of some crumbling piers that doesn’t hit the mark:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We decree that Piers 30–32 in San Francisco shall be rebuilt as a mixed-use development with public recreation, retail, office, and housing!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The pro-development tweet was it seemed inauthentic in the worst possible way. It didn’t capture the historical voice. It was like seeing an ad for breakfast cereal featuring an Emperor Norton proclamation on whole grains. I wanted to understand if this was truly a limitation of the current models, or something that could be improved.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;finding-norton-s-authentic-voice-in-the-llm&quot;&gt;&lt;strong&gt;Finding Norton’s authentic voice in the LLM&lt;&#x2F;strong&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;One of the issues with recreating Norton’s voice is that, while everything he wrote is in the public domain, there is a thin historical record for someone who continues to be famous in the modern era. The proclamations he published were short, and no journal or diary survived. With the help of local historians I became more familiar with gold rush era San Francisco. John Lumea of the&lt;a href=&quot;https:&#x2F;&#x2F;emperornortontrust.org&#x2F;&quot;&gt;Emperor Norton Trust&lt;&#x2F;a&gt;, the world’s foremost expert on Norton, sent me several helpful emails, and Taryn Edwards of the &lt;a href=&quot;https:&#x2F;&#x2F;www.milibrary.org&#x2F;&quot;&gt;Mechanics’ Institute&lt;&#x2F;a&gt; (where the real Norton was a member) shared a reading list with a warning: &lt;em&gt;San Francisco history is rife with lore and legend&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I can never know Norton’s views on the correct amount to tip at restaurants, but did know he was always against corruption and wanted to keep politicians accountable… to the point of calling for Congress to be abolished. Using more AI, I generated a map of the themes and topics Norton wrote about and hoped it would guide better responses.&lt;&#x2F;p&gt;
&lt;p&gt;For example, Norton had a proclamation that referred to sailing ships — but his writing on the ships thematically touched on justice, authority, responsibility and safety. When he wrote about being refused entrance to an event by “snobs in charge of the door” — the map pointed to his concerns around the abuse of power and social hierarchies.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of parroting or sounding like a shill, the bot’s replies seemed to improve with the new context. Here’s the reply to the news of a local brewery closing:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Great Scott! Anchor Brewing, a city gem, faces peril? We’ll rally, oh we will! Workers, I stand in solidarity with you! Citizens, let us preserve our city’s heritage!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It was around this time a controversial local billionaire changed policies and most bots, including the Emperor Bot, were kicked off Twitter.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bluesky-social-hackerspaces-and-vegetables&quot;&gt;&lt;strong&gt;BlueSky Social, hackerspaces and vegetables&lt;&#x2F;strong&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Elon Musk kicked the bots off Twitter because he wanted money for the type of access the bots needed to post, which was previously free. This resulted in the Emperor Bot moving to the newer social network &lt;a href=&quot;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;emperornorton.bsky.social&quot;&gt;BlueSky&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Despite being a friendlier and invite-only version of Twitter at the time, I was concerned what might happen. Despite profile disclosures and a prominent robot icon, my impression is that most people assumed the text was written by a cosplaying San Francisco history nerd pretending to be a digital version of Norton.&lt;&#x2F;p&gt;
&lt;p&gt;Non-public tests with the bot commenting on trending topics went badly. The bot never generated outright offensive content, but anything related to politics was inflammatory. Even positive replies, like support for the Hollywood writers’ strike, were problematic. People striking for reasons related to losing their livelihoods over AI-generated content do not want a supportive yet eccentric 19th century bot to join the conversation. The fully-automated social media version was never released, so I reviewed replies and posted the unaltered text every few days.&lt;&#x2F;p&gt;
&lt;p&gt;The most engaging conversations with the bot were in private channels on Discord, the popular chat app. A local hackerspace added it to their AI channel and started interacting with it. Using the latest tricks being spread around the LLM space, bot replies seemed to improve when I added an “therapy” mode where the bot would articulate its sensations, fears and desires before replying.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s the bot’s inner monologue for replying to something related political corruption:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sensations: A harsh, sneering laughter rings in my ears, leaving an unpleasant taste in my mouth.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Fears: Anarchy and corruption taking over my beloved city.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Desires: To enact justice and maintain the order of my realm.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Somebody also asked the bot what vegetable it found the most threatening.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would say it is the artichoke. Its sharp exterior might be seen as a symbol of uprising, but in truth, it’s the tastiest rebellion I’ve ever faced.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h4 id=&quot;a-folk-hero-emerges-on-bluesky-social&quot;&gt;&lt;strong&gt;A folk hero emerges on BlueSky Social&lt;&#x2F;strong&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The bot was finding a certain groove on Discord, but on BlueSky it was limited to replys related to a very small number of topics: mostly pets and Norton himself. I was surprised how culturally relevant Norton, who died in 1880, is to modern-day San Francisco and how deep that appreciation and fandom goes. An award-winning author was one of the first followers.&lt;&#x2F;p&gt;
&lt;p&gt;My impression is that Norton is a folk hero on social media or, depending on who you ask, the world’s first crypto bro. (He printed his own money and passed it off as legal tender.) People imply they would love to vote for him in the current election. He’s also considered a minor saint in a belief system that started in 1960s Berkeley called &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Discordianism&quot;&gt;Discordianism&lt;&#x2F;a&gt; (no relation to the app Discord).&lt;&#x2F;p&gt;
&lt;p&gt;The most liked posts from the bot, after some trial and error, were those that acted as a kind of promoter for San Francisco and the historical Norton. Anything else tended to annoy or confuse people, like John Adams showing up on Instagram and liking your vacation photo. When someone skeeted (that’s BlueSky for tweet) that Norton was cool and they wished he could save everyone now, it replied:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Indeed, time unceasingly marches forward. While I can’t step forth in flesh, my philosophies persist. Lead with benevolence, act with authenticity.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In another instance, the bot replied to a post memorializing Norton with a familiar sounding phrase: “death is but a door, time a window.” It’s a quote from the villain in the 1989 movie &lt;em&gt;Ghostbusters II&lt;&#x2F;em&gt;… a 16th century historical figure that comes back to life in an evil painting.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;what-are-the-chances-of-the-emperor-bot-taking-over-the-world&quot;&gt;&lt;strong&gt;What are the chances of the Emperor Bot taking over the world?&lt;&#x2F;strong&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The 1906 earthquake destroyed much of Norton’s San Francisco. The murkiness of the late 19th century also means that it’s easier for every generation to repurpose Norton to fit their mood and anxieties. In present day San Francisco and the United States: there are plenty of issues to choose from.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, one of the largest local conversations in technology is the promise and perils of generative AI itself.&lt;&#x2F;p&gt;
&lt;p&gt;It’s not hard to imagine these sorts of bots finding a place in educational settings or museums to make history more interactive and conversational, at least for well-liked and popular historical figures. The bot would be an excellent companion in a self-guided walking tour of the Emperor’s old neighborhood.&lt;&#x2F;p&gt;
&lt;p&gt;There’s also the obvious danger of these bots on social media, which has been written about at length in many different places. My assessment is the risk of the Emperor Bot causing global conflict or taking over governments as a digital Emperor is effectively zero. However, it’s hard to think of any scenario where more advanced gen AI bots will improve the overall quality of conversation in the near-term on social media.&lt;&#x2F;p&gt;
&lt;p&gt;There has been an enormous amount of investment from major companies to make sure the output of LLMs is inoffensive and not harmful, but that is changing as individuals and groups create their own models. Using a collection of scanned 19th century newspapers, I started on an experiment with fine-tuning the bot to make it sound more like actual writers from the 1880s. That was paused once I realized that I did not want my bot to pick up on the views that existed in the vast majority of late 19th-century American newspapers.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;norton-generative-ai-and-a-way-forward&quot;&gt;&lt;strong&gt;Norton, generative AI, and a way forward&lt;&#x2F;strong&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Joshua Norton is buried in Colma, the cemetery city just south of San Francisco. There’s a marble headstone that says &lt;em&gt;Norton I Emperor&lt;&#x2F;em&gt; in large letters. E Clampus Vitus, a 19th-century fraternal organization (sometimes called in the press a “historical drinking society”) throws a party honoring his legacy once a year on “Norton Day” at his gravesite. There’s a convenient Irish bar a short stroll away.&lt;&#x2F;p&gt;
&lt;p&gt;My theory on why Norton still has a large number of admirers who toast him is because, in his own chaotic and eccentric way, he is remembered for fighting against injustice. He also was an immigrant to San Francisco who made and lost a fortune but still figured out a way to get free lunches and had a popular newspaper column for twenty years.&lt;&#x2F;p&gt;
&lt;p&gt;Generative AI will never be able to capture the full context, intricacies, and contradictions of the human Norton but it can be strange and unexpected. Despite the peril of thousands of trolling bots taking over social media, in the right context this technology can be fun and creative — without taking over the world or your job.&lt;&#x2F;p&gt;
&lt;p&gt;Uncertainty hangs over the air right now for both AI and San Francisco. There are plenty of doom loop scenarios being discussed for both. Economically, the success or failure of generative AI will also have a direct impact on The City for years to come. The open question for local entrepreneurs depositing checks from venture capitalists is what happens next. At one extreme: collapse from malevolent AI. Another direction is the mediocrity of bots that make life worse via annoying assistants that get in the way.&lt;&#x2F;p&gt;
&lt;p&gt;A happy outcome would be technology — and a city — where people are able to be thoughtful, explore new ideas, and maybe have fun while holding those in power accountable. It might also get a little weird and off-putting at times. We should, in other words, take more inspiration from Emperor Norton.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks to John Lumea of the Emperor Norton Trust, Tayrn Edwards of the San Francisco Mechanics Institute, the weekend staff at The San Francisco History Center, the Prelinger Library, The Ancient and Honorable Order of E Clampus Vitus, the Misalignment Museum, and various adherents of Discordianism active on BlueSky and Discord.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;You can follow the Emperor Bot at&lt;&#x2F;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;emperornorton.bsky.social&quot;&gt;&lt;em&gt;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;emperornorton.bsky.social&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My neural net turned into an Apple zealot after training it on mid-1990s WIRED issues</title>
        <published>2019-07-02T00:00:00+00:00</published>
        <updated>2019-07-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/neural-net-apple-zealot-wired-training/"/>
        <id>https://clay.fyi/blog/neural-net-apple-zealot-wired-training/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/neural-net-apple-zealot-wired-training/">&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_MV3CFoPXFNieMtAmHfBzew.png&quot; alt=&quot;Generated text using OpenAI’s GPT-2 and OCR text from mid-1990s WIRED issues&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Generated text using OpenAI’s GPT-2 and OCR text from mid-1990s WIRED issues&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I trained a bleeding-edge machine learning (ML) model with a dozen issues of WIRED magazine from the mid-1990s and things got weird. The new model started generating a unique blend of 90s-era techno-optimism, product advertisements for microprocessors and gaming systems that never existed, odd interviews with tech luminaries and… a profound love for all things Apple.&lt;&#x2F;p&gt;
&lt;p&gt;There’s a popular ML technique going around where people generate text using neural networks based on different source materials. The technology isn’t advanced enough to make us think that a sensible person actually wrote the generated text—a serious problem we’re going to have to deal with—but it’s often good enough to produce bizarre statements that are funny in context.&lt;&#x2F;p&gt;
&lt;p&gt;I first tried it around &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;clog&#x2F;using-ai-to-generate-an-ico-white-paper-d328f26577f3&quot;&gt;two years ago to generate an ICO whitepaper&lt;&#x2F;a&gt; and there have been many other developers, data scientists, and researchers who have continued to explore. &lt;a href=&quot;https:&#x2F;&#x2F;aiweirdness.com&#x2F;aboutme&quot;&gt;Janelle Shane’s posts on this subject&lt;&#x2F;a&gt; are wonderful and inspired this work. In the past two years, the effectiveness of the models have significantly improved.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about what happened when I wanted to train one of the newer models going around (&lt;a href=&quot;https:&#x2F;&#x2F;openai.com&#x2F;&quot;&gt;OpenAI’s&lt;&#x2F;a&gt; GPT-2) with mid-1990s WIRED magazine issues.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wired-nostalgia-and-machine-learning&quot;&gt;WIRED, Nostalgia and Machine Learning&lt;&#x2F;h4&gt;
&lt;p&gt;I grew up reading WIRED magazine, and I’m not immune to the nostalgia for the period of the 1990s when the internet and personal computers were a new and amazing thing for many people in the United States.&lt;&#x2F;p&gt;
&lt;p&gt;Generating text from the model is surprisingly addictive—with a click of a button, it’s like reading something a WIRED journalist might write after not sleeping for three days.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_dT1Xoxn_UesQcpnZ2_y_OA.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is also a strange blend of marketing and journalism is impacted by the training data—WIRED issues in the 90s, after all, had plenty of print ads. As a result you get serious-sounding statements about history mixed with product marketing speak:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_MRMxUNJjri33TuHdsUeXtQ.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes this veers off into editorialization. After a quote around how you can get the latest PowerBook “when it’s available” (which every tech company would write in announcements if they could get away with it), this paragraph ends with something that sounds like a melody:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_hWcqId3l-SgVdJ3fIPdzFg.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When it comes to technology luminaries of the mid-1990s, even seemingly critical quotes of Jobs end up making him look good:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_xwyX_GIO6lFeOtGMrr3qUw.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Bill Gates, in contrast, basically has to plead that Microsoft isn’t actually in trouble when seeking an ambitious target of “download hits”:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_XOZmjNKiRNTk3zQZzD-7RQ.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-machine-learning-hype-is-real&quot;&gt;The machine learning hype is real?&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_KCP6IU0uWk9GrwZHp2HC5Q.png&quot; alt=&quot;The hype, man, it’s real.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The hype, man, it’s real.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This started as a quick AI experiment to see how text generation models were advancing. From start to finish (including training time), it took only about two hours before text started coming out, including some text that wouldn’t look out of place in an experimental poetry journal.&lt;&#x2F;p&gt;
&lt;p&gt;While it was fun and fascinating to see what was generated, it also doesn’t seem unrealistic that more advanced versions of the model would probably be good enough to generate entirely convincing WIRED articles that seemed like they were written in the 90s. That’s disturbing for anyone who reads digitized historical sources of anything on the internet.&lt;&#x2F;p&gt;
&lt;p&gt;The creator of the GPT-2 model, OpenAI, &lt;a href=&quot;https:&#x2F;&#x2F;openai.com&#x2F;blog&#x2F;better-language-models&#x2F;#update&quot;&gt;has committed to working with research and governmental institutions on the societal impact of widespread use of models like this&lt;&#x2F;a&gt;, including an advanced version of GPT-2 that hasn’t been publicly released yet.&lt;&#x2F;p&gt;
&lt;p&gt;As a creative tool, however, my experiments with WIRED and neural networks were unexpectedly inspiring. Strange alternate histories, quotes, bizarre products, and koan-like statements around the future emerged.&lt;&#x2F;p&gt;
&lt;p&gt;My deep concern for the future of these language models is combined with a desire to read the first few chapters of a book featuring Larry Ellison, his wife FmmL, and Denny Dallas. Maybe they’re all Apple fans.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_vRa0PhaxK1OrvpC-brN3ww.png&quot; alt=&quot;The plot of 2025’s bestselling tech thriller?&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The plot of 2025’s bestselling tech thriller?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks for reading! Might get around to turning this model into some kind of chat bot in the next few weeks. Find me on twitter: &lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;smithclay&quot;&gt;&lt;em&gt;@smithclay&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>TCP&#x2F;IP over Amazon Cloudwatch Logs</title>
        <published>2019-05-02T00:00:00+00:00</published>
        <updated>2019-05-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/tcp-ip-over-cloudwatch-logs/"/>
        <id>https://clay.fyi/blog/tcp-ip-over-cloudwatch-logs/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/tcp-ip-over-cloudwatch-logs/">&lt;h4 id=&quot;running-network-services-inside-aws-lambda-functions&quot;&gt;Running network services inside AWS Lambda Functions&lt;&#x2F;h4&gt;
&lt;p&gt;You can’t ping an AWS Lambda function. You can’t SSH into a running function &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;clog&#x2F;ssh-ing-into-your-aws-lambda-functions-c940cebf7646&quot;&gt;without reverse tunneling&lt;&#x2F;a&gt;. You would never run nginx or Rails inside of a function because there’s little point — accepting an inbound TCP connection on a port like a Docker container isn’t supported by function-as-a-service (FaaS) platforms. It is possible, however, to write your own userspace networking stack and do what you want with it —including using Amazon Cloudwatch Logs (or even AWS Lambda tags) as the data link layer.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about implementing TCP&#x2F;IP over Amazon Cloudwatch Logs using Go, which enables you to access network services running inside of AWS Lambda functions.&lt;&#x2F;p&gt;
&lt;p&gt;It’s slow and not very useful, but it was a fun way to learn more about Linux networking and using AWS services in a way that might horrify some AWS engineering teams. I’m calling this proof-of-concept “&lt;strong&gt;Richard Linklayer”&lt;&#x2F;strong&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;rlinklayer&quot;&gt;published the full Github code today.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-serverless-architecture-darkly&quot;&gt;A Serverless Architecture Darkly&lt;&#x2F;h3&gt;
&lt;p&gt;This experiment was the escalation of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Trying to run progressively stranger things in AWS Lambda.&lt;&#x2F;li&gt;
&lt;li&gt;Interest in exploring netstack, an IPv4&#x2F;IPv6 userland networking stack from Google.&lt;&#x2F;li&gt;
&lt;li&gt;Hearing contradictory and confusing things from some clients and engineering teams around different limitations of serverless functions.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I attempted to check off all three of those things using Go. With netstack, it’s possible to do some exotic things with networking in unexpected places, no kernel hacking (i.e. writing C code) required. Any Go program with read and write access to reliable-ish bi-directional communication channel — like logs, tags or &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;IP_over_Avian_Carriers&quot;&gt;carrier pigeon&lt;&#x2F;a&gt; — can implement full in-process TCP&#x2F;IP networking. There are many AWS services that meet this criteria.&lt;&#x2F;p&gt;
&lt;p&gt;Network tunneling is not a new idea, but inside the AWS Lambda execution environment a userspace solution is needed because it’s not possible to change routing tables, modify network interfaces, or change OS-level networking (unlike container-based network overlays or service meshes).&lt;&#x2F;p&gt;
&lt;p&gt;Running in a standard Go process, Richard Linklayer tunnels IP packets over Amazon Cloudwatch Log Streams that follow a special naming convention — the stream and log group names are just MAC addresses. Using a tun or tap interface, I can bridge my AWS Lambda network endpoints to my local development machine:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;0_sYCn3BhpTon89Nt8.png&quot; alt=&quot;High-level architecture of Richard Linklayer where a Linux host communicates with a TCP network service running inside of an AWS Lambda function. The process polls Amazon Cloudwatch for new inbound packets.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;High-level architecture of Richard Linklayer where a Linux host communicates with a TCP network service running inside of an AWS Lambda function. The process polls Amazon Cloudwatch for new inbound packets.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This design ignores normal AWS Lambda event-driven patterns for processing inbound packets and sending outbound packets —only a single instance of a function starts, reads and writes from Cloudwatch using polling, then processes packets until it times out. There is only one instance of a function running a unique network service running at any given time, and it can only accept network connections for up to 15 minutes, the maximum timeout of an AWS Lambda function in early 2019.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;0_wPTklPyIuEI3LoXT.png&quot; alt=&quot;Lifecycle of an AWS Lambda function that accepts inbound TCP requests via Cloudwatch. Don’t do this.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Lifecycle of an AWS Lambda function that accepts inbound TCP requests via Cloudwatch. Don’t do this.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When you put it all together, the result is that you can ping a Lambda function.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;0_2wJktNatuiupDEbN.png&quot; alt=&quot;Pinging an AWS Lambda function on Mac OS X via a Linux container. It’s kind of slow but remember everything is running over Amazon Cloudwatch.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Pinging an AWS Lambda function on Mac OS X via a Linux container. It’s kind of slow but remember everything is running over Amazon Cloudwatch.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The underlying implementation over Amazon Cloudwatch is at &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Data_link_layer&quot;&gt;layer 2,&lt;&#x2F;a&gt; so it’s reading and writing ethernet packets delivered to MAC addresses. MAC address discovery happens using &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Address_Resolution_Protocol&quot;&gt;ARP&lt;&#x2F;a&gt; with a special-purpose log stream that acts as a “broadcast address” for the entire network.&lt;&#x2F;p&gt;
&lt;p&gt;Since it supports anything that runs over TCP, you can also cURL a node.js HTTP server running inside of a function, or anything else that listens on a TCP port. This server returns some text after a few seconds.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;0_pVFK-lYuXlqFkNXn.png&quot; alt=&quot;Making an HTTP request to a node.js server running inside of an AWS Lambda function.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Making an HTTP request to a node.js server running inside of an AWS Lambda function.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The latency means that this would likely never work for anything production-level, but TCP is resilient enough to handle the slow connection.&lt;&#x2F;p&gt;
&lt;p&gt;We have full transparency into the network traffic since it’s just data inside of log streams. It looks like this, which is the stream of IPv4 traffic from &lt;em&gt;74:74:74:74:74&lt;&#x2F;em&gt; to &lt;em&gt;42:42:42:42:42&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;0_biGVnazegJoe0ZG8.png&quot; alt=&quot;Layer 2 Observability? Traffic from one MAC address to another that uses Cloudwatch as the channel.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Layer 2 Observability? Traffic from one MAC address to another that uses Cloudwatch as the channel.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;An example on how to get this working with a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;rlinklayer&#x2F;tree&#x2F;master&#x2F;examples&quot;&gt;simple network running an HTTP server is on Github&lt;&#x2F;a&gt;. While not described in this post, there is also an additional link layer that uses AWS Lambda function tags as a transport that has slightly better performance results.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dazed-and-lambfused&quot;&gt;Dazed and Lambfused&lt;&#x2F;h3&gt;
&lt;p&gt;There’s a lot of interesting open-source networking projects in the container space right now: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;envoyproxy&#x2F;envoy&quot;&gt;Envoy&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;linkerd.io&#x2F;&quot;&gt;Linkerd&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cilium&#x2F;cilium&quot;&gt;Cilium&lt;&#x2F;a&gt; to name a few. With support for custom AWS Lambda runtimes released at re:Invent in 2018, I’m curious how emerging control or data planes are going to impact serverless functions beyond additional language support and monitoring and security solutions.&lt;&#x2F;p&gt;
&lt;p&gt;Beyond that, there’s a few interesting ideas to explore beyond this proof-of-concept, including:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;UDP services (DNS over AWS Lambda, anyone?)&lt;&#x2F;li&gt;
&lt;li&gt;Any kind of performance optimizations (everything is based on polling APIs and could be tuned)&lt;&#x2F;li&gt;
&lt;li&gt;Exploration of other link layer transports in AWS (DynamoDB streams? SNS? SQS? S3?)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;rlinklayer&quot;&gt;Pull requests and ideas welcome.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using AI to generate an ICO white paper</title>
        <published>2017-11-16T00:00:00+00:00</published>
        <updated>2017-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/ai-generate-ico-white-paper/"/>
        <id>https://clay.fyi/blog/ai-generate-ico-white-paper/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/ai-generate-ico-white-paper/">&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_rH9Lh2RZprmbUp4FXM6U0w.png&quot; alt=&quot;The white paper generated using a recurrent neural network.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The white paper generated using a recurrent neural network.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With ICOs, or token sales of cryptocurrencies, &lt;a href=&quot;https:&#x2F;&#x2F;hackernoon.com&#x2F;insider-reflections-on-the-ico-bubble-6304c4d25ed8&quot;&gt;having reportedly raised $2.3 billion in 2017,&lt;&#x2F;a&gt; the time seemed right to use AI to generate a fake ICO white paper programmatically using a &lt;a href=&quot;http:&#x2F;&#x2F;karpathy.github.io&#x2F;2015&#x2F;05&#x2F;21&#x2F;rnn-effectiveness&#x2F;&quot;&gt;recurrent neural network&lt;&#x2F;a&gt; (RNN) in the cloud.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve titled it “RNN-Coin” &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;char-embeddings-ml-engine&#x2F;blob&#x2F;master&#x2F;whitepaper.pdf&quot;&gt;and have published the full white paper here (pdf)&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;RNNs are a machine learning technique that have frequently and hilariously been used to generate arbitrary text from text-based training data. Some of my favorite examples are generating &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@hondanhon&#x2F;i-trained-a-neural-net-to-generate-british-placenames-9460e907e4e9&quot;&gt;new British town names&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nihitx&#x2F;game-of-thrones-&quot;&gt;a Game of Thrones&lt;&#x2F;a&gt; book. However, unlike most town names or the work of George R.R. Martin, there are plenty of people that claim that ICO whitepapers are mostly incomprehensible anyway. This post shares some the technical background and some of the excerpts from the paper (it’s painful to read in full).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;building-the-model&quot;&gt;Building the model&lt;&#x2F;h4&gt;
&lt;p&gt;Using &lt;a href=&quot;https:&#x2F;&#x2F;www.tensorflow.org&#x2F;&quot;&gt;Tensorflow&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;ml-engine&#x2F;&quot;&gt;Google Cloud ML engine&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mfclarke&#x2F;char-embeddings-ml-engine&quot;&gt;and a modified RNN example I came across on Github from Maximilian Clarke&lt;&#x2F;a&gt;, I created a model using a GPU-powered cloud training job that ran for a few hours (my MacBook Pro was too slow and RNNs are resource-intensive to train). The trained model could then output text that eerily echoed many ICO white papers.&lt;&#x2F;p&gt;
&lt;p&gt;For the published white paper, I generated around 2,000 characters at a time from 40-character sentence seeds I pulled from real white papers (&lt;a href=&quot;https:&#x2F;&#x2F;bitcoin.org&#x2F;bitcoin.pdf&quot;&gt;like the Bitcoin white paper&lt;&#x2F;a&gt;). I then manually organized the output into sections — formatted in LaTeX for full white paper legitimacy, of course.&lt;&#x2F;p&gt;
&lt;p&gt;One of the first outputs became the basis of the white paper abstract:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A purely peer-to-peer version of the company system with the price of the system to develop and the contract agent servers and the price of the system of the system which is a simple decentralized system which is the contract to provide the contract […] can be considered as a result of the blockchain.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The algorithm created peer-to-peer, blockchain-backed alternative to corporate structure! It’s just a simple decentralized system. The price is still developing, however.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-local-revolution-of-experience-is-here&quot;&gt;The local revolution of experience is here&lt;&#x2F;h4&gt;
&lt;p&gt;RNNs have been described by&lt;a href=&quot;http:&#x2F;&#x2F;karpathy.github.io&#x2F;2015&#x2F;05&#x2F;21&#x2F;rnn-effectiveness&#x2F;&quot;&gt;some people as “magical” or “uncanny”&lt;&#x2F;a&gt; and after going through this exercise I agree. There are points in the white paper that sound perfectly reasonable — and even decent marketing. What millennial, including myself, &lt;em&gt;wouldn’t&lt;&#x2F;em&gt;want to be part of “the local revolution of experience”?&lt;&#x2F;p&gt;
&lt;p&gt;It often generates language constructions that don’t sound natural. This could probably be tuned further by trying cleaner training data and different model parameters. Yet strange-sounding sentences also have gems:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the highest commitment wallet that a confident of money for medical currencies of the project in the initiative instruments of the project is the farmer of the cost of the system.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A “highest commitment wallet” might be a reassuring concept for an ICO investor in medical currencies.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;a-pledge-to-the-smart-token-and-having-fun-with-ml&quot;&gt;“A pledge to the smart token” (and having fun with ML)&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;The first social advance is a problem for control of the platform.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I don’t see RNNs replacing real white paper writers or creating ICOs, but the experience of building and training fairly complex machine learning models that were previously the exclusive domain of academics and researchers does seem to be getting much easier for the average developer.&lt;&#x2F;p&gt;
&lt;p&gt;Much of the code for sample tensorflow projects using different ML techniques is freely available on Github and it seems cloud providers are now &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;ec2&#x2F;elastic-gpus&#x2F;&quot;&gt;racing to rent GPUs&lt;&#x2F;a&gt; to developers for model training purposes. I think as more tensorflow projects get shared the potential for collaboration also goes up: if a real AI&#x2F;ML researcher comes across this post, p&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;char-embeddings-ml-engine&quot;&gt;ull requests are welcome&lt;&#x2F;a&gt; to refine the model.&lt;&#x2F;p&gt;
&lt;p&gt;There is much speculation and uncertainty around the future of ICOs, but it also seems like there has never been a better time to generate fake ICO white papers with AI.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;char-embeddings-ml-engine&#x2F;raw&#x2F;master&#x2F;whitepaper.pdf&quot;&gt;The full paper is available for download here (pdf).&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Writing an Ethereum smart contract from the 19th century</title>
        <published>2017-10-17T00:00:00+00:00</published>
        <updated>2017-10-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/ethereum-smart-contract-19th-century/"/>
        <id>https://clay.fyi/blog/ethereum-smart-contract-19th-century/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/ethereum-smart-contract-19th-century/">&lt;p&gt;Thought it’d be fun to explore blockchain hype and write a simple &lt;a href=&quot;https:&#x2F;&#x2F;www.coindesk.com&#x2F;information&#x2F;ethereum-smart-contracts-work&#x2F;&quot;&gt;Ethereum smart contract&lt;&#x2F;a&gt; based on an short story from the 19th century.&lt;&#x2F;p&gt;
&lt;p&gt;In middle school, I remember reading Robert Louis Stevenson’s &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;The_Bottle_Imp&quot;&gt;&lt;em&gt;&lt;strong&gt;The Bottle Imp&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, written in 1891. The story describes the the struggles of a person who buys a magic bottle from an elderly man who lives in a “fine house on a hill” in San Francisco. It grants unlimited wishes with certain &lt;em&gt;evil&lt;&#x2F;em&gt; conditions: you can’t sell it to anyone else for more than you paid for it and if you can’t find a willing buyer before you expire &lt;strong&gt;you’re doomed&lt;&#x2F;strong&gt;. You also have to pay in real minted coins: no paper money is allowed so there’s some physical limit to how cheaply you can sell it.&lt;&#x2F;p&gt;
&lt;p&gt;Remembering this story while reading about the blockchain, the obvious thing to do was attempt to implement it as an &lt;a href=&quot;https:&#x2F;&#x2F;ethereum.org&#x2F;&quot;&gt;Ethereum&lt;&#x2F;a&gt; smart contract on a sunny Sunday afternoon. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;etherimp&quot;&gt;I’ve published the code on Github here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;my-attempt-at-sounding-like-i-know-about-what-i-m-talking-about-about-with-blockchain&quot;&gt;My attempt at sounding like I know about what I’m talking about about with blockchain&lt;&#x2F;h4&gt;
&lt;p&gt;This was my first attempt and building a ÐApp (&lt;a href=&quot;https:&#x2F;&#x2F;ethereum.stackexchange.com&#x2F;questions&#x2F;383&#x2F;what-is-a-dapp&quot;&gt;decentralized app&lt;&#x2F;a&gt;). I feel like including this diagram of distributed networks from a 1962 academic paper gives this post some additional gravitas and is completely self-explanatory on the benefits of decentralized applications, the nature of distributed ledgers, and inherent security considerations of applications built using this technology:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;0_tbaizJv1spbNUhG2.png&quot; alt=&quot;On Distributed Communications Networks, Paul Baran (1962)&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;On Distributed Communications Networks, Paul Baran (1962)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;now-that-we-have-a-full-and-complete-understanding-of-ethereum-and-distributed-applications-how-does-the-contract-work&quot;&gt;Now that we have a full and complete understanding of Ethereum and distributed applications, how does the contract work?&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;As for why I sell so cheap, I must explain to you there is a peculiarity about the bottle. — The Bottle Imp&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In the process of writing this smart contract, I was terrified. Outside of the usual pains of figuring out how to write, deploy, and build things using a completely new toolchain (the &lt;a href=&quot;https:&#x2F;&#x2F;solidity.readthedocs.io&#x2F;en&#x2F;develop&#x2F;&quot;&gt;Solidity&lt;&#x2F;a&gt; language, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ethereumjs&#x2F;testrpc&quot;&gt;testrpc&lt;&#x2F;a&gt;, and the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ethereum&#x2F;wiki&#x2F;wiki&#x2F;JavaScript-API&quot;&gt;web3&lt;&#x2F;a&gt; APIs), it was unsettling to read though a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ConsenSys&#x2F;smart-contract-best-practices&quot;&gt;popular resource on potential security vulnerabilities from ConsenSys&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The contract I wrote (my first) does only one trivial thing by design: &lt;em&gt;transfer ownership of the (virtual) bottle if they pay less than the previous owner&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;However, from best practices I found it seems clear that production-grade smart contracts need to go through a lot of testing and analysis. Specifically, it’s a good idea to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Really understand the Solidity language and know the danger of external calls, transfer calls, and reentrancy vulnerabilities.&lt;&#x2F;li&gt;
&lt;li&gt;Integrate rate-limiting and “pause” functionality if things go wrong.&lt;&#x2F;li&gt;
&lt;li&gt;Use static analysis and linting to catch vulnerabilities.&lt;&#x2F;li&gt;
&lt;li&gt;Explore different patterns to upgrade your contract as new vulnerabilities are discovered.&lt;&#x2F;li&gt;
&lt;li&gt;Start a bug bounty program to thoroughly test it (with rewards to those who find issues) before the contract goes live on the main ethereum network. Maybe hire some consultants for auditing purposes, too (they were all over my Google search results for Solidity development questions).&lt;&#x2F;li&gt;
&lt;li&gt;Consider using frameworks like truffle or OpenZepplin when you realize you’re probably doing it wrong.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Attempting to, uh, “implement” some of those best practices over a couple hours, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;etherimp&#x2F;blob&#x2F;master&#x2F;contracts&#x2F;EtherImp.sol#L24&quot;&gt;I came up with this code for the key logic in the contract to transfer the bottle&lt;&#x2F;a&gt;. If the price is less than the price the previous owner paid and it’s non transferred back to the same account— the bottle has a new owner and the price paid is transferred back the previous owner:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;solidity&quot; class=&quot;language-solidity &quot;&gt;&lt;code class=&quot;language-solidity&quot; data-lang=&quot;solidity&quot;&gt;function buyBottle() payable public {
  &amp;#x2F;&amp;#x2F; Conditions
  require(msg.sender != currentOwner);
  require(msg.value &amp;gt; 0);
  require(msg.value &amp;lt; lastPricePaid);

  &amp;#x2F;&amp;#x2F; Effects
  previousOwner = currentOwner;
  currentOwner = msg.sender;
  lastPricePaid = msg.value;
  LogTransfer(previousOwner, currentOwner, lastPricePaid);

  &amp;#x2F;&amp;#x2F; Interactions
  &amp;#x2F;&amp;#x2F; *Security alert (correction on October 27, 2017):*
  &amp;#x2F;&amp;#x2F; This implementation isn&amp;#x27;t safe! See GitHub and comments.
  previousOwner.transfer(msg.value);
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The “conditions”, “effects” and “interactions” comments are only for reassurance based on a &lt;a href=&quot;https:&#x2F;&#x2F;ethereum.stackexchange.com&#x2F;questions&#x2F;19341&#x2F;address-send-vs-address-transfer-best-practice-usage&quot;&gt;solidity question I read on StackOverflow&lt;&#x2F;a&gt;. Otherwise, I’m conservatively ~40% sure it works without a major structural flaw or security vulnerability.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Like the original short story, there’s ultimately a loser in this contract: if the value paid is 1 wei — the smallest allowable unit in Ethereum, you’re stuck with ownership of this smart contract forever.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After some local testing and &lt;a href=&quot;https:&#x2F;&#x2F;www.rinkeby.io&#x2F;&quot;&gt;trying it out on the Rinkeby test network&lt;&#x2F;a&gt;, decided it was time to ship it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;it-s-live-on-the-main-ethereum-network-beware&quot;&gt;It’s live on the main Ethereum network, beware&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;“This is the bottle,” said the man; and, when Keawe laughed, “You do not believe me?” he added. “Try, then, for yourself. See if you can break it.” — The Bottle Imp&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The contract&#x2F;virtual bottle has been published to the main Ethereum network and is able to be transferred to anyone who wants it— as long as you pay less than the previous account did. I’m curious to see how long it takes for the value to get down to 1 wei and what happens (if anything).&lt;&#x2F;p&gt;
&lt;p&gt;To kick things off, &lt;em&gt;I’ve started the price of the contract at the going rate of a of a typical sandwich in the Financial District of San Francisco: 0.05 ETH&lt;&#x2F;em&gt; (~16 USD).&lt;&#x2F;p&gt;
&lt;p&gt;If you’re new to Ethereum, I’d recommend downloading the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ethereum&#x2F;mist&#x2F;releases&quot;&gt;Mist browser&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;chrome.google.com&#x2F;webstore&#x2F;detail&#x2F;metamask&#x2F;nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en&quot;&gt;Metamask extension&lt;&#x2F;a&gt; and then visiting the following address: &lt;a href=&quot;https:&#x2F;&#x2F;wallet.ethereum.org&#x2F;account&#x2F;0x9fcd683584173c5df0ee47852f5592f68ed72a0c&quot;&gt;https:&#x2F;&#x2F;wallet.ethereum.org&#x2F;account&#x2F;0x9fcd683584173c5df0ee47852f5592f68ed72a0c&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_qChPwEBf0lLVbFIfjP40uQ.png&quot; alt=&quot;The contract visualized in the Mist browser. The &amp;quot;buy bottle&amp;quot; function transfers ownership.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The contract visualized in the Mist browser. The “buy bottle” function transfers ownership.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Feedback and vulnerability assessments welcome in the comments, on &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;smithclay&quot;&gt;twitter&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;EtherImp&quot;&gt;or on the Github project page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re new to all this can also recommend &lt;a href=&quot;https:&#x2F;&#x2F;www.coinbase.com&#x2F;what-is-ethereum?locale=en-US&quot;&gt;Coinbase’s general introduction to Ethereum (and how it’s different from Bitcoin)&lt;&#x2F;a&gt;. For getting started with development, the &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;ethereum&#x2F;&quot;&gt;Reddit community&lt;&#x2F;a&gt; was useful too.&lt;&#x2F;p&gt;
&lt;p&gt;Regardless of where the blockchain hype goes — and who ends up stuck with the smart contract I created when they buy inevitably buy it for 1 wei — it was really interesting to learn more about the process of building and testing smart contracts. Last word, as it does in &lt;em&gt;&lt;strong&gt;The Bottle Imp&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;, goes to the boatswain:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I don’t value any of your talk,” replied the boatswain. “You thought I was a flat; now you see I’m not; and there’s an end. If you won’t have a swallow of the rum, I’ll have one myself. Here’s your health, and good-night to you!”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Running Selenium and Headless Chrome on AWS Lambda</title>
        <published>2017-07-28T00:00:00+00:00</published>
        <updated>2017-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/running-selenium-headless-chrome-aws-lambda/"/>
        <id>https://clay.fyi/blog/running-selenium-headless-chrome-aws-lambda/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/running-selenium-headless-chrome-aws-lambda/">&lt;p&gt;There seems to be a small community of developers building interesting browser things on AWS Lambda with Headless Chrome.&lt;&#x2F;p&gt;
&lt;p&gt;Around a month ago, I was also prototyping a small &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;lambda&#x2F;&quot;&gt;AWS Lambda&lt;&#x2F;a&gt; project that enabled running &lt;a href=&quot;http:&#x2F;&#x2F;www.seleniumhq.org&#x2F;projects&#x2F;webdriver&#x2F;&quot;&gt;Selenium Webdriver&lt;&#x2F;a&gt; with &lt;a href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;web&#x2F;updates&#x2F;2017&#x2F;04&#x2F;headless-chrome&quot;&gt;Headless Chrome&lt;&#x2F;a&gt; inside a Lambda function called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;lambdium&quot;&gt;lambdium&lt;&#x2F;a&gt; (disclaimer: no relation to laudanum).&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to share the general architecture of the app and some things I learned while building it to help any other developers wanting to give serverless and Headless Chrome a try.&lt;&#x2F;p&gt;
&lt;p&gt;There’s also great post &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@kensoh&#x2F;chromeless-chrominator-chromy-navalia-lambdium-ghostjs-autogcd-ef34bcd26907&quot;&gt;by Ken Soh for an overview&lt;&#x2F;a&gt; of other projects experimenting with Headless Chrome and AWS Lambda. &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=14859084&quot;&gt;The comments in a recent Hacker News post about the Chromeless project&lt;&#x2F;a&gt; are worth a read, too.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;binaries-not-included&quot;&gt;Binaries not included&lt;&#x2F;h4&gt;
&lt;p&gt;In brief, AWS Lambda is just an event-driven Linux container on EC2 with some &lt;a href=&quot;https:&#x2F;&#x2F;www.kernel.org&#x2F;doc&#x2F;Documentation&#x2F;cgroup-v1&#x2F;freezer-subsystem.txt&quot;&gt;special cgroup sauce&lt;&#x2F;a&gt; — &lt;a href=&quot;https:&#x2F;&#x2F;speakerdeck.com&#x2F;smithclay&#x2F;faas-measurement-fundamentals&quot;&gt;see my Gluecon talk for more on that&lt;&#x2F;a&gt;. Because of that, it’s perfectly acceptable to run almost any non-privileged process you want inside Lambda as you would in a container. You just invoke a 64-bit Linux binary that doesn’t require special privileges using something like Node.js’s &lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;api&#x2F;child_process.html&quot;&gt;child_process module&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;2&#x2F;library&#x2F;subprocess.html&quot;&gt;Python’s subprocess library&lt;&#x2F;a&gt; in code in the main function handler.&lt;&#x2F;p&gt;
&lt;p&gt;Complex Linux binaries like chromium, though, need lots of shared libraries and assume a couple things about the Linux environment that don’t play perfectly with Lambda. &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@marco.luethy?source=post_header_lockup&quot;&gt;Marco Lüthy&lt;&#x2F;a&gt; came to the rescue in early March and figured out how exactly to get the chromium binary working. His &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;adieuadieu&#x2F;serverless-chrome&quot;&gt;serverless-chrome&lt;&#x2F;a&gt; project is now the reference implementation for many projects, including &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;graphcool&#x2F;chromeless&quot;&gt;chromeless&lt;&#x2F;a&gt; and lambdium.&lt;&#x2F;p&gt;
&lt;p&gt;With the ability to run Headless Chrome in AWS Lambda, I was curious if I could get &lt;a href=&quot;http:&#x2F;&#x2F;www.seleniumhq.org&#x2F;projects&#x2F;webdriver&#x2F;&quot;&gt;Selenium&lt;&#x2F;a&gt; tests to run using &lt;a href=&quot;https:&#x2F;&#x2F;sites.google.com&#x2F;a&#x2F;chromium.org&#x2F;chromedriver&#x2F;home&quot;&gt;ChromeDriver&lt;&#x2F;a&gt;. Then, using one of the &lt;a href=&quot;http:&#x2F;&#x2F;docs.seleniumhq.org&#x2F;download&#x2F;&quot;&gt;many Selenium client libraries&lt;&#x2F;a&gt;, I could run pre-existing tests in different languages. After many iterations of copying shared libraries around — I &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;smithclay&#x2F;7852bfdbf565ba6fe0601b0e2d01b56b&quot;&gt;detailed the general recipe for getting untested binaries working on Github&lt;&#x2F;a&gt; — ChromeDriver was able to run and interact with Headless Chrome inside a Lambda function. With ChromeDriver running, I just had to connect a Selenium client library to it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;finally-running-selenium-tests&quot;&gt;Finally: running Selenium tests&lt;&#x2F;h4&gt;
&lt;p&gt;By going through the process above, I was able to get ChromeDriver and Headless Chrome running inside an AWS Lambda function — barely making it under the compressed 50mb size limit for functions (the Chrome binary is around 80% of this).&lt;&#x2F;p&gt;
&lt;p&gt;Surprisingly, running the Selenium tests using the Node.js &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;selenium-webdriver&quot;&gt;selenium-webdriver&lt;&#x2F;a&gt; module ended up being possible with just a few changes to the default options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There are a bunch of Headless Chrome-specific command line options you need to specify.&lt;&#x2F;li&gt;
&lt;li&gt;You have to tell Selenium where to find the Headless Chrome binary (because it’s in AWS Lambda, it’s not in the PATH).
With that done, there was a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;lambdium&#x2F;issues&quot;&gt;mostly working&lt;&#x2F;a&gt; Selenium Webdriver session with Headless Chrome running in AWS Lambda ready to go. The output of a script that visits Google.com and checks the title looks like this in AWS Cloudwatch(again, not much to see because it’s headless after all):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_8R-I5RmiPeFz1Ozy2cKuOQ.png&quot; alt=&quot;Output of a Selenium script running in Headless Chrome on AWS Lambda&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Output of a Selenium script running in Headless Chrome on AWS Lambda&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https:&#x2F;&#x2F;s3.amazonaws.com&#x2F;lambda-tools&#x2F;pricing-calculator.html&quot;&gt;AWS Lambda calculator&lt;&#x2F;a&gt;, if I ran that same function 1,000 more times, my monthly bill (&lt;em&gt;not&lt;&#x2F;em&gt; including the free tier of the service) would come to $0.04. I increased the function memory to 1152mb—running Chrome takes a lot more than the default memory size in AWS Lambda.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;project-setup-and-known-issues&quot;&gt;Project setup and known issues&lt;&#x2F;h4&gt;
&lt;p&gt;The prototype project, which initially tried to design as “framework-less” and as boing as possible (it’s just regular Node.js, a &lt;code&gt;Makefile&lt;&#x2F;code&gt; , and some shell-scripts), is&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;lambdium&quot;&gt;up on Github&lt;&#x2F;a&gt;. It shouldn’t be too hard to port to, say, Python, C# or Java (all AWS Lambda-supported languages). As of early January 2018, I migrated the project to use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;awslabs&#x2F;aws-sam-local&quot;&gt;AWS SAM Local&lt;&#x2F;a&gt; and in March 2018 &lt;a href=&quot;https:&#x2F;&#x2F;serverlessrepo.aws.amazon.com&#x2F;applications&#x2F;arn:aws:serverlessrepo:us-east-1:156280089524:applications~lambdium&quot;&gt;I ported it the AWS Serverless Application Repository (one click install!)&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I’m still working on a new branch that should improve startup times, but that’s a subject for another post.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;will-headless-chrome-shake-things-up-in-the-browser-qa-test-world&quot;&gt;Will Headless Chrome shake things up in the browser QA&#x2F;test world?&lt;&#x2F;h4&gt;
&lt;p&gt;The duo of Headless Chrome and AWS Lambda seems like it has a real possibility to shake up automated browser testing — due in no small part to how cost effective serverless is for low-volume traffic like occasional browser testing in QA. With the generous Lambda free tier, you could probably run low-volume browser tests on AWS infrastructure for free.&lt;&#x2F;p&gt;
&lt;p&gt;Also, there’s no reason why Headless Chrome couldn’t also work on other serverless compute platforms, and there seems to be plenty of interest &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yukinying&#x2F;chrome-headless-browser-docker&quot;&gt;running it in Docker as well&lt;&#x2F;a&gt;. I give it 6 months, max, before someone writes some sort of Kubernetes cluster that runs Headless Chrome at scale.&lt;&#x2F;p&gt;
&lt;p&gt;Think it’s a pretty fun time to be hacking on serverless compute platforms or even containers in general — especially with the recent release of Headless Chrome and the cool things that are going to be built with it.&lt;&#x2F;p&gt;
&lt;p&gt;Pull requests and questions welcome. Also highly recommend you check out the &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@kensoh&#x2F;chromeless-chrominator-chromy-navalia-lambdium-ghostjs-autogcd-ef34bcd26907&quot;&gt;other projects in Ken Soh’s post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>SSH-ing into your AWS Lambda Functions</title>
        <published>2017-05-02T00:00:00+00:00</published>
        <updated>2017-05-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://clay.fyi/blog/ssh-ing-into-aws-lambda-functions/"/>
        <id>https://clay.fyi/blog/ssh-ing-into-aws-lambda-functions/</id>
        
        <content type="html" xml:base="https://clay.fyi/blog/ssh-ing-into-aws-lambda-functions/">&lt;blockquote&gt;
&lt;p&gt;Finally proof that serverless has servers?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I spoke at &lt;a href=&quot;https:&#x2F;&#x2F;dotscale.io&quot;&gt;dotScale&lt;&#x2F;a&gt; in Paris last week &lt;a href=&quot;https:&#x2F;&#x2F;speakerdeck.com&#x2F;smithclay&#x2F;searching-for-the-server-in-serverless&quot;&gt;about some stuff I’ve been working on to learn more about Function-as-a-Service (FaaS) performance&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While my talk was mostly about cold starts, function warming, and understanding the internal architecture of FaaS, lots of people were curious how I was able to SSH into a running AWS Lambda function. This post describes how to turn Lambda functions into a short-lived Linux servers with some help from &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;golang.org&#x2F;x&#x2F;crypto&#x2F;ssh&quot;&gt;Go’s SSH crypto libraries&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;adventures-in-missing-dependencies&quot;&gt;Adventures in missing dependencies&lt;&#x2F;h4&gt;
&lt;p&gt;Many popular FaaS services are built on Linux containers—Azure seems to use IIS voodoo I don’t understand. From an serverless app developer perspective, you write a handler function in a supported language that’s bundled with its dependencies. When a predefined event like an HTTP request occurs, the handler function is invoked inside the container and your code runs (or has an error, or times out).&lt;&#x2F;p&gt;
&lt;p&gt;Because the environment is just a container, you’re allowed to execute binaries using standard language features like &lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;api&#x2F;child_process.html&quot;&gt;exec() in node.js&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;2&#x2F;library&#x2F;subprocess.html&quot;&gt;python subprocess&lt;&#x2F;a&gt;. This is also how open-source projects like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;apex&#x2F;apex&quot;&gt;Apex&lt;&#x2F;a&gt; let developers run Go in AWS Lambda.&lt;&#x2F;p&gt;
&lt;p&gt;With a 12-line lambda function, I tried running the sshd process on a non-privileged port with a host key and configuration I bundled with my the function. This was a bad idea for several reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;AWS Lambda’s container environment is missing several libraries that the ssh daemon requires.&lt;&#x2F;li&gt;
&lt;li&gt;AWS Lambda functions do not seem to allow any inbound port access (yes, even if running in a VPC with a security group that explicitly allows it).&lt;&#x2F;li&gt;
&lt;li&gt;Exotic ssh configuration files are hard to write and filled with weird options.&lt;&#x2F;li&gt;
&lt;li&gt;There’s no easy way to automatically determine the external IP address of a Lambda function.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It was time for some SSH tunneling and copy-and-paste Golang code from Github.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;writing-an-ssh-server-that-creates-a-tunnel-in-go&quot;&gt;Writing an SSH server that creates a tunnel in Go&lt;&#x2F;h4&gt;
&lt;p&gt;Go has some nice high-level SSH libraries for creating servers, clients, and tunnels. Unfortunately, my first &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;jpillora&#x2F;b480fde82bff51a06238&quot;&gt;copy-and-paste attempt&lt;&#x2F;a&gt; had a dependency on an OS feature (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Pseudoterminal&quot;&gt;unix pseudoterminals&lt;&#x2F;a&gt;) that did not exist in the AWS Lambda environment. Digging around a bit more in the Go documentation, I found some libraries designed for interacting with terminals that didn’t have a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kr&#x2F;pty&quot;&gt;dependency on the pty package&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Cobbling it all together, I had a simple go service that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Runs an SSH server capable of executing commands in bash on a non-privileged port.&lt;&#x2F;li&gt;
&lt;li&gt;Creates a tunnel to a remote host (i.e. my laptop via ngrok or an EC2 host running inside my VPC) and requests a port to be opened that forwards to the SSH server running inside the Lambda function.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I then wrote a Lambda function that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Is configured with a 5-minute timeout (the maximum allowed value).&lt;&#x2F;li&gt;
&lt;li&gt;Executes the Go SSH binary I built for 64-bit Linux (an under-appreciated feature of Go development on macs).&lt;&#x2F;li&gt;
&lt;li&gt;Used Lambda environment variables to connect to a specific host and port number (i.e. my laptop via a jump proxy).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With this in place, using the tunnel I can access my go SSH server and run shell commands from another host using a standard SSH client until the Lambda function times out:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;1_LwTTzR9xRfeelnvPBKdVkw.png&quot; alt=&quot;Interacting with the AWS Lambda Environment using faasssh&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Interacting with the AWS Lambda Environment using faasssh&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;this-isn-t-a-good-idea-but-it-was-fun-to-build&quot;&gt;This isn’t a good idea (but it was fun to build)&lt;&#x2F;h4&gt;
&lt;p&gt;I don’t think creating SSH tunnels and running random servers inside AWS Lambda is a particularly good way to take advantage of serverless, but it was fun to learn more about the execution environment by building it. Couple other possibilities I’m thinking of now that there’s a proof on concept:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Running other types of servers in Lambda that allow inbound connections through tunneling.&lt;&#x2F;li&gt;
&lt;li&gt;Integration with a key-management services for SSH authentication (KMS or Vault perhaps?)&lt;&#x2F;li&gt;
&lt;li&gt;Support for concurrent sessions or integration with a TCP load balancer.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The code is up on Github (project name: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smithclay&#x2F;faassh&quot;&gt;faassh&lt;&#x2F;a&gt;). Pull requests and questions welcome.&lt;&#x2F;p&gt;
&lt;p&gt;In the next few weeks, I’ll be at&lt;a href=&quot;http:&#x2F;&#x2F;www.devopsdaysaustin.com&#x2F;&quot;&gt;DevOpsDays Austin&lt;&#x2F;a&gt; and &lt;a href=&quot;http:&#x2F;&#x2F;gluecon.com&#x2F;&quot;&gt;Gluecon&lt;&#x2F;a&gt; speaking more about FaaS if you want to chat in person.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;links&quot;&gt;Links&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;“Searching for the Server in Serverless” talk (dotScale Paris 2017)&lt;&#x2F;li&gt;
&lt;li&gt;“Serverless Measurement Limits” talk (DevOpsDays Austin)&lt;&#x2F;li&gt;
&lt;li&gt;Measuring Cold Starts in AWS Lambda (New Relic Blog Post)&lt;&#x2F;li&gt;
&lt;li&gt;faash source code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
</feed>
