<?xml version="1.0" encoding="UTF-8" standalone="no"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:idx="urn:atom-extension:indexing" xmlns:media="http://search.yahoo.com/mrss/" idx:index="no"><subtitle>tipy na zajímavé články</subtitle>
    <!--
Content-type: Preventing XSRF in IE.

-->
    <generator uri="https://cloud.feedly.com">feedly cloud</generator>
    <id>tag:feedly.com,2013:cloud/feed/https://feedly.com/f/rJIIpYUxxvSQIe3s6CKpJaO8</id>
    <link href="https://feedly.com/f/rJIIpYUxxvSQIe3s6CKpJaO8" rel="self" type="application/rss+xml"/>
    <link href="https://feedly.com/f/rJIIpYUxxvSQIe3s6CKpJaO8?continuation=19d96383ef6:a4ef3:fad010ff" rel="next" type="application/rss+xml"/>
    <title>rarouš.w3b</title>
    <updated>2026-06-21T18:45:50Z</updated>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19eeb80c989:771fcb:d67a104c</id>
        <title type="html">untitled</title>
        <published>2026-06-21T18:45:46Z</published>
        <updated>2026-06-21T18:45:50Z</updated>
        <link href="https://thinkinglabs.io/articles/2026/06/20/beyond-shades-of-conways-law-validation.html" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;article&gt;
&lt;p&gt;Moving beyond hunch and silencing the sceptics. Over the years, Conway’s Law has resurfaced as a guiding principle to organise teams. Yet, many people still hesitate to accept the idea. Ultimately, it remains a hypothesis. Melvin Conway did not provide any proof when publishing the thesis. However, in the meantime, decades later, a good deal of academic research supports the thesis. Ultimately, Harvard and MIT researchers provided robust empirical validation for the hypothesis across the software industry, further confirmed by the CHAOS Report from the Standish Group, reinforcing &lt;em&gt;The Mirroring Principle&lt;/em&gt; as an undeniable organisational reality.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Henderson and Clark observed in 1990 that established firms are unable to adopt architectural innovation because the architectural knowledge is embedded in the communication structures of the organisation, preventing them from adopting new designs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We show that architectural innovations destroy the usefulness of the architectural knowledge of established firms, and that since architectural knowledge tends to become embedded in the structure and information-processing procedures of established organizations, this destruction is difficult for firms to recognize and hard to correct. Architectural innovation therefore presents established organizations with subtle challenges that may have significant competitive implications.&lt;/p&gt;
&lt;p&gt;– &lt;a href="http://www.iot.ntnu.no/innovation/norsi-pims-courses/tushman/Handerson%20&amp;amp;%20Clark%20(1990).pdf"&gt;Architectural Innovation: The Reconfiguration of Existing Product Technologies and the Failure of Established Firms&lt;/a&gt;, Henderson and Clark, 1990&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;“&lt;em&gt;Architectural innovation&lt;/em&gt;” is defined as innovations that change the way product components are linked together, while leaving the core design concepts — and thus the basic knowledge underlying the components — untouched. That does not mean that components remain untouched. Often, architectural innovation is triggered by a change in a component that triggers new interactions and connections with other components.&lt;/p&gt;
&lt;p&gt;The study’s outcome is based on the analysis of 20 years of data (1962-1986) from the photolithographic alignment equipment industry, where four organisations subsequently took over market leadership because a competitor uncovered an architectural innovation that the previous leader was unable to detect due to its organisational structure.&lt;/p&gt;
&lt;p&gt;There has been growing evidence of numerous technical innovations that involved apparent modest changes to existing technology, but that had dramatic competitive consequences. In the mid-1950s, RCA developed a prototype for a portable radio receiver. It utilised technology that RCA mastered well. Yet, they did not pursue the idea. Until Sony, a small, relatively new company, entered the US market with portable radio receivers. RCA remained a follower. The irony was that Sony produced radios during many years using RCA’s licensed technology. The case of Xerox, in the mid-1970s, although the inventor of the plain-paper copier, was, during eight years of strategic errors, unable to compete with competitors offering smaller, more reliable copiers. Xerox lost half of the market. For a more recent example, we look at the German car industry, which had trouble marketing electric cars until Tesla, and later the Chinese car manufacturers took over the market. Meanwhile, Audi closes its Belgian factory that produces the expensive electric SUV because of a market mismatch.&lt;/p&gt;
&lt;p&gt;This inability to adopt architectural innovation happens because an organisation organises itself around the product’s key components that make up the “&lt;em&gt;dominant design&lt;/em&gt;”. The “&lt;em&gt;dominant design&lt;/em&gt;” is characterised by a general acceptance of a particular product architecture. It incorporates a range of basic design choices that are not revisited — think car platforms, or in software, a CRUD design. It results in communication channels between the design teams that reflect the organisation’s knowledge about the critical interactions between these components. Thus, an organisation’s communication channels embody the organisation’s architectural knowledge that is critical to effective design. From this moment on, organisations incline to focus only on “&lt;em&gt;incremental innovation&lt;/em&gt;”, tending to reinforce the competitive positions of established firms as it builds on their core competencies.&lt;/p&gt;
&lt;p&gt;As such, the architectural knowledge is a mirror of the communication structures organised around the product being designed. Somehow, this is the knowledge parallel of Conway’s thesis “&lt;em&gt;[Any] organisation which designs systems is constrained to produce designs which are copies of the communication structures of the organisation&lt;/em&gt;”. If communication structures dictate design (Conway, 1968), they also dictate what an organisation is capable of knowing (Henderson and Clark, 1990).&lt;/p&gt;
&lt;p&gt;Henderson and Clark establish that, given how knowledge is organised in firms, learning about changes in the product architecture is unlikely to happen. It requires explicit management, or even a different organisation. To counter this, the authors suggest cross-functional teams as a possible response to the danger of architectural knowledge becoming embedded within tacit communication lines.&lt;/p&gt;
&lt;p&gt;Recently, I was front-seat observing an organisation unable to run only a simple experiment, which would let them eventually discover a potential innovation, merely because of the structures in place.&lt;/p&gt;
&lt;p&gt;Because the communication channels are “frozen” around the old product architecture, the knowledge is also frozen; the organisation cannot distinguish or adjust to a possible new architecture reality, or even conceive a new design reality. This is Conway’s Law working back over time (more on this in &lt;em&gt;Dynamics: Conway’s Time Component&lt;/em&gt;).&lt;/p&gt;
&lt;h2&gt;The Aircraft Engine/Automotive Validations (2004, 2007)&lt;/h2&gt;
&lt;p&gt;In 2004, Sosa et al. published a study that investigated the design process of a commercial aircraft jet engine, the PW4098, at Pratt &amp;amp; Whitney. They studied how design interfaces in the product architecture map onto communication patterns within the development organisation. More specifically, they explored whether organisational problems exist at the boundaries of the components inside the jet engine, i.e. whether a misalignment exists between the design team boundaries and system component interfaces.&lt;/p&gt;
&lt;p&gt;According to Henderson and Clarke, architectural knowledge is embedded in the communication structures of organisations; therefore, organisations need to understand how they manage embedded knowledge. This is especially relevant when designing new products. In this context, system design interfaces are the primary sources of team interdependence, or at least, they should be. Meaning, when a specific team designs their own component with an interface to a neighboring component, there should be a corresponding technical interaction between the two respective teams. These are the expected cases. On the other hand, unmatched design interfaces and unmatched team interactions are unexpected. As it seems, these cases were largely ignored in previous research.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;However, we believe that considering these cases is important because their existence would indicate that not all known product-related interdependences are addressed by direct technical communication, and that technical communication (where not expected) may uncover undocumented product-related interdependences.&lt;/p&gt;
&lt;p&gt;– &lt;a href="http://users.ece.utexas.edu/~perry/education/382v-s08/papers/sosa.pdf"&gt;The Misalignment of Product Architecture and Organizational Structure in Complex Product Development&lt;/a&gt;, Sosa et al, 2004&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, the study concludes that …&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… we provide empirical evidence that product ambiguity exists, and it is more likely to be present across organizational and system boundaries&lt;/p&gt;
&lt;p&gt;– &lt;a href="http://users.ece.utexas.edu/~perry/education/382v-s08/papers/sosa.pdf"&gt;The Misalignment of Product Architecture and Organizational Structure in Complex Product Development&lt;/a&gt;, Sosa et al, 2004&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Meaning, that there is a great likelihood that some of the system component interfaces are not covered by team interactions, which can lead to major setbacks involving significant rework that can be extremely costly. The paper, exactly, refers to two major setbacks during the design of that specific jet engine.&lt;/p&gt;
&lt;p&gt;What they are essentially saying here is: &lt;strong&gt;If there is a misalignment between the organisation and the product architecture, there will be problems&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In 2007, Gokpinar et al. conducted a study examining the design of cars in the automotive industry.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[…] a key challenge for product development organizations is matching the organization to the product being developed. This involves two fundamental problems: (1) How to assign people to the parts and subsystems that make up the product, and (2) How to ensure that people communicate/collaborate effectively in the performance of design tasks.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=5553ae01b690837f755cd2c91fa6a4b7fdec6345"&gt;The Impact of Misalignment of Organization Structure and Product Architecture on Quality in Complex Product Development&lt;/a&gt;, Gokpinar et al, 2007&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The research defined a new metric &lt;em&gt;coordination deficit&lt;/em&gt;, which quantifies mismatches between product architecture and organisation structure, and investigated the effect of coordination deficit on product quality.&lt;/p&gt;
&lt;p&gt;They concluded that mismatches between product architecture and organisational structure are positively associated with quality problems.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our results suggest that misalignment of the design organization with the product architecture negatively affects product quality”&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=5553ae01b690837f755cd2c91fa6a4b7fdec6345"&gt;The Impact of Misalignment of Organization Structure and Product Architecture on Quality in Complex Product Development&lt;/a&gt;, Gokpinar et al, 2007&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once more, &lt;strong&gt;if there is a misalignment between organisation and product architecture, there will be problems&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The Micro Software Validation (2006)&lt;/h2&gt;
&lt;p&gt;In 2006, Cataldo et al. investigated the consequences of congruence on the speed and efficiency of software development work inside a single software project. They investigated the impact of misalignment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is particularly important in product development organizations which organize themselves around their products’ architectures because the main components of their products define the organization’s key sub-tasks.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://www.cs.drexel.edu/~yfcai/CS680/Readings/Week8/Identification%20of%20Coordination%20Requirements.pdf"&gt;Identification of Coordination Requirements: Implications for the Design of Collaboration and Awareness Tools&lt;/a&gt;, Cataldo et al, 2006&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matters because, as architectural knowledge is embedded in the communication structures of organisations (Henderson and Clark, 1990), any minor changes to the product architecture can disrupt the organisation’s ability to coordinate effectively.&lt;/p&gt;
&lt;p&gt;Ultimately, Cataldo et al. found that tasks were completed more rapidly when the patterns of communication between team members were congruent with the patterns of interdependency between components (more on &lt;em&gt;congruence&lt;/em&gt; in &lt;em&gt;Mechanics: The Mathematical &amp;amp; Geometrical Shades&lt;/em&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Congruence&lt;/strong&gt; between coordination requirements and coordination activities shortened development time.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://www.cs.drexel.edu/~yfcai/CS680/Readings/Week8/Identification%20of%20Coordination%20Requirements.pdf"&gt;Identification of Coordination Requirements: Implications for the Design of Collaboration and Awareness Tools&lt;/a&gt;, Cataldo et al, 2006&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Fundamentally, the research indicates, &lt;strong&gt;if teams are aligned with the components they work on, teams are more efficient&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The Macro Software Validation (2012)&lt;/h2&gt;
&lt;p&gt;In 2012, we had the first empirical evidence for the software industry provided by the research paper &lt;a href="https://www.hbs.edu/ris/Publication%20Files/08-039_1861e507-1dc1-4602-85b8-90d71559d85b.pdf"&gt;Exploring the Duality between Product and Organizational Architectures: A Test of the “Mirroring” Hypothesis&lt;/a&gt; from Baldwin et al.&lt;/p&gt;
&lt;p&gt;One of their findings was: software products tend to mirror the structure of the organisation in which they are developed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We find strong evidence to support the hypothesis that a product’s architecture tends to mirror the structure of the organization in which it is developed.&lt;/p&gt;
&lt;p&gt;– Exploring the Duality between Product and Organizational Architectures: A Test of the “Mirroring” Hypothesis, Baldwin, MacCormack and Rusnak, 2012&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Conway's Law Sketchplanations" src="https://thinkinglabs.io/images/shades-of-conways-law/sketchplanations-conways-law.jpg"&gt;&lt;/p&gt;
&lt;center&gt;&lt;i&gt;Source: Sketchplanations &lt;a href="https://sketchplanations.com/conways-law"&gt;https://sketchplanations.com/conways-law&lt;/a&gt;&lt;/i&gt;&lt;/center&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;To validate this hypothesis, they compared open-source with closed-source software. Based on the idea that the open-source communities are loosely coupled organisations by design because contributors have diverse goals, belong to different organisations, work in different geographical locations and time zones, and have no formal authority to guide development activities. Whereas closed-source organisations are more tightly-coupled, as they consist of smaller, co-located teams that share common goals, and are dedicated full-time to products and have formal decision-making authority to guide development.&lt;/p&gt;
&lt;p&gt;The study’s approach focused on comparing the architectures of open-source and closed-source software products that have similar functions and are of a similar size. They identified five software categories: financial management, word processing, spreadsheets, operating systems and databases. For each category, they selected an open-source and a closed-source software product.&lt;/p&gt;
&lt;p&gt;Because commercial firms are somehow reluctant to share their code base, especially for research comparing with “free” equivalents, researchers did not always have a closed-source codebase at their disposal. When commercial options were unavailable, they turned to formerly closed-source products that had been open-sourced. In those instances, they analysed the first open-source version.&lt;/p&gt;
&lt;p&gt;So they ended up comparing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For financial management: GnuCash vs MyBooks&lt;/li&gt;
&lt;li&gt;For word processing: Abiword vs StarWriter&lt;/li&gt;
&lt;li&gt;For spreadsheets: Gnumeric vs StarCalc&lt;/li&gt;
&lt;li&gt;For operating systems: Linux vs Solaris&lt;/li&gt;
&lt;li&gt;For databases: MySQL vs Berkeley DB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The paper primarily explores whether organisations with distinctly different forms develop products with distinctly different architectures.&lt;/p&gt;
&lt;p&gt;The outcome of the study was that &lt;strong&gt;loosely-coupled organisations, such as open-source, tend to develop products with a more modular architecture&lt;/strong&gt; than tightly-coupled organisations such as commercial firms. This confirms Parnas’ information hiding concept (Parnas 1972), which allows individual, isolated contributors to work independently. It also reaffirms Henderson and Clark’s observation. Loosely coupled, open-source organisations prevent architectural knowledge from becoming a central, siloed trap. They adopt cross-functional, loosely coupled teams to democratise knowledge. Tightly-coupled organisations, on the other hand, stumble when faced with innovations, because knowledge is tightly frozen, siloed, in the structures of the organisation.&lt;/p&gt;
&lt;h2&gt;The Industry Reality Check (1995-2020)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;We know why projects fail, we know how to prevent their failure – so why do they still fail?&lt;/p&gt;
&lt;p&gt;– Cobb’s Paradox, Martin Cobb, CIO for the Secretariat of Treasury Board of Canada, CHAOS University, 1995, published in &lt;a href="https://www.inf.ufpr.br/urban/2019-1_205_e_220/205e220_Ler_ver_para_complementar/StandishGroup__UnfinishedVoyages-I.pdf"&gt;Unfinished Voyages I&lt;/a&gt;, Standish Group, 1995&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When organisations keep treating software products as mere, rigid projects, the resulting architecture and outcomes for users will be notably poor, leading to surprising failures.&lt;/p&gt;
&lt;p&gt;From 1994 until 2020, the Standish Group published the CHAOS Report, a study on the success, failure, and challenge rates of IT projects.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Challenged&lt;/th&gt;
&lt;th&gt;Cancelled&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1994&lt;/td&gt;
&lt;td&gt;16%&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2000&lt;/td&gt;
&lt;td&gt;28%&lt;/td&gt;
&lt;td&gt;49%&lt;/td&gt;
&lt;td&gt;23%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2002&lt;/td&gt;
&lt;td&gt;34%&lt;/td&gt;
&lt;td&gt;51%&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2004&lt;/td&gt;
&lt;td&gt;29%&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2006&lt;/td&gt;
&lt;td&gt;35%&lt;/td&gt;
&lt;td&gt;46%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;32%&lt;/td&gt;
&lt;td&gt;44%&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2011&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;37%&lt;/td&gt;
&lt;td&gt;46%&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;41%&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;td&gt;47%&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;td&gt;45%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;– source: CHAOS Reports 1995, 2009, 2015, 2020, Standish Group.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Although this may look baffling at first, this stagnation blindsides no experienced practitioners. The numbers reveal a flatline trend. Despite agile and DevOps, the success rate is capped at approximately 30%.&lt;/p&gt;
&lt;p&gt;Till 2015, the Standish Group focused mainly on classic project management requirements to measure success, such as well-defined upfront requirements, proper planning, skilled project managers, as well as user involvement and project sponsor. Success was defined in classic PMI terms, the &lt;a href="https://en.wikipedia.org/wiki/Project_management_triangle"&gt;Iron Triangle&lt;/a&gt;, i.e. on time, on budget, with all features as initially specified. Then, in 2015, a first significant shift happened. The success resolution definition changed. It was still on time and on budget, however, with satisfactory results. All requirements no longer have to be implemented, since data shows that some projects meet the Iron Triangle yet do not return value. In the end, satisfaction and value are greater when fewer features are delivered, but meeting obvious user needs. This is aligned with Lean principles, as well as another Standish Group study revealing &lt;a href="https://www.mountaingoatsoftware.com/blog/are-64-of-features-really-rarely-or-never-used"&gt;that 64% of delivered features are rarely used&lt;/a&gt;. Later confirmed by the 2019 &lt;a href="https://www.pendo.io/resources/the-2019-feature-adoption-report/"&gt;Pendo Feature Adoption Study&lt;/a&gt;, revealing 80% of features see low to zero adoption.&lt;/p&gt;
&lt;p&gt;By 2020, the Standish Group had pivoted and reported an astonishing outcome that contradicts their early recommendations. Stop treating software products as a project because, by definition, projects have a start and an end, but products do &lt;em&gt;not&lt;/em&gt;. They explicitly stated that &lt;a href="https://medium.com/leadership-and-agility/project-managers-fail-to-help-software-projects-standish-group-chaos-2020-e65d803e99f2"&gt;project managers harm software products&lt;/a&gt; and increase the failure rate. Specifically, the report demonstrates that project managers often generate more paperwork, resulting in slower decision-making that inevitably slows down feedback. While, once more, not surprising for experienced practitioners — it was already established long before, and brought to the public in 2017 with the &lt;a href="https://www.allankelly.net/archives/4796/what-ever-happened-to-noprojects-post-projects/"&gt;#NoProjects movement&lt;/a&gt; — the Standish Group brought it to the masses.&lt;/p&gt;
&lt;p&gt;Ultimately, the Standish Group concluded that project success is defined by &lt;em&gt;Good Sponsor&lt;/em&gt;, &lt;em&gt;Good Team&lt;/em&gt;, &lt;em&gt;Good Place&lt;/em&gt;, and &lt;em&gt;Short Decision Latency&lt;/em&gt;. To a large extent, this aligns with the 2001 findings from the book &lt;a href="https://app.thestorygraph.com/books/1771dbe4-16a3-4316-83a1-ed2b21984bc9"&gt;Good to Great&lt;/a&gt;. Organisations become great when they have &lt;em&gt;Level 5 Leadership&lt;/em&gt; (Good Sponsor) to build enduring greatness through a paradoxical blend of personal humility and professional will. They focus on &lt;em&gt;First Who … Then What&lt;/em&gt; (Good Team), first securing the right people before figuring out where to drive it, before vision, before strategy, before tactics, before organisational structure, before technology. Lastly, they &lt;em&gt;Confront the Brutal Facts of Reality&lt;/em&gt; (Good Place), by fostering a climate where the truth is heard.&lt;/p&gt;
&lt;p&gt;These crucial prerequisites enable &lt;em&gt;Short Decision Latency&lt;/em&gt;, allowing information to circulate rapidly. However, low decision latency is entirely dependent on communication paths. Since Conway’s Law dictates that systems reflect the organisation’s communication structures, high-latency organisations stuck in silos are unable to produce high-velocity, decoupled system designs. This architectural congestion hampers communication and impedes overall success.&lt;/p&gt;
&lt;p&gt;Ultimately, the CHAOS Report recommends moving away from “projects” and adopting Infinite Flow, which Lean has been promoting since the 1980’s. Somehow, this is the business world finally accepting what Baldwin et al. proved in 2012: tightly coupled organisations produce tightly coupled system architectures that work against flow, while loosely-coupled organisations thrive because they produce loosely coupled system architectures that enable flow, short time to markets, and consequently accelerated feedback to meet user expectations better. The 2020 CHAOS Report &lt;em&gt;Good Place&lt;/em&gt; — where truth is heard to reduce Decision Latency — is exactly the type of environment needed to break the “frozen”, siloed, tacit communication lines Henderson and Clark blamed for corporate blindness.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;To conclude, the tech industry spent decades trying to fix management processes, only to realise they were fighting a losing battle against the structural gravity of Conway’s Law and its implications on innovation and market leadership. Mediocrity is the default. Success is optional. Just saying …&lt;/p&gt;
&lt;h2&gt;The Series: Navigating the Shades&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://thinkinglabs.io/articles/2026/04/24/beyond-the-shades-of-conways-law.html"&gt;Beyond the Shades of Conway’s Law series&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thinkinglabs.io/articles/2026/06/07/beyond-shades-of-conways-law-foundations.html"&gt;Foundations: The Origin &amp;amp; The Mirroring Principle&lt;/a&gt; - How the worlds of organisation and product design observed the same thesis independently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validation: The Research &amp;amp; Reality Check&lt;/strong&gt; - Moving beyond the “hunch”, how researchers proved the Law in different industries, but especially in software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mechanics: The Mathematical &amp;amp; Geometrical Shades&lt;/strong&gt; - The geometry of design: from mathematical isomorphism, homomorphism, congruence to compatibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strategy: Reversing the Law&lt;/strong&gt; - How the system ultimately forces the organisation to change versus deliberately changing the organisation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale: Conway’s Corollary&lt;/strong&gt; - The required organisational flexibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamics: Conway’s Time Component&lt;/strong&gt; - The “Engineer Half Life” and why architecture is “sticky” long after teams change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conclusion: The Different Lenses&lt;/strong&gt; - A concluding look at how we perceive organisations and their systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bibliography&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.melconway.com/Home/Committees_Paper.html"&gt;How Do Committees Invent?&lt;/a&gt;, Melvin Conway, 1968&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.win.tue.nl/~wstomv/edu/2ip30/references/criteria_for_modularization.pdf"&gt;On the Criteria To Be Used in Decomposing Systems into Modules&lt;/a&gt;, Parnas, 1972&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.iot.ntnu.no/innovation/norsi-pims-courses/tushman/Handerson%20&amp;amp;%20Clark%20(1990).pdf"&gt;Architectural Innovation: The Reconfiguration of Existing Product Technologies and the Failure of Established Firms&lt;/a&gt;, Henderson and Clark, 1990&lt;/li&gt;
&lt;li&gt;&lt;a href="https://personal.utdallas.edu/~chung/SYSM6309/chaos_report.pdf"&gt;CHAOS Report 1995&lt;/a&gt;, Standish Group, 1995&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.inf.ufpr.br/urban/2019-1_205_e_220/205e220_Ler_ver_para_complementar/StandishGroup__UnfinishedVoyages-I.pdf"&gt;Unfinished Voyages I&lt;/a&gt;, Standish Group, 1995&lt;/li&gt;
&lt;li&gt;&lt;a href="https://app.thestorygraph.com/books/1771dbe4-16a3-4316-83a1-ed2b21984bc9"&gt;Good to Great&lt;/a&gt;, Jim Collins, 2001&lt;/li&gt;
&lt;li&gt;&lt;a href="http://users.ece.utexas.edu/~perry/education/382v-s08/papers/sosa.pdf"&gt;The Misalignment of Product Architecture and Organizational Structure in Complex Product Development&lt;/a&gt;, Sosa et al, 2004&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cs.drexel.edu/~yfcai/CS680/Readings/Week8/Identification%20of%20Coordination%20Requirements.pdf"&gt;Identification of Coordination Requirements: Implications for the Design of Collaboration and Awareness Tools&lt;/a&gt;, Cataldo 2006&lt;/li&gt;
&lt;li&gt;&lt;a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=5553ae01b690837f755cd2c91fa6a4b7fdec6345"&gt;The Impact of Misalignment of Organization Structure and Product Architecture on Quality in Complex Product Development&lt;/a&gt;, Gokpinar et al, 2007&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.classes.cs.uchicago.edu/archive/2014/fall/51210-1/required.reading/Standish.Group.Chaos.2009.pdf"&gt;CHAOS Report 2009&lt;/a&gt;, Standish Group, 2009&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hbs.edu/ris/Publication%20Files/08-039_1861e507-1dc1-4602-85b8-90d71559d85b.pdf"&gt;Exploring the Duality between Product and Organizational Architecture: A Test of the “Mirroring” Hypothesis&lt;/a&gt;, Baldwin, MacCormack, Rusnak, 2012&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdn1-public.infotech.com/agile/CHAOSReport2015-Final.pdf"&gt;CHAOS Report 2015&lt;/a&gt;, Standish Group, 2015&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/articles/standish-chaos-2015/"&gt;Standish Group 2015 Chaos Report - Q&amp;amp;A with Jennifer Lynch&lt;/a&gt;, InnoQ, 2015&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mountaingoatsoftware.com/blog/are-64-of-features-really-rarely-or-never-used"&gt;Are 64% of Features Really Rarely or Never Used?&lt;/a&gt;, Mike Cohn, 2016&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pendo.io/resources/the-2019-feature-adoption-report/"&gt;The 2019 Feature Adoption Report&lt;/a&gt;, Pendo, 2019&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thestory.is/en/journal/chaos-report/"&gt;Chaos Report — why this study about IT project management is so unique&lt;/a&gt; about the CHAOS Report 2020, Radek, The Story, 2024&lt;/li&gt;
&lt;/ul&gt;
&lt;/article&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://thinkinglabs.io/images/shades-of-conways-law/sketchplanations-conways-law.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://thinkinglabs.io/feed.xml</id>
            <title type="html">ThinkingLabs:: Thierry de Pauw</title>
            <link href="https://thinkinglabs.io" rel="alternate" type="text/html"/>
            <updated>2026-06-21T18:45:50Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ec6e6229f:633c44:5df5eb75</id>
        <title type="html">untitled</title>
        <published>2026-06-14T16:10:31Z</published>
        <updated>2026-06-14T16:10:37Z</updated>
        <link href="https://www.funnyshit.com.au/the_plan.html" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;h1&gt;The Plan&lt;/h1&gt;
&lt;p&gt;In the beginning was the Plan.&lt;/p&gt;
&lt;p&gt;And then came the Assumptions.&lt;/p&gt;
&lt;p&gt;And the Assumptions were without form.&lt;/p&gt;
&lt;p&gt;And darkness was upon the face of the Workers.&lt;/p&gt;
&lt;p&gt;And they spoke among themselves, saying, "It is a crock of shit, and it
stinketh."&lt;/p&gt;
&lt;p&gt; And the workers went unto their Supervisors and said, "It is a pail of
dung, and none may abide the odour thereof."&lt;/p&gt;
&lt;p&gt; And the Supervisors went unto their Managers, saying, "It is a container
of excrement, and it is very strong, such that none may abide by it."&lt;/p&gt;
&lt;p&gt; And the Managers went unto their Directors, saying, "It is a vessel of
fertiliser, and none may abide its strength."&lt;/p&gt;
&lt;p&gt; And the Directors spoke amongst themselves, saying one to another, "It
contains that which aids plant growth, and it is very strong."&lt;/p&gt;
&lt;p&gt;And the Directors then went onto the Vice Presidents, saying unto them, "It
promotes growth and is very powerful."&lt;/p&gt;
&lt;p&gt;And the Vice Presidents went unto the President, saying unto him, "This
new plan will actively promote the growth and vigour of the company; with powerful
effects."&lt;/p&gt;
&lt;p&gt;And the President looked upon the Plan, and saw that it was good.&lt;/p&gt;
&lt;p&gt;And the Plan became Policy.&lt;/p&gt;
&lt;p&gt;This is How Shit Happens.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.funnyshit.com.au/"&gt;→ Check out more Funny Shit&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ec4cf4358:49c447:b0e60e29</id>
        <title type="html">untitled</title>
        <published>2026-06-14T06:26:18Z</published>
        <updated>2026-06-14T06:26:22Z</updated>
        <link href="https://www.psychologytoday.com/gb/blog/creating-successful-change/202606/why-its-so-hard-to-know-if-youre-really-making-progress" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;
&lt;p&gt;Think about the last time you tracked your progress on a personal goal. Maybe you were trying to exercise more frequently, be more patient with a spouse or partner, or learn a new skill. Chances are, you had a rough sense of how it was going. And chances are that sense was more &lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/optimism" title="Psychology Today looks at optimistic"&gt;optimistic&lt;/a&gt; than the evidence warranted.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is not a character flaw. It is one of the most reliable findings in behavioral science: We are systematically poor judges of our own progress, and during periods of active change, we tend to get worse.&lt;/p&gt;
&lt;p&gt;One reason is what psychologists Terence Mitchell and Leigh Thompson at the University of Washington termed rosy retrospection. This is our consistent tendency to remember past experiences more favorably than we rated them in the moment. When we look back over a month of trying to exercise more or sleep differently, our &lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/memory" title="Psychology Today looks at memory"&gt;memory&lt;/a&gt; selectively surfaces the days we succeeded. The days we failed are there, but they are not as memorable. We feel we’ve been doing better than we really have.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A second explanation is subtler. &lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/motivation" title="Psychology Today looks at Motivation"&gt;Motivation&lt;/a&gt; isn’t a fixed state; it shifts gradually, often well before behavior changes in any visible way. The enthusiasm that felt solid in January has usually started to erode by March. But because the erosion is incremental, we rarely catch it in real time. We trust that how we feel today is roughly how we’ve been feeling all along. It almost never is.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Together, these two patterns create a specific failure mode: We assume the feedback loop is running when it isn’t. We trust that because we know our own goals and intentions, we know how we’re actually doing. But intention and behavior are not the same thing. How we feel about a change is not the same as how we’re enacting it. And until we close that gap with something more rigorous than a gut check, we’re optimizing for a version of our progress that may not exist.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In researching what separates change successes from failures at organizations for my book, my coauthors and I found that leaders designing change were consistently more confident about employee progress on change than the employees’ actual behavior or stated experience supported. They trusted their read on the room. The data, when they gathered it, told a different story. The wider the gap, the more likely the change failure.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The same gap operates in personal change. And the same remedy applies: Use measurement, not instinct.&lt;/p&gt;
&lt;p&gt;This doesn’t require an elaborate system. Research by James Pennebaker at the University of Texas at Austin shows that the act of writing specifically about your experience—not vague journaling, but naming exactly what you did, how it felt, and where the friction was—produces measurable improvements in self-understanding and in the likelihood of follow-through. Writing forces the brain to translate impression into information. The gap between what you felt and what actually happened becomes clearer.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Social accountability produces a related effect. A study by psychologist Gail Matthews at Dominican University found that people who wrote down their goals and reported progress to a friend were 42 percent more likely to achieve those goals than people who kept them private. Part of what accountability does is discipline the story you tell yourself. You can’t smooth over the gaps as easily when someone else is watching for them.&lt;/p&gt;

&lt;p&gt;Both interventions do the same thing: They replace vague impressions with actual information. When people begin gathering that information honestly, they usually discover not only the gaps in their efforts, but also the progress they had stopped noticing.&lt;/p&gt;
&lt;p&gt;Teresa Amabile, a psychologist at Harvard Business School, spent years studying what helps people stay motivated during sustained effort. Her research, drawn from the daily diaries of 238 professionals tracked over several months, produced a counterintuitive finding: The single biggest day-to-day motivator wasn’t recognition from a manager or a meaningful reward. It was the perception of making progress on work that mattered. Small wins, when noticed specifically, produced outsized improvements in engagement and follow-through. The operative word is “noticed.” Progress that goes unobserved doesn’t produce the same effect. It needs to be named to become fuel.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is what honest self-assessment makes possible. When you journal about your week in specific detail, you often discover you accomplished much less than your vague, optimistic impression suggested—and you'll have a record you can point to as concrete evidence. That specificity matters more than it might seem. Ayelet Fishbach at the University of Chicago Booth School of Business has shown across repeated experiments that concrete progress feedback strengthens goal commitment more than encouragement does. What keeps people going isn’t being told they’re doing well. It’s seeing, with some precision, that they truly are.&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;Motivation Essential Reads&lt;/p&gt;

&lt;/div&gt;
&lt;p&gt;The story you’re telling yourself about your progress probably has some blind spots. But it may also be overlooking real growth that felt too small or too ordinary to count. Learning to see your progress more clearly, without judgment, isn’t self-&lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/punishment" title="Psychology Today looks at punishment"&gt;punishment&lt;/a&gt;. It’s an invaluable form of self-support.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="http://www.googletagmanager.com/ns.html?id=GTM-NW5PKS"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19eb821c9a4:1c44b1:ac72f017</id>
        <title type="html">untitled</title>
        <published>2026-06-11T19:21:22Z</published>
        <updated>2026-06-11T19:21:25Z</updated>
        <link href="https://mixpanel.com/blog/ai-adoption-gap/" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Chances are you used an AI tool before 9 a.m. today. Chances are most of your users didn't.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;It's a simple contrast, but Debbie McMahon, VP of Product at &lt;a rel="noreferrer noopener" href="https://www.loveholidays.ie/"&gt;Loveholidays&lt;/a&gt;, built an entire &lt;a href="https://mixpanel.com/mxp/mxp-2026"&gt;MXP London&lt;/a&gt; session around it and what it means for every product decision. She called it the AI adoption gap, and she was refreshingly clear-eyed about the ways it had distorted her own judgment.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;The AI adoption gap and the 0.04%&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;An infographic Debbie shared on stage breaks the world's relationship with AI into four tiers:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;span&gt;&lt;li&gt;&lt;strong&gt;84%&lt;/strong&gt; of people have never used AI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;16%&lt;/strong&gt; have tried a free chatbot&lt;/li&gt;&lt;li&gt;&lt;strong&gt;0.3%&lt;/strong&gt; pay for AI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;0.04%&lt;/strong&gt; are actively building with it&lt;/li&gt;&lt;/span&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;That last group is the one that fills rooms like MXP London. And Debbie's point is that this creates a fundamental disconnect. The people designing AI-powered products are a statistical anomaly relative to the people they're designing for.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;She made that concrete with a story about her friend Agnes. Agnes is a primary school teacher who spends her days helping children from difficult family circumstances settle into school. She's smart, busy, and has a free ChatGPT account she’s never used. Why? Her school doesn't allow AI tools on work devices. The budget isn't there for anything paid. And nobody on staff is confident enough with the technology to know where to start, even if the first two problems disappeared tomorrow.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Agnes is not an edge case. She's a typical Loveholidays customer, and she's who Debbie asked the MXP London audience to hold in mind.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;



&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span&gt;“&lt;/span&gt;
&lt;p&gt;We're living in a bubble. We're the 0.04% and our daily experience of AI has essentially nothing to do with our users’ reality.&lt;span&gt;”&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Debbie McMahon&lt;/p&gt;
&lt;p&gt;VP of Product, Loveholidays&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;em&gt;For a look at how AI engagement is trending across real products, Mixpanel's &lt;a rel="noreferrer noopener" href="https://mixpanel.com/blog/ai-benchmarks-2026/"&gt;2026 AI benchmarks&lt;/a&gt; draw on nearly 290 billion AI events across 2.6 billion devices, and the picture is more nuanced than adoption headlines suggest.&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Three ways the bubble distorts your decisions&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie named three patterns she sees in herself and across the industry.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;Over-prioritizing AI features.&lt;/strong&gt; "Have you put something out there just because it was easy to do?" She raised her own hand. The pressure is real: boards want AI in the roadmap, investors want AI in the roadmap, competitors are shipping. And the tooling makes it fast to build something. But fast to build isn't the same as worth building.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;Underestimating friction.&lt;/strong&gt; Most people are forming their views on AI right now without the benefit of product communities or conference sessions to help them process it. That's not a knock on users. They're figuring out whether to trust the technology at the same moment they're being asked to use it. Dropping a disclaimer like "generated by AI, so may not be accurate" into a set of results doesn't help someone who's already uncertain about the output.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;Asking users to change behavior.&lt;/strong&gt; Behavior change is extraordinarily difficult even when people want to change. For users with an ingrained habit and no clear reason to do anything differently, it's close to impossible. The customers who are confused or skeptical about AI aren't going to reroute their entire search behavior for a feature they weren't asking for. If what they want is a keyword search and a filter, that's what they'll reach for.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;

&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Go deeper&lt;/p&gt;
&lt;p&gt;See whether your AI features are delivering value, or just getting used:&lt;/p&gt;


&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;What the data actually showed&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Two case studies from Loveholidays that Debbie shared with the kind of candor that makes MXP sessions worth attending.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;The room grouping problem.&lt;/strong&gt; Hotel listings at Loveholidays had accumulated a long tail of duplicated, poorly named room types, a messy, years-old problem that had resisted every previous attempt to fix it. When a team went after it with AI, they went in with confidence. What they found was that the problem was deterministic: two rooms are either the same or they're not, and AI isn't built for that kind of precision. The model grouped rooms in ways that didn't hold up to scrutiny, renamed options customers recognized, and quietly dropped listings that people had been booking. The output looked coherent, but it wasn't. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;In Debbie’s own words: "Don't put AI on top of a broken thing. Fix the actual problem."&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;The Q&amp;amp;A tool.&lt;/strong&gt; Another team shipped an LLM-based Q&amp;amp;A feature into the pre-booking journey, built around the hypothesis that customers comparing hotels would want to ask questions. The feature got used, a lot, actually. But when the team looked at who was using it, the data didn't match the hypothesis at all.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The users weren't pre-booking customers weighing their options. They were people who had already made a booking, navigating a long and confusing post-booking experience to find basic information about their upcoming trip. And the questions they were asking weren't the ones the team had imagined. The top two were whether their room came with a kettle and whether it had an iron. Third most common: the wi-fi password.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;In other words, customers who needed information Loveholidays already had were threading through a complex AI-powered feature just to ask if they needed to pack a travel iron. The team proposed moving the tool into the post-booking journey where the actual demand was. Debbie shut it down, and the reasoning is worth sitting with.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;



&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span&gt;“&lt;/span&gt;
&lt;p&gt;Just because it does answer those users’ questions doesn't mean that this is the right solution. We have that information. It’s a fact. We just need to give it to them in a form that they need it in.&lt;span&gt;”&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Debbie McMahon&lt;/p&gt;
&lt;p&gt;VP of Product, Loveholidays&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The post-booking journey got fixed instead, and the Q&amp;amp;A tool got pulled. The story doesn't end there: the team is relaunching the feature with a genuinely different hypothesis behind it. Since that original experiment, Loveholidays launched its own reviews platform, which changes what a Q&amp;amp;A tool can meaningfully do and who it's actually for. There's a real use case now tied to a specific user group, and that's what "not now" looks like when it's used well.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;

&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Go deeper&lt;/p&gt;
&lt;p&gt;Further reading on building and measuring product adoption in the AI era:&lt;/p&gt;


&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Building different journeys for different people&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Loveholidays went to their customers directly and asked how they'd feel about AI-mediated booking experiences. The words that came back included "anxiety," "I trust filters more," and "I prefer clicking." There was also genuine enthusiasm from a portion of respondents. But the skepticism was real, present across a significant share of the audience, and Debbie didn't try to explain it away.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;a rel="noreferrer noopener" href="https://www.gartner.com/en/newsroom/press-releases/2025-09-03-gartner-survey-finds-53-percent-of-consumers-distrust-ai-powered-search-results0"&gt;53% of consumers distrust AI-powered search results&lt;/a&gt;, according to a Gartner survey published in September 2025. Debbie cited a similar figure on stage and her read was plain: "I can't ignore that. That’s the reality for my customers, and that's where we're starting from."&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Her answer is to segment more precisely. AI comfort and interest now sit alongside demographics, holiday type, and referral route as a dimension Debbie's team uses to personalize the experience. Design one AI-mediated journey for everyone and you either hold back the users who want to explore new things or push unwanted change onto the customers who aren't ready for it. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie's dad started using email about a decade after she did. She uses that as a reference point: adoption follows its own timeline, and you can't compress it by shipping a feature and calling it done.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;The playground: A structural answer&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Recognizing a problem clearly is useful. Building an organizational system to address it at scale is something else. That's what separates this MXP session from a general mindset piece.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie's team built what she calls "out the bubble," an internal prototyping environment that anyone at Loveholidays with access to Codex or Claude Code can use, with no prior technical setup required. It's pre-integrated with the company's design system, data mesh, live pricing, and real images, which matters more than it might sound. When you put a prototype in front of a customer in a travel context, the first thing they notice is whether the hotel price looks real. This system removes that distraction so the feedback is about the experience itself.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The pipeline works in three stages:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;span&gt;&lt;li&gt;Anyone at the company builds a prototype, sometimes in 10 minutes, sometimes a couple of hours, and pushes it to an internal testing environment for NMD review.&lt;/li&gt;&lt;li&gt;Prototypes that clear that bar are automatically routed to Usertesting.com, with the builder setting up the user panel themselves, no dependency on a separate research team.&lt;/li&gt;&lt;li&gt;The ones that pass user testing move to a small live test, pushed out via socials, CRM, or the homepage to a defined customer segment.&lt;/li&gt;&lt;/span&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The important shift isn't the speed, though. It's that the people with the best product ideas are no longer gated by their ability to build prototypes. The system makes that capability available to the whole company. Fewer ideas get duplicated. More of them get in front of real customers. And when something earns the full investment of a real build, there's actual evidence behind the decision.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Start with the problem. The tools will follow.&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie closed by revisiting the Henry Ford quote, carefully, because she's not entirely sure Ford said it. But the argument holds regardless. The car achieved adoption because Ford understood what his customers actually needed. He built a faster horse, not a different animal entirely.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;



&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span&gt;“&lt;/span&gt;
&lt;p&gt;AI doesn't let us skip understanding what our users need. If you want people to adopt things, you have to start with their problem.&lt;span&gt;”&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Debbie McMahon&lt;/p&gt;
&lt;p&gt;VP of Product, Loveholidays&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The tools have changed. The product job hasn't. What AI does change is how big your answers can be once you've understood the question clearly. The potential is genuinely extraordinary, but only after you've done the harder work of understanding what your users need and why they'd change.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Remember Agnes from the opening of this piece? The primary school teacher who supports children through difficult family circumstances, has a ChatGPT account she never uses, and has no budget, no tech support, and no time to figure it out herself. She's never going to ask for an AI-powered solution to her problems. But she'd benefit enormously if someone took the time to understand those problems and built something that addressed them, with AI as part of the answer rather than the starting point. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;That's what good product work looks like, and as Debbie put it, that's always been the job.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Build the bridge your users are ready to cross&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The product teams pulling ahead right now aren't the ones shipping the most AI features. They're the ones who understand the gap between their own relationship with AI and their users', and who've built the systems to keep closing it.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;To understand what your users are doing in your product, &lt;a rel="noreferrer noopener" href="https://mixpanel.com/ai"&gt;Mixpanel AI&lt;/a&gt; is built to surface that signal proactively, so the insight finds you rather than the other way around. For technical teams who want to go further, &lt;a rel="noreferrer noopener" href="https://mixpanel.com/blog/mixpanel-headless/"&gt;try Mixpanel Headless&lt;/a&gt; where AI agents read and drive Mixpanel programmatically.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19e83d95682:e7fa0:6797f8b7</id>
        <title type="html">Social RSS (?)</title>
        <published>2026-06-01T15:41:59Z</published>
        <updated>2026-06-01T15:42:03Z</updated>
        <link href="https://chriscoyier.net/2026/06/01/social-rss-2/" rel="alternate" type="text/html"/>
        <summary type="html">What’s up with this? The spirit, of course, is “I want my Google Reader back.” I should be careful though: I imagine if we really did have it back exactly as it was, it would prob…</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
        
&lt;p class="wp-block-paragraph"&gt;What’s up with this?&lt;/p&gt;



&lt;figure class="wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;The spirit, of course, is “I want my Google Reader back.” &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;I should be careful though: I imagine if we really did have it back exactly as it was, it would probably not be as cool as we remember. That was a long-ass time ago and web software just wasn’t as nice as it is now. But: it &lt;em&gt;really did&lt;/em&gt; have social features in it! I could leave comments and see comments from my friends. (How “friends” worked back then, I scarcely remember.)&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;I’m not alone in wanting this. Here’s what I’ve seen so far.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;There is &lt;a href="https://skyreader.app/"&gt;Skyreader&lt;/a&gt;, and it looks like Tim Disney is still actively working on it:&lt;/p&gt;



&lt;figure class="wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;In my mind (and perhaps not reality?) Skyreader solves a “cold start” problem where signing up for a new social site is a lonely bummer because you don’t have any friends. Well, if all my Bluesky friends were automatically my friends on Skyreader, wouldn’t that be, like, good? &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;My problem is… I don’t… get it. &lt;/p&gt;



&lt;ul class="wp-block-list"&gt;&lt;li&gt;Maybe there aren’t really “friends” on Skyreader? If there are, I can’t really find a list of them. If there aren’t, I guess this isn’t what I’m looking for. Or is it just assumed they are the same as Bluesky friends? I dunno, I think I have a lot to learn about this AT Proto stuff. &lt;/li&gt;



&lt;li&gt;What happens when I share an article? Does it share on my Bluesky? It doesn’t look like it. Who sees my shared articles then? My friends, if there are such a thing? Other people who explicitly subscribe? How would anyone figure out how to do that?&lt;/li&gt;



&lt;li&gt;It looks like I can share and I can highlight stuff specifically in what I share. Who sees the highlight? I imagine they need to use Skyreader to see that highlight? Or does it republish as RSS somehow?&lt;/li&gt;



&lt;li&gt;No comments… right? That’s weird to me for a social feed reader. What I think of when I think &lt;strong&gt;social feed reader&lt;/strong&gt; is 90% writing and reading my friends comments.&lt;/li&gt;



&lt;li&gt;The UI is confusing to me. I don’t love the expanding list format that jumps around a lot.&lt;/li&gt;
&lt;/ul&gt;&lt;p class="wp-block-paragraph"&gt;The main reason I can’t “really use it,” though, is that it doesn’t sync with Feedbin. I understand that’s a niche concern, but I need &lt;em&gt;one&lt;/em&gt; canonical feed source, so everything I’m subscribed to is in one place, and how much I’ve read is also canonical. That might be antithetical to AT Proto? I have no idea.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/social_reader"&gt;Indieweb has their own version&lt;/a&gt; of all this, but it’s seriously no joke getting involved. From one of the reader sites, &lt;a href="https://alltogethernow.io/"&gt;Togther&lt;/a&gt;:&lt;/p&gt;



&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
&lt;p class="wp-block-paragraph"&gt;What You Need&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#1 Your own website&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;On the IndieWeb your website is your identity. It doesn’t need to be advanced, it doesn’t need to be pretty, it just needs to be &lt;strong&gt;yours&lt;/strong&gt;.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#2 IndieAuth&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/indieauth"&gt;IndieAuth&lt;/a&gt; is a technology to allow you to sign into services using only your website.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#3 A MicroSub Server&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/microsub"&gt;MicroSub&lt;/a&gt; is where the real magic happens. There are 2 parts; the server and the client. Together is the client, but it needs a server to go along with it. The server does a lot of tricky work such as fetching &amp;amp; parsing feeds as well as keeping track of what you have read and how you have everything organized. If you don’t already have a MicroSub server then check out &lt;a href="https://aperture.p3k.io/"&gt;Aperture&lt;/a&gt;.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#4 MicroPub (optional)&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/micropub"&gt;MicroPub&lt;/a&gt; is an optional but very cool piece of the puzzle. If your own website has MicroPub functionality then you can use Together not just to read content, but also to reply and create your own content. You can like, repost and reply to stuff you are following, you can even write full on blog posts from inside Together.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p class="wp-block-paragraph"&gt;I mean, I’m not above some nerd hackin’, but this is 100% not ever going to be a place with a critical mass of my friends just chillin’ and sharing/commenting on blog posts. Way too hard.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;The closest thing I’ve seen is &lt;a href="https://www.commonrss.com/"&gt;CommonRSS&lt;/a&gt; by Brad Coffield which was build in a direct response to my original Bluesky post.&lt;/p&gt;



&lt;figure class="wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;I originally had a gut reaction to the vibe coding nature of it. Like &lt;em&gt;I don’t wanna use this thing you slopped together over a weekend, you gotta get more serious about it first.&lt;/em&gt; Which I admit is just a gut reaction and not a fair assessment of the software. &lt;a href="https://bsky.app/profile/studio303.bsky.social/post/3mhsvtzd5mk2r"&gt;Brad does seem quite sincere&lt;/a&gt; about making it a thing. But at the same time, it feels like it’s been months without updates, which is a bad sign for &lt;em&gt;super brand new&lt;/em&gt; software.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;In testing it lately…&lt;/p&gt;



&lt;ul class="wp-block-list"&gt;&lt;li&gt;It looks great, love the design — but it feels quite slow across the board.&lt;/li&gt;



&lt;li&gt;The Feedbin sync (yay!) didn’t work at first, but appears to be working now.&lt;/li&gt;



&lt;li&gt;It now claims to have 2-way syncing (yay!) but that doesn’t work for me. The articles I read aren’t read in feedbin. The stars don’t sync.&lt;/li&gt;



&lt;li&gt;It’s got the cold-start social problem: there isn’t anyone to follow. The discover page only lists Brad, and clicking the follow button from him doesn’t work.&lt;/li&gt;
&lt;/ul&gt;&lt;p class="wp-block-paragraph"&gt;It feels like it’s close, but it needs to work better. The commenting feature (again, like my main thing for wanting this) requires you to highlight some of the post first, then you can comment, but the comment won’t save. &lt;/p&gt;



&lt;figure class="wp-block-image size-full is-resized"&gt;&lt;img src="https://i0.wp.com/chriscoyier.net/wp-content/uploads/2026/06/Screenshot-2026-06-01-at-8.06.26-AM.png?resize=666%2C678&amp;amp;ssl=1" alt="Screenshot of a user interface with a text box for notes, showcasing a message about a group walk and an error related to JSON input." class="wp-image-13628"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;CommonRSS feels like a solid concept, but it’s both got the cold-start thing in terms of the social network, but also the cold-start thing in terms of a business. My guess is it’s hard for Brad to muster a ton of enthusiasm to work on the project all day every day with low users. &lt;/p&gt;



&lt;hr class="wp-block-separator has-alpha-channel-opacity"&gt;&lt;p class="wp-block-paragraph"&gt;I’m thinking the main problem is that there just isn’t much of a business to be built around RSS. It can build boutique one-person companies with a passion for it, but even then, difficult. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;On top of that, maybe there just isn’t much appetite for the social part? There are a number of arguably already-decently-successful boutique RSS apps, so why haven’t they done it? Why hasn’t Feedbin done anything terribly social? Why not Feedly? Remember FlipBoard’s kind weird taking on RSS? Now they have &lt;a href="https://about.surf.social/"&gt;an equally weird spinoff&lt;/a&gt;. My guess is it’s just too hard of work for too little payoff.&lt;/p&gt;

      &lt;/div&gt;

      
    &lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://jetpack.com/redirect/?source=sigenerate&amp;query=t%3DeyJpbWciOiJodHRwczpcL1wvY2hyaXNjb3lpZXIubmV0XC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI2XC8wNlwvcGV4ZWxzLXBob3RvLTM3MDAyNTAtMTAyNHg2ODIuanBlZyIsInR4dCI6IlNvY2lhbCBSU1MgKD8pIiwidGVtcGxhdGUiOiJmdWxsc2NyZWVuIiwiZm9udCI6IiIsImJsb2dfaWQiOjc0MDc0MjQ4fQ.WOLKCjcfUYr5xMP44ix1Q1xnFK06KcqYxXL96613SoUMQ"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://chriscoyier.net/feed/</id>
            <title type="html">Chris Coyier</title>
            <link href="https://chriscoyier.net" rel="alternate" type="text/html"/>
            <updated>2026-06-01T15:42:03Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19e744060dd:50c55:c9e040d5</id>
        <title type="html">On wintering.</title>
        <published>2026-05-29T15:00:36Z</published>
        <updated>2026-05-29T15:00:42Z</updated>
        <link href="https://www.joanwestenberg.com/on-wintering/" rel="alternate" type="text/html"/>
        <summary type="html">The winterer is out of the loop; they're not maintaining a position because they don't have a position to maintain. They can do work that takes longer than a quarter, longer than a year, longer than 5 years, because nobody is auditing the line item.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;section&gt;&lt;p&gt;Abraham Lincoln rode home from Washington in December 1849, with what looked like the end of his career packed into his luggage. He'd served one term in the House, alienated his constituents by opposing the Mexican War, and lost his shot at a federal Land Office appointment.&lt;/p&gt;&lt;p&gt;He went back to Springfield to practice law, a near-broken man. And, for nearly 5 years, he barely participated in national politics.&lt;/p&gt;&lt;p&gt;He rode the Illinois circuit, argued patent disputes, and taught himself geometry from Euclid by candlelight in coach inns. He read newspapers obsessively; he read Shakespeare and the King James Bible until he could quote either from pretty much any starting point.&lt;/p&gt;&lt;p&gt;The folks who saw him in those years said he looked...tired.&lt;/p&gt;&lt;p&gt;When he returned to the spotlight, in October 1854, the Kansas-Nebraska Act had cracked the country open. Lincoln walked onto the stage at Peoria and spoke for 3 hours straight. The man who'd been a country lawyer that morning was a national figure by midnight.&lt;/p&gt;&lt;p&gt;Six years later, he was president. &lt;/p&gt;&lt;p&gt;Lincoln's lost years are the part of the biography American children skip past in school; they get the rail-splitter, the beard, the debates, the war, the emancipation, the address, the assassination.&lt;/p&gt;&lt;p&gt;But the 5 years we skip over are the whole ballgame. &lt;/p&gt;&lt;p&gt;They rebuilt the instrument.&lt;/p&gt;&lt;p&gt;The English writer Katherine May coined the modern usage in her 2020 book &lt;em&gt;Wintering&lt;/em&gt;, but the idea is older than the word. Russian peasants called the long quiet stretches between harvests &lt;em&gt;zima&lt;/em&gt; and treated them as a season for weaving, sleeping, repairing tools, and telling stories. Japanese Buddhist monasteries built whole liturgies around rohatsu sesshin, the seven-day winter retreat that closes the year. Foragers like the !Kung and the Hadza, spent something like 4 hours a day on subsistence and the rest on…rest.&lt;/p&gt;&lt;p&gt;Productivity is a recent invention; wintering is not.&lt;/p&gt;&lt;p&gt;Cormac McCarthy published &lt;em&gt;Blood Meridian&lt;/em&gt; in 1985 to a shrugging response. The New York Times reviewed it in a single column. He'd been writing in El Paso for years, broke and largely forgotten. Friends thought he'd peaked. Then in 1992 &lt;em&gt;All the Pretty Horses&lt;/em&gt; came out, won the National Book Award, sold half a million copies, and the back catalog got reissued. McCarthy hadn't been recovering. He'd been finishing something the culture wasn't ready for in 1985 and was ready for by 1992.&lt;/p&gt;&lt;p&gt;He'd been wintering.&lt;/p&gt;&lt;p&gt;Daniel Day-Lewis stopped acting in 1997 and apprenticed as a cobbler in Florence. He came back, played Bill the Butcher in &lt;em&gt;Gangs of New York&lt;/em&gt;, and won an Oscar. He stopped again. Came back. Won another Oscar. Stopped again, and by all reports has actually stopped this time, though I wouldn't bet on it. The cobbler years were how he reset the instrument.&lt;/p&gt;&lt;p&gt;In the long winter, organisms route metabolism inward.&lt;/p&gt;&lt;p&gt;Trees pull resources out of leaves, drop the leaves, and push the sugars down into root systems. Bears don't sleep, exactly. Their core temperature drops a few degrees, their metabolism halves, and they cycle slowly through fat reserves while their kidneys learn to recycle urea into protein. They come out in spring with their bones still mineralized and their muscles roughly intact, which is something no human has yet figured out how to do. What the bear performs is one of the most metabolically sophisticated tricks in the animal kingdom.&lt;/p&gt;&lt;p&gt;The Romans understood that a field left fallow for a season produced more in the next cycle than one worked continuously. Norfolk farmers in the 18th century made it a four-course rotation: wheat, turnips, barley, clover, with the clover restoring nitrogen the wheat had pulled out. The land that looks unused is doing the most useful work.&lt;/p&gt;&lt;p&gt;People who winter well are doing something analogous. They route attention inward and downward, into the parts of the system that don't show up on the surface. They read, they revise, they take long walks they can't account for, and they think the same thought 400 times until it cracks.&lt;/p&gt;&lt;p&gt;Most of what gets published, shipped, posted, and announced is washed off the rocks within a quarter. The people doing it are running on a treadmill that resets their position to zero every Monday. They have to keep producing to stay visible, and visibility is how they earn the right to keep producing.&lt;/p&gt;&lt;p&gt;It's a closed loop, and it generates very little compound interest.&lt;/p&gt;&lt;p&gt;The winterer is off the loop. They aren't maintaining a position because they don't have a position to maintain.&lt;/p&gt;&lt;p&gt;In the short term, you pay dearly for it.&lt;/p&gt;&lt;p&gt;People forget you exist. Calls dry up. Old collaborators stop replying. Younger versions of you lap you in the standings.&lt;/p&gt;&lt;p&gt;The benefit is that you can do work that takes longer than a quarter, and longer than a year, and longer than 5 years, because nobody is auditing the line item.&lt;/p&gt;&lt;p&gt;Charles Darwin came back from the &lt;em&gt;Beagle&lt;/em&gt; voyage in 1836 with the rough outline of natural selection in his head. He published &lt;em&gt;On the Origin of Species&lt;/em&gt; in 1859. The intervening 23 years included long stretches when he wrote almost nothing in his theory notebooks, partly because he was sick, partly because he was writing 8 volumes about barnacles, and partly because he understood the case had to be airtight. When he finally published, the argument was so heavily fortified that the church spent the next 50 years trying to find a hairline crack and failing.&lt;/p&gt;&lt;p&gt;If Darwin had published in 1840, he might be a footnote. His 23 years of comparative silence were the moat.&lt;/p&gt;&lt;p&gt;Robert Caro started his Lyndon Johnson biography in 1976. He's published 4 volumes of an intended 5. He's now 90. He moved to the Texas Hill Country to live among the people Johnson grew up with, because he thought he couldn't write about a man without inhabiting his weather. Each volume took roughly a decade. The publishing world treats him as a slow eccentric. Anyone who's read the books knows he's running a different clock, on a different scale, and that no one currently working at speed is going to produce anything close.&lt;/p&gt;&lt;p&gt;Plenty of people stop and produce nothing. The graveyard of failed comebacks is large, and wintering is dangerous as a strategy because most attempts at it collapse into actual stagnation.&lt;/p&gt;&lt;p&gt;The difference between the two is invisible from the outside, until the end.&lt;/p&gt;&lt;p&gt;The reason the wintering few register as dangerous, when they re-emerge, is that they have something the still-busy don't have: a center of gravity. They've spent enough time alone with a single problem to develop actual opinions about it, opinions that don't move when other people push on them. In a culture optimized for constant repositioning, conviction is a structural advantage. The market doesn't know how to price it.&lt;/p&gt;&lt;p&gt;The winterer has been watching while you weren't looking. They've watched the consensus shift, watched the mistakes pile up. When they come back, they come back with reads you can't get from inside the swirl, because the swirl makes you stupid.&lt;/p&gt;&lt;p&gt;The philosopher Hannah Arendt, writing in &lt;em&gt;The Life of the Mind&lt;/em&gt; in the 1970s, described thinking itself as a form of withdrawal. You can't think and act at the same time, she said, because thinking pulls you out of the stream of ongoing events. She was suspicious of people who claimed to do both at once.&lt;/p&gt;&lt;p&gt;The British psychiatrist Anthony Storr, in &lt;em&gt;Solitude&lt;/em&gt; (1988), made the case that the most original work of major figures often came out of long isolated stretches. Newton in plague-year Cambridge. Wittgenstein in Norway. Kafka in Zürau. Beckett in his Paris apartment with the curtains drawn. Storr wasn't romanticizing it; the isolated stretches were often miserable, sometimes pathological. But the work that came out of them had a density that wasn't available to people doing it part-time.&lt;/p&gt;&lt;p&gt;Any culture that systematically punishes withdrawal is going to lose its most concentrated thinkers to either burnout or invisibility. The modern knowledge economy, with its ambient pressure to post, ship, and stay in the conversation, is a machine for producing exactly that loss. The people we'll wish we had in 15 years are, right now, being shamed into producing slop they don't believe in, because the alternative is to drop out, and dropping out reads as failure.&lt;/p&gt;&lt;p&gt;The winterers who survive this will be those who can tolerate looking like they failed. This is a real and rare psychological skill, and most people don't have it. It requires you to be okay with the wrong kind of silence around your name for years. It requires you to pass on small wins that would re-establish your position. It requires you to bet that what you're working on is worth more than what you're giving up, when the only person who can evaluate the bet is you, and you might be wrong, and you'll only know in 7 years.&lt;/p&gt;&lt;p&gt;Lincoln didn't know in 1851 that he was wintering.&lt;/p&gt;&lt;p&gt;He thought he was finished.&lt;/p&gt;&lt;p&gt;He told his law partner William Herndon that his political career was over, and he believed it. And then his country produced an emergency that demanded exactly the kind of mind he'd been nurturing, and he was the man of the hour whose hour had finally come.&lt;/p&gt;&lt;p&gt;The people who appear to have stopped, in any given year, are mostly people who have actually stopped. But small fraction of them are doing the other thing. &lt;/p&gt;&lt;p&gt;Our world produces emergencies on a reliable schedule; when the next one comes, watch who walks out of the woods.&lt;/p&gt;
  &lt;/section&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://www.joanwestenberg.com/content/images/size/w1200/2026/04/lincoln-1863-gettyimages-177602038-1377722101.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://www.joanwestenberg.com/rss/</id>
            <title type="html">Westenberg.</title>
            <link href="https://www.joanwestenberg.com" rel="alternate" type="text/html"/>
            <updated>2026-05-29T15:00:42Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19e4e658f88:2824bd:1d3b9ec4</id>
        <title type="html">10x engineers: the Purple Developer</title>
        <published>2026-05-22T06:35:39Z</published>
        <updated>2026-05-22T06:35:45Z</updated>
        <link href="https://jessitron.com/2020/12/26/purple-developer/" rel="alternate" type="text/html"/>
        <summary type="html">One way to be a 10x developer is to write the system, so you know more about it that anyone else. I call this person the “purple developer.”</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
			
&lt;p class="wp-block-paragraph"&gt;What makes a software engineer productive? You can list attributes like experience with the language, scientific mindset, intelligence, focus, a personally crafted IDE setup. Yet, in my experience, far and away the biggest factor is: familiarity with the codebase they’re changing.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;That knowledge of what the system does and needs to do, it’s completely essential and entirely contextual. The easiest way to get is to create it — that is, to write that software from scratch. And then it feels completely natural. It doesn’t feel like knowledge, it feels like breathing. It feels obvious.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;When you have a decent mental model of a system, sharing that with others is hard. You don’t know how much you know. If you’re the purple developer in this picture:&lt;/p&gt;



&lt;figure class="wp-block-image"&gt;&lt;img src="https://i1.wp.com/jessitron.com/wp-content/uploads/2018/04/4e765-1fneesy_bw-arepj9j6adsa.png?w=840&amp;amp;ssl=1" alt=""&gt;&lt;figcaption&gt;below the line, a software system; above it, a purple person with a mental model of the system. A blue person and a green person with very incomplete models and lots of questions.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;then you have a mental model of this system because you built it. The green and blue developers have been assigned to help, but they can’t change the system because they don’t understand it. Meanwhile, you may be changing the system fast enough that it’s impossible for them to get a grasp on it, no matter how smart they are. (I’ve been in their situation.) The solution is to work &lt;em&gt;together&lt;/em&gt; on the system, and invest attention in transferring your mental model. Until then, Blue and Green get in your way.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;This slide is part of my &lt;a href="https://jessitron.com/camerata"&gt;Collective Problem Solving&lt;/a&gt; keynote (also seen as Origins of Opera). People came up to me afterward: “We have a purple developer. Now I don’t feel so bad about being blue or green” or “Oh no, I’m the purple developer!” &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Which developer in this picture is ten times more productive than the others? Purple developer!&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Is it because they are inherently smarter? Nah. Circumstances lead to this situation.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;There is not any “productive” software engineer so much as “productive in this codebase.” &lt;a href="https://jessitron.com/2017/06/24/the-most-productive-circumstances-for/"&gt;Spread the knowledge, spread the productivity.&lt;/a&gt;&lt;/p&gt;
		&lt;/div&gt;

					
			&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>jessitron</name>
        </author>
        <media:content medium="image" url="https://i0.wp.com/jessitron.com/wp-content/uploads/2019/05/cropped-cropped-logo-by-linda-2.png?fit=240%2C240&amp;ssl=1"/>
        <link href="https://s0.wp.com/_si/?t=eyJpbWciOiJodHRwczpcL1wvaTAud3AuY29tXC9qZXNzaXRyb24uY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDE5XC8wNVwvY3JvcHBlZC1jcm9wcGVkLWxvZ28tYnktbGluZGEtMi5wbmc_Zml0PTUxMiUyQzUxMiZzc2w9MSIsInR4dCI6Ikplc3NpdHJvbiIsInRlbXBsYXRlIjoiZWRnZSIsImZvbnQiOiIiLCJibG9nX2lkIjoxNjIzNTk4MzZ9.H4uKTfuoWCLiBn0JsgmqYGglAJIdwtAJ-del4_sSPNcMQ" rel="enclosure" type="image"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://jessitron.com/feed/</id>
            <title type="html">Jessitron</title>
            <link href="https://jessitron.com" rel="alternate" type="text/html"/>
            <updated>2026-05-22T06:35:45Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19e2a09a0f2:5db05a:10dfc124</id>
        <title type="html">Native Apps Should Be Avoided Whenever Possible</title>
        <published>2026-05-15T05:08:54Z</published>
        <updated>2026-05-15T05:08:58Z</updated>
        <link href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/" rel="alternate" type="text/html"/>
        <summary type="html">Why native apps have become privacy liabilities, and why the browser is almost always the better choice.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt; &lt;article&gt;&lt;header&gt;&lt;time datetime="2026-04-12"&gt;April 12, 2026&lt;/time&gt;&lt;h1&gt;Native Apps Should Be Avoided Whenever Possible&lt;/h1&gt; &lt;/header&gt;&lt;blockquote&gt;
&lt;p&gt;TL;DR: &lt;strong&gt;What you should do:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Openly refuse apps, and vocally advocate for the web instead.&lt;/li&gt;
&lt;li&gt;Try not to install any apps if you don’t need to.&lt;/li&gt;
&lt;li&gt;If a service has a functioning website, use it instead.&lt;/li&gt;
&lt;li&gt;Revoke all permissions by default, including background location, microphone, and camera permissions for anything that doesn’t require them to function.&lt;/li&gt;
&lt;li&gt;Audit your installed apps. Uninstall all apps you don’t actively need.&lt;/li&gt;
&lt;li&gt;Treat every “download our app” prompt with skepticism.&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
&lt;p&gt;Most native apps collect far more data than their website equivalents ever could. They request permissions to hardware, sensors, and background processes that browsers deliberately restrict. The third-party software embedded in these apps frequently transmits your location, device identifiers, and behavioral data to third parties before you even see a consent prompt. This data is in tandem bought, sold, and aggregated by brokers. It has been used to out individuals, track immigrants, and enable prosecution over reproductive healthcare.&lt;/p&gt;
&lt;h2 id="the-white-house-app"&gt;The White House App&lt;/h2&gt;
&lt;p&gt;On March 27, 2026, the Trump administration released an official White House app for iOS and Android. Within hours, two independent security researchers decompiled it and published their findings [&lt;a href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/#ref-1"&gt;1&lt;/a&gt;]. The app is a textbook example of everything wrong with the native app model.&lt;/p&gt;
&lt;p&gt;Apple requires apps to submit a privacy manifest disclosing what data they collect. The White House app declared an empty array. Zero data collection. Meanwhile, the actual binary contained ten analytics frameworks, including the full OneSignal SDK with a sub-framework specifically for location tracking [&lt;a href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/#ref-2"&gt;2&lt;/a&gt;]. The GPS pipeline polled precise coordinates every 4.5 minutes in the foreground and every 9.5 minutes in the background, syncing everything to OneSignal’s commercial servers. A boolean flag in OneSignal’s server responses could remotely enable or disable GPS tracking without an app update and without Apple review.&lt;/p&gt;
&lt;p&gt;An Exodus Privacy audit identified three embedded trackers, one of which was Huawei Mobile Services Core [&lt;a href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/#ref-3"&gt;3&lt;/a&gt;]. The app’s privacy policy, last updated January 20, 2025, makes no mention of GPS tracking, OneSignal, or background data collection.&lt;/p&gt;
&lt;p&gt;Nearly everything in the app is available on whitehouse.gov. The app’s unique additions are push notifications, a pre-filled text message to the President, and an ICE tip button (also available on ice.gov). What it actually added at scale was a surveillance pipeline: 77% of the app’s network requests go to third parties, not whitehouse.gov.&lt;/p&gt;
&lt;h2 id="the-software-embedded-in-apps"&gt;The Software Embedded in Apps&lt;/h2&gt;
&lt;p&gt;Most people think of apps as products built by a single company. In practice, the average app is a thin wrapper around dozens of third-party software packages, each with its own data collection pipeline and commercial incentives. When you grant an app permission to access your location, every package embedded in that app inherits that permission. A single package can appear in hundreds of apps, feeding location data on millions of people to a single aggregator.&lt;/p&gt;
&lt;p&gt;In January 2025, a hacker breached Gravy Analytics and leaked roughly 30 million location records collected from 3,455 apps — dating, fitness, gaming, and health apps among them [&lt;a href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/#ref-4"&gt;4&lt;/a&gt;]. The FTC subsequently banned Gravy Analytics from selling Americans’ location data [&lt;a href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/#ref-5"&gt;5&lt;/a&gt;], but by then the data was already circulating on cybercrime forums.&lt;/p&gt;
&lt;p&gt;In a separate case, Google paid $391.5 million to settle claims from 40 states for continuing to collect location data even when users explicitly disabled location tracking [&lt;a href="https://nooneshappy.com/article/native-apps-should-be-avoided-whenever-possible/#ref-6"&gt;6&lt;/a&gt;].&lt;/p&gt;
&lt;h2 id="why-everyone-should-care"&gt;Why Everyone Should Care&lt;/h2&gt;
&lt;p&gt;This data is bought, sold, and aggregated by brokers. It has been used to out individuals, track immigrants, and enable prosecution over reproductive healthcare. In multiple cases, journalists and private groups have purchased app-derived location data to identify specific people based on their movements.&lt;/p&gt;
&lt;p&gt;There are virtually no restrictions in the United States on buying, selling, or weaponizing this data. There is no comprehensive federal privacy law. And there isn’t likely to be one soon. The best we can do is minimize the data we share in the first place.&lt;/p&gt;
&lt;h2 id="what-apps-can-do-that-websites-cant"&gt;What Apps Can Do That Websites Can’t&lt;/h2&gt;
&lt;p&gt;The core argument for using the website instead of the app comes down to what each platform is technically capable of doing without your knowledge.&lt;/p&gt;

































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Capability&lt;/th&gt;&lt;th&gt;Native App&lt;/th&gt;&lt;th&gt;Website / PWA&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Background location tracking&lt;/td&gt;&lt;td&gt;Yes, can poll GPS continuously&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Run at device startup&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Access biometric hardware&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Limited (WebAuthn, user initiated)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Modify or delete device storage&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No (sandboxed to browser)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Embed invisible third-party software&lt;/td&gt;&lt;td&gt;Yes, all inherit granted permissions&lt;/td&gt;&lt;td&gt;No, scripts visible in page source&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Transmit data before consent prompt&lt;/td&gt;&lt;td&gt;Yes (common with third-party software)&lt;/td&gt;&lt;td&gt;Restricted by browser policies&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Push notifications while closed&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes (PWA, user opt in required)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Access contacts, call logs, SMS&lt;/td&gt;&lt;td&gt;Yes (if permitted)&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Prevent phone from sleeping&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Camera and microphone&lt;/td&gt;&lt;td&gt;Yes (persistent if granted)&lt;/td&gt;&lt;td&gt;Yes (per session, prompted each time)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Offline functionality&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes (via service workers)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The browser is the security boundary. Websites operate within it. Native apps bypass it.&lt;/p&gt;
&lt;h2 id="the-access-provided-by-default-is-enough-to-do-real-harm"&gt;The Access Provided by Default is Enough to Do Real Harm&lt;/h2&gt;
&lt;p&gt;The moment you install an app, before you allow a single permission prompt, it can:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Reach any server on the internet&lt;/li&gt;
&lt;li&gt;Read your IP address, device model, OS version, timezone, country, carrier, and network type&lt;/li&gt;
&lt;li&gt;Generate and persist a unique identifier tied to your device&lt;/li&gt;
&lt;li&gt;Run code at device startup (Android) and wake up in the background&lt;/li&gt;
&lt;li&gt;Fingerprint your device by combining the above into a signature that follows you across sessions&lt;/li&gt;
&lt;li&gt;Grant all of this same access to every third-party software package embedded in the app&lt;/li&gt;
&lt;li&gt;Compare this data to other datasets to infer your identity, demographics, interests, and habits&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The runtime permission prompts you actually see (location, camera, contacts) are helpful while annoying, but the majority of the default access permissions do not require your consent.&lt;/p&gt;
&lt;p&gt;A website, by contrast, starts with almost none of this: no persistent identifier, no background execution, no third-party software inheritance, no startup hooks.&lt;/p&gt;
&lt;h2 id="some-things-need-to-be-apps-but-most-dont"&gt;Some Things Need to Be Apps, But Most Don’t&lt;/h2&gt;
&lt;p&gt;Some things need to be apps. AR and VR, real-time games, anything talking to NFC or Bluetooth hardware, serious audio and video work, accessibility tools. These are legitimate cases where the browser sandbox is the limitation. In these circumtances, I personally use a full computer as opposed to my phone.&lt;/p&gt;
&lt;p&gt;Almost nothing else qualifies. Your banking, your travel, your grocery store, the restaurant down the street — none of it needs an app. And rewards be damned. No rewards are worth the data you are willingly giving them.&lt;/p&gt;
&lt;p&gt;Same goes for hardware. If a thermostat or a fitness tracker can’t be set up without a proprietary app, that’s a flaw in the product, not a feature. I immediately avoid such products. You’re buying an ongoing relationship with someone else’s servers and guaranteeing that you’ll forget that corporations are watching everything they can.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I avoid most apps. It turns out this is easier than most people assume, because the app is almost never the only option. It is just the option the company wants you to take and not enough people question.&lt;/p&gt;
&lt;p&gt;We are at a very specific time in humanity right now. Where aggregating data is a currency, and it is actively being utilized at a scale never before seen. I recommend you at least take stock of what you’re freely giving away.&lt;/p&gt;
&lt;hr&gt;&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;span id="ref-1"&gt;1.&lt;/span&gt; &lt;a href="https://thereallo.dev/blog/decompiling-the-white-house-app"&gt;I Decompiled the White House’s New App&lt;/a&gt; (Thereallo, March 28, 2026) and &lt;a href="https://www.atomic.computer/blog/white-house-app-security-analysis/"&gt;Security Analysis of the Official White House iOS App&lt;/a&gt; (atomic.computer, March 27, 2026). Two independent researchers decompiled the app on Android and iOS within hours of release.&lt;/p&gt;
&lt;p&gt;&lt;span id="ref-2"&gt;2.&lt;/span&gt; &lt;a href="https://www.atomic.computer/blog/white-house-app-security-analysis/"&gt;Security Analysis of the Official White House iOS App&lt;/a&gt; (atomic.computer). Documents the empty privacy manifest, OneSignal SDK with ten sub-frameworks, and the remote GPS toggle via server response.&lt;/p&gt;
&lt;p&gt;&lt;span id="ref-3"&gt;3.&lt;/span&gt; &lt;a href="https://reports.exodus-privacy.eu.org/en/reports/gov.whitehouse.app/latest/"&gt;Exodus Privacy Report: gov.whitehouse.app&lt;/a&gt;. Automated audit identifying three embedded trackers including Huawei Mobile Services Core. Additional context in &lt;a href="https://www.sambent.com/the-white-house-app-has-huawei-spyware-and-an-ice-tip-line/"&gt;Fedware: 13 Government Apps That Spy Harder Than the Apps They Ban&lt;/a&gt; (Sam Bent).&lt;/p&gt;
&lt;p&gt;&lt;span id="ref-4"&gt;4.&lt;/span&gt; &lt;a href="https://techcrunch.com/2025/01/13/gravy-analytics-data-broker-breach-trove-of-location-data-threatens-privacy-millions/"&gt;A breach of Gravy Analytics’ huge trove of location data threatens the privacy of millions&lt;/a&gt; (TechCrunch, January 13, 2025).&lt;/p&gt;
&lt;p&gt;&lt;span id="ref-5"&gt;5.&lt;/span&gt; &lt;a href="https://www.ftc.gov/news-events/news/press-releases/2025/01/ftc-finalizes-order-prohibiting-gravy-analytics-venntel-selling-sensitive-location-data"&gt;FTC Finalizes Order Prohibiting Gravy Analytics, Venntel from Selling Sensitive Location Data&lt;/a&gt; (FTC, January 14, 2025).&lt;/p&gt;
&lt;p&gt;&lt;span id="ref-6"&gt;6.&lt;/span&gt; &lt;a href="https://techcrunch.com/2022/11/14/google-pay-391-5-million-location-tracking-settlement/"&gt;Google to pay $391.5 million in location tracking settlement with 40 states&lt;/a&gt; (TechCrunch, November 14, 2022).&lt;/p&gt;  &lt;/article&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>No One's Happy</name>
        </author>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://nooneshappy.com/rss.xml</id>
            <title type="html">No One's Happy</title>
            <link href="https://nooneshappy.com" rel="alternate" type="text/html"/>
            <updated>2026-05-15T05:08:58Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19e00ecc82e:330034:c12867d</id>
        <title type="html">AI dělá chyby a autisté je dokážou najít. Z jejich jinakosti dělá Češka ve svém startupu přednost</title>
        <published>2026-05-07T05:32:58Z</published>
        <updated>2026-05-07T05:33:01Z</updated>
        <link href="https://cc.cz/ai-dela-chyby-a-autiste-je-dokazou-najit-z-jejich-jinakosti-dela-ceska-ve-svem-startupu-prednost/" rel="alternate" type="text/html"/>
        <summary type="html">„I nejpokročilejší vývojářské týmy musí manuálně odstraňovat duplicity a řešit případy, kde automatizace selhává,“ říká Irena Zatloukalová.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;

					
&lt;p&gt;

	
	

	
	

	
&lt;/p&gt;
&lt;p&gt;Představte si konkrétní situaci: datoví vědci ve startupu trénují svůj analytický model, ale v určitých případech narážejí na vysokou chybovost v podobě falešně pozitivních výsledků. Místo toho, aby se pálil drahý čas seniorních inženýrů na manuálním čištění datasetů, předá se úkol ven. Výsledek? Najatý tým projde pět iterací dat a pomůže tak o polovinu zlepšit úspěšnost modelu. Právě na tom staví mladý pražský startup Diversight. Jeho tajnou zbraní nejsou pokročilejší algoritmy, nýbrž dedikovaní analytici na autistickém spektru. Ukazuje tak, že vlastnosti, kvůli kterým se jiné firmy těmto lidem někdy vyhýbají, se mohou proměnit na silné stránky.&lt;/p&gt;&lt;p id="czech-bbc21e7e4b919b9a9b21266f2b6fc810"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;„Nejsme charita ani neziskovka, jsme klasická technologická firma,“ &lt;/em&gt;říká hned na úvod spoluzakladatelka a ředitelka Diversightu Irena Zatloukalová. V českém technologickém prostředí se pohybuje dvacet let, a to zejména v oblasti komunikace, kdy sedm let byla mluvčí tuzemské internetové jedničky Seznam.cz. Mimo jiné působila i jako spolumajitelka PR agentury Fenek a později na volné noze radila s komunikací technologickým firmám jako Heureka, Apify, Liftago či Ackee. Loni v červenci ale všechny klienty předala dál a naplno se vrhla do nového byznysu.&lt;/p&gt;
&lt;p&gt;V Diversightu se zaměřuje na takzvaný datový dluh. V éře, kdy se prakticky každá firma snaží do svých procesů implementovat umělou inteligenci, stojí budoucí úspěch primárně na kvalitě vstupních dat. &lt;em&gt;„I ty nejpokročilejší vývojářské týmy totiž musí manuálně odstraňovat duplicity a řešit hraniční případy, v nichž zavedená automatizace zkrátka selhává a modely začínají generovat nesmysly. Diversight funguje jako filtr, který přebírá na přesnost náročnou vrstvu ekosystému a zajišťuje potřebnou lidskou pojistku proti halucinacím,“&lt;/em&gt; vysvětluje pro CzechCrunch Zatloukalová.&lt;/p&gt;


&lt;div&gt;
	&lt;div&gt;&lt;a href="https://cc.cz/ai-dela-chyby-a-autiste-je-dokazou-najit-z-jejich-jinakosti-dela-ceska-ve-svem-startupu-prednost/galerie/541483/"&gt;&lt;span&gt;&lt;img src="https://cc.cz/wp-content/uploads/2026/04/zatloukalova-simon-815x509.jpg" alt="zatloukalova-simon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;p&gt;&lt;span&gt;Foto: Diversight&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Irena Zatloukalová s kolegou Šimonem&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Právě v oblasti hledání nepatrných vzorců totiž samotná umělá inteligence běžně naráží na své limity, dodává. Pokud modely generují opakující se chyby, vývojářům nepostačí jen jednoduché přepsání textového promptu. Týmy potřebují člověka, který dokáže precizně pochopit byznysovou logiku a vyčíst přímo ze syrové databáze konkrétní pravidla a odchylky. &lt;em&gt;„V tom máme ohromnou výhodu, protože máme lidi, kterým jednou řeknete pravidla a oni pak po nich neúnavně půjdou, od prvního řádku až po řádek číslo 1 500,“&lt;/em&gt; popisuje specifikum práce Zatloukalová.&lt;/p&gt;
&lt;p&gt;Doplňuje přitom, že jejím lidem nedělá problém udržet stejnou hladinu pozornosti na jednom tématu klidně i osm dní v kuse: &lt;em&gt;„Prostě potřebují problém vyřešit, neznámou najít a věc dotáhnout do konce. To pomyšlení &lt;/em&gt;&lt;em&gt;na rozlousknutí problému pomáhá jejich fokusu.“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Na samotném začátku příběhu Diversightu přitom nestála Zatloukalová. &lt;em&gt;„Ráda bych to říkala, ale bohužel to nebyl můj nápad,“ &lt;/em&gt;usmívá se a jako originálního tvůrce označuje Tomáše Borovičku z technologické společnosti Datamole. Ten na zahraničních trzích narazil na izraelskou firmu Point AI, která budovala data-anotační byznys (zaměřený na označování surových dat) na přesné práci lidí na spektru. Od té doby přemýšlel nad tím, jak vyzkoušet podobný koncept i v Česku.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;„Byla to jedna večeře, jedno potkání, tři hodiny povídání. Šli jsme hodně do hloubky a do našich hodnot a na konci mi řekl: Tak jo, pojďme to zkusit,“&lt;/em&gt; vzpomíná Zatloukalová na zlom, který přišel v prosinci 2024. A dodává, že na začátku je propojila Lenka Kučerová ze spolku prg.ai.&lt;/p&gt;



&lt;p&gt;Původní plán Diversightu počítal s tím, že se do Prahy přenese jako franšíza přímo izraelský model. Rychlý průzkum trhu ale ukázal, že běžný anotační byznys je v tuzemsku v podstatě jen levná komodita, kde firmy často najímají studenty a na finální kvalitě jim tolik nesejde. Zatímco v Izraeli se data pro zdravotnictví či zbrojní průmysl anotují s kritickou přesností, v lokálních podmínkách by takový model ekonomicky nepřežil. Diversight proto musel okamžitě změnit plány. Zaměřil se na komplexnější analytické úkoly a takzvanou validaci s člověkem v procesu (human-in-the-loop), kde se naplno využije kognitivní preciznost ve složitých datech.&lt;/p&gt;
&lt;p&gt;Projekt od začátku strategicky i finančně podpořil zmíněný Datamole, který mu poskytl peníze na bezpečný roční provoz a působí v roli partnera. Mimo jiné nabízí i přímý přístup ke svým seniorním AI architektům. Hlavním byznysovým úkolem pro nadcházející měsíce je tak dostat Diversight do zisku díky příjmům ze zakázek.&lt;/p&gt;


&lt;div&gt;
	&lt;div&gt;&lt;a href="https://cc.cz/ai-dela-chyby-a-autiste-je-dokazou-najit-z-jejich-jinakosti-dela-ceska-ve-svem-startupu-prednost/galerie/541481/"&gt;&lt;span&gt;&lt;img src="https://cc.cz/wp-content/uploads/2026/04/borovicka-zatloukalova-815x509.jpg" alt="borovicka-zatloukalova"&gt;&lt;/span&gt;&lt;/a&gt;&lt;p&gt;&lt;span&gt;Foto: Diversight&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Tomáš Borovička a Irena Zatloukalová&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Mezi aktuální klienty vedle &lt;a href="https://cc.cz/sbiraji-data-o-kozach-e-shopech-i-teslach-cesky-startup-dela-byznys-na-tom-co-na-sebe-firmy-prozradi/"&gt;analytické společnosti BizMachine,&lt;/a&gt; pro kterou startup dělá v úvodu zmíněné čištění dat, patří například také InfraHex, jedna z firem holdingu Respilon. Ta vyvinula technologii, která upravuje tepelnou stopu živých i neživých objektů a tím omezuje jejich detekci pomocí termokamer. V současnosti se využívá i na Ukrajině, kde pomáhá vojákům v terénu. I drobné odchylky zde hrají zásadní roli, protože se systém opírá o přesné analýzy obrazů v různých infra spektrech. A tak se na této detailní práci vyhodnocování a výběru dat podílejí lidé na autistickém spektru.&lt;/p&gt;
&lt;p&gt;Českým mediálním i firemním prostorem dnes podle Zatloukalové bohužel rezonují převážně extrémní obrazy autismu, kdy na jedné straně stojí příběhy nespolupracujících jedinců s těžkým postižením, na straně druhé zprofanované postavy geniálních vědců, jako je například Sheldon Cooper z &lt;em&gt;Teorie velkého třesku&lt;/em&gt;. Diversight ale systematicky hledá velkou množinu lidí s nadprůměrnou analytickou inteligencí ležící mezi těmito dvěma extrémy. Většina z nich má však potíže projít přes výběrová řízení či běžná HR oddělení, protože standardní firemní postupy zkrátka neumí s neurodiverzitou pracovat.&lt;/p&gt;
&lt;div&gt;&lt;blockquote&gt;Pokud zahodíte své ego a uznáte, že někdo našel lepší cestu, firmě to hodně pomůže.&lt;/blockquote&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;„Už jen to, když do pracovního inzerátu vypíšete deset věcí jako ‚nice to have‘. Člověk na spektru si to projde, u třetí položky si řekne, že má jen dva a půl roku zkušeností místo tří, takže to není práce pro něj, a jde dál,“ &lt;/em&gt;vysvětluje Zatloukalová jeden z mnoha detailů, proč kandidáti sami rovnou z recruitmentu vypadávají. Značná část lidí na spektru tak i kvůli tomu končí na úřadu práce či na podřadných pozicích a ve firmách běžně narazí kvůli logickému poukazování na neefektivitu a navrhování lepších postupů.&lt;/p&gt;
&lt;p&gt;Zatloukalová proto změnila rovnou i náborový proces a přizpůsobila ho lidem na spektru. Přesné zadání úkolů a otázky zasílá s předstihem, kandidáty navíc netestuje pod umělým tlakem, ale zaměřuje se striktně na jejich schopnost strukturovaně přemýšlet. Aktuálně má firma pět lidí – vedle samotné Zatloukalové je to technologický manažer Adam Vesecký a interní tým tří specializovaných analytiků. Záměrem je tým rozšiřovat, nicméně se stropem na hranici deseti zaměstnanců, aby firma nepřišla o klidné prostředí komorního kolektivu.&lt;/p&gt;



&lt;p&gt;Zatloukalová navíc varuje před zjednodušováním nebo předsudky, že práce s neurodivergentními lidmi přináší jen těžkosti a komunikační zádrhele. Na první pohled nepříjemné situace, kdy podřízený manažerovi zcela upřímně oznámí, že zadal úkol špatně, totiž vedou k lepším procesům do budoucna, a tím i výsledkům. &lt;em&gt;„Pokud zahodíte své ego a uznáte, že někdo našel lepší cestu, firmě to hodně pomůže,“ &lt;/em&gt;vysvětluje Zatloukalová.&lt;/p&gt;
&lt;p&gt;Lidé na spektru podle ní přinášejí do byznysu inovace a odlišné úhly pohledu nejen v datové analytice, ale napříč všemi obory od zákaznické podpory po design. &lt;em&gt;„Mají přirozenou schopnost jít striktně po podstatě problému a všímat si i drobných odchylek, které neurotypická většina snadno přehlédne,“ &lt;/em&gt;doplňuje. Výměnou za tento výkon často vyžadují jen velmi specifické, avšak finančně nenáročné úpravy pracovního prostředí – typicky přesun stolu dál od rušné kuchyňky, utlumení ostrého kancelářského osvětlení nebo zkrátka jen možnost po náročné schůzce úplně vypnout a odejít domů.&lt;/p&gt;
&lt;div&gt;&lt;blockquote&gt;Cítím nespravedlnost. Jsem naštvaná na to, jakým způsobem se společnost staví k lidem, kteří jsou jiní.&lt;/blockquote&gt;&lt;/div&gt;
&lt;p&gt;Častou chybou některých manažerů je tyto potřeby zpochybňovat jen proto, že zaměstnanec nemá na stole oficiální lékařskou diagnózu. &lt;em&gt;„Moje velká prosba k jakémukoliv manažerovi je: i bez diagnózy člověka poslouchejte. Když říká, že něco potřebuje, respektujte to, nezpochybňujte a hledejte cesty, jak zařídit, aby mohl svoji práci vykonávat co nejlépe,“&lt;/em&gt; apeluje. Jak sama upozorňuje, nevyžaduje to stavbu drahých oddělených kanceláří, často stačí investice do kvalitních sluchátek s aktivním potlačením hluku.&lt;/p&gt;
&lt;p&gt;Přístup orientovaný na lidi se specifickými potřebami má podle Zatloukalové přesah do celkového chodu jakékoliv firmy. Veškerá drobná provozní pravidla a limity, které startup explicitně zavádí kvůli neurodivergentním kolegům, totiž v důsledku ulehčují práci všem. &lt;em&gt;„Každý zaměstnanec občas potřebuje ničím nerušený čas na práci, jen se to v běžných korporacích tolik neakcentuje. Funguje zde stejný princip jako v moderním urbanismu. Pokud navrhnete veřejný prostor tak, aby vyhovoval lidem na vozíku, dětem i rodičům s kočárky, vytvoříte ve finále vysoce funkční prostředí, ve kterém se bude dobře žít úplně všem,“ &lt;/em&gt;srovnává.&lt;/p&gt;
&lt;p&gt;Na druhou stranu Zatloukalová doplňuje, že budování neurodiverzního týmu s sebou pro vedení nese nutnost preciznosti v mezilidské komunikaci. &lt;em&gt;„Tou se živím dvacet let a největší paradox je, že se mi opravdu stává, že si s kolegy nerozumím v zadání,“&lt;/em&gt; přiznává s tím, že sama musela odložit své ego stranou a opět se učit zadávat úkoly maximálně jasně, doslovně a bez zbytečného sarkasmu.&lt;/p&gt;
&lt;p id="czech-7d2b87ee68a47d335feeceb6c53fb9c5"&gt;&lt;/p&gt;&lt;p id="czech-e70ae63f514d21e002eeddcd6c33af7a"&gt;&lt;/p&gt;&lt;p&gt;To vše se promítá i do samotného workflow při řešení úkolů pro klienty. &lt;em&gt;„Když řeknu ‚udělej A‘, kolega si začne domýšlet B, C a D. A místo toho, aby se zeptal, radši to rovnou udělá až po to D. Já mu pak musím říkat, že zašel moc daleko. Pro příště ale vím, že potřebuji dát víc kontextu k tomu, proč má v bodě A skončit,“&lt;/em&gt; ilustruje občasné komunikační střety zakladatelka. Doslovnost a analytický rozklad problémů se však startupu okamžitě vrací ve chvíli, kdy tým narazí na nekvalitní a chaotická data v zadání.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;„Cítím nespravedlnost. Jsem naštvaná na to, jakým způsobem se společnost staví k lidem, kteří jsou jiní. Ta jinakost nemusí být na první pohled patrná, ale znamená pro ně spoustu překážek, které jim vyrábíme my všichni. Angličtina pro to má slovní spojení ‚disabled by society‘. Jakmile mi ukážete něco takového, jsem totální buldok a jdu po narovnávání té nespravedlnosti,“ &lt;/em&gt;vysvětluje Zatloukalová, pro kterou se tak v Diversightu kromě společenského přesahu spojuje vášeň pro data a práce s lidmi. &lt;em&gt;„Baví mě dělat věci, které nikdo předtím moc nedělal nebo neprozkoumal. Diversight má v sobě všechno,“ &lt;/em&gt;uzavírá.&lt;/p&gt;
				&lt;/div&gt;

				
&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Peter Brejčák</name>
        </author>
        <media:content medium="image" url="https://cc.cz/wp-content/uploads/2026/05/irena-zatloukalova.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://feeds.buzzsprout.com/1104554.rss</id>
            <title type="html">CzechCrunch</title>
            <link href="https://cc.cz" rel="alternate" type="text/html"/>
            <updated>2026-05-07T05:33:01Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19dfd361137:57fe1:e753ea5d</id>
        <title type="html">Using safe-area-inset to build mobile-safe layouts</title>
        <published>2026-05-06T12:14:32Z</published>
        <updated>2026-05-06T12:14:35Z</updated>
        <link href="https://polypane.app/blog/using-safe-area-inset-to-build-mobile-safe-layouts/" rel="alternate" type="text/html"/>
        <summary type="html">Modern phones are not simple rectangles. They have rounded corners, camera cutouts, dynamic islands, and home indicators that double as gesture areas. Browsers…</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div id="article"&gt;&lt;p&gt;Modern phones are not simple rectangles. They have rounded corners, camera cutouts, dynamic islands, and home indicators that double as gesture areas. Browsers know the dimensions of all of these and expose the parts that could obscure content as &lt;strong&gt;safe area insets&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The "safe area" is the portion of the screen that is guaranteed to be free from being obscured by system UI. The safe-area-inset is the measurement of how much space the system UI is taking up on each edge of the screen. By using these values in your CSS, you can make sure that important content and controls are not obscured by the system UI.&lt;/p&gt;&lt;p&gt;If you don't want your floating chat button to end up sitting behind the home indicator, where it's unreachable, you need to account for the safe area inset.&lt;/p&gt;&lt;h2 id="environment-variables-for-safe-area-insets"&gt;Environment variables for safe area insets&lt;/h2&gt;&lt;p&gt;With the safe-area-inset environment variables, you can make your layout adapt to the current device's safe area and avoid those bugs. The &lt;code&gt;env()&lt;/code&gt; function is how you read those values in CSS:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;padding-top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-top&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-right&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-right&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-left&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-left&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="browser-support"&gt;Browser support&lt;/h3&gt;&lt;p&gt;Safe-area-insets are &lt;strong&gt;baseline widely available&lt;/strong&gt;, which means you can use them in production today and be confident that they will work for almost all your users on mobile devices.&lt;/p&gt;&lt;p&gt;Since it's baseline widely available, you don't &lt;em&gt;really&lt;/em&gt; need to think about fallbacks but if you want to be extra safe, you can provide a fallback padding if the browser doesn't support &lt;code&gt;env()&lt;/code&gt;:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;padding-top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1rem&lt;span&gt;;&lt;/span&gt; 
  &lt;span&gt;padding-top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-top&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And for browsers that support &lt;code&gt;env()&lt;/code&gt; but not &lt;code&gt;safe-area-inset-*&lt;/code&gt; variables, you can provide a fallback value directly in the &lt;code&gt;env()&lt;/code&gt; function. If &lt;code&gt;safe-area-inset-top&lt;/code&gt; is not supported, it will fall back to &lt;code&gt;1rem&lt;/code&gt;:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;padding-top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-top&lt;span&gt;,&lt;/span&gt; 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It's worth noting that this situation is purely theoretical, as all browsers that support &lt;code&gt;env()&lt;/code&gt; also support &lt;code&gt;safe-area-inset-*&lt;/code&gt; variables. That might not be the case with other environment variables, so it's good to know that this fallback mechanism exists.&lt;/p&gt;&lt;h2 id="using-safe-area-inset-values"&gt;Using safe-area-inset values&lt;/h2&gt;&lt;p&gt;Before we get into problem areas, let's get a general understanding of how these variables work and how you can use them to affect the layout.&lt;/p&gt;&lt;p&gt;In this demo, change each inset and watch how a realistic mobile UI that takes safe area insets into account shifts to remain usable:&lt;/p&gt;&lt;p&gt;The specific px values here are not particular to any specific device, the point is to show how the device sets these variables and how your layout can respond to them as they change.&lt;/p&gt;&lt;p&gt;On real devices, you can think of these values as constants. They're provided by the browser and while they might change when switching from portrait to landscape or between OS updates that change the device UI as well as simply differ per device, they don't change dynamically as the user scrolls or interacts with the page. The browser provides them as a constant value that you can use to ensure your content is not obscured by the system UI.&lt;/p&gt;&lt;h2 id="which-web-pages-actually-need-this"&gt;Which web pages actually need this?&lt;/h2&gt;&lt;p&gt;If you want your pages to look the best they can, all of them.&lt;/p&gt;&lt;p&gt;Browsers by default will prevent your site from being obscured by the notch or home indicator, so your content will be safe without any special handling. That does come with a downside, which is that the browser will give you a smaller viewport to reserve space:&lt;/p&gt;&lt;p&gt;You'll notice the edges here keep the site from being obscured, but they also don't look that great. Ideally we want the content to stretch edge-to-edge, but we want to make sure they're not obscured by system UI. To get that, you need to opt in to the full viewport and handle safe areas yourself.&lt;/p&gt;&lt;p&gt;To do so is a two-step process. First, you need to add &lt;code&gt;viewport-fit=cover&lt;/code&gt; to your meta viewport tag:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;viewport&lt;span&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span&gt;content&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;width=device-width, viewport-fit=cover&lt;span&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will make the browser stretch your page edge-to-edge:&lt;/p&gt;&lt;p&gt;As you can see, the content sits behind the notch/dynamic island. &lt;code&gt;viewport-fit=cover&lt;/code&gt; tells the browser that you want to be responsible for making sure your content is not obscured by the system UI.&lt;/p&gt;&lt;p&gt;And now we can move those elements away from behind the system UI using &lt;code&gt;env(safe-area-inset-*)&lt;/code&gt;.&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;.content&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;padding-right&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-right&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-left&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-left&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you opt into the full viewport, here are the kinds of things you now have to think about to make sure your content is not obscured by the system UI:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Fixed headers and navigation bars should have enough space above or below for the notch or home indicator&lt;/li&gt;&lt;li&gt;Floating chat or help buttons should remain inside the safe area&lt;/li&gt;&lt;li&gt;Full-screen dialogs and drawers should take up the right height without being obscured by the home indicator&lt;/li&gt;&lt;li&gt;Map or video controls near screen corners&lt;/li&gt;&lt;/ul&gt;&lt;h3 id="safe-area-inset-doesnt-provide-margins"&gt;Safe-area-inset doesn't provide margins&lt;/h3&gt;&lt;p&gt;Safe-area-insets are defined to be exactly the space that the system UI is taking up. That means they don't provide any margin between the system UI's edge and your content.&lt;/p&gt;&lt;p&gt;If you set padding to just the safe-area-inset value, your content will sit right up against the edge of the safe area, which is right up against the system UI.&lt;/p&gt;&lt;p&gt;To add some breathing room, you can add your own padding on top of the safe area insets by combining things with &lt;code&gt;calc()&lt;/code&gt;. This way you can ensure that your content is not only safe from being obscured but also has some space to breathe:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;padding-top&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-top&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-right&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-right&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-left&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-left&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="safe-area-inset-only-has-non-zero-values-on-mobile-devices"&gt;Safe-area-inset only has non-zero values on mobile devices&lt;/h2&gt;&lt;p&gt;&lt;code&gt;env()&lt;/code&gt; is supported across browsers and platforms, but the &lt;code&gt;safe-area-inset-*&lt;/code&gt; variables really only have non-zero values on mobile devices.&lt;/p&gt;&lt;p&gt;Desktop browsers always return 0 because there is no UI on top of pages in desktop browsers. It's only on mobile devices that these values are non-zero and that you have to account for them.&lt;/p&gt;&lt;p&gt;That's exactly why these bugs are so easy to miss. If you test in the Chrome responsive view, the safe area insets will still be 0 and you won't see any issues during development.&lt;/p&gt;&lt;p&gt;Testing on real devices is often delegated to the end of the project. By then, making layout changes to accommodate safe areas can be expensive, and the bugs can slip through to production. Even worse, they often only affect users on certain devices, so they can go unnoticed for a long time.&lt;/p&gt;&lt;h2 id="polypanes-device-emulation-supports-safe-area-insets"&gt;Polypane's device emulation supports safe area insets&lt;/h2&gt;&lt;p&gt;Polypane is the first and only desktop browser to emulate safe area insets. Every device in Polypane has correct safe area inset values for both portrait and landscape orientations.&lt;/p&gt;&lt;p&gt;Here's Polypane showing the safe area insets in blue, and the small viewport height difference in pink:&lt;/p&gt;&lt;img src="https://polypane.app/static/insetviz-be77ee8c0ab29ccda899743cd9a9b339.png" alt="a device showing safe area and small viewport overlays in Polypane"&gt;&lt;p&gt;&lt;em&gt;We're also the only desktop browser to emulate &lt;code&gt;svh&lt;/code&gt;, but that's a topic for another article.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;With Polypane's safe-area-inset &lt;strong&gt;overlay visualization&lt;/strong&gt;, you can see exactly where the unsafe areas are on each device.&lt;/p&gt;&lt;h2 id="solving-a-specific-issue-floating-buttons"&gt;Solving a specific issue: Floating buttons&lt;/h2&gt;&lt;p&gt;Let's look at a specific example of how to use &lt;code&gt;safe-area-inset&lt;/code&gt; values to solve a common issue: floating buttons that end up behind the home indicator and become unreachable.&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;.chat-button&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;position&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; fixed&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;right&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 10px&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 10px&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Toggle the positioning between 'Fixed position' and 'Using env()' to switch between hard-coded offsets and offsets that use &lt;code&gt;safe-area-inset-bottom&lt;/code&gt; and &lt;code&gt;safe-area-inset-right&lt;/code&gt;.&lt;/p&gt;&lt;h2 id="safe-area-max-inset"&gt;&lt;code&gt;safe-area-max-inset&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Along with &lt;code&gt;safe-area-inset-*&lt;/code&gt;, the specification also describes &lt;code&gt;safe-area-max-inset-*&lt;/code&gt; variables. Those are not widely supported yet, but they are worth mentioning because they have slightly different behavior:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;safe-area-inset-*&lt;/code&gt;&lt;/strong&gt; gives you the &lt;em&gt;current&lt;/em&gt; inset value right now. On scroll, the browser chrome can collapse, and the inset value can shrink all the way to &lt;code&gt;0&lt;/code&gt;. Your element moves with that change.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;safe-area-max-inset-*&lt;/code&gt;&lt;/strong&gt; gives you the &lt;em&gt;maximum&lt;/em&gt; inset the browser can report for that edge. It stays stable even when the browser chrome collapses. Use it when you want a reserved zone that does not jump around.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;Drag or scroll the phone to simulate the browser address bar appearing and disappearing.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;green button&lt;/strong&gt; uses &lt;code&gt;safe-area-inset-bottom&lt;/code&gt; and follows the current inset. The &lt;strong&gt;blue button&lt;/strong&gt; uses &lt;code&gt;safe-area-max-inset-bottom&lt;/code&gt; and stays put.&lt;/p&gt;&lt;p&gt;&lt;label&gt;Overlay&lt;/label&gt;&lt;label&gt;Show safe inset areas&lt;/label&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;inset vs max-inset&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When to use which really depends on your situation. For some UI components it makes sense to move with the live viewport state, like a floating chat button that should always be just above the home indicator.&lt;/p&gt;&lt;p&gt;For other things, like a persistent cookie banner or a full-screen dialog, it can be better to reserve a stable zone that doesn't shift when the browser chrome collapses so that users don't inadvertently tap the wrong button.&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;
&lt;span&gt;.floating-cta&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;


&lt;span&gt;.persistent-zone&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-max-inset-bottom&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="browser-support-for-safe-area-max-inset-"&gt;Browser support for &lt;code&gt;safe-area-max-inset-*&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;As of now, only Chromium implements &lt;code&gt;safe-area-max-inset-*&lt;/code&gt; so there is no support in (mobile) Safari or Firefox. That means you can't depend on it yet and should provide a fallback stack for other browsers:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;.bottom-spacer&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;padding-bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1rem&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;padding-bottom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-max-inset-bottom&lt;span&gt;,&lt;/span&gt; &lt;span&gt;env&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; + 1rem&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how we're using the &lt;code&gt;env()&lt;/code&gt; fallback mechanism to fall back to &lt;code&gt;safe-area-inset-bottom&lt;/code&gt; if &lt;code&gt;safe-area-max-inset-bottom&lt;/code&gt; is not supported.&lt;/p&gt;&lt;h2 id="testing-safe-areas-in-polypane"&gt;Testing safe areas in Polypane&lt;/h2&gt;&lt;p&gt;As covered earlier, safe area bugs are easy to miss during development. Chrome's responsive view always reports inset values of 0, and real-device testing tends to get pushed to the end of the project, when fixing layout issues is expensive and bugs have already reached production.&lt;/p&gt;&lt;p&gt;Polypane changes that. You can test safe area insets (and small viewport behavior) directly on your desktop, across multiple devices and orientations simultaneously, as part of your normal development workflow.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Catch real-device layout failures during development, not after shipping.&lt;/strong&gt;&lt;/p&gt;&lt;img src="https://polypane.app/static/overlays-8190bd849571d99d1511f3cb9969653c.png" alt="mobile panes in Polypane showing visualized safe area and small viewport difference"&gt;&lt;h2 id="what-to-take-away"&gt;What to take away&lt;/h2&gt;&lt;p&gt;Safe areas are not edge cases for unusual devices. They are the reality of modern mobile devices with camera punch holes, notches, dynamic islands, and home indicator bars. Your users are on those devices, and if you want to give them the best experience, you need to make sure your content is not obscured by the system UI.&lt;/p&gt;&lt;p&gt;Make sure your viewport is set to &lt;code&gt;viewport-fit=cover&lt;/code&gt; so you get the full viewport and that you use &lt;code&gt;env(safe-area-inset-*)&lt;/code&gt; so all your content is visible and accessible. That gives users the best experience.&lt;/p&gt;&lt;p&gt;Get started with testing safe areas in Polypane today, and catch real-device layout failures during development, not after shipping.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://polypane.app/og-images/using-safe-area-inset-to-build-mobile-safe-layouts.png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://polypane.app/rss.xml</id>
            <title type="html">polypane.app</title>
            <link href="https://polypane.app" rel="alternate" type="text/html"/>
            <updated>2026-05-06T12:14:35Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19df28a4f04:bf55cf:ff5f181c</id>
        <title type="html">Design Token Naming Conventions: A Practical Guide</title>
        <published>2026-05-04T10:30:43Z</published>
        <updated>2026-05-04T10:30:47Z</updated>
        <link href="https://www.alwaystwisted.com/articles/design-token-naming-conventions.html" rel="alternate" type="text/html"/>
        <summary type="html">A practical guide to naming design tokens, including token tiers, common conventions, and rules that keep systems consistent and scalable.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;article&gt;&lt;p&gt;
    
    &lt;/p&gt;&lt;h1&gt;Design Token Naming Conventions: A Practical Guide&lt;/h1&gt;
  
      &lt;time datetime="2026-04-23T00:00:00.000Z"&gt;23rd of April 2026&lt;/time&gt;&lt;details&gt;&lt;summary&gt;On This Page:&lt;/summary&gt;&lt;/details&gt;&lt;p&gt;If you have ever stared at a long token list and wondered whether a name should be &lt;code&gt;button-primary-bg&lt;/code&gt;, &lt;code&gt;primary-button-background&lt;/code&gt;, or &lt;code&gt;color-button-primary&lt;/code&gt;, you are not alone.&lt;/p&gt;
&lt;p&gt;Naming design tokens can look and feel simple right up until you have to do it for real. Choose a weak pattern and things get inconsistent fast. Choose a clear pattern and you get a shared language that helps both design and engineering move quicker.&lt;/p&gt;
&lt;p&gt;There is no single correct convention. A two-person team shipping one product has very different needs from a large organisation running multiple brands across multiple platforms.&lt;/p&gt;
&lt;p&gt;What you can do is pick a convention that is clear, consistent, and scalable. This article walks through the core token tiers, common naming models, and practical rules that help avoid naming drift.&lt;/p&gt;
&lt;h2 id="why-naming-gets-hard-faster-than-expected" tabindex="-1"&gt;Why naming gets hard faster than expected&lt;/h2&gt;
&lt;p&gt;Most teams don't struggle creating tokens. They struggle to keep token meaning stable as more people and components work on the Design System.&lt;/p&gt;
&lt;p&gt;You can often see multiple names emerge for the same idea:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"spacing.small"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"space-2"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"gap-md"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"paddingStandard"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Each name might make sense in isolation. Together, they signal a semantic drift. The naming model is no longer acting as a shared language.&lt;/p&gt;
&lt;p&gt;This is why naming quality can affect delivery speed. If token intent is unclear, every usage becomes a local judgement call, and those judgement calls will accumulate into inconsistency.&lt;/p&gt;
&lt;div&gt;&lt;svg&gt;&lt;/svg&gt;&lt;p&gt;The examples in this article follow the &lt;a href="https://www.designtokens.org"&gt;Design Tokens Community Group&lt;/a&gt; specification using &lt;code&gt;$value&lt;/code&gt;, &lt;code&gt;$type&lt;/code&gt;, and dot-notation aliases like &lt;code&gt;{color.brand.primary}&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id="what-are-design-tokens%2C-a-quick-recap" tabindex="-1"&gt;What are design tokens, a quick recap&lt;/h2&gt;
&lt;p&gt;Design tokens are a tooling and platform agnostic way to store design decisions as named values: colour, spacing, typography, radius, shadows, motion, and (much) more.&lt;/p&gt;
&lt;p&gt;The key difference is where and how many times you define those values.&lt;/p&gt;
&lt;p&gt;Without tokens, you might use &lt;code&gt;#BADA55&lt;/code&gt; in dozens of places across components, stylesheets, and design files. When that green needs to change, you might have to hunt down every instance to make sure you're updating everything that needs to be updated.&lt;/p&gt;
&lt;p&gt;With tokens, you create a design token file, a JSON object following the &lt;a href="https://www.designtokens.org"&gt;Design Tokens Community Group specification&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"green"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"400"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"#BADA55"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;You define &lt;code&gt;#BADA55&lt;/code&gt; once as &lt;code&gt;green.400&lt;/code&gt;, then you can reference that name everywhere. The value is still defined somewhere, but it is defined in one place with a meaningful name and consumed by both design tools and code by reference via tooling like Tokens Studio or Style Dictionary.&lt;/p&gt;
&lt;p&gt;This centralisation helps to give you three major benefits:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Better consistency across UI&lt;/li&gt;
&lt;li&gt;Easier theming and re-branding&lt;/li&gt;
&lt;li&gt;Safer changes at scale because you update once and propagate everywhere&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Tokens are not just a technical detail. They are a shared language between design and development (and the whole team) that ends up in the product(s).&lt;/p&gt;
&lt;h2 id="the-three-token-tiers" tabindex="-1"&gt;The three token tiers&lt;/h2&gt;
&lt;p&gt;Most modern token workflows follow a three-tier model. You will see different names for each tier, but the same underlying structure keeps showing up.&lt;/p&gt;
&lt;h3 id="tier-1---primitive-tokens" tabindex="-1"&gt;Tier 1 - Primitive tokens&lt;/h3&gt;
&lt;p&gt;These are your raw values.&lt;/p&gt;
&lt;p&gt;They describe what something is, not why it exists.&lt;/p&gt;
&lt;p&gt;They are also referred to as reference, base, options, or global tokens.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"blue"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"500"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"#0066CC"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"spacing"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"8"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"8px"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"dimension"&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Primitives should not (imho) and are usually not consumed directly by components. They are source values for more specific design decisions and token tiers.&lt;/p&gt;
&lt;p&gt;A common failure is skipping straight to component naming because it feels productive in the short term. In practice, that locks in naming decisions before your core scales are stable.&lt;/p&gt;
&lt;p&gt;When possible, stabilise primitives first, attach intent later.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"core"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"dimension"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"100"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt; &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"8px"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"dimension"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"200"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt; &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"16px"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"dimension"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"300"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt; &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"24px"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"dimension"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="tier-2---semantic-tokens" tabindex="-1"&gt;Tier 2 - Semantic tokens&lt;/h3&gt;
&lt;p&gt;Also known as alias, decision, theme, or system tokens.&lt;/p&gt;
&lt;p&gt;Semantic tokens can express intent. They describe why a value is used or what it is for.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"color"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"brand"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{blue.500}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;This is where tokens become resilient.&lt;/p&gt;
&lt;p&gt;If brand blue changes, the semantic name can stay the same while the underlying primitive reference changes.&lt;/p&gt;
&lt;p&gt;You can update once and it will change everywhere.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"layout"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"spacing"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"formStack"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt; &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{core.dimension.300}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"dimension"&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"contentToButton"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{core.dimension.200}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"dimension"&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="tier-3---component-tokens" tabindex="-1"&gt;Tier 3 - Component tokens&lt;/h3&gt;
&lt;p&gt;Also known as "component specific" or contextual tokens.&lt;/p&gt;
&lt;p&gt;This layer maps semantic intent to specific elements, components, and parts thereof.&lt;/p&gt;
&lt;p&gt;Component tokens should always reference semantic tokens, never primitives directly. That indirection is what can keep the system flexible.&lt;/p&gt;
&lt;p&gt;It is optional, but very useful in larger systems.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"button"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"background"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{color.brand.primary}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Component tokens give you precision. You can tune one component without creating side effects and explosions elsewhere.&lt;/p&gt;
&lt;h2 id="common-naming-conventions" tabindex="-1"&gt;Common naming conventions&lt;/h2&gt;
&lt;p&gt;There is no universal winner ... "It depends". Different teams will always optimise for different makeup, needs, and constraints.&lt;/p&gt;
&lt;p&gt;These diagrams show the core layers for each convention. Real-world usage may add optional layers like state or namespace for more specificity.&lt;/p&gt;
&lt;h3 id="1.-category-property-modifier-(cpm)" tabindex="-1"&gt;1. Category-Property-Modifier (CPM)&lt;/h3&gt;
&lt;div&gt;
  &lt;p&gt;
    &lt;span&gt;category&lt;/span&gt;
    &lt;span&gt;property&lt;/span&gt;
    &lt;span&gt;role&lt;/span&gt;
    &lt;span&gt;state&lt;/span&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This starts broad and gets more specific: category, property, role (and optionally state for interactions). It is easy to scan and keeps naming fairly lightweight.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"color"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"button"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"hover"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
          &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{color.brand.primary-hover}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
          &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
        &lt;span&gt;}&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="2.-tier-based-naming" tabindex="-1"&gt;2. Tier-based naming&lt;/h3&gt;
&lt;div&gt;
  &lt;p&gt;
    &lt;span&gt;primitive&lt;/span&gt;
    &lt;span&gt;semantic&lt;/span&gt;
    &lt;span&gt;component&lt;/span&gt;
    &lt;span&gt;state&lt;/span&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Here, the naming pattern changes depending on the tier. Primitives hold values, semantic tokens hold intent, and component tokens hold local UI decisions, which keeps each layer focused. The diagram shows the token tiers; actual nesting depth varies (e.g., primitives are often 2 layers, while component tokens may be 3+).&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"blue"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"500"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"#2196F3"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
      &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"brand"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{blue.500}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"background"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{color.brand.primary}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="3.-object-property-modifier-(opm)" tabindex="-1"&gt;3. Object-Property-Modifier (OPM)&lt;/h3&gt;
&lt;div&gt;
  &lt;p&gt;
    &lt;span&gt;object&lt;/span&gt;
    &lt;span&gt;property&lt;/span&gt;
    &lt;span&gt;modifier&lt;/span&gt;
    &lt;span&gt;state&lt;/span&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This one starts with the UI object, then narrows to property and variant. It tends to feel natural in component-led workflows because it mirrors how teams talk about UI day to day.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"button"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"background"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"hover"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
          &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{color.brand.primary-hover}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
          &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
        &lt;span&gt;}&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="4.-context-first-naming" tabindex="-1"&gt;4. Context-first naming&lt;/h3&gt;
&lt;div&gt;
  &lt;p&gt;
    &lt;span&gt;tier&lt;/span&gt;
    &lt;span&gt;category&lt;/span&gt;
    &lt;span&gt;property&lt;/span&gt;
    &lt;span&gt;modifier&lt;/span&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Context (e.g., 'brand' or 'theme') comes first, followed by category, property, and modifier. This ensures you know the layer before the specific decision. That works well when the same semantic token needs to exist across several themes or brands.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"brand"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"color"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"default"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
          &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{primitive.color.blue.500}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
          &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
        &lt;span&gt;}&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="5.-a-comprehensive-structure" tabindex="-1"&gt;5. A Comprehensive Structure&lt;/h3&gt;
&lt;div&gt;
  &lt;p&gt;
    &lt;span&gt;namespace&lt;/span&gt;
    &lt;span&gt;category&lt;/span&gt;
    &lt;span&gt;concept&lt;/span&gt;
    &lt;span&gt;property&lt;/span&gt;
    &lt;span&gt;variant&lt;/span&gt;
    &lt;span&gt;state&lt;/span&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Each segment has a specific job, from namespace through to state. This comprehensive structure allows for up to 6 layers, but start with the core 5 and add as needed for complexity. Names are longer, but they are very explicit, which helps when multiple teams and brands share the same system. Nathan Curtis's article &lt;a href="https://medium.com/eightshapes-llc/naming-tokens-in-design-systems-9e86c7444676"&gt;Naming Tokens in Design Systems&lt;/a&gt; goes much deeper on this taxonomy, it's well worth a reading (and bookmarking).&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"acme"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;"color"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
      &lt;span&gt;"button"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
        &lt;span&gt;"background"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
          &lt;span&gt;"primary"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
            &lt;span&gt;"hover"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
              &lt;span&gt;"$value"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"{acme.color.brand.primary}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
              &lt;span&gt;"$type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"color"&lt;/span&gt;
            &lt;span&gt;}&lt;/span&gt;
          &lt;span&gt;}&lt;/span&gt;
        &lt;span&gt;}&lt;/span&gt;
      &lt;span&gt;}&lt;/span&gt;
    &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h2 id="handling-variants%2C-states%2C-and-modes" tabindex="-1"&gt;Handling variants, states, and modes&lt;/h2&gt;
&lt;p&gt;Whatever base naming model you choose, you still need clear, predictable state and variant placement patterns.&lt;/p&gt;
&lt;p&gt;One practical way to stay coherent is to define where each concept lives in the name:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Variant: what version of the same thing it is (for example &lt;code&gt;primary&lt;/code&gt;, &lt;code&gt;secondary&lt;/code&gt;, &lt;code&gt;danger&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;State: how that version is currently behaving (for example &lt;code&gt;hover&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Mode: which environment it belongs to (for example &lt;code&gt;light&lt;/code&gt;, &lt;code&gt;dark&lt;/code&gt;, &lt;code&gt;highContrast&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;If those three ideas float around in different positions, naming gets noisy fast.&lt;/p&gt;
&lt;h3 id="the-same-decision-in-different-conventions" tabindex="-1"&gt;The same decision in different conventions&lt;/h3&gt;
&lt;p&gt;These examples all express the same meaning: primary button background on hover in dark mode.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"button.primary.background.hover.dark"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button-background-primary-hover-dark"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"dark.button.primary.background.hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"acme.color.button.background.primary.hover.dark"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"theme.dark.component.button.primary.background.hover"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Pick one ordering model and enforce it consistently. Mixing these patterns inside one system is where discoverability starts to break down.&lt;/p&gt;
&lt;h3 id="variants" tabindex="-1"&gt;Variants&lt;/h3&gt;
&lt;p&gt;Variants describe alternatives at the same hierarchy level, not interaction changes.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"button.primary.background"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button.secondary.background"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button.danger.background"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Equivalent variant naming in other conventions:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"button-background-primary"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button-background-secondary"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button-background-danger"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.background.primary"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.background.secondary"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.background.danger"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="states" tabindex="-1"&gt;States&lt;/h3&gt;
&lt;p&gt;States represent interaction or status changes for the same UI element. Keep state terms predictable (&lt;code&gt;hover&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;focus&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;) so engineers can find related tokens quickly.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"button.primary.background.hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button.primary.background.active"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button.primary.background.disabled"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button.primary.background.focus"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;The same state grouping works across naming styles:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"button-background-primary-hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button-background-primary-active"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button-background-primary-focus"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button-background-primary-disabled"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"component.button.background.primary.hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.background.primary.active"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.background.primary.focus"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.background.primary.disabled"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="scale-sizes" tabindex="-1"&gt;Scale sizes&lt;/h3&gt;
&lt;p&gt;Scale tokens create consistent step-based sizing. Whether you use &lt;code&gt;xs-xl&lt;/code&gt; labels or numeric steps, use one scale pattern and apply it everywhere to avoid drift.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;"spacing.sm"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"spacing.md"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"spacing.lg"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;For type scales, the same rule applies: keep naming sequential so size relationships are obvious at a glance.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"font.size.sm"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"font.size.md"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"font.size.lg"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="modes-and-themes" tabindex="-1"&gt;Modes and themes&lt;/h3&gt;
&lt;p&gt;Modes are contextual variants of the same decision (for example light and dark). The token purpose stays the same, only the value changes per mode.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"color.background.default.light"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"color.background.default.dark"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Other mode-placement patterns you will see in real systems:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"light.color.background.default"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"dark.color.background.default"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"theme.light.color.background.default"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"theme.dark.color.background.default"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Whichever pattern you choose, keep mode placement fixed. A stable suffix or a stable prefix both work; switching between them does not.&lt;/p&gt;
&lt;h3 id="numbered-scales" tabindex="-1"&gt;Numbered scales&lt;/h3&gt;
&lt;p&gt;Numbered scales are useful when you need many fine-grained steps, especially for colour ramps and heading systems. The key is to keep the numbering direction and increments consistent.&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"font.size.heading.1"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"font.size.heading.2"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"font.size.heading.3"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color.gray.50"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color.gray.100"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color.gray.200"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Numbered scales also appear in alternative patterns:&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"size-font-heading-100"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"size-font-heading-200"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"size-font-heading-300"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"scale.type.heading.100"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"scale.type.heading.200"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"scale.type.heading.300"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h2 id="rules-that-keep-token-naming-healthy" tabindex="-1"&gt;Rules that keep token naming healthy&lt;/h2&gt;
&lt;p&gt;Most strong naming systems follow a predictable anatomy: an optional prefix (namespace or context), a core meaning (category and property), optional modifiers (intent, variant, scale), and an optional suffix (state or mode).&lt;/p&gt;
&lt;p&gt;You do not need every part in every token, but when a part is present it should always appear in the same position.&lt;/p&gt;
&lt;h3 id="1.-consistency-beats-perfection" tabindex="-1"&gt;1. Consistency beats perfection&lt;/h3&gt;
&lt;p&gt;The best naming system is the one your whole team actually follows.&lt;/p&gt;
&lt;p&gt;Drifting tends to start not where the convention is broken outright, but where it is ambiguous enough to invite interpretation. A token set that mixes naming styles makes it impossible to predict what a new token should be called.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; mixing conventions across the same token tier&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"color.button.primary.default"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"button.background.primary.hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"theme.dark.component.button.primary.background.default"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Prefer:&lt;/strong&gt; one convention applied throughout&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"component.button.primary.background.default"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.primary.background.hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"component.button.secondary.background.default"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="2.-optimise-for-clarity-over-brevity" tabindex="-1"&gt;2. Optimise for clarity over brevity&lt;/h3&gt;
&lt;p&gt;Autocompletion means typing long names is rarely a problem, but reading a token list at a glance still relies on human pattern recognition. Abbreviating too aggressively trades a small typing convenience for an ongoing cognitive overhead every time someone reads or audits the system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid&lt;/strong&gt; abbreviations that require decoding&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"btn-bg-pri-hvr"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"c-txt-err"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"sp-md"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Prefer&lt;/strong&gt; names that read on first glance&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"button-background-primary-hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"color-text-error"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing-md"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="3.-keep-semantics-semantic" tabindex="-1"&gt;3. Keep semantics semantic&lt;/h3&gt;
&lt;p&gt;A name like &lt;code&gt;color-text-red&lt;/code&gt; describes a visual value, which means the name breaks as soon as the colour changes. A name like &lt;code&gt;color-text-error&lt;/code&gt; describes intent, which stays stable even when the underlying value does not. At semantic and component levels, name for why, not what.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; naming for the visual value&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"color.text.red"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"color.background.grey"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"color.border.green"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Prefer:&lt;/strong&gt; naming for the intent&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"color.text.error"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"color.background.subtle"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"color.border.success"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="4.-order-from-broad-to-specific" tabindex="-1"&gt;4. Order from broad to specific&lt;/h3&gt;
&lt;p&gt;Names are easier to scan when they move from the widest concept toward specificity. This also means that alphabetically sorted token lists naturally group related tokens together, which makes auditing and searching much faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; specificity before category&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"hover.primary.button.color"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"disabled.background.input"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"lg.font.heading"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Prefer:&lt;/strong&gt; broad category first, narrowing toward specificity&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"color.button.primary.hover"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color.input.background.disabled"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"font.heading.lg"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="5.-make-names-self-explanatory" tabindex="-1"&gt;5. Make names self-explanatory&lt;/h3&gt;
&lt;p&gt;A developer who has never seen your design tokens before should be able to make a reasonable guesstimate about what it does. If a name requires specific knowledge or a colleague to interpret, it is a candidate for renaming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; names that only make sense in context&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"token-1"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"c-bd-x"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"primary-a"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Prefer:&lt;/strong&gt; names that explain themselves&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;
  &lt;span&gt;"button.border.radius"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color.border.focus"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
  &lt;span&gt;"color.button.background.primary"&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h3 id="6.-design-for-growth" tabindex="-1"&gt;6. Design for growth&lt;/h3&gt;
&lt;p&gt;Your naming model should be able to absorb more components, more themes, and potentially more brands without a heavy structural rewrite. That does not mean you need to "over engineer" from the start, but it does mean avoiding patterns that only work at small scale, like implicit positional meaning or naming steps after their current count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; patterns that hit a ceiling&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"spacing-small"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing-smaller"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing-new"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing-new-2"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Prefer:&lt;/strong&gt; a scale that can always grow in either direction&lt;/p&gt;
&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Code language&lt;/span&gt;json&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"spacing.100"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing.200"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing.300"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;"spacing.420"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;h2 id="choosing-your-convention" tabindex="-1"&gt;Choosing your convention&lt;/h2&gt;
&lt;p&gt;A practical way to decide is to consider your team size, system scope, tooling, and existing patterns together.&lt;/p&gt;
&lt;p&gt;Larger teams will need a stronger structure, single product systems can probably stay simpler, and tools like Figma, Tokens Studio, and your build pipeline may naturally favour one style.&lt;/p&gt;
&lt;p&gt;It is also usually safer to evolve existing patterns than replace everything at once, and the best outcome comes when designers and developers both buy into the same model.&lt;/p&gt;
&lt;p&gt;Start simple, document it clearly, add examples, and revisit as the system grows.&lt;/p&gt;
&lt;p&gt;Token names do not need to be academically perfect. They need to be clear enough that your team can apply them without hesitation.&lt;/p&gt;
&lt;h2 id="keep-naming-alive-with-lightweight-governance" tabindex="-1"&gt;Keep naming alive with lightweight governance&lt;/h2&gt;
&lt;p&gt;Naming is not a one time decision.&lt;/p&gt;
&lt;p&gt;It is an ongoing Design System practice.&lt;/p&gt;
&lt;p&gt;If you want conventions to hold up over time, define lightweight governance:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;One canonical naming guide with approved examples&lt;/li&gt;
&lt;li&gt;A short review checklist for new tokens&lt;/li&gt;
&lt;li&gt;Periodic cleanup of duplicate or ambiguous names&lt;/li&gt;
&lt;li&gt;Shared ownership across design and engineering&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;The goal is not "process for process' sake".&lt;/p&gt;
&lt;p&gt;A token system in a Design System that people trust is one they will actually use, and consistent naming is how that trust gets built.&lt;/p&gt;


    
      
      
    
    
    

  &lt;/article&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://news.design.systems/embed?color1=ffffff&amp;amp;color2=2c2c2d&amp;amp;color_bg_button=2c2c2d&amp;amp;color_border=ccc&amp;amp;color_button=ffffff&amp;amp;color_links=979797&amp;amp;color_terms=808080&amp;amp;title=Subscribe+to+Design+Systems+News"/>
        <link href="/images/articles/meta-images/design-tokens-naming.png" rel="enclosure" type="image/png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://www.alwaystwisted.com/feed.xml</id>
            <title type="html">Always Twisted</title>
            <link href="https://www.alwaystwisted.com" rel="alternate" type="text/html"/>
            <updated>2026-05-04T10:30:47Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ded75aef3:8a7f89:3a607295</id>
        <title type="html">Upgrading Forgejo with S3 Object Storage and Actions!</title>
        <published>2026-05-03T10:50:05Z</published>
        <updated>2026-05-03T10:50:09Z</updated>
        <link href="https://blog.alexsguardian.net/posts/2024/06/03/upgradingforgejo" rel="alternate" type="text/html"/>
        <summary type="html">Migrating my Forgejo server to s3 object storage and adding action runners for workflows!</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;hr&gt;&lt;p&gt;Awhile ago I spun up my own instance of Forgejo. &lt;a href="https://forgejo.org"&gt;Forgejo&lt;/a&gt;, if you are not familiar, is a hard fork of &lt;a href="https://gitea.com"&gt;Gitea&lt;/a&gt; when they became a &lt;em&gt;for-profit&lt;/em&gt; company and is run/managed by &lt;a href="https://codeberg.org"&gt;Codeberg&lt;/a&gt;. If you want more details on why Forgejo came to be, you can read their blog post &lt;a href="https://forgejo.org/2022-12-15-hello-forgejo/"&gt;here&lt;/a&gt;. As of now it’s basically a drop-in replacement for Gitea. Though over time this will change as the two projects drift apart. So it may not be one in the future! Anyhow, I’ve been rolling my own instance since September 2023 and, recently I have been moving my entire lab towards a more &lt;a href="https://about.gitlab.com/topics/gitops/"&gt;GitOps&lt;/a&gt; approach. To do this and do it securely (in the confines of my internal network), I’ve decided to turn Forgejo into my full CI/CD manager and config storage place. I’ll also be using &lt;a href="https://bitwarden.com/products/secrets-manager/"&gt;Bitwarden’s Secret Manager&lt;/a&gt; to manage, well, secrets.&lt;/p&gt;&lt;p&gt;Before I begin re-doing my lab for the umpteenth time, I need to change how Forgejo operates currently. That way Forgejo can support the transition from ‘FileOps’ (aka loose files) to GitOps. Now this isn’t the ‘perfect’ setup process as the repo storage still lives in /data, and it’s not currently replicated across my small swarm cluster. This will change when I get to actually rebuilding the cluster from the ground up to utilize NFS mounts for shared storage. Though that requires more hardware, which the wife probably won’t be happy about. &lt;span&gt;&#128517;&lt;/span&gt;&lt;/p&gt;&lt;h2 id="migrating-to-s3-object-storage"&gt;Migrating to S3 Object Storage&lt;/h2&gt;&lt;p&gt;Forgejo (in Docker), by default, has you map a &lt;code&gt;/data&lt;/code&gt; directory where all the necessary configs/repos/logs/etc. live. As you can see by the example compose file &lt;a href="https://forgejo.org/docs/latest/admin/installation-docker/"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;docker-compose.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;codeberg.org/forgejo/forgejo:7&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;./forgejo:/data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# &amp;lt;---- data volume&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/etc/timezone:/etc/timezone:ro&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/etc/localtime:/etc/localtime:ro&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;This is great if you plan on just running a simple setup, or you already have shared storage. For me though, I have no shared storage, and a &lt;a href="https://min.io/"&gt;MinIO&lt;/a&gt; server that is currently doing nothing but holding attachments for &lt;a href="https://github.com/outline/outline"&gt;Outline&lt;/a&gt;. I figured it’s time to put it to more use. That, and my day job involves doing AWS things so figured it would be good practice.&lt;/p&gt;&lt;p&gt;Currently, Forgejo supports the following directories (under &lt;code&gt;/appdata&lt;/code&gt;) for object storage.&lt;/p&gt;&lt;div&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Subsystem&lt;/th&gt;&lt;th&gt;Directory&lt;/th&gt;&lt;th&gt;app.ini Sections&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Attachments&lt;/td&gt;&lt;td&gt;attachments/&lt;/td&gt;&lt;td&gt;[attachment]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LFS&lt;/td&gt;&lt;td&gt;lfs/&lt;/td&gt;&lt;td&gt;[lfs]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Avatars&lt;/td&gt;&lt;td&gt;avatars/&lt;/td&gt;&lt;td&gt;[avatar]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Repository avatars&lt;/td&gt;&lt;td&gt;repo-avatars/&lt;/td&gt;&lt;td&gt;[repo-avatar]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Repository archives&lt;/td&gt;&lt;td&gt;repo-archive/&lt;/td&gt;&lt;td&gt;[repo-archive]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Packages&lt;/td&gt;&lt;td&gt;packages/&lt;/td&gt;&lt;td&gt;[packages]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Actions logs&lt;/td&gt;&lt;td&gt;actions_log/&lt;/td&gt;&lt;td&gt;[storage.actions_log]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Actions Artifacts&lt;/td&gt;&lt;td&gt;actions_artifacts/&lt;/td&gt;&lt;td&gt;[actions.artifacts]&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;caption&gt;&lt;a href="https://forgejo.org/docs/latest/admin/storage/"&gt;Forgejo Docs: Storage Settings&lt;/a&gt;&lt;/caption&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;As you can see in the table above there are a few storage directories missing. Mainly repositories. Since repositories do not work well in object storage they are unsupported. Which means they will still be living on one of my hosts temporarily until I finalize my cross cluster storage solution. In order migrate the storage to MinIO, I first need to create a bucket (named &lt;code&gt;forgejo&lt;/code&gt;), an Access Identity, and an ACL for it.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;access-credentials&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Access_key:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;O3n9bNhrbadkeyAGKOuhIUzMGF&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Secret_key:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;czgQT1fb1l3GD2nRQEPebadsecretbWG0xGIu20hjOeBfoSGo&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Access Key User Policy&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;"s3:ListBucketMultipartUploads"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;"s3:AbortMultipartUpload"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;"s3:ListMultipartUploadParts"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;The above ACL replaces the default one under the “Current User Policy” after enabling the “ON” switch for “Restrict beyond user policy” setting in &lt;code&gt;User &amp;gt; Access Keys &amp;gt; Create access key&lt;/code&gt;. It limits the access key to the &lt;code&gt;forgejo&lt;/code&gt; bucket. The bucket is also private by default, so I do not have to change the bucket ACL.&lt;/p&gt;&lt;p&gt;&lt;img alt="Create Access Key" src="https://blog.alexsguardian.net/_astro/fj-create-access-key.2JyFthvY_11P6bC.webp"&gt;&lt;span&gt;MinIO Create Access Key with a custom user policy&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Next was to upload the existing files to MinIO so that when Forgejo restarted with the new s3 config it would pick them back up. As you can see in the picture below there are a few absent directories. I decided to condense a few related directories into parent ones for my ease of use. Also, some of them do not exist yet and will be created when needed (e.g. packages/).&lt;/p&gt;&lt;p&gt;&lt;img alt="forgejo bucket dir 1" src="https://blog.alexsguardian.net/_astro/fj-s3-dir_1.DFDEWvjv_wh5yr.webp"&gt;&lt;span&gt;Example MinIO bucket directory layout&lt;/span&gt;&lt;/p&gt;&lt;p&gt;My current s3 bucket directory layout:&lt;/p&gt;&lt;p&gt;With MinIO ready to go, all I had to do was update the Forgejo container environment variables for the MinIO connection and re-deploy. Forgejo environment variables can be defined using the &lt;code&gt;env -&amp;gt; ini&lt;/code&gt; notation. So in your &lt;code&gt;app.ini&lt;/code&gt; you have sections that are labeled, such as ‘[security].’ In order to update the INI value for say, ‘INSTALL_LOCK’, you’d need to define it as &lt;code&gt;FORGEJO__security__INSTALL_LOCK&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;As you can see in the example snippet file below, I am defining the MinIO connection information, as well as, where to look for specific directories in the bucket.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;forgejo-minio-env-example&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__STORAGE_TYPE="minio"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_USE_SSL="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_ENDPOINT="s3.${LAB_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_ACCESS_KEY_ID=${FJ_S3_ACCESS_ID}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_SECRET_ACCESS_KEY=${FJ_S3_ACCESS_KEY}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_BUCKET="forgejo"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_LOCATION="us-east-1"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__attachment__MINIO_BASE_PATH="attachments/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__lfs__MINIO_BASE_PATH="lfs/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__avatar__MINIO_BASE_PATH="avatars/users/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repo-avatar__MINIO_BASE_PATH="avatars/repositories/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repo-archive__MINIO_BASE_PATH="archives/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__packages__MINIO_BASE_PATH="packages/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage.actions_log__MINIO_BASE_PATH="actions/logs/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__actions.artifacts__MINIO_BASE_PATH="actions/artifacts/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;a name="compose-example"&gt;&lt;/a&gt;&lt;details&gt;&lt;summary&gt;Forgejo Compose Example [Expand me]&lt;/summary&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;forgejo.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"CMD"&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;"curl"&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;"-fSs"&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;"localhost:3000/api/healthz"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;codeberg.org/forgejo/forgejo:7.0&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO_APP_NAME="Forgejo"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO_RUN_MODE="prod"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO_WORK_PATH="/data/gitea"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repository__ROOT="/data/git/repositories"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repository.local__LOCAL_COPY_PATH="/data/gitea/tmp/local-repo"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repository.pull-request__DEFAULT_MERGE_STYLE="merge"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repository.signing__DEFAULT_TRUST_MODEL="committer"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__APP_DATA_PATH="/data/gitea"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__DOMAIN="git.${LAB_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__SSH_DOMAIN="git.${LAB_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__HTTP_PORT="3000"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__ROOT_URL="https://git.${LAB_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__DISABLE_SSH="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__SSH_PORT="22"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__SSH_LISTEN_PORT="22"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__LFS_START_SERVER="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__LFS_JWT_SECRET="${FJ_JWT_SECRET}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__server__OFFLINE_MODE="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__DB_TYPE="postgres"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__HOST="${FJ_DB_HOST}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__NAME="${FJ_DB}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__USER="${FJ_DB_USER}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__PASSWD="${FJ_DB_PASS}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__LOG_SQL="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__SCHEMA=""&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__database__SSL_MODE="disable"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__indexer__ISSUE_INDEXER_PATH="/data/gitea/indexers/issues.bleve"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__session__PROVIDER_CONFIG="/data/gitea/sessions"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__session__PROVIDER="file"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__session__COOKIE_SECURE="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__log__MODE="console"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__log__LEVEL="info"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__log__ROOT_PATH="/data/gitea/log"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__security__INSTALL_LOCK="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__security__SECRET_KEY="${FJ_SECRET_KEY}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__security__REVERSE_PROXY_LIMIT="1"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__security__REVERSE_PROXY_TRUSTED_PROXIES="${CADDY_INT_IP}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__security__INTERNAL_TOKEN="${FJ_INT_TOKEN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__security__PASSWORD_HASH_ALGO="${FJ_PASS_ALGO}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__DISABLE_REGISTRATION="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__REQUIRE_SIGNIN_VIEW="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__REGISTER_EMAIL_CONFIRM="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__ENABLE_NOTIFY_MAIL="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__ALLOW_ONLY_EXTERNAL_REGISTRATION="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__ENABLE_CAPTCHA="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__CAPTCHA_TYPE="cfturnstile"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__REQUIRE_CAPTCHA_FOR_LOGIN="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__CF_TURNSTILE_SECRET="${FJ_CFT_SECRET}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__CF_TURNSTILE_SITEKEY="${FJ_CFT_SITE_KEY}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__DEFAULT_KEEP_EMAIL_PRIVATE="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__DEFAULT_ALLOW_CREATE_ORGANIZATION="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__DEFAULT_ENABLE_TIMETRACKING="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__service__NO_REPLY_ADDRESS="noreply@${FJ_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__mailier__ENABLED="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__openid__ENABLE_OPENID_SIGNIN="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__openid__ENABLE_OPENID_SIGNUP="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FROGEJO__cron.update_checker__ENABLED="false"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__oauth2__JWT_SECRET="${FJ_OAUTH_SEC}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__actions__ENABLED="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__actions__DEFAULT_ACTIONS_URL="https://git.${LAB_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__actions__ARTIFACT_RETENTION_DAYS="90"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__STORAGE_TYPE="minio"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_USE_SSL="true"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_ENDPOINT="s3.${LAB_DOMAIN}"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_ACCESS_KEY_ID=${FJ_S3_ACCESS_ID}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_SECRET_ACCESS_KEY=${FJ_S3_ACCESS_KEY}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_BUCKET="forgejo"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage__MINIO_LOCATION="us-east-1"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__attachment__MINIO_BASE_PATH="attachments/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__lfs__MINIO_BASE_PATH="lfs/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__avatar__MINIO_BASE_PATH="avatars/users/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repo-avatar__MINIO_BASE_PATH="avatars/repositories/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__repo-archive__MINIO_BASE_PATH="archives/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__packages__MINIO_BASE_PATH="packages/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__storage.actions_log__MINIO_BASE_PATH="actions/logs/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;FORGEJO__actions.artifacts__MINIO_BASE_PATH="actions/artifacts/"&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/etc/timezone:/etc/timezone:ro&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/etc/localtime:/etc/localtime:ro&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;aside&gt;&lt;p&gt;&lt;svg&gt;&lt;/svg&gt; Tip&lt;/p&gt;&lt;section&gt;&lt;p&gt;If you are curious about all the configuration options for Forgejo, you can check out the configuration cheat sheet &lt;a href="https://forgejo.org/docs/latest/admin/config-cheat-sheet/"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/section&gt;&lt;/aside&gt;&lt;/div&gt;&lt;/details&gt;&lt;p&gt;With the environment variables updated, and a quick redeploy of Forgejo, I was up and running with object storage via MinIO! A quick trip into the &lt;code&gt;Site Administration &amp;gt; Configuration &amp;gt; Summary&lt;/code&gt; in Forgejo shows that LFS was now using MinIO as seen in the below image.&lt;/p&gt;&lt;p&gt;&lt;img alt="FJ Site Admin Config for LFS" src="https://blog.alexsguardian.net/_astro/fj-site-admin.BuBy27dw_1f0fNQ.webp"&gt;&lt;span&gt;Forgejo site admin config showing LFS using MinIO&lt;/span&gt;&lt;/p&gt;&lt;h2 id="setting-up-forgejo-actions"&gt;Setting up Forgejo Actions&lt;/h2&gt;&lt;p&gt;&lt;img alt="FJ Actions Meme" src="https://blog.alexsguardian.net/_astro/fj_actions_meme.CHp604cc_1rm3bC.webp"&gt;&lt;/p&gt;&lt;p&gt;All jokes aside, Forgejo Actions is actually fairly decent. The runner is not production ready (according to &lt;a href="https://code.forgejo.org/forgejo/runner#forgejo-runner"&gt;Forgejo themselves&lt;/a&gt;) but it works well enough that I am going all in on it for my lab. Setting up the action runner was fairly straight forward. It’s got a similar setup process to the self-hosted GitHub runner.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Enable Actions in the &lt;code&gt;app.ini&lt;/code&gt; or using environment variables. Restart/Redeploy Forgejo.&lt;/li&gt;&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://blog.alexsguardian.net/posts/2024/06/03/upgradingforgejo#compose-example"&gt;See above&lt;/a&gt; for an example using environment variables under &lt;code&gt;#Actions Configs&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;ol start="2"&gt;&lt;li&gt;Create a new runner in &lt;code&gt;Site Administration &amp;gt; Actions &amp;gt; Runners &amp;gt; Create new runner&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Copy the new runner registration token.&lt;/li&gt;&lt;li&gt;Deploy Forgejo Runner and register it with the token from step 3.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Now there are a few ways you could deploy the runner, but I decided to deploy it in Docker. You can bind the runner container to the Docker sock, but I did not. I really do not like doing that so, I ended up using &lt;a href="https://hub.docker.com/_/docker"&gt;Docker in Docker (DIND)&lt;/a&gt;. It’s not really recommended to use Docker this way but for my lab it will do fine. The compose file was also pretty easy to set up.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;forgejo-actions.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;'dockerd'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'-H'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'tcp://0.0.0.0:2375'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'--tls=false'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;code.forgejo.org/forgejo/runner:3.4.1&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;condition&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;service_started&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'/bin/sh -c "while : ; do sleep 1 ; done ;"'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Change to this command after registration&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#command: '/bin/sh -c "sleep 5; forgejo-runner daemon"'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;DOCKER_HOST=tcp://dind:2375&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;As you can see above there are two &lt;code&gt;commands:&lt;/code&gt;. &lt;code&gt;'/bin/sh -c "while : ; do sleep 1 ; done ;"'&lt;/code&gt; starts up the runner container and does nothing. This allows you to enter the container via &lt;code&gt;docker exec&lt;/code&gt; and run the registration command using the token generated in the Site Administration settings. After registering the container, it can be brought down and the current command can be replaced with the commented out one (&lt;code&gt;'/bin/sh -c "sleep 5; forgejo-runner daemon"'&lt;/code&gt;). Then after redeploying the container the runner will become available in Forgejo globally. Having a global runner means it can be used by all repositories. Which for me works fine since my Forgejo instance is mainly private except for a few public repositories and a few GitHub mirrors.&lt;/p&gt;&lt;p&gt;&lt;img alt="FJ Manage Runners" src="https://blog.alexsguardian.net/_astro/fj-manage-runners.Bgx3xUe8_ZdqLTf.webp"&gt;&lt;span&gt;Forgejo Manage Runners&lt;/span&gt;&lt;/p&gt;&lt;p&gt;With my runner online and working, I am now able to have workflows kick off from steps defined in files located in &lt;code&gt;.forgejo/workflows/&lt;/code&gt;. The syntax is fairly similar to GitHub actions so most of the actions you can run there can run in Forgejo. The best part is I can mirror the action repositories and have my runner pull them directly from my Forgejo instance. The first thing I set up was Renovate Bot to keep my Docker images updated. The bot runs on a cron based workflow as seen below and opens a PR for image updates.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.forgejo/workflows/renovate.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;cron&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'0 0/6 * * *'&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# every 6 hours, every day&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;RENOVATE_REPOSITORIES&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.repository }}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.RENOVATE_TOKEN != '' }}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;renovate/renovate:full&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GITHUB_COM_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.RENOVATE_GITHUB_COM_TOKEN }}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;RENOVATE_BASE_DIR&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.workspace }}/.tmp&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;RENOVATE_ENDPOINT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.server_url }}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;RENOVATE_REPOSITORY_CACHE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'enabled'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;RENOVATE_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.RENOVATE_TOKEN }}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;RENOVATE_GIT_AUTHOR&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'Renovate Bot &amp;lt;renovate@mydomain.dev&amp;gt;'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GIT_AUTHOR_NAME&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'Renovate'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GIT_AUTHOR_EMAIL&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'renovate@mydomain.dev'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GIT_COMMITTER_NAME&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'Renovate Bot'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GIT_COMMITTER_EMAIL&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'renovate@mydomain.dev'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Get Ntfy Token from SM&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/sm-action@v4&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;access_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.BITWARDEN_LAB_KEY }}&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;some-secret-id-string &amp;gt; NTFY_TOKEN&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;some-secret-id-string &amp;gt; LAB_DOMAIN&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/ntfy-action@master&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'https://ntfy.${LAB_DOMAIN}'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'{"authorization": "bearer $NTFY_TOKEN"}'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Renovate Bot failed to run!&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;The bot is configured via a &lt;code&gt;renovate.json&lt;/code&gt; file located in the root of the repository.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;renovate.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;"$schema"&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;"https://docs.renovatebot.com/renovate-schema.json"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;"extends"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"config:base"&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;"assignees"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"alexandzors"&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;"dependencyDashboard"&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;"labels"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"maintenance"&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;"enabledManagers"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"docker-compose"&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;"fileMatch"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"^.*&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;.yml$"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;"managers"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"docker-compose"&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;"depTypeList"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"services"&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;"datasources"&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;"docker"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;At the moment I have it scanning through all YAML files since I do not name my compose files &lt;code&gt;docker-compose.yml&lt;/code&gt;. They are named as the service so: &lt;code&gt;forge.yml&lt;/code&gt;, &lt;code&gt;caddy.yml&lt;/code&gt;, &lt;code&gt;authelia.yml&lt;/code&gt;, etc. They are also located in their own folders. This will be changing as I move more to GitOps but for now it’s fine.&lt;/p&gt;&lt;p&gt;&lt;img alt="FJ Renovate PR" src="https://blog.alexsguardian.net/_astro/fj-renovate-pr.BKbIoFtc_28Kiq4.webp"&gt;&lt;span&gt;PR Opened by Renovate Bot using the Forgejo Action workflow&lt;/span&gt;&lt;/p&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;&lt;p&gt;This was a fun project to work on in between work and family stuff. Hopefully this inspires one of you to upgrade your own local git setup with an ‘Actions’ runner or object storage! I’ll have a full config dump for MinIO, Forgejo, Forgejo Actions, and an example workflow in the &lt;a href="https://github.com/alexandzors/blog-files/tree/main/posts/upgradingforgejo"&gt;blog-files repo&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;On to the next project. &lt;span&gt;&#128640;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://blog.alexsguardian.net/_astro/fj_actions_banner.Ly7zaxsc_C1Dxb.webp"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ded720baf:89c617:5071632d</id>
        <title type="html">Rebuilding My Home Lab</title>
        <published>2026-05-03T10:46:07Z</published>
        <updated>2026-05-03T10:46:12Z</updated>
        <link href="https://blog.alexsguardian.net/posts/2025/02/25/rebuildingmyhomelab" rel="alternate" type="text/html"/>
        <summary type="html">Rebuilding my homelab 'cluster' into a proper 'cluster'.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;sub&gt;FTC: Some links in this post are income earning affiliate links.&lt;/sub&gt;&lt;/p&gt;&lt;p&gt;It’s been a while since I last posted about my homelab or well posted on the blog. So I figured I’d start off with a banger for 2025. Rebuilding my homelab into a Docker Swarm cluster that supports high availability. Hope you’re ready for a ride, because this was definitely a journey. &lt;span&gt;&#128517;&lt;/span&gt;&lt;/p&gt;&lt;sub&gt;&lt;em&gt;Side note: I also recently acquired a Bambu Lab A1 3D printer (with the AMS :)) to replace my old Anet A8. I’d say it’s mine, but I’ve only been printing stuff for my wife so far..&lt;/em&gt;&lt;/sub&gt;&lt;h2 id="the-plan"&gt;The Plan&lt;/h2&gt;&lt;p&gt;The plan for this project was to:&lt;/p&gt;&lt;ol type="a"&gt;&lt;li&gt;Create a cluster that supported high availability &lt;span&gt;✅&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Setup a distributed storage solution that supported the above &lt;span&gt;✅&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Setup a load balancer &lt;span&gt;✅&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Have the cluster communicate over 2.5gb or faster networking &lt;span&gt;✅&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Have a minimum of 3 nodes &lt;span&gt;✅&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Be small and efficient &lt;span&gt;✅&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Easy enough right?… &lt;em&gt;right?&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Since I had two Dell Optiplex 5050 Micros that I was using already, I decided to grab a third micro to complete the 3 node cluster. As for the actual orchestration software I decided to stick with Docker and use Docker Swarm. I’m a Docker guy but never had a chance to use Swarm properly with a shared storage system. Figured it was time to give it a proper go.&lt;/p&gt;&lt;div&gt;&lt;strong&gt;Wait… Docker Swarm? Alex you should use Kubernetes!!!!!!&lt;/strong&gt;&lt;img alt="Dwight Schrute saying 'Let's put it this way... no' from The Office" src="https://media4.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWZ2eWN6czc3NWN1OXJyeDRvZ210azVzamF4ejNzejVkeWQ3djZkdCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/daPCSjwus6UR2JxRX1/giphy.gif"&gt;&lt;/div&gt;&lt;h2 id="current-v2-cluster-specs"&gt;Current v2 Cluster Specs&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;2x Dell Optiplex 5050 Micros&lt;/li&gt;&lt;li&gt;1x Dell Optiplex 7040 Micros&lt;/li&gt;&lt;li&gt;3x Realtek 8125B 2.5gb A+E NICs&lt;/li&gt;&lt;li&gt;3x Intel 700p 256GB SSDs&lt;/li&gt;&lt;li&gt;3x Crucial BX500 1TB SSDs&lt;/li&gt;&lt;li&gt;Ubiquiti Flex-2.5g Switch&lt;/li&gt;&lt;li&gt;Ubuntu 24.04 LTS&lt;/li&gt;&lt;li&gt;GlusterFS&lt;/li&gt;&lt;/ul&gt;&lt;h2 id="cluster-v1"&gt;Cluster v1&lt;/h2&gt;&lt;sub&gt;circa 2023&lt;/sub&gt;&lt;p&gt;Cluster v1 never actually made it to ‘production’. It ended up being a test bed and a learning experience on gotchas when it came to Linux, Intel, drivers, and poor research on my part…&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; I originally bought some Intel i225-v 2.5gb m.2 cards from IOCrest thinking they’d work. Come to find out these particular chips have some &lt;a href="https://duckduckgo.com/?t=h_&amp;amp;q=intel+225-v+linux+driver+issues&amp;amp;ia=web" target="_blank"&gt;driver issues&lt;/a&gt; on Linux (specifically Ubuntu).&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;When originally designing the cluster I had planned on using M.2 2.5g cards since the Micro’s had M.2 M-key slots. That way I could get a less expensive SATA SSD for storage. After doing some research I settled on some &lt;a href="https://www.aliexpress.us/item/3256805389320511.html" target="_blank"&gt;Intel 225-V 2.5g cards&lt;/a&gt; from IOCrest on AliExpress. The one downside to these cards was the vertical connector pins. The chassis did not have enough space with a SATA SSD installed. So I did what any tinkerer would do and replaced the pins with right angle ones. The first two cards went good, but my old soldering iron doesnt keep temp well. On the third card’s NIC module I messed up and destroyed one of the traces. So I had to fix it with a jumper wire. Live and learn I suppose.&lt;/p&gt;&lt;p&gt;As for the NICs themselves, I started experiencing driver issues after getting two of the nodes up and running in a ‘mock’ swarm. These issues manifested as random disconnects or complete NIC power loss. I tried to fix it by messing with grub configs for pcie_aspm thinking it was related to Active State Power Management (ASPM) at first. I then tried manually setting the autonegotiation via ethtool. Also double checked UEFI settings, etc. Nothing seemed to work. So I ended up scrapping the Intel cards and going with Realtek 8125B cards for cluster v2.&lt;/p&gt;&lt;p&gt;Anyways, I’ll leave you with a few pictures from the v1 prototype, and we can move on to v2.&lt;/p&gt;&lt;h2 id="cluster-v2"&gt;Cluster v2&lt;/h2&gt;&lt;sub&gt;circa 2024&lt;/sub&gt;&lt;p&gt;My second attempt at the cluster faired a bit better. Well mostly (more on that later). After my first purchase mistake I went back and bought &lt;a href="https://www.aliexpress.us/item/3256803984886712.html" target="_blank"&gt;different M.2 adapters&lt;/a&gt;. These were A+E keyed and used the WLAN slot rather than the M.2 M-keyed slot which got freed up for more storage. These new adapters also use a Realtek chipset (8125B) rather than an Intel one. So no more odd driver issues.&lt;/p&gt;&lt;p&gt;&lt;img alt="M.2 A+E keyed adapter installed" src="https://blog.alexsguardian.net/_astro/PXL_20241022_172523762.MP.CizhE_LT_1pzDqf.webp"&gt;&lt;span&gt;M.2 A+E keyed adapter installed&lt;/span&gt;&lt;/p&gt;&lt;p&gt;With the new adapters using the A+E slot, I decided to use the M slot for a boot drive and the SATA port for cluster storage. For the boot drive I went with some Intel 700p 256GB SSDs I bought off someone from the Ubiquiti Discord server. As for cluster storage I went with &lt;a href="https://amzn.to/4hCYdg4" target="_blank"&gt;1TB Crucial BX500 SSDs&lt;/a&gt; from Amazon. These are not the fastest drives by any means but for my use case they should do just fine.&lt;/p&gt;&lt;p&gt;&lt;img alt="Storage SSDs" src="https://blog.alexsguardian.net/_astro/PXL_20241023_170004511.MP.ZdGI53cL_Z1W8jpI.webp"&gt;&lt;span&gt;Storage SSDs&lt;/span&gt;&lt;/p&gt;&lt;p&gt;There was one downside to the Intel SSDs though. Their physical size. They are 2230 and the Micro chassis only supports 2260 and 2280. So in order to use them I had to print out some 2230 to 2280 adapter brackets. I also printed out a 2.5in tray for the third Micro since it did not come with one. The print files are linked below.&lt;/p&gt;&lt;h3 id="the-os"&gt;The OS&lt;/h3&gt;&lt;p&gt;For the OS I decided to stick with my tried and true &lt;a href="https://ubuntu.com/server"&gt;Ubuntu &lt;strong&gt;Server&lt;/strong&gt;&lt;/a&gt; (24.04 LTS btw). It’s stable and easy to manage. Though, I may eventually re-image to &lt;a href="https://rockylinux.org/"&gt;Rocky Linux&lt;/a&gt; for easy live patching when I rebuild again. The process was pretty standard. I booted via my &lt;a href="https://netboot.xyz/"&gt;netboot.xyz&lt;/a&gt; PXE server, ran through the installer and then used &lt;a href="https://docs.ansible.com/"&gt;Ansible&lt;/a&gt; to configure the OS. I’ll have the playbooks + task files in the blog-files repo after this post is live.&lt;/p&gt;&lt;aside&gt;&lt;p&gt;&lt;svg&gt;&lt;/svg&gt; Tip&lt;/p&gt;&lt;section&gt;&lt;p&gt;Looking at getting started with Ansible? I highly recommend Jeff Geerling’s &lt;a href="https://www.ansiblefordevops.com/"&gt;Ansible for DevOps&lt;/a&gt; handbook.&lt;/p&gt;&lt;/section&gt;&lt;/aside&gt;&lt;aside&gt;&lt;p&gt;&lt;svg&gt;&lt;/svg&gt; Tip&lt;/p&gt;&lt;section&gt;&lt;p&gt;If you want to know more about the netboot.xyz server, Techno Tim has a great video on it &lt;a href="https://www.youtube.com/watch?v=4btW5x_clpg&amp;amp;pp=ygUSdGVjaG5vIHRpbSBuZXRib290"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/section&gt;&lt;/aside&gt;&lt;video&gt;&lt;source src="https://blog.alexsguardian.net/_astro/ansible_prep.BIf03Imk.webm" type="video/webm"&gt;Your browser does not support webm.&lt;/source&gt;&lt;/video&gt;&lt;span&gt;Ansible playbook running on the cluster nodes&lt;/span&gt;&lt;h3 id="networking"&gt;Networking&lt;/h3&gt;&lt;p&gt;After the OS was set up on all the nodes, I went ahead configured the network interfaces. During the v2 build Ubiquiti launched their Flex 2.5g switch line. Since this was an SFF cluster the &lt;a href="https://store.ui.com/us/en/category/all-switching/products/usw-flex-2-5g-5" target="_blank"&gt;Flex Mini 2.5g&lt;/a&gt; was a perfect fit as the cluster’s network backbone. Also with it being powered by POE made for one less cable to deal with. The cluster network is also a fairly simple one. It has no access to the internet and no access to any other networks. Other networks also can’t access it. Don’t need stuff snooping on my unencrypted SQL traffic..&lt;/p&gt;&lt;p&gt;With the network interfaces configured and working, it was time to test the speed using &lt;a href="https://iperf.fr/" target="_blank"&gt;iperf&lt;/a&gt;. As you can see by the short video below, I was able to get roughly 2.5gbps of bandwidth between the nodes.&lt;/p&gt;&lt;video&gt;&lt;source src="https://blog.alexsguardian.net/_astro/cluster_speed.Crkfr9Hw.webm" type="video/webm"&gt;Your browser does not support webm.&lt;/source&gt;&lt;/video&gt;&lt;span&gt;iperf testing&lt;/span&gt;&lt;h3 id="distributed-file-system"&gt;Distributed File System&lt;/h3&gt;&lt;p&gt;This was probably the most difficult part of the cluster build. At the time I had really no experience dealing with distributed file systems. I narrowed down my options to GlusterFS and Ceph. I ended up going with &lt;a href="https://www.gluster.org/" target="_blank"&gt;GlusterFS&lt;/a&gt; for this iteration. Using the &lt;a href="https://docs.gluster.org/en/main/Quick-Start-Guide/Quickstart/" target="_blank"&gt;official documentation&lt;/a&gt; I was able to get a basic cluster up and running fairly quickly. I also created a dedicated Docker user ‘doc’ and set its home directory to the GlusterFS volume on each of the nodes. I thought this would be a great idea, but it ended up being a pain later on.&lt;/p&gt;&lt;p&gt;&lt;img alt="GlusterFS setup" src="https://blog.alexsguardian.net/_astro/gluster-setup.1YBYIxUu_Z1NJ65g.webp"&gt;&lt;span&gt;GlusterFS setup&lt;/span&gt;&lt;/p&gt;&lt;h3 id="docker-swarm"&gt;Docker Swarm&lt;/h3&gt;&lt;p&gt;With the OS, Networking, and DFS setup, it was time to set up Docker Swarm. Setting up Docker and Docker Swarm is fairly straight forward using the &lt;a href="https://docs.docker.com/engine/install/ubuntu/" target="_blank"&gt;official documentation&lt;/a&gt;. You basically follow the installation on each of the nodes. Then you initialize the swarm on one and join the other nodes to it. In my case my commands were:&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;swarm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--advertise-addr&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;192.168.100.2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;## init the swarm on the cluster network interface&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;swarm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SWMTKN-1-4c2d6gncjh15gaznem5m8t2twwfn09o0paqpjxwdkqr1n76h8j-chr42vcbaobx7v2nrk86q9e93&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;192.168.100.2:2377&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;## joining other nodes to the swarm&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;I then promoted the other two nodes to managers. This way I had proper high availability as it requires a minimum of 3 managers to be in the swarm.&lt;/p&gt;&lt;p&gt;&lt;img alt="Docker Swarm cluster" src="https://blog.alexsguardian.net/_astro/swarm-cluster.MJ-L-vim_2ga04o.webp"&gt;&lt;span&gt;Docker Swarm cluster&lt;/span&gt;&lt;/p&gt;&lt;h3 id="keepalived"&gt;Keepalived&lt;/h3&gt;&lt;p&gt;Next up was setting up &lt;a href="https://www.keepalived.org/" target="_blank"&gt;Keepalived&lt;/a&gt; to handle &lt;a href="https://en.wikipedia.org/wiki/Virtual_Router_Redundancy_Protocol" target="_blank"&gt;VRRP&lt;/a&gt; for the cluster. This allowed me to have a single IP address that automatically failed over to the next node automatically. The video below shows the failover in action (sped up to keep the video short).&lt;/p&gt;&lt;video&gt;&lt;source src="https://blog.alexsguardian.net/_astro/keepalived_node_switching.JYJzVdxF.webm" type="video/webm"&gt;Your browser does not support webm.&lt;/source&gt;&lt;/video&gt;&lt;span&gt;Keepalived VRRP failover&lt;/span&gt;&lt;p&gt;As you can see the active node switches as each keepalived service is terminated. The lower right shows a constant ping to the VRRP virtual IP (VIP). In this case 10.8.8.11. Having this VIP allows me to bring down hosts for maintenance or updates without having to worry about service connectivity.&lt;/p&gt;&lt;h3 id="ha-caddy"&gt;HA Caddy&lt;/h3&gt;&lt;p&gt;The final step before I could start using the cluster was to set up a load balancer/reverse proxy. There are a few different options but, if you’ve read any of my &lt;a href="https://blog.alexsguardian.net/tag/caddy" target="_blank"&gt;previous posts&lt;/a&gt; you know I love using &lt;a href="https://caddyserver.com/" target="_blank"&gt;Caddy&lt;/a&gt;. With its easy-to-use configuration and automatic certificate management makes it hard to beat. Also, Caddy is a great choice for this since it automatically clusters itself if all instances use the same storage volume.&lt;/p&gt;&lt;p&gt;First thing I did was create the new overlay network for Caddy. This network would be for any services that I wanted to be able to access from outside the cluster.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;network&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--opt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;encrypted=&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--driver&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;overlay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--attachable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--internal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--subnet=172.0.96.0/20&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;caddy-internal&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Creating the Caddy service file.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;caddy.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;alexandzors/caddy:2.9.1&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/swarm/volumes/doc/caddy/Caddyfile:/etc/caddy/Caddyfile:ro&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/swarm/volumes/doc/caddy/.data:/data&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/swarm/volumes/doc/caddy/configs:/etc/caddy/configs:ro&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Then deploying Caddy to the swarm.&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stack&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;caddy.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;caddy&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="docker service ls command showing Caddy" src="https://blog.alexsguardian.net/_astro/deployed_caddy.Ci8ZMIoI_1MONUY.webp"&gt;&lt;span&gt;docker service ls command showing Caddy running on all 3 nodes&lt;/span&gt;&lt;/p&gt;&lt;p&gt;I eventually need to update the Caddy deployment to use host mode for port assignments. That way I can get the source IP address of incoming requests and not the docker proxy IP. But that will be part of v3.&lt;/p&gt;&lt;h2 id="cluster-v3"&gt;Cluster v3&lt;/h2&gt;&lt;sub&gt;circa 2025&lt;/sub&gt;&lt;p&gt;So here we are. Cluster v2 has been running great for a while now, but I’ve been having some issues with it. The main, and arguably the largest, issue I have been running into is the slow performance of GlusterFS. I’m not sure if it’s the hardware choices I made, or a misconfiguration? I tried the performance tuning tips from the &lt;a href="https://docs.gluster.org/en/main/Administrator-Guide/Performance-Tuning/" target="_blank"&gt;official documentation&lt;/a&gt; but was still running into issues. Like switching to the cluster user ‘doc’ would take anywhere from 30 to 45 seconds to complete. Also, deploying stacks of services could take up to 5 minutes before they were alive and running.&lt;/p&gt;&lt;p&gt;Either way for v3 I’m probably going to move to &lt;a href="https://ceph.com/" target="_blank"&gt;Ceph&lt;/a&gt; as my DFS solution. Since it seems to be the more popular of the two after digging around more. I may need to upgrade the RAM on the nodes to handle it though. I’m also going to try and add a 4th node to the cluster with beefier hardware since I still have one 2.5g port left on the switch. Maybe one with some GPU compute to handle local LLMs?&lt;/p&gt;&lt;p&gt;&lt;img alt="docker services ls" src="https://blog.alexsguardian.net/_astro/docker_services_ls.GNY8tZvm_Z1uGAAF.webp"&gt;&lt;span&gt;Current services running on the cluster&lt;/span&gt;&lt;/p&gt;&lt;p&gt;However, what won’t be changing is my use of Docker Swarm. &lt;span&gt;&#128521;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://blog.alexsguardian.net/_astro/rebuild_banner.AfLrHjH8_ZS3Mfz.webp"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19de73bd2a9:21797a:4bf1e6c6</id>
        <title type="html">How to smoke</title>
        <published>2026-05-02T05:49:10Z</published>
        <updated>2026-05-02T05:49:14Z</updated>
        <link href="https://buttondown.com/monteiro/archive/how-to-smoke/" rel="alternate" type="text/html"/>
        <summary type="html">I used to love to smoke. If it weren’t for the whole lung cancer, emphysema, death thing, would you recommend smoking?</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
    
        &lt;figure&gt;&lt;img alt="Four zippo lighters stacked up, sitting on pink paper." src="https://assets.buttondown.email/images/c2f95460-4ccf-4823-a800-01c37d5a8ced.jpeg?w=960&amp;amp;fit=max"&gt;&lt;figcaption&gt;&lt;em&gt;The best part of smoking was always the click of the Zippo.&lt;/em&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;This week’s question comes to us from Mat Honan:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I used to love to smoke. If it weren’t for the whole lung cancer, emphysema, death thing, would you recommend smoking?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You’re forgetting the smell.&lt;/p&gt;
&lt;p&gt;Last week I was coming back from the record store and feeling lazy, so I jumped on the bus. A couple of stops after I got on, a dude got on and sat down next to me. Well-dressed dude, also carrying an Amoeba Records shopping bag. Out and about on a Sunday afternoon, doing his record shopping just like me. Within seconds it became clear this dude had &lt;em&gt;just&lt;/em&gt; had a cigarette. And he stunk. The bus was also packed at this point, so I was pretty much stuck in that seat until I got off, which luckily wasn’t for too much longer. Also, it feels kinda shitty when you sit down next to somebody on the bus and they immediately get up. (Unless you’re getting a creeper vibe, of course.) I just had to suck it up for a few more stops. But man, it was rough. I’m not trying to disparage the guy or anything. Especially because I used to be that guy, and you used to be that guy, and I’m guessing a lot of our readers used to be that guy. We just walked around smelling awful.&lt;/p&gt;
&lt;p&gt;Which is not to say that’s the worst part of smoking, but you took lung cancer, emphysema, and the dead thing off the table. Which leaves us with the smell.&lt;/p&gt;
&lt;p&gt;I started smoking my freshman year in college. All reasons to take up smoking are stupid, but this one might be the stupidest. This being the mid-80s our dorm had a cigarette machine in the lobby. One of those old machines you might still see in old man dive bars, with two rows of big ka-chunky knobs, which felt objectively good to pull. It was like you could feel the entire mechanism of the machine come to life when you pulled that knob. And it took some strength to do it! But you could feel the knob hit a gear, you could hear the gear spin, you could feel a pack of cigarettes get pushed free from deep inside the machine. You could hear it slide down a little ramp, and then you’d see it appear down in the landing zone, where you’d push your hand past the trap door, grab it, and somehow have the pack open, and a cigarette in your mouth before the rest of the pack hit your pocket.&lt;/p&gt;
&lt;p&gt;If memory serves, a pack of smokes was between $1.25 and $1.50 around that time. (Fun fact, I attempted to Google this and the slop top on the search results page said 26¢ a pack, which is… not true. But please, continue to rebuild society around such amazing technology.) Being the art school miscreants that we were, and also broke, we discovered that if you pulled a knob halfway out, inserted a quarter, and then pulled the knob the rest of the way out it would release a pack of cigarettes. (Ok, that may not have been the &lt;em&gt;actual&lt;/em&gt; process, but it was a long time ago, and it’s very close to the &lt;em&gt;spirit&lt;/em&gt; of the actual process, so let’s run with it. So I guess we &lt;em&gt;were&lt;/em&gt; getting a pack of cigarettes for what Google’s stupid slop robot said, but it included doing crimes.) The knowledge of how to hack the cigarette machine spread through the dorms like wildfire, and soon we were all smoking. Because we were idiots. Also, it felt like we were getting one over on The Man. But mostly because we were idiots.&lt;/p&gt;
&lt;p&gt;Also, being art school kids we were very visually-driven people, and all the photos of cool people that we’d hang up in our dorm rooms showed them holding a cigarette. And we very much wanted to be cool people. (True fact: take a photo of Humphrey Bogart, replace the ever-present cigarette with a vape and Humphrey Bogart will look like an herb.) Again, mostly we were idiots. &lt;/p&gt;
&lt;p&gt;The vending machine company tried to patch the hack several times, eventually gave up and just took the machine away. This was our first lesson that sometimes doing crime is in the public interest, but that lesson didn’t occur to us right away. At the time, we were just pissed that we had to pay retail for cigarettes again. &lt;/p&gt;
&lt;p&gt;By the time I started smoking society was pretty much done with the pretense that smoking was doing anything but murdering you slowly. I know this because we’d sit around in art classes making collages using old cigarette ads where doctors would tell you smoking was good for your nerves, and we would laugh at people for believing this, as we lit cigarette after cigarette. (Yes, you could smoke in class.) And we thought “Boy, our grandparents sure were chumps for believing cigarettes were healthy.” Then we would have a coughing fit. But there was definitely the sense that doing this thing that we all knew had a very very high probability of killing us wasn’t a big deal, mostly because we were in our 20s when nothing can hurt you, Reagan was president and, just to reiterate—we were idiots.&lt;/p&gt;
&lt;p&gt;My first post-college job was at a copy shop, and you got one 15 minute break during your shift. Unless you smoked, then you could get as many breaks as you needed. Several people started smoking while working there.&lt;/p&gt;
&lt;p&gt;We all stunk. We’d come in from smoking out back, and immediately walk up to the service counter to help a customer. A customer who either stunk as bad as we did, or had become inured to the stench because it was all around them, emanating from everyone.&lt;/p&gt;
&lt;p&gt;We smoked in class. We smoked at the movies. We smoked at the supermarket. We smoked at sporting events. My friend Jeff, who grew up in Boston, tells a good story about going to Celtics games as a kid and having to look past the hovering cloud of smoke between the cheap seats and the court. We smoked in restaurants, where the smoking and non-smoking sections were often divided by nothing more than a paper sign denoting the territorial boundary. We smoked on planes, man.&lt;/p&gt;
&lt;p&gt;It wasn’t too long after college that the world began to shift. In 2003 New York City banned smoking in bars. And I was visiting at the time. By 2003, I was no longer “a smoker” but I was very much someone who would look for reasons to bum a smoke from someone if the situation arose, and very likely to put myself in situations where it might. But I remember the rage from several friends and from the owners and bartenders of any bar we’d walk into. The ban was going to kill bars all over the city. It was going to kill nightlife. It was going to completely take down the economy. New York, as we know it, would cease to exist. Which of course, it didn’t. Everyone adjusted. They went outside. They eventually started smoking less because it was cold outside. People enjoyed being able to hang out in rooms that weren’t making them sick, and they enjoyed going home not reeking of cigarette smoke. If I could go back in time I’d reassure all those bartenders that it wasn’t the smoking ban they had to worry about. It was the kids who’d stop going out at all because they needed to sit at home and tend to their AI agents. &lt;/p&gt;
&lt;p&gt;I selected your question this week because I’ve actually been thinking of the smoking ban lately. We grew up in a time when smoking, or dealing with other smokers was an inevitability. Even if you didn’t smoke, you’d most likely work next to someone who did, or sit down next to someone who did at a restaurant, or at the movies. And even if they weren’t actively smoking, and covering you in second-hand smoke, you’d go home with the stench of smoke all over you. Airing your clothes out was an inevitability. Having to wash the stench out of your hair was an inevitability. Society smoked, so you did too. Whether you wanted to or not. And then it changed. Most cities in America now have smoking bans and rules about how far away you have to be from a public entrance to smoke, which get enforced to various degrees. &lt;/p&gt;
&lt;p&gt;The change came in a couple of very interesting ways. One, cigarettes are now hovering between $12 to $14 a pack. (I had to look this up!) They’re also available in less places. (You used to be able to buy cigarettes at the drug store!) Secondly, people just look at you weird if you start smoking now. Like, what the fuck dude, did you just light a cigarette!? Are you from the past? Gross.&lt;/p&gt;
&lt;p&gt;Which of course makes me think of some of the things that we have currently accepted as a society, things which we &lt;em&gt;fully know&lt;/em&gt; are not healthy for society, that we are currently tolerating. And also thinking there’s no way it will ever change, because we appear to be in an era of “what if everyone modeled themselves off the stupidest people?”&lt;/p&gt;
&lt;p&gt;Right now there is someone firing up ChatGPT because it’s cheap. Right now there is someone writing a prompt in Claude because it brings him closer to his co-workers. Right now there is someone walking a co-worker through his agentic workflow, in the same way we attempted to impress one another by blowing smoke rings. Right now there is someone parking a Cybertruck on your street, believing that leaving his divorce where everyone can see it is somehow impressive. We have always been good at ignoring the warnings that came with the pack. &lt;/p&gt;
&lt;p&gt;Our parents packed their homes with asbestos. They heated their homes with coal. They packed their Big Macs in styrofoam. Making mistakes will always be cheaper than fixing them. But nothing is more expensive than ignoring them. &lt;/p&gt;
&lt;p&gt;Cultural norms are an ever-changing thing. History is the story of what was once desirable becoming unacceptable. Something that used to be an inevitability is no longer inevitable. Something that used to be tolerated is no longer tolerated. Something that was seen as a cultural norm no longer is. Even when those things were backed by entire industries with very strong lobbies, as the tobacco lobby once was. The same fate will someday befall the NRA. The same fate will someday befall AIPAC. The same fate will someday befall the slop lobby.&lt;/p&gt;
&lt;p&gt;There was a time we thought if we prohibited people from smoking in bars it would lead to societal collapse. I think it was a good idea. More importantly, it was an idea that worked. It improved not just our personal health but the health of our communities.&lt;/p&gt;
&lt;p&gt;The basic strategy of all addictive technologies is very simple. They make you feel extra capable, they addict you, then they make you feel inadequate without them. They start by making you feel cool, and confident. Relax. Put your feet up. Hang with the fellas. Social anxiety? It’s toasted, dog! Let me write that résumé for you. Anniversary card for your wife? I can write that for you. Light one up. It’s a great way to start a relationship. But that initial boost eventually turns to reliance, and suddenly you can’t get out of bed without a hit. You can’t write your kid a love note without firing up a slop engine. And suddenly an entire industry is telling you that you’re not capable of moving through your day without their help. An entire industry gaslighting you, until it becomes easier to just gaslight yourself into believing that you were never truly capable of things you are very much capable of.&lt;/p&gt;
&lt;p&gt;I don’t miss smoking. Maybe I did at one point. But eventually the whiff of cigarette smoke went from smelling nostalgic to just smelling bad. Thankfully, knock on wood, I’ve been able to escape years of smoking without any &lt;em&gt;major&lt;/em&gt; lasting effects, but trust that I carry every pack I ever smoked with me every time I walk up a flight of stairs. If I’m doing anything strenuous, it’s always my lungs that give up first. &lt;/p&gt;
&lt;p&gt;Thankfully, I still have some lung capacity. I enjoy using it. You should too.&lt;/p&gt;
&lt;p&gt;&#128684;&lt;/p&gt;
&lt;hr&gt;&lt;p&gt;&#128587; Got a question? &lt;a href="https://www.mikemonteiro.com/ask-a-question?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;Ask it&lt;/a&gt;! I will somewhat answer it!&lt;/p&gt;
&lt;p&gt;&#128211; Get your sexy copy of my new book &lt;a href="https://www.mulebooks.com/store/how-to-die-and-other-stories?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;How to Die (and other stories)&lt;/a&gt;! And if you’re in the Bay Area, &lt;a href="https://booksmith.com/event/monteiro26?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;come see me and Annalee Newitz talk about it on May 11 at Booksmith&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&#128674; My friend Lucy Bellwood made a &lt;a href="https://lucybellwood.com/new-comic-the-scale-of-a-man/?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;wonderful comic about loss and building&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&#128581; My friend Jason Cosper made a &lt;a href="https://jasoncosper.com/kill-yr-substack/?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;great tool for fucking with Substack’s dollar&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&#128227; I’ve got a few seats left in next week’s &lt;a href="https://www.eventbrite.com/e/1987951529539?aff=oddtdtcreator&amp;amp;utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;Presenting w/Confidence&lt;/a&gt; workshop where you can learn &lt;em&gt;true&lt;/em&gt; confidence. The one inside you.&lt;/p&gt;
&lt;p&gt;&#127817; Please support &lt;a href="https://www.pcrf.net/?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;the children of Palestine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&#127987;️‍⚧️ Please &lt;a href="https://translifeline.org/?utm_source=monteiro&amp;amp;utm_medium=email&amp;amp;utm_campaign=how-to-smoke" rel="noopener noreferrer nofollow" target="_blank"&gt;support trans kids&lt;/a&gt;. And if there is a trans person in your life please tell them you love them.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
    
&lt;/div&gt;


                


    &lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://assets.buttondown.email/images/c2f95460-4ccf-4823-a800-01c37d5a8ced.jpeg?w=960&amp;fit=max"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://buttondown.com/monteiro/rss</id>
            <title type="html">buttondown.com</title>
            <link href="https://buttondown.com" rel="alternate" type="text/html"/>
            <updated>2026-05-02T05:49:14Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19dcdfde5b1:853577:18edc835</id>
        <title type="html">Crashing hard: why talking about bubbles obscures the real social cost of overinvesting into “Artificial Intelligence”</title>
        <published>2026-04-27T08:11:01Z</published>
        <updated>2026-04-27T08:11:06Z</updated>
        <link href="https://www.structural-integrity.eu/crashing-hard-why-talking-about-bubbles-obscures-the-real-social-cost-of-overinvesting-into-artificial-intelligence/" rel="alternate" type="text/html"/>
        <summary type="html">More and more commentators talk about and warn of an “AI bubble”, and everybody seems to congratulate each other on being such a smart financial analyst. BUT: A bubble pops and you are left with air and maybe a splash of soap somewhere on the floor. A fairly clean affair. This kind of investor speak</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
&lt;p&gt;&lt;a href="https://observer.co.uk/news/science-technology/article/a-new-dotcom-bubble-ai-hype-has-yet-to-translate-into-profits"&gt;More&lt;/a&gt; &lt;a href="https://fluxus.io/article/a-hitchhikers-guide-to-the-ai-bubble"&gt;and&lt;/a&gt; &lt;a href="https://www.goldmansachs.com/insights/top-of-mind/gen-ai-too-much-spend-too-little-benefit"&gt;more&lt;/a&gt; &lt;a href="https://www.economist.com/business/2025/05/21/welcome-to-the-ai-trough-of-disillusionment"&gt;commentators&lt;/a&gt; &lt;a href="https://paulkedrosky.com/honey-ai-capex-ate-the-economy/"&gt;talk&lt;/a&gt; about and warn of an “AI bubble”, and everybody seems to congratulate each other on being such a smart financial analyst. BUT: A bubble pops and you are left with air and maybe a splash of soap somewhere on the floor. A fairly clean affair. This kind of investor speak obscures the severe consequences economic crashes cause, coming from someone’s point of view for whom this is more likely to be a spectacle than a direct threat.&lt;/p&gt;



&lt;p&gt;When the “AI” market crashes, there will be NO “reset button”, NO “rollercoaster” continuing on an orderly path after having come down, NO “bubble” that just lets off hot air. These are all metaphors that heavily misrepresent what it means for markets to crash, or, as they say, “correct”. We might be in for a long and painful struggle to at least reduce the grip of “AI” on current core societal functions like government administration, education and research funding. In this article, I want to illustrate the broad range of costs that BOTH the buildup of “AI” overvaluations AND their coming down will have. The current “AI” investments will have long-term costs by creating significant path dependencies: They make harmful things cheaper, speed up the commodification of human labour and shift social norms. Just to be clear: I am referring to the current “AI” boom which is driven mostly by generative AI (“genAI”) applications, not necessarily the things that have been around for decades (e.g. various forms of pattern recognition) and that did not induce companies to spend hundreds of billions on data centres.&lt;/p&gt;



&lt;p&gt;To better understand what is going on, let’s first look at the outcomes of previous instances of overinvestment, including the 2000 dot-com “bubble” and the significant piles of money Uber burnt for many years, before turning to contemporary “AI” path dependencies.&lt;/p&gt;



&lt;h3 class="wp-block-heading has-custom-pink-color has-text-color has-link-color wp-elements-b1db5c78d832fe90a6c94a3d6c122255"&gt;&lt;strong&gt;&lt;strong&gt;Overinvestments shape technological paths&lt;/strong&gt;&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;Let’s start with the obvious: The so-called dot-com boom crashed between 2000 and 2002 – this already hints at the fact that “bubble bursting” is a long period during which no one knows when it will end. When it did end, investors lost money, and it is mostly their perspective that was &lt;a href="https://www.businessinsider.com/speculative-bubble-lessons-stock-crypto-outlook-dotcom-era-henry-blodget-2021-11"&gt;covered&lt;/a&gt; in the media (and they whined about a crash being less bad than the pain inflicted by missing out on a boom). Many people lost their jobs, their livelihoods, and needed to find other ways to make ends meet (&lt;a href="https://www.reddit.com/r/programming/comments/1cgf1fd/ask_hn_what_was_the_job_market_like_during_the/"&gt;developers on Reddit&lt;/a&gt; gave an account, but they were probably among the more privileged). Unemployment in the US increased from about 4% to almost 6%.&lt;br&gt;What might be a little less obvious: A few key developments that the dot-com boom had started persisted long after. While the internet was still a mostly academic affair until the 1990s, the dot-com boom kicked off the scale-over-everything, ad-based internet we know today. We saw &lt;a href="https://manifold.umn.edu/read/profit-over-privacy/section/ee270b37-d3d9-4312-b318-57ea01c2328f"&gt;alignment of advertising business and finance&lt;/a&gt; as well as a massive drive to consolidation during the crash. Google, eBay, Amazon, Nvidia, they all became central players in the commercial internet. What now seems inevitable to most people seemed coincidental before the boom – but then today’s driving forces crowded out most other, less commercial forms of existing on the internet.&lt;/p&gt;



&lt;hr class="wp-block-separator has-text-color has-custom-pink-color has-alpha-channel-opacity has-custom-pink-background-color has-background is-style-wide"&gt;&lt;h2 class="wp-block-heading has-text-align-center has-custom-pink-color has-text-color has-link-color wp-elements-f4e142a27545cd194d5a3365a0e90d36"&gt;“AI” investments make harmful things cheaper, speed up the commodification of human labour and shift social norms.&lt;/h2&gt;



&lt;hr class="wp-block-separator has-text-color has-custom-pink-color has-alpha-channel-opacity has-custom-pink-background-color has-background is-style-wide"&gt;&lt;p&gt;The investment logic has shaped the internet ever since: Uber accumulated almost $34bn USD in losses (excluding losses while it was not yet public between 2009 and 2014) before it started to generate profits in &lt;a href="https://www.theguardian.com/technology/2024/feb/07/landmark-moment-as-uber-unveils-first-annual-profit-as-limited-company"&gt;2023&lt;/a&gt;. (And also various &lt;a href="https://wolfstreet.com/2021/07/05/todays-unicorns-have-bigger-cumulative-losses-than-amazon-had-lost-money-far-longer-than-amazon-still-dont-show-a-turnaround/"&gt;other unicorns&lt;/a&gt; incur massive losses they may never recoup.) Money also creates habits and legitimises actions: Uber “&lt;a href="https://www.theguardian.com/news/2022/jul/10/uber-files-leak-reveals-global-lobbying-campaign"&gt;broke laws, duped police and secretly lobbied governments&lt;/a&gt;”, as the Guardian titled in 2022 and its controversies got their own &lt;a href="https://en.wikipedia.org/wiki/Controversies_surrounding_Uber"&gt;Wikipedia page&lt;/a&gt;. But also their very official business model is based on a) normalising precarious working conditions by eroding labour protections as they fought countless lawsuits over giving their workers only freelance and not an employee status, thereby avoiding sick pay, holidays etc., and b) making human work seem more automated and less visible by mediating passengers and drivers through an algorithm in an app, reducing the need for actual human interaction. Both developments shift social norms in ways that are likely to be profitable for businesses even beyond Uber: &lt;strong&gt;Uber’s mission can be understood as reducing the value of workers and human interaction, and with that long-term goal it is commercially rational to rack up significant losses in the short to medium term.&lt;/strong&gt;&lt;/p&gt;



&lt;h3 class="wp-block-heading has-custom-pink-color has-text-color has-link-color wp-elements-ef31312ad6f1dee58b7bf2382857e854"&gt;&lt;strong&gt;The “AI” path dependencies we will not correct&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;It is plausible to expect something similar to happen with “AI”. The ongoing investment boom is creating significant overcapacity with effects lasting long after many “AI” startups have gone bust. These range from very visible and direct to the more indirect and structural.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Lower costs for compute and energy&lt;/strong&gt;: In order to sustain the boom, investments follow projections of endless “AI” growth, which translate into hundreds of billions currently being invested into data centre construction, alongside an expansion of energy infrastructure. And once they are built, it does not make sense to stop them, does it? Keeping them running is much cheaper than constructing them. This puts us on a path of energy-intensive technology even once these data centres are no longer needed for “AI” applications. This eradicates any incentive for resource-efficient coding or low-computation technology. At the same time, this infrastructure is not costless to maintain (to my knowledge, chips need to be replaced about every 7 years) – but possibly that cost is still lower than doing anything else, hence continuing on that trajectory will remain cheaper than alternatives for quite some time to come. These artificially low costs of compute and energy will be even further away from the “real” costs when factoring in just the environmental harms they produce. That is very bad news for anybody still hoping for a combined digital and environmental transition.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Sectoral knowledge destruction&lt;/strong&gt;: Some people may get used to using “AI” for a variety of tasks, even where this is neither actually helpful nor profitable for the “AI” providers. As is widely reported, managers often &lt;a href="https://www.telegraph.co.uk/business/2025/07/21/bosses-warn-workers-use-ai-or-face-the-sack/"&gt;encourage&lt;/a&gt; or &lt;a href="https://www.pcgamer.com/gaming-industry/ai-is-no-longer-optional-microsoft-is-allegedly-pressuring-employees-to-use-ai-tools-through-manager-evaluations/"&gt;force&lt;/a&gt; their employees to e.g. code using genAI applications, &lt;a href="https://www.nature.com/articles/s41598-025-92937-2"&gt;university students use genAI&lt;/a&gt; applications for writing, public bodies are continuing to move onto fancy “AI” clouds and &lt;a href="https://berthub.eu/articles/posts/our-self-inflicted-cloud-crisis/"&gt;forget how to do on-premise computing&lt;/a&gt;, and we are likely to see more diffusion before the boom crashes. Just as Uber’s mission was broader than just individual transport, “AI” has an inbuilt contempt for human interaction (as it is built to automate speech while avoiding any interpersonal &lt;a href="https://tante.cc/2025/07/30/friction-and-not-being-touched/"&gt;friction&lt;/a&gt;) and workers (as it seeks to make them even more interchangeable and subordinate to machine processes). Hence, using “AI” often destroys established processes of developing skills and sharing knowledge. Rebuilding them will take much longer and possibly cost more than might have been saved in the meantime.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Again more economic inequality&lt;/strong&gt;: An economic crisis is not equally dangerous for everyone. Only few companies are benefitting from the “AI” boom and most of the stock market gains in recent years were driven by Big Tech valuations going absolutely through the roof. However, we can expect that the losses will be shared more widely, based on the experience of past financial crises. Big Tech is trying to portray itself as &lt;a href="https://ainowinstitute.org/publications/research/1-2-too-big-to-fail-infrastructure-and-capital-push"&gt;too big to fail&lt;/a&gt;, which means that their &lt;a href="https://www.wheresyoured.at/ai-is-a-money-trap/"&gt;systemic relevance&lt;/a&gt; would prompt governments to inject tax money to reduce any losses they might incur. A financial crisis has ripple effects that go far beyond the market in question – just as the 2008 US housing crisis did not only lead to people losing their homes, but caused a huge &lt;a href="https://www.federalreservehistory.org/essays/great-recession-and-its-aftermath"&gt;recession&lt;/a&gt; with a stark increase in unemployment and financial instability.  &lt;/p&gt;



&lt;h3 class="wp-block-heading has-custom-pink-color has-text-color has-link-color wp-elements-bec4897a635f69b0ad9ad50bc79da3ef"&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;Why “AI” overinvestments might take a long time to unwind&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;There is no way of knowing when the “AI” boom is likely to crash – that is the whole point of markets that are supposed to create collective rationality from individual choices. I see a few reasons to suspect an even longer and more painful struggle than the dot-com crash in 2000. First, the boom is &lt;a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5377426"&gt;orchestrated&lt;/a&gt; or arguably &lt;a href="https://www.tandfonline.com/doi/full/10.1080/09692290.2024.2365757"&gt;planned&lt;/a&gt; by a handful of extremely powerful companies. Being few increases the scope to act strategically. Second, these companies are very close not only to the US government, but through their start-up investments also to governments across the world, selling “AI” promises and lies to politicians. The push of small and large “AI” firms into military tech aggravates this dynamic: It is the area in which talking of an “AI race” carries quite intuitive meaning because having more destructive power translates into military power, though not necessarily into better societal outcomes. And third, the intention of reaching systematic relevance is bearing some fruit as more and more institutions are becoming financially invested into “AI success”. Not only VC investors, but &lt;a href="https://www.noahpinion.blog/p/will-data-centers-crash-the-economy"&gt;large parts of society including life insurance and pension funds&lt;/a&gt; will bear the cost of its failure, giving them an incentive to prolong the boom at fairly high costs.&lt;/p&gt;



&lt;hr class="wp-block-separator has-text-color has-custom-pink-color has-alpha-channel-opacity has-custom-pink-background-color has-background is-style-wide"&gt;&lt;h2 class="wp-block-heading has-text-align-center has-custom-pink-color has-text-color has-link-color wp-elements-345884f2ed11f719063a6e54d5330c77"&gt;There are a few reasons to suspect an even longer and more painful struggle than the dot-com crash: market concentration, closeness to governments, and financial actors being invested into &lt;strong&gt;&lt;strong&gt;&lt;strong&gt;“&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;AI success&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;”&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;.&lt;/h2&gt;



&lt;hr class="wp-block-separator has-text-color has-custom-pink-color has-alpha-channel-opacity has-custom-pink-background-color has-background is-style-wide"&gt;&lt;h3 class="wp-block-heading has-custom-pink-color has-text-color has-link-color wp-elements-5e90c772184a31765a55f8b88dde9ff6"&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;What to do and why not to despair&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/h3&gt;



&lt;p&gt;It is important to analyse the abyss, but don’t stare into the abyss, as Jathan Sadowski sometimes says on my currently favourite podcast &lt;a href="https://soundcloud.com/thismachinekillspod"&gt;This Machine Kills&lt;/a&gt;. Understanding what is happening is essential to figure out a plan and I am keen to do that with others (i.e. I am aware my suggestions are insufficient). Anything that contributes to not making the boom bigger than it needs to be is helpful (e.g. do not invest into “AI”, tell your friends not to, do not use genAI applications or pay for them). Anything that helps us to talk in less delusional terms about what is going on is helpful (e.g. do not join those talking about “bubbles” suggesting they are merely financial events or that have one moment of coming down after which everything will be okay again). And let’s try to preserve that knowledge that companies are keen to replace with “AI”. We will need it.&lt;/p&gt;



&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@nampoh?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Maxim Hopman&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/red-and-blue-light-streaks-fiXLQXAhCfk?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://www.structural-integrity.eu/wp-content/uploads/2025/08/maxim-hopman-fiXLQXAhCfk-unsplash-scaled.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://www.structural-integrity.eu/feed/</id>
            <title type="html">Structural Integrity</title>
            <link href="https://www.structural-integrity.eu" rel="alternate" type="text/html"/>
            <updated>2026-04-27T08:11:06Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19dc5b77d61:2d20d7:348926d</id>
        <title type="html">Wrap Text Around Images with CSS shape-outside</title>
        <published>2026-04-25T17:37:09Z</published>
        <updated>2026-04-25T17:37:14Z</updated>
        <link href="https://theosoti.com/short/wrap-text-around-images/" rel="alternate" type="text/html"/>
        <summary type="html">Use shape-outside in CSS to wrap text around custom image shapes—no JavaScript, just clean, creative layout control.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;article&gt;&lt;source type="image/webp"&gt;&lt;img src="https://theosoti.com/short/07-2025/shape-outside-image.avif" alt="Text wrapping tightly around the shape of an image using CSS shape-outside and float for a refined layout"&gt;&lt;/source&gt;&lt;h2 id="make-your-text-wrap-around-images-perfectly"&gt;Make your text wrap around images perfectly.&lt;/h2&gt;
&lt;p&gt;By default, text wraps around a boring rectangle.
But what if you could make it hug the actual shape of the image?&lt;/p&gt;
&lt;p&gt;You can.
With just one CSS property:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shape-outside: url(your-img.png);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Add a &lt;code&gt;float&lt;/code&gt; and a bit of margin with &lt;code&gt;shape-margin&lt;/code&gt;,
and your layout feels instantly more refined.&lt;/p&gt;
&lt;p&gt;No JavaScript. No layout hacks.
Just native CSS support and it’s supported in over 95% of browsers.&lt;/p&gt;
&lt;p&gt;Use it with transparent PNGs, SVGs, or even basic shapes like &lt;code&gt;circle()&lt;/code&gt; or &lt;code&gt;polygon()&lt;/code&gt;.
Ideal for editorial layouts, landing pages, or any design that needs more personality.&lt;/p&gt;
&lt;p&gt;Checkout the codepen: &lt;a href="https://codepen.io/theosoti/pen/ogjjged"&gt;https://codepen.io/theosoti/pen/ogjjged&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shape-outside&lt;/code&gt; can produce elegant editorial layouts when image silhouettes stay simple. Test long paragraphs and varied image ratios, because float-based wrapping can become fragile in edge cases.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shape-outside&lt;/code&gt; can create editorial layouts with strong flow, but test with varied image sizes and long text. Float-based wrapping can break faster than block layouts.&lt;/p&gt;
&lt;p&gt;Use this as a readability tool, not only a visual effect. The best result is when style improves scanning speed without adding cognitive load.&lt;/p&gt;
&lt;p&gt;To roll this out safely, start by applying &lt;code&gt;shape-outside: url(your-img.png);&lt;/code&gt; in a single UI surface where the benefit is obvious. Then reuse that same pattern in similar contexts so behavior stays consistent and review time stays low.&lt;/p&gt;
&lt;p&gt;Before shipping, test &lt;code&gt;shape-outside: url(your-img.png);&lt;/code&gt; with both short and long content, then verify behavior in narrow and wide containers.&lt;/p&gt;
&lt;hr&gt;&lt;p&gt;If you liked this tip, you might enjoy the book, which is packed with similar insights to help you build better websites without relying on JavaScript.&lt;/p&gt;
&lt;p&gt;Go check it out &lt;a href="https://theosoti.com/you-dont-need-js/"&gt;https://theosoti.com/you-dont-need-js/&lt;/a&gt; and enjoy 20% OFF for a limited time!&lt;/p&gt;  &lt;/article&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://theosoti.com/short/07-2025/shape-outside-image.avif"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19dbdc617f2:2a260d:93a1d9ea</id>
        <title type="html">Why The Split? - MeshCore Blog</title>
        <published>2026-04-24T04:36:09Z</published>
        <updated>2026-04-24T04:36:13Z</updated>
        <link href="https://blog.meshcore.io/2026/04/23/the-split" rel="alternate" type="text/html"/>
        <summary type="html">Migrating to the new meshcore.io site</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
        &lt;p&gt;Since inception, the MeshCore development team have been working hard to build MeshCore.&lt;/p&gt;

&lt;p&gt;We’ve released more than 85 versions of the MeshCore Companion, Repeater and Room Server firmwares with support for more than 75 hardware variants.
All of this has been hand crafted, by humans.&lt;/p&gt;

&lt;p&gt;We have always been wary of AI generated code, but felt everyone is free to do what
they want and experiment, etc. But, one of our own, Andy Kirby, decided to branch out
and extensively use Claude Code, and has decided to aggressively take over
all of the components of the MeshCore ecosystem: standalone devices, mobile app, 
web flasher and web config tools.&lt;/p&gt;

&lt;p&gt;And, he’s kept that &lt;em&gt;small&lt;/em&gt; detail a secret - that it’s all majority &lt;em&gt;vibe coded&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We ran a poll recently, and asked in the MeshCore Discord about AI and trust, and these are the results:&lt;/p&gt;

&lt;p&gt;&lt;img alt="" src="https://blog.meshcore.io/assets/images/2026/04/23/trust-ai-gen-firmware.png"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img alt="" src="https://blog.meshcore.io/assets/images/2026/04/23/have-right-to-know.png"&gt;&lt;/p&gt;

&lt;p&gt;The team didn’t feel it was our place to protest, until we recently discovered that Andy
applied for the MeshCore Trademark (on the 29th March, according to filings) and didn’t tell
any of us. We have tried discussing this, and what his intentions are, but those broke down
and we now have no communication with Andy.&lt;/p&gt;

&lt;p&gt;It’s been a stressful few months trying to sort this out, and is now a sad day
to bring this out to the public. It’s been a slap in the face to the team that
have worked so hard on this project, to have an insider team up with a robot
and a lawyer.&lt;/p&gt;

&lt;h2 id="official-meshcore"&gt;“Official” MeshCore&lt;/h2&gt;

&lt;p&gt;The use of the ‘official’ status is what is currently being contested. Andy is adamant 
that he &lt;em&gt;owns&lt;/em&gt; the brand, and is using the word very heavily with his MeshOS line.&lt;/p&gt;

&lt;p&gt;Meanwhile, in reality, the only ‘official’ MeshCore is the github repo. It’s the
&lt;em&gt;source of truth&lt;/em&gt; in terms of what is MeshCore, and Andy has &lt;em&gt;never&lt;/em&gt; contributed
to that.&lt;/p&gt;

&lt;p&gt;Since the internal split, we launched the &lt;a href="https://meshcore.io"&gt;meshcore.io&lt;/a&gt; site, as Andy controls
the meshcore.co.uk site and original discord server. We’ve been left with little other recourse. And, since 
launching the site, Andy copied the look and feel (again, using Claude) even though
we asked him not to.&lt;/p&gt;

&lt;h2 id="project-growth"&gt;Project Growth&lt;/h2&gt;

&lt;p&gt;The MeshCore project has been on an incredible journey.&lt;/p&gt;

&lt;p&gt;Having only started in January 2025, we have grown extremely fast!&lt;/p&gt;

&lt;p&gt;As of this post, the official &lt;a href="https://map.meshcore.io"&gt;MeshCore Map&lt;/a&gt; shows 38,000+ nodes around the world, and the official &lt;a href="https://meshcore.io"&gt;MeshCore App&lt;/a&gt; has more than 100,000+ active users across Android and iOS.&lt;/p&gt;

&lt;p&gt;It’s pretty epic how we’ve all built such an incredible community in such as a short time!&lt;/p&gt;

&lt;p&gt;As the project grows, so does our need for a dedicated space that provides you with official information from the &lt;em&gt;core team&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In recent times, we’ve seen an explosion of growth in MeshCore web sites dedicated to specific countries and mesh communities.&lt;/p&gt;

&lt;p&gt;To name a few, we’ve seen:&lt;/p&gt;



&lt;p&gt;Andy Kirby did do an amazing job helping to promote the MeshCore project on his personal YouTube, but only promotes his own products now.&lt;/p&gt;

&lt;h2 id="where-to-from-here"&gt;Where To From Here?&lt;/h2&gt;

&lt;p&gt;So, the core team are pushing ahead with the &lt;a href="https://meshcore.io"&gt;meshcore.io&lt;/a&gt; website, the ongoing work of firmware feature development,
bug fixes, managing PR’s and developer discussions, etc.&lt;/p&gt;

&lt;p&gt;We now release change logs, blog posts and technical documentation for all of our new firmware and app releases here.&lt;/p&gt;



&lt;p&gt;You’ll also find some familiar faces on our blog posts, such as:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Scott&lt;/strong&gt; our project founder, lead firmware engineer and developer of the Ripple firmware!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Recrof&lt;/strong&gt; our official MeshCore Map developer and Firmware Flasher guru. He has shared some insights into the early development of the MeshCore Map.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Liam Cottle&lt;/strong&gt; the official MeshCore App developer who will be posting useful guides for getting started with the MeshCore App.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FDLamotte&lt;/strong&gt; who has done epic work on the Python tooling for MeshCore, as well as the STM32 firmware variants.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Oltaco&lt;/strong&gt; (Che Aporeps) who has done amazing work on the new OTA Fix bootloader that makes firmware updates much more reliable.&lt;/li&gt;
&lt;/ul&gt;&lt;h2 id="the-core-team"&gt;The Core Team&lt;/h2&gt;

&lt;p&gt;The MeshCore team, now consisting of &lt;strong&gt;Scott&lt;/strong&gt;, &lt;strong&gt;Liam&lt;/strong&gt;, &lt;strong&gt;Recrof&lt;/strong&gt;, &lt;strong&gt;FDLamotte&lt;/strong&gt; and now &lt;strong&gt;Oltaco&lt;/strong&gt; remain committed to designing and developing high quality, &lt;em&gt;human-written&lt;/em&gt; software.&lt;/p&gt;

&lt;h2 id="our-new-home"&gt;Our New Home&lt;/h2&gt;

&lt;p&gt;Please update your bookmarks!&lt;/p&gt;

&lt;p&gt;This is where we will be hosting all official releases, technical documentation, and community discussions moving forward.&lt;/p&gt;

&lt;p&gt;With the new website, we are also starting fresh with a new Discord server!&lt;/p&gt;

&lt;p&gt;This is where you can interact directly with the MeshCore developers, get help with your projects, and contribute to the future of MeshCore.&lt;/p&gt;



&lt;p&gt;Thanks for being a part of this journey!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The MeshCore Team&lt;/em&gt;&lt;/p&gt;

    &lt;/div&gt;

    

&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://blog.meshcore.io/assets/images/icon.png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://blog.meshcore.io/feed.xml</id>
            <title type="html">blog.meshcore.io</title>
            <link href="https://blog.meshcore.io" rel="alternate" type="text/html"/>
            <updated>2026-04-24T04:36:13Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19dba76e5d6:36e5c9:335607a2</id>
        <title type="html">How to Stop A Data Center in Your Backyard ~ L.A. TACO</title>
        <published>2026-04-23T13:10:47Z</published>
        <updated>2026-04-23T13:10:51Z</updated>
        <link href="https://lataco.com/stop-sgv-data-center-building" rel="alternate" type="text/html"/>
        <summary type="html">These are lessons from San Gabriel Valley neighbors and activists who outsmarted developers and lobbyists.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;When the people of Monterey Park found that their local government was going to approve a &lt;a href="https://www.datacenterdynamics.com/en/news/proposal-for-250000-sq-ft-data-center-in-monterey-park-california-facing-opposition/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;250,000&lt;/strong&gt;&lt;/a&gt;-square-foot data center just 500 feet from their homes, they organized. &lt;/p&gt;&lt;p&gt;And within a few months, the developer withdrew their application.&lt;/p&gt;&lt;p&gt;Andrew Yip, an organizer with&lt;a href="https://www.sgvprogressiveaction.org/" target="_blank" rel="noreferrer noopener"&gt; &lt;strong&gt;SGV Progressive Action&lt;/strong&gt;&lt;/a&gt;, tells L.A. TACO that the organization’s success started with their “existing network of volunteers,” noting that “the community was able to jump in at a moment's notice.” &lt;/p&gt;&lt;p&gt;SGV Progressive Action was founded in 2020 to “address the Black Lives Matter uprisings," Yip says. "To support our Black community." &lt;/p&gt;&lt;p&gt;Then it organized &lt;a href="https://lapublicpress.org/2024/05/ceasefire-resolutions-have-spread-across-san-gabriel-valley-and-southeast-la/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;local resolutions&lt;/strong&gt;&lt;/a&gt; advocating for a ceasefire in Palestine, and built a lending library in El Monte called &lt;a href="https://www.instagram.com/matilijacollective/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;Matilija&lt;/strong&gt; &lt;strong&gt;Collective&lt;/strong&gt;&lt;/a&gt;, where they trained volunteers in community defense against ICE, hosted organizers, and stored 20 canopies and a speaker system. &lt;/p&gt;&lt;p&gt;"So that existed," Yip says.&lt;/p&gt;&lt;p&gt;In November, a community member who had come to a council meeting for other business saw the data center on the agenda and called on SGV Progressive Action. &lt;/p&gt;&lt;p&gt;"They asked if we can take a look at this," Yip says. "And see if that's something that communities should be concerned about."&lt;/p&gt;&lt;p&gt;All that was needed was one last council vote. But the developer requested a delay to the next meeting. &lt;/p&gt;&lt;p&gt;"Had they voted that day, it would have been done, right? It would have been done," Yip says. “But we found out about it, and we turned out hundreds of people to the next meeting.”&lt;/p&gt;&lt;h3 class="wp-block-heading"&gt;CA PUBLIC RECORDS ACT&lt;/h3&gt;&lt;p&gt;Under &lt;a href="https://oag.ca.gov/open-meetings" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;California's Sunshine Laws&lt;/strong&gt;&lt;/a&gt;, local governments are required to turn over agendas, meeting minutes, attendance records, and emails. SGV Progressive Action immediately filed public records requests. &lt;/p&gt;&lt;p&gt;"That's really how we found out," Yip says.&lt;/p&gt;&lt;p&gt;The records showed city planners had given their &lt;a href="https://www.montereypark.ca.gov/DocumentCenter/View/16863/1977-Saturn-Data-Center---Notice-of-Intent-to-Adopt-a-Mitigated-Negative-Declaration" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;blessings&lt;/strong&gt;&lt;/a&gt; to the data center, saying it would not result in any “significant environmental impacts.” They had used the developer's own impact &lt;a href="https://ceqanet.lci.ca.gov/2024101397#:~:text=The%20Project%20would%20demolish%20the,of%20mechanical%20equipment%20platform%20screening)." target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;assessment&lt;/strong&gt;&lt;/a&gt; in place of a more thorough state environmental review. &lt;/p&gt;&lt;p&gt;The records also showed the city had held a series of community meetings to ask residents what should be built at 1977 Saturn Street, but only notified people living within 500 feet. Each meeting drew between 20 and 60 people. Residents who were there told Yip and other organizers that the city clerk brought in people who backed the data center. It won with roughly 20 votes.&lt;/p&gt;&lt;p&gt;“Twenty-something votes determined [that] residents here wanted a data center,” Yip says. ”That just seemed like a weird recommendation coming out of a community town hall.”&lt;/p&gt;&lt;p&gt;Council Member Thomas Wong, who would vote for having the data center, also works at the power company that would sell its electricity.&lt;/p&gt;&lt;p&gt;The developer also bought a larger property at 1980 Saturn Street across the street. The data center trade magazines &lt;a href="https://www.latimes.com/b2b/ai-technology/story/2024-12-20/vacant-monterey-park-office-building-to-be-converted-into-data-center" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;reported&lt;/strong&gt;&lt;/a&gt; that these were part of a 13-parcel assembly, all meant for data centers. Yip asked the developers what they planned to do with 1980 Saturn; they said they were not authorized to discuss it.&lt;/p&gt;&lt;h3 class="wp-block-heading"&gt;NO DATA CENTER MPK &amp;amp; THE INFORMATION CAMPAIGN&lt;/h3&gt;&lt;p&gt;The residents of Monterey Park bought the domain &lt;a href="https://www.nodatacentermpk.org/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;No Data Center MPK&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;“And we had a ton of people come out to support, whether it's walking the neighborhoods, distributing fliers, calling folks, creating artwork,” Yip says. “It was a big showing.”&lt;/p&gt;&lt;p&gt;They went door-to-door to tell their neighbors what the city had not told them: The data center would use twice as much electricity as all of Monterey Park. The 14 “backup” &lt;a href="https://lapublicpress.org/2026/01/a-data-center-boom-is-coming-to-the-san-gabriel-valley-residents-had-no-idea/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;generators&lt;/strong&gt;&lt;/a&gt; would burn 200,000 gallons of diesel every year, without a blackout. And more when the grid price was high.&lt;/p&gt;&lt;p&gt;They held a teach-in. 150 people came. &lt;/p&gt;&lt;p&gt;“We have a lot of very smart residents who were able to do a lot of this research and fact-finding. Many of the residents we work with are researchers or hold PhDs, and they work in universities. So they know how to find this information,” Yip says. &lt;/p&gt;&lt;p&gt;One resident 3D-printed a model of the data center to show just how much of the neighborhood’s space it would take up. Another resident mixed noise recordings from data centers and played them over a loudspeaker. You couldn't hear the birds. &lt;/p&gt;&lt;p&gt;Two dozen people in Virginia who lived near a data center were ready to fly out to testify on their own dime. &lt;/p&gt;&lt;p&gt;“Virginia became ground zero for data center proliferation,” says Yip. “The people didn't know enough about data centers at the time.”&lt;/p&gt;&lt;p&gt;The vibrations and noise never stop, the people from Virginia warned. The reported sound levels of 60db and higher are far from the data center. They have taken to &lt;a href="https://www.businessinsider.com/data-centers-northern-virginia-noise-air-pollution-cost-2025-5" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;sleeping&lt;/strong&gt;&lt;/a&gt; in their basements. Neither a decibel meter nor the law measures vibration.&lt;/p&gt;&lt;p&gt;Yip and the organizers found that almost nobody in Monterey Park that they spoke to had heard of the data center. The city’s notification had only reached 40 people living within 500 feet of its proposed location, in English. &lt;/p&gt;&lt;p&gt;The neighborhood is 65 percent Asian and 27 percent Latino. The community’s outreach was done in at least three languages, five when necessary. It extended to all the surrounding neighborhoods.&lt;/p&gt;&lt;p&gt;“Data centers, their pollution, and their effects don't just stop at the border,” Yip says.&lt;/p&gt;&lt;p&gt;They started a petition in English, Chinese, and Spanish. It grew to 4,500 signatures.&lt;/p&gt;&lt;h3 class="wp-block-heading"&gt;THE DEVELOPERS &amp;amp; THE DISINFORMATION CAMPAIGN&lt;/h3&gt;&lt;p&gt;The developers retained a law firm with 1,000 attorneys on four continents. They &lt;a href="https://www.sgvtribune.com/2026/03/03/monterey-park-data-center-plan-back-in-front-of-city-council/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;hired&lt;/strong&gt;&lt;/a&gt; &lt;a href="https://actumllc.com/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;Actum&lt;/strong&gt;&lt;/a&gt;, the lobbying firm that represents Amazon and Clorox. Actum lists Trump’s former chief of staff as a &lt;a href="https://actumllc.com/people/mick-mulvaney/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;partner&lt;/strong&gt;&lt;/a&gt; and another who exploited a loophole so large that California had to &lt;a href="https://www.sacbee.com/news/politics-government/capitol-alert/article314044368.html" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;legislate&lt;/strong&gt;&lt;/a&gt; to close it.&lt;/p&gt;&lt;p&gt;"The applicant sent a letter to the city council talking about misinformation being spread, and that was exactly what the council members said," Yip tells us. "They just parroted the same talking points."&lt;/p&gt;&lt;p&gt;The political lobbyists canvassed neighborhoods and local shops. &lt;/p&gt;&lt;p&gt;"One of the public benefits of the project they were touting was a pocket park ... The pocket park is basically just leftover land on their property that they didn't really need for the data center," Yip says.&lt;/p&gt;&lt;p&gt;They promised 200 jobs and, in the same conversation, no traffic. &lt;/p&gt;&lt;p&gt;“Our people would poke holes in it,” Yip says. “Why wouldn't there be cars in that facility if you're going to have a lot of employees?”&lt;/p&gt;&lt;p&gt;No Data Center MPK retained an environmental and land use attorney. They recommended an ordinance banning data centers immediately, then to reinforce it with a ballot measure.&lt;/p&gt;&lt;p&gt;They set up a one-click email so residents could send comments to City Council demanding both.&lt;/p&gt;&lt;p&gt;Hundreds showed up to the next three council meetings. &lt;/p&gt;&lt;p&gt;“We played mahjong on the City Hall lawn while we waited. We had a lion dance right outside to cheer people on as they entered the council chambers,” Yip says. &lt;/p&gt;&lt;p&gt;The chambers filled, overflowing into the aisles and hallways.&lt;/p&gt;&lt;p&gt;The first meeting produced a 45-day ban on data centers, the second brought a ballot measure that would ban them forever. &lt;/p&gt;&lt;p&gt;During the third meeting, opponents of the data center called for a rally at 5:30 p.m. before the 6:30 p.m. meeting. Steven Kung, the Monterey Park resident who purchased the No Data Center MPK domain, addressed the developers, their lawyers, and the lobby firm directly.&lt;/p&gt;&lt;p&gt;“You’re fighting an uphill battle against an entire city that doesn’t want you here and yet you continue to bully your way into this community of color, to pollute the air we breathe, to make electricity more expensive, to devalue our homes, to drain our energy and resources like a parasite,” Kung &lt;a href="https://www.instagram.com/reels/DUYeFzXgCYe/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;says&lt;/strong&gt;&lt;/a&gt;. “You think you can take us on? You’ve messed with the wrong city. ”&lt;/p&gt;&lt;p&gt;On March 31, the developer withdrew its application.&lt;/p&gt;&lt;p&gt;“They underestimated the community's passion. And we never underestimated them. And I think that was a good strategy,” says Yip.&lt;/p&gt;&lt;p&gt;The &lt;a href="https://www.ca.gov/departments/235/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;FPPC&lt;/strong&gt;&lt;/a&gt;, California’s political ethics commission, saw the data center would have a “material financial effect” on councilmember Wong. His power to vote on them was &lt;a href="https://www.fppc.ca.gov/siteassets/documents/legal_div/advice_letters/2020-2026/2026/25147.pdf" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;stripped&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Yip says the people who joined for the fight over 1977 Saturn Street have overwhelmingly stayed active with SGV Progressive Action.&lt;/p&gt;&lt;p&gt;"They recognize it's not just about data centers. It's about building community and protecting your community," he says.&lt;/p&gt;&lt;p&gt;The volunteers who organized against the data center are now working to &lt;a href="https://www.instagram.com/p/DVrs4wxDwKk/?img_index=1" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;strengthen&lt;/strong&gt;&lt;/a&gt; sanctuary ordinances to protect their communities from ICE.&lt;/p&gt;&lt;p&gt;“And now we have a coalition of all these community members coming from La Puente, Avocado Heights, Rowland Heights, and Hacienda Heights coming together to fight this common enemy. And I'm just going to name it the City of Industry,” Yip says.&lt;/p&gt;&lt;h3 class="wp-block-heading"&gt;NO DATA CENTERS SGV COALITION&lt;/h3&gt;&lt;p&gt;Samuel Brown Vazquez rode ten miles horseback from Avocado Heights to the Monterey Park council meeting. Vazquez is a community organizer and founding member of the &lt;a href="https://www.instagram.com/avocadoheightsvaqueros/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;Avocado Heights Vaqueros&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;The City of Industry is in the process of approving zoning changes that would clear the way for three data centers and battery energy storage that would affect the residents of Hacienda Heights, La Puente, Walnut, Diamond Bar, West Covina and others.&lt;/p&gt;&lt;p&gt;The Avocado Heights Vaqueros, SGV Progressive Action, and others organized across city and county lines. &lt;/p&gt;&lt;p&gt;“We created an infographic and started mobilizing folks,” Vazquez says. They are &lt;a href="https://www.nodatacenterssgvcoalition.org/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;No Data Centers SGV Coalition&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Like Monterey Park, they canvassed door-to-door, showed up to every City of Industry meeting, started a petition, and filed public records requests.&lt;/p&gt;&lt;p&gt;The record requests showed the City of Industry had been &lt;a href="https://www.documentcloud.org/documents/27693914-re-quick-call-07-05-2025-produce/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;discussing&lt;/strong&gt;&lt;/a&gt; zoning changes with developers at Puente Hills Mall, Madrid Middle School, and two battery storage facilities near Hacienda Heights. All just feet from homes and schools.&lt;/p&gt;&lt;p&gt;In February, the city unanimously rezoned Puente Hills Mall for battery storage. Months earlier, the city manager, Joshua Nelson, &lt;a href="https://investigatela.org/2026/03/02/city-of-industry-discussed-data-center-sites-months-before-zoning-vote/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;emailed the developers that&lt;/strong&gt;&lt;/a&gt; they were working to allow data centers anywhere in the city.&lt;/p&gt;&lt;p&gt;Again, organizers found that people had no idea what the city was planning to put next to their homes. Battery centers burned for days when they caught fire. One at Moss Landing had &lt;a href="https://www.youtube.com/watch?v=ooYmpF0utVs" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;spilled&lt;/strong&gt;&lt;/a&gt; 55,000 tons of nickel, cobalt, and lithium.&lt;/p&gt;&lt;p&gt;“Maybe only one or two people had heard," said Sophia Ramirez, an organizer and the daughter of Zacatecan immigrants. “That was pretty shocking.”' &lt;/p&gt;&lt;p&gt;Ramirez, a Cal Poly biology grad, explained in the outreach, in plain language, how PM2.5 and PM10 particles could slip past the body's filters into the lungs, then the blood. &lt;/p&gt;&lt;p&gt;The No Data Center SGV petition grew to 18,000 signatures.&lt;/p&gt;&lt;p&gt;In Monterey Park, the people who organized were also the people who vote. In the City of Industry, there hasn’t been a competitive election in &lt;a href="https://www.latimes.com/local/politics/la-me-industry-audit-20160129-story.html" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;10 years&lt;/strong&gt;&lt;/a&gt;, and only four &lt;a href="https://www.latimes.com/archives/la-xpm-1992-03-22-ga-7418-story.html" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;since 1957&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;State law says when fewer people run than there are open seats for, a city doesn't have to hold an election. The City of Industry council appoints its council members and some of its members are descendents of the original founders.&lt;/p&gt;&lt;p&gt;The City of Industry has 256 residents, the largest financial reserves of any city in the San Gabriel Valley, and a &lt;a href="https://www.bibliovault.org/BV.book.epl?ISBN=9780813551920" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;history&lt;/strong&gt;&lt;/a&gt; of building its wealth at the expense of the surrounding working-class Latino and Asian Pacific Islander communities. &lt;/p&gt;&lt;p&gt;"I had normalized what it was like to live next to City of Industry,” Vazquez says. “I thought it was normal to just grow up near all these warehouses."&lt;/p&gt;&lt;p&gt;The people of the surrounding areas, Covina, Diamond Bar, El Monte, La Puente, Pomona, Walnut, and West Covina, would all be affected by the data centers, but have no political power over the City of Industry.&lt;/p&gt;&lt;p&gt;“To think of it in the context of environmental racism, environmental injustices, it’s really crazy that it's like the city of industry has decided that these communities that live around them are not valuable lives,” Ramirez says. “Zonas de sacrificio.”&lt;/p&gt;&lt;p&gt;People from La Puente, Avocado Heights, Rowland Heights, Diamond Bar, Walnut, and Hacienda Heights came to the City of Industry’s March 26 council meeting where they planned to change the city's zoning code to allow data centers. &lt;/p&gt;&lt;p&gt;The city’s email went down before the &lt;a href="https://www.instagram.com/reels/DWXiRobj0OI/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;meeting&lt;/strong&gt;&lt;/a&gt;. People said it often does. The only way to comment is to show up. More than 100 people did, at 9 a.m. on a workday. Before public comment, the council went into closed session. &lt;/p&gt;&lt;p&gt;“They made us all wait outside in the heat,” Ramirez says. &lt;/p&gt;&lt;p&gt;Ramirez said that it was 90 degrees, and there were many elders. After two hours, roughly 40 were let inside. Outside, there was no livestream. They chanted, "Let us in." &lt;/p&gt;&lt;p&gt;When the doors opened, only about 30 people were allowed to speak. Their comment time was cut from three minutes to one minute. Mayor Pro Tem Greubel got up and walked out halfway through.&lt;/p&gt;&lt;p&gt;The City of Industry does not provide interpreters, translated materials, or any way for people who speak other languages to comment. They haven't posted meeting minutes in over a year. More than a quarter of their meetings are called with 24 hours notice.&lt;/p&gt;&lt;p&gt;“Everything about City of Industry is designed to minimize participation of the public,” Vazquez tells us. ”Like, that is not an exaggeration to say that.”&lt;/p&gt;&lt;h3 class="wp-block-heading"&gt;VALLE IMPERIAL RESISTE VS. IMPERIAL COUNTY&lt;/h3&gt;&lt;p&gt;Gilberto Manzanarez, an organizer and founder of &lt;a href="https://www.instagram.com/valleimperialresiste/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;Valle Imperial Resiste&lt;/strong&gt;&lt;/a&gt;, learned of the data center on Facebook. Someone had leaked the planning commission’s map over Thanksgiving break. &lt;/p&gt;&lt;p&gt;Manzanarez grabbed his camera, drove to the site, and &lt;a href="https://www.instagram.com/p/DRnBkV9ktI2/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;posted&lt;/strong&gt;&lt;/a&gt; a video that spread across the valley. &lt;/p&gt;&lt;p&gt;The Imperial Valley data center would be one of the world’s largest at nearly one million square feet. It would use double the electricity of Imperial Valley and 750,000 gallons of water every day. County planning staff decided they “qualified as a permitted industrial use,” and there would be no need for environmental review.&lt;/p&gt;&lt;p&gt;Manzanarez, also a history teacher, says, “Public health always takes a backseat to economic development in the Imperial Valley.” &lt;/p&gt;&lt;p&gt;The &lt;a href="https://www.niehs.nih.gov/research/supported/translational/community/imperial#:~:text=Air%20pollution%20in%20Imperial%20County%20comes%20from%20agricultural%20burns%2C%20diesel,sources%20such%20as%20automobile%20exhaust." target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;air carries&lt;/strong&gt;&lt;/a&gt; cropland burnings, diesel fumes, and dust from the drying Salton Sea, laced with pesticides and heavy metals. More than &lt;a href="https://keck.usc.edu/news/children-living-near-the-salton-sea-in-southern-california-show-slower-lung-function-growth/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;one in five children&lt;/strong&gt;&lt;/a&gt; that live around the Salton Sea have asthma.&lt;/p&gt;&lt;p&gt;Despite decades of investment, solar farms, geothermal plants, military bases, and canals, the 80 percent Latino region’s unemployment sits at &lt;a href="https://labormarketinfo.edd.ca.gov/file/lfmonth/lf_geomaps.pdf" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;three times&lt;/strong&gt;&lt;/a&gt; the national average.&lt;/p&gt;&lt;p&gt;“The promise of economic development and jobs, the same thing, these are stories that we already heard when we had the solar farm boom. Those solar panel projects were sold to us,” Manzanarez says. “It's like, this is going to save us. This is going to lift us out of poverty ... Thousands of people across the Imperial Valley were hired, and they were excited to go work. Fast forward 10 years, 2026, guess what? We have the highest unemployment rate in the state of California. Again.”&lt;/p&gt;&lt;p&gt;Bryan Vega, another local organizer, saw the Valle Imperial Resiste post and joined the other activists. They knocked on doors, handed out flyers, and flooded Instagram with informational videos. &lt;/p&gt;&lt;p&gt;“We started to share information about what the data center is and invited folks to submit public comment,” Vega says.&lt;/p&gt;&lt;p&gt;In January, they held a protest on Main Street and Imperial Avenue. They started a petition to enact the Imperial County Data Center Prohibition Act. &lt;/p&gt;&lt;p&gt;On March 26, the Imperial County Board of Supervisors called for an evening meeting, outside of their normal schedule, specifically about the data center. &lt;/p&gt;&lt;p&gt;The main chamber filled 30 minutes before it started. Two overflow rooms opened in a separate building. And when those filled, too, more than 60 people stood in the parking lot. &lt;/p&gt;&lt;p&gt;The developer, Sebastian Rucci, spoke. There were to be no questions.&lt;/p&gt;&lt;p&gt;Vega recalled standing out in the parking lot. &lt;/p&gt;&lt;p&gt;“Someone outside said, ‘Why are we just standing here? Why are we not more upset? They're making decisions about us in there. And we're out here. And we should be in there. We go to the door and we're like, ‘No Data Center. No Data Center.’ And it's like a battle cry from our soul,” he says.&lt;/p&gt;&lt;p&gt;Vega said one of the organizers, from &lt;a href="https://www.instagram.com/ivforpalestine/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;Imperial Valley for Palestine&lt;/strong&gt;&lt;/a&gt;, had a bullhorn in her car, “because, duh, she's an organizer and she's always prepared for these things."&lt;/p&gt;&lt;p&gt;&lt;em&gt;KPBS&lt;/em&gt; &lt;a href="https://www.kpbs.org/news/environment/2026/04/03/imperial-county-supervisors-to-hold-key-vote-on-controversial-data-center-project" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;reported&lt;/strong&gt;&lt;/a&gt; that “county officials paused the meeting and Rucci departed early after protestors drowned him out.” &lt;/p&gt;&lt;p&gt;Videos &lt;a href="https://www.instagram.com/p/DWZ-xwCkpns/" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;show&lt;/strong&gt;&lt;/a&gt; Imperial County sheriff’s deputies escorting Rucci and his business partner, Hector Casas, to their car. &lt;/p&gt;&lt;p&gt;Outside, the 60 people who weren’t allowed in chanted “Fuera! Fuera!” at Rucci and Casas as they drove off.&lt;/p&gt;&lt;p&gt;"This meeting was a sham,” Manzanarez says. “They didn't want to educate us. They didn't want to hear people. They just wanted to check a box."&lt;/p&gt;&lt;p&gt;&lt;em&gt;KPBS&lt;/em&gt; &lt;a href="https://www.kpbs.org/news/environment/2026/01/22/4-takeaways-from-kpbs-investigation-into-a-massive-data-center-project-in-imperial-county" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;reported&lt;/strong&gt;&lt;/a&gt; that in 2010, Ohio prosecutors charged Rucci with money laundering, promoting prostitution, and perjury at a Youngstown nightclub. The felonies were thrown out, but he served 30 days for selling alcohol on an expired license. He later opened an addiction treatment center. The state revoked its certification after finding falsified records. &lt;/p&gt;&lt;p&gt;In 2021, the FBI raided the treatment center and seized more than $600,000. No criminal charges were filed. Rucci sued, got the money back, and is still &lt;a href="https://www.opn.ca6.uscourts.gov/opinions.pdf/25a0306p-06.pdf" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;fighting&lt;/strong&gt;&lt;/a&gt; in federal court. &lt;/p&gt;&lt;p&gt;In an &lt;a href="https://www.kpbs.org/news/environment/2026/01/21/the-plan-to-build-a-massive-data-center-in-imperial-county-without-environmental-review" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;interview&lt;/strong&gt;&lt;/a&gt; with &lt;em&gt;KPBS&lt;/em&gt;, he said, "I won them all but one."&lt;/p&gt;&lt;p&gt;Rucci and his partner, Hector Casas, targeted Imperial County because the zoning laws allowed them to skip environmental review. &lt;/p&gt;&lt;p&gt;"Our whole goal is speed," Rucci &lt;a href="https://www.kpbs.org/news/environment/2026/01/21/the-plan-to-build-a-massive-data-center-in-imperial-county-without-environmental-review" target="_blank" rel="noreferrer noopener"&gt;&lt;strong&gt;told&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;&lt;em&gt;KPBS&lt;/em&gt;. "That is not sneaky. That's just smart."&lt;/p&gt;&lt;p&gt;On April 7, at 8 a.m., 50 men got off a tour bus with identical orange vests that said, "Data centers equal jobs and prosperity." &lt;/p&gt;&lt;p&gt;They were led through the side entrance of the County Administration Building before any members of the community were allowed through the front. A leaked internal county email sent the night before warned of high turnout and increased security. Despite that, the county provided no overflow room. &lt;/p&gt;&lt;p&gt;Over 100 community members were left outside in 96-degree heat for five to six hours. Elders with walkers. No shade, no water, no chairs. One community member got kicked out for calling out the outsiders taking seats from residents.&lt;/p&gt;&lt;p&gt;The board of supervisors voted to approve the lot merger. Only one supervisor voted no.&lt;/p&gt;&lt;p&gt;Senator Padilla's SB 887 would require all new data centers in California to go through environmental review. The bill does not ban data centers, it only requires developers to study their impact and hear from the public before building.&lt;/p&gt;&lt;p&gt;On April 2, the organizers filed the Imperial County Data Center Prohibition Act, the first step toward a ballot measure that would ban data centers across the county. &lt;/p&gt;&lt;p&gt;“Our parents and grandparents sacrificed so much to be able to be in the Imperial Valley ... part of the sacrifice was having to accept a hard hand,“ Vegas says. “Like when I think about what my Mexican farmworker parents had to undergo, it makes it almost intuitive to fight for the environment, intuitive to fight for the things that I know are true to them, but also very simply put, we would not be here without that."&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Julianne Le</name>
        </author>
        <media:content medium="image" url="https://lede-admin.lataco.com/wp-content/uploads/sites/45/2026/04/Document-e1776358247494.jpeg"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19d9992bf3a:36b762:9f9ae75a</id>
        <title type="html">How one programmer's pet project changed how we think about software</title>
        <published>2026-04-17T03:53:44Z</published>
        <updated>2026-04-17T03:54:07Z</updated>
        <link href="https://www.youtube.com/watch?v=Y24vK_QDLFg" rel="alternate" type="text/html"/>
        <summary type="html">This is the story of how one programmer's obsession with simplicity quietly reshaped how the software world thinks about time, immutability, and what it mean...</summary>
        <content type="html">&lt;div&gt;&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/Y24vK_QDLFg"&gt;&lt;/iframe&gt;&lt;br&gt;&lt;p&gt;This is the story of how one programmer's obsession with simplicity quietly reshaped how the software world thinks about time, immutability, and what it means to write code that lasts. From a sabbatical pet-project to the backbone of one of the world's largest fintechs and a global community that treats their language like a philosophy. This is the story of Clojure. 

---

This documentary wouldn't exist without the kind support of Nubank: https://building.nubank.com/engineering/

Thanks to Railway, our channel sponsor, for supporting all of our films:
Railway is the all-in-one intelligent cloud provider ➡️ https://railway.com/?referralCode=cultrepo

---

The Clojure Documentary features:

Alessandra Sierra, Alex Miller, Chris Houser, David Nolen, Ed Wible, Eric Normand, Eric Thorsen, Lucas Cavalcanti, Michael Fogus, Nathan Marz, Rich Hickey, Steph Hickey, and Stuart Halloway.

Film Credits: 
Directed by: Cormac Dunne
Produced by: Emma Tracey
Additional direction: Joey Bania 
Music supervision and sound design: Tomás Malara

---

For a full overview of all the papers, talks, essays, etc. that appear in the film, check this link: https://clojure.org/about/documentary


---

Follow us:
X: x.com/CultRepo
Bluesky: cultrepo.bsky.social
Instagram: www.instagram.com/cult.repo
LinkedIn: https://www.linkedin.com/company/cult-repo&lt;/p&gt;&lt;/div&gt;</content>
        <author>
            <name>TLDR News EU</name>
        </author>
        <media:content medium="image" url="https://i.ytimg.com/vi/Y24vK_QDLFg/maxresdefault.jpg"/>
        <link href="https://www.youtube.com/embed/Y24vK_QDLFg" rel="enclosure" type="text/html"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19d96383117:a4ef3:e8c372da</id>
        <title type="html">‘By Design’ Flaw in MCP Could Enable Widespread AI Supply Chain Attacks</title>
        <published>2026-04-16T12:15:58Z</published>
        <updated>2026-04-16T12:16:01Z</updated>
        <link href="https://www.securityweek.com/by-design-flaw-in-mcp-could-enable-widespread-ai-supply-chain-attacks/" rel="alternate" type="text/html"/>
        <summary type="html">Researchers warn that a flaw in Anthropic’s Model Context Protocol allows unsanitized commands to execute silently, enabling full system compromise across widely used AI environments.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
		
&lt;p&gt;&lt;strong&gt;Model Context Protocol (MCP) has been a boon to agentic AI users and is widely used and trusted locally by companies adopting agentic AI internally. &lt;/strong&gt;



&lt;/p&gt;&lt;p&gt;Introduced by Anthropic in November 2024, it provides a standard connector between agents and data. Enterprises use it locally to avoid the pain of developing their own connectors, and it is in widespread use as a local STDIO MCP server.



&lt;/p&gt;&lt;p&gt;There are multiple providers of MCP servers, almost all inheriting Anthropic’s code. The problem, &lt;a href="https://20204725.hs-sites.com/the-mother-of-all-ai-supply-chains" target="_blank"&gt;reports&lt;/a&gt; OX Security, is what it terms an architectural flaw in Anthropic’s MCP code embedded within most of these local STDIO MCPs.



&lt;/p&gt;&lt;p&gt;In a nutshell, OX Security says this flaw can result in a complete adversarial takeover over the user’s computer system. “And the exploit mechanism is straightforward, MCP’s STDIO interface was designed to launch a local server process. But the command is executed regardless of whether the process starts successfully,” reports OX.



&lt;/p&gt;&lt;p&gt;“Pass in a malicious command, receive an error – and the command still runs. No sanitization warnings. No red flags in the developer toolchain. Nothing.”



&lt;/p&gt;&lt;p&gt;OX extensively tested whether this ‘flaw’ was exploitable, extensively succeeded, and extensively disclosed its findings to the MCP providers; from Anthropic downward. Initially it had little response. Eventually, the common response was inaction coupled with the suggestion that this behavior was ‘by design’. &lt;/p&gt;&lt;div&gt;&lt;span&gt;Advertisement. Scroll to continue reading.&lt;/span&gt;&lt;/div&gt;



&lt;p&gt;But OX discovered, and demonstrated, that this ‘by design’ behavior could be easily exploited, leaving potentially millions of downstream users exposed to sensitive data, API key and internal corporate data theft, the exposure of chat histories, and more. If the process that MCP failed included malware, that malware could be silently installed, potentially leading to complete system takeover.



&lt;/p&gt;&lt;p&gt;Eventually, the only apparent action from Anthropic was to quietly update its security guidance to recommend MCP adapters be used ‘with caution’ – “leaving the flaw intact and shifting responsibility to developers”.



&lt;/p&gt;&lt;p&gt;This is an interesting position to take. It suggests that developers are responsible for the security of what they develop, which is fair. It possibly also suggests that any company so breached is not the responsibility of Anthropic, but the fault of misconfiguring the MCP installation – which certainly &lt;a href="https://www.securityweek.com/the-wild-wild-west-of-agentic-ai-an-attack-surface-cisos-cant-afford-to-ignore/"&gt;does happen&lt;/a&gt;. And to be fair, GitHub’s own installation was an exception to the OX testing, proving that security gating on installation is possible.



&lt;/p&gt;&lt;p&gt;&lt;a href="https://www.airisksummit.com/" target="_blank"&gt;&lt;strong&gt;Learn More at the AI Risk Summit | Ritz-Carlton, Half Moon Bay&lt;/strong&gt;&lt;/a&gt;



&lt;/p&gt;&lt;p&gt;But the sheer volume of successful compromises conducted by OX demonstrates that the developers installing MCP servers are failing to install successfully. This should be no surprise when AI is automating so many aspects of security and lowering the bar of security competence among developers.



&lt;/p&gt;&lt;p&gt;The OX position is that Anthropic should take responsibility and fix this ‘architectural flaw’ itself. Without doing so, it is leaving industry open to “the mother of all supply chain attacks”, starting from Anthropic, fanning out to many thousands of local MCP users, and from those compromised systems to who knows how many other servers.



&lt;/p&gt;&lt;p&gt;During its research, OX adopted a coordinated disclosure process, leading to more than 30 accepted disclosures and more than 10 high and critical vulnerabilities patched. But the underlying design flaw, it says, remains, leaving millions of users and thousands of systems exposed to unauthorized access. “The current implementation of the Model Context Protocol places the entire burden of security on the downstream developer – a structural failure that guarantees vulnerability at scale.”



&lt;/p&gt;&lt;p&gt;The OX report on its findings includes details on how Anthropic could solve the problem by deprecating unsanitized STDIO connections, introducing protocol level command sandboxing, including a ‘dangerous mode’ explicit opt-in, and developing marketplace verification standards to include a standardized security manifest.



&lt;/p&gt;&lt;p&gt;In the meantime, any company adopting STDIO MCP as part of an agentic AI development should do so ‘with caution’.



&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Related&lt;/strong&gt;: &lt;a href="https://www.securityweek.com/anthropic-mcp-server-flaws-lead-to-code-execution-data-exposure/"&gt;Anthropic MCP Server Flaws Lead to Code Execution, Data Exposure&lt;/a&gt;



&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Related&lt;/strong&gt;: &lt;a href="https://www.securityweek.com/top-25-mcp-vulnerabilities-reveal-how-ai-agents-can-be-exploited/"&gt;Top 25 MCP Vulnerabilities Reveal How AI Agents Can Be Exploited&lt;/a&gt;



&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Related&lt;/strong&gt;: &lt;a href="https://www.securityweek.com/the-new-rules-of-engagement-matching-agentic-attack-speed/"&gt;The New Rules of Engagement: Matching Agentic Attack Speed&lt;/a&gt;



&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Related&lt;/strong&gt;: &lt;a href="https://www.securityweek.com/anthropic-unveils-claude-mythos-a-cybersecurity-breakthrough-that-could-also-supercharge-attacks/"&gt;Anthropic Unveils ‘Claude Mythos’ – A Cybersecurity Breakthrough That Could Also Supercharge Attacks&lt;/a&gt;
			&lt;/p&gt;&lt;/div&gt;
	&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Kevin Townsend</name>
        </author>
        <media:content medium="image" url="https://www.securityweek.com/wp-content/uploads/2026/04/MCP_Vulnerability.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://www.securityweek.com/feed/</id>
            <title type="html">SecurityWeek</title>
            <link href="https://www.securityweek.com" rel="alternate" type="text/html"/>
            <updated>2026-04-16T12:16:01Z</updated>
        </source>
    </entry>
</feed>