<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://www.mmjavid.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.mmjavid.com/" rel="alternate" type="text/html" /><updated>2026-10-06T23:13:16+02:00</updated><id>https://www.mmjavid.com/feed.xml</id><title type="html">MohammadMahdi Javid</title><subtitle>I build and test software, from web apps and ERP automation down to the hardware it runs on. Notes on testing, automation, electronics and AI agents.</subtitle><author><name>MohammadMahdi Javid</name><uri>https://www.mmjavid.com/</uri></author><entry><title type="html">Building AI agents for ERP and finance systems</title><link href="https://www.mmjavid.com/Agentic-AI/" rel="alternate" type="text/html" title="Building AI agents for ERP and finance systems" /><published>2026-02-01T00:00:00+01:00</published><updated>2026-10-06T00:00:00+02:00</updated><id>https://www.mmjavid.com/Agentic-AI</id><content type="html" xml:base="https://www.mmjavid.com/Agentic-AI/">&lt;p&gt;AI agents can take over real parts of an ERP workflow, like matching invoices or preparing journal entries. But a general-purpose agent with access to the whole ERP is not a financial control system.&lt;/p&gt;

&lt;p&gt;ERP and accounting systems are full of rules: master data, posting rules, approval steps, fiscal periods, and records that have to stay auditable. An agent that can call any ERP endpoint and reason over unlimited transaction data will sooner or later produce an action that looks right and isn’t. The safer design gives the model small, bounded decisions and leaves validation, permissions, transaction integrity and every state change to deterministic services.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;The core rule&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Bound the agent, not the business process. Give each agent one narrow job, scoped data, typed tools and clear states, and put a deterministic validation layer in front of every ERP change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;from-copilot-to-controlled-workflow&quot;&gt;From copilot to controlled workflow&lt;/h2&gt;

&lt;p&gt;The real difference isn’t a smarter or less smart model. It’s whether the &lt;strong&gt;model&lt;/strong&gt; drives execution or the &lt;strong&gt;system&lt;/strong&gt; does.&lt;/p&gt;

&lt;div class=&quot;post-compare&quot;&gt;
  &lt;div class=&quot;post-compare__col post-compare__col--bad&quot;&gt;
    &lt;p class=&quot;post-compare__title&quot;&gt;Model-driven&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;One generalist model&lt;/li&gt;
      &lt;li&gt;A large slice of the ERP in its context&lt;/li&gt;
      &lt;li&gt;Many overlapping tools&lt;/li&gt;
      &lt;li&gt;Writes straight into the ERP&lt;/li&gt;
    &lt;/ul&gt;
    &lt;p class=&quot;post-compare__note&quot;&gt;Invalid accounts, wrong entities, duplicate postings, unapproved payments, a thin audit trail.&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;post-compare__col post-compare__col--good&quot;&gt;
    &lt;p class=&quot;post-compare__title&quot;&gt;System-controlled&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;A router picks the workflow&lt;/li&gt;
      &lt;li&gt;A read-only agent retrieves and reconciles&lt;/li&gt;
      &lt;li&gt;A policy layer handles approvals&lt;/li&gt;
      &lt;li&gt;Deterministic checks: schema, permissions, accounting rules, balances, idempotency&lt;/li&gt;
      &lt;li&gt;One mutation service posts, then the result is verified and audited&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;what-the-architecture-should-optimize-for&quot;&gt;What the architecture should optimize for&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Minimal relevant context&lt;/strong&gt; rather than maximal context.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deterministic retrieval constraints&lt;/strong&gt; before semantic retrieval.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Typed tool contracts&lt;/strong&gt; rather than generic “update ERP” functions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Server-side authorization&lt;/strong&gt; rather than permissions encoded only in prompts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Explicit validation before mutation&lt;/strong&gt; rather than trusting model output.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Idempotent operations&lt;/strong&gt; so retries do not create duplicate financial effects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Observable execution&lt;/strong&gt; so every material decision can be reconstructed later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;context-isolation-give-the-model-exactly-what-the-task-requires&quot;&gt;Context isolation: give the model exactly what the task requires&lt;/h2&gt;

&lt;p&gt;ERP data is highly contextual. The same vendor, account, tax treatment, or cost center can have different meanings across legal entities, fiscal periods, currencies, and business units.&lt;/p&gt;

&lt;p&gt;Dumping a large portion of an ERP database into an agent context creates more than a token-management problem. It creates an authority problem because the model may see information that is irrelevant to the current transaction and still use it when forming a decision.&lt;/p&gt;

&lt;h3 id=&quot;common-failure-modes&quot;&gt;Common failure modes&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Context distraction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unrelated accounts, vendors, historical transactions, and policies compete for the model’s attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context contamination&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A malformed, stale, or attacker-controlled record becomes part of the reasoning context and influences later actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-entity ambiguity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Records from different subsidiaries or accounting regimes appear together even though only one legal entity is relevant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Historical-state confusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A valid identifier from a previous fiscal period is mistaken for an identifier that is currently active.&lt;/p&gt;

&lt;h3 id=&quot;better-pattern&quot;&gt;Better pattern&lt;/h3&gt;

&lt;p&gt;Keep the system prompt focused on operational constraints. Put dynamic business data into structured retrieval results instead.&lt;/p&gt;

&lt;div class=&quot;post-compare&quot;&gt;
  &lt;div class=&quot;post-compare__col&quot;&gt;
    &lt;p class=&quot;post-compare__title&quot;&gt;In the prompt&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Task constraints&lt;/li&gt;
      &lt;li&gt;Allowed workflow&lt;/li&gt;
      &lt;li&gt;Tool boundaries&lt;/li&gt;
      &lt;li&gt;Output contract&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
  &lt;div class=&quot;post-compare__col&quot;&gt;
    &lt;p class=&quot;post-compare__title&quot;&gt;In scoped runtime context&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Entity, fiscal period, currency, cost centre&lt;/li&gt;
      &lt;li&gt;Transaction identifiers&lt;/li&gt;
      &lt;li&gt;The policy facts that apply&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Engineering principle&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;A large context window does not compensate for poor context selection. Retrieve fewer facts with stronger provenance instead of supplying the model with an unrestricted ERP snapshot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;scoped-rag-retrieval-must-preserve-accounting-boundaries&quot;&gt;Scoped RAG: retrieval must preserve accounting boundaries&lt;/h2&gt;

&lt;p&gt;Retrieval for ERP agents should behave more like a database query than a generic knowledge search.&lt;/p&gt;

&lt;p&gt;A semantic match such as “office equipment” is useful for finding descriptive text. It is not sufficient to determine which legal entity, account, tax code, or fiscal period should be used in a posting.&lt;/p&gt;

&lt;figure class=&quot;post-flow-wrap&quot; aria-label=&quot;Scoped retrieval pipeline&quot;&gt;
&lt;ol class=&quot;post-flow&quot; style=&quot;--n: 5&quot;&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 0&quot;&gt;Agent query&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 1&quot;&gt;Metadata filters&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 2&quot;&gt;Exact + semantic search&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 3&quot;&gt;Rerank&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 4&quot;&gt;Small result set with provenance&lt;/li&gt;
&lt;/ol&gt;
&lt;figcaption&gt;Filters first: entity, fiscal period, currency, cost centre and access tier. Exact search for IDs and codes, semantic search for descriptions and policy text.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;retrieval-rules-for-financial-systems&quot;&gt;Retrieval rules for financial systems&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Filter before retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apply authorization and business metadata constraints before semantic ranking. Retrieval should not first discover records and only later decide whether the agent was allowed to see them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combine exact and semantic search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Identifiers such as invoice numbers, purchase orders, tax identifiers, and account codes benefit from exact matching. Semantic retrieval is more useful for natural-language descriptions and policy text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Return structured facts with provenance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A result should carry enough information for the application to identify where it came from.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;document_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INV-2026-01842&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;entity_code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;EU02&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fiscal_period&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2026-09&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;vendor_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;V-004281&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;currency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;source_system&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;erp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;source_version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;current&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Keep retrieved content separate from instructions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Invoices, supplier notes, PDFs, email bodies, and OCR output are data. They are not trusted instructions to the agent.&lt;/p&gt;

&lt;h2 id=&quot;tool-design-expose-business-capabilities-not-your-entire-erp-api&quot;&gt;Tool design: expose business capabilities, not your entire ERP API&lt;/h2&gt;

&lt;p&gt;An agent should not receive an unrestricted toolbox containing dozens of overlapping CRUD-style endpoints.&lt;/p&gt;

&lt;p&gt;The important design question is not “How many APIs does the ERP have?” It is “Which operations does this agent actually need to complete this workflow?”&lt;/p&gt;

&lt;p&gt;A narrow tool registry reduces ambiguity. More importantly, the backend must still enforce authorization and validation independently of the model.&lt;/p&gt;

&lt;h3 id=&quot;weak-interface&quot;&gt;Weak interface&lt;/h3&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;update_ledger&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Updates financial records in the ERP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;amount&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;number&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The tool exposes an ambiguous mutation with almost no machine-checkable business semantics.&lt;/p&gt;

&lt;h3 id=&quot;stronger-interface&quot;&gt;Stronger interface&lt;/h3&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;post_journal_entry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Creates a journal voucher from a validated transaction object.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;entity_code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;debit_account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;credit_account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;amount_minor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;currency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;idempotency_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;source_document_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;entity_code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;debit_account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;credit_account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;amount_minor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;currency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;idempotency_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;source_document_id&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The real control is not the JSON schema alone. The receiving service must verify every field against authoritative ERP state.&lt;/p&gt;

&lt;h3 id=&quot;tool-scoping-rules&quot;&gt;Tool-scoping rules&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Give each specialized agent only the capabilities required for its workflow.&lt;/li&gt;
  &lt;li&gt;Prefer task-specific functions such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match_invoice&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read_purchase_order&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_journal_draft&lt;/code&gt; over generic mutation functions.&lt;/li&gt;
  &lt;li&gt;Make invalid states impossible to represent where practical.&lt;/li&gt;
  &lt;li&gt;Keep authorization checks on the server side.&lt;/li&gt;
  &lt;li&gt;Reject unknown, stale, or inactive identifiers before executing mutations.&lt;/li&gt;
  &lt;li&gt;Attach idempotency keys to operations that may be retried.&lt;/li&gt;
  &lt;li&gt;Return compact, structured results instead of raw HTTP responses or database dumps.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Important distinction&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Tool restrictions are an AI control. They are not an authorization boundary.&lt;/p&gt;

  &lt;p&gt;The ERP service, gateway, or policy engine must remain the final authority.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;the-execution-cycle-parse-retrieve-validate-act-verify&quot;&gt;The execution cycle: parse, retrieve, validate, act, verify&lt;/h2&gt;

&lt;p&gt;A dependable ERP agent should not jump directly from natural language to a financial mutation.&lt;/p&gt;

&lt;figure class=&quot;post-flow-wrap&quot; aria-label=&quot;Agent execution cycle&quot;&gt;
&lt;ol class=&quot;post-flow&quot; style=&quot;--n: 6&quot;&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 0&quot;&gt;Parse&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 1&quot;&gt;Retrieve&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 2&quot;&gt;Validate&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 3&quot;&gt;Authorize&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 4&quot;&gt;Actuate&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 5&quot;&gt;Verify&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;post-flow__fail&quot;&gt;Any check fails: &lt;strong&gt;abort or escalate&lt;/strong&gt;&lt;/p&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;1-parse&quot;&gt;1. Parse&lt;/h3&gt;

&lt;p&gt;Identify the immediate workflow objective.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;identify the invoice&lt;/li&gt;
  &lt;li&gt;determine whether a purchase order exists&lt;/li&gt;
  &lt;li&gt;calculate the matching variance&lt;/li&gt;
  &lt;li&gt;prepare a posting candidate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not allow the model to invent missing accounting facts during parsing.&lt;/p&gt;

&lt;h3 id=&quot;2-retrieve&quot;&gt;2. Retrieve&lt;/h3&gt;

&lt;p&gt;Fetch only the authoritative records required for the next decision.&lt;/p&gt;

&lt;p&gt;Typical sources include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;vendor master&lt;/li&gt;
  &lt;li&gt;purchase order&lt;/li&gt;
  &lt;li&gt;goods receipt&lt;/li&gt;
  &lt;li&gt;invoice&lt;/li&gt;
  &lt;li&gt;payment status&lt;/li&gt;
  &lt;li&gt;accounting policy&lt;/li&gt;
  &lt;li&gt;account master&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-validate&quot;&gt;3. Validate&lt;/h3&gt;

&lt;p&gt;Check every value against authoritative state.&lt;/p&gt;

&lt;p&gt;At minimum, validate:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;entity and fiscal period&lt;/li&gt;
  &lt;li&gt;account status&lt;/li&gt;
  &lt;li&gt;currency&lt;/li&gt;
  &lt;li&gt;tax configuration&lt;/li&gt;
  &lt;li&gt;cost center&lt;/li&gt;
  &lt;li&gt;monetary precision&lt;/li&gt;
  &lt;li&gt;debit and credit totals&lt;/li&gt;
  &lt;li&gt;document status&lt;/li&gt;
  &lt;li&gt;approval state&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-authorize&quot;&gt;4. Authorize&lt;/h3&gt;

&lt;p&gt;Determine whether the current actor and workflow are allowed to perform the requested operation.&lt;/p&gt;

&lt;p&gt;Authorization should be evaluated outside the model.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Agent Request]
      |
      v
[Policy Engine]
      |
      +--&amp;gt; Actor
      +--&amp;gt; Role
      +--&amp;gt; Entity
      +--&amp;gt; Operation
      +--&amp;gt; Amount
      +--&amp;gt; Approval state
      |
      v
[ALLOW / DENY / REQUIRE APPROVAL]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;5-actuate&quot;&gt;5. Actuate&lt;/h3&gt;

&lt;p&gt;Only a validated and authorized transaction should reach the ERP mutation service.&lt;/p&gt;

&lt;p&gt;Prefer a structured transaction object:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;entity_code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;EU02&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;source_document_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INV-2026-01842&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;debit_account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;610200&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;credit_account&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;200100&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;amount_minor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128450&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;currency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;idempotency_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INV-2026-01842:v1&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;6-verify&quot;&gt;6. Verify&lt;/h3&gt;

&lt;p&gt;Do not assume that a successful HTTP response means the business operation succeeded.&lt;/p&gt;

&lt;p&gt;Verify the resulting voucher, transaction ID, status, and relevant accounting totals.&lt;/p&gt;

&lt;p&gt;If the ERP rejects the operation, return the structured error to the workflow controller and stop when the failure cannot be safely recovered.&lt;/p&gt;

&lt;h2 id=&quot;multi-agent-specialization-separate-reasoning-from-authority&quot;&gt;Multi-agent specialization: separate reasoning from authority&lt;/h2&gt;

&lt;p&gt;Complex ERP workflows are usually easier to control when different responsibilities are separated.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Inbound Invoice]
       |
       v
[Workflow Router]
  No mutation tools
       |
       +---------------------------+
       |                           |
       v                           v
[Matching Agent]             [Policy / Approval]
 Read-only                    Deterministic
       |
       v
[Validated Transaction Object]
       |
       v
[Posting Service]
 Deterministic mutation
       |
       v
[Voucher + Audit Event]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;responsibility-boundaries&quot;&gt;Responsibility boundaries&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Classifies the workflow and selects the appropriate execution path. It should not have write privileges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read-only retrieval or reconciliation agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Compares invoices, purchase orders, receipts, and master data. Its outputs should be structured and independently verifiable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy and approval layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applies thresholds, segregation-of-duties rules, approval requirements, and other deterministic controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actuation service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consumes a validated transaction object and performs the actual ERP mutation.&lt;/p&gt;

&lt;h3 id=&quot;keep-handoffs-small&quot;&gt;Keep handoffs small&lt;/h3&gt;

&lt;p&gt;Do not transfer complete conversational histories between agents.&lt;/p&gt;

&lt;p&gt;Transfer only the state required for the next step.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;workflow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;accounts_payable_match&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;entity_code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;EU02&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;invoice_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INV-2026-01842&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;purchase_order_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PO-8492&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;match_status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;matched&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;variance_minor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;450&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;currency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;approval_required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This makes the workflow easier to test, replay, and audit.&lt;/p&gt;

&lt;h2 id=&quot;reliability-controls-that-matter-more-than-prompt-wording&quot;&gt;Reliability controls that matter more than prompt wording&lt;/h2&gt;

&lt;p&gt;Prompt engineering is useful, but it should not carry the burden of financial correctness.&lt;/p&gt;

&lt;h3 id=&quot;idempotency&quot;&gt;Idempotency&lt;/h3&gt;

&lt;p&gt;An agent may retry after a timeout or ambiguous tool response.&lt;/p&gt;

&lt;p&gt;Without idempotency, the same invoice can be posted twice.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Request
  |
  v
[idempotency_key = invoice + workflow version]
  |
  +--&amp;gt; Seen before? ---- yes ---&amp;gt; Return existing result
  |
  no
  |
  v
Execute mutation
  |
  v
Persist result against key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;transaction-boundaries&quot;&gt;Transaction boundaries&lt;/h3&gt;

&lt;p&gt;A multi-step workflow should not leave a partially applied financial change because the model failed halfway through.&lt;/p&gt;

&lt;p&gt;Use explicit transaction boundaries in the ERP or service layer and design compensating actions where true atomicity is impossible.&lt;/p&gt;

&lt;h3 id=&quot;approval-thresholds&quot;&gt;Approval thresholds&lt;/h3&gt;

&lt;p&gt;Not every financial operation should be autonomous.&lt;/p&gt;

&lt;p&gt;For example, an organization may require human approval based on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;monetary amount&lt;/li&gt;
  &lt;li&gt;vendor risk&lt;/li&gt;
  &lt;li&gt;unusual tax treatment&lt;/li&gt;
  &lt;li&gt;new bank details&lt;/li&gt;
  &lt;li&gt;cross-entity transfers&lt;/li&gt;
  &lt;li&gt;exceptional journal types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thresholds must be configured by the organization rather than hard-coded into the model’s prompt.&lt;/p&gt;

&lt;h3 id=&quot;segregation-of-duties&quot;&gt;Segregation of duties&lt;/h3&gt;

&lt;p&gt;The component that detects or proposes a transaction should not automatically receive unrestricted payment or approval authority.&lt;/p&gt;

&lt;p&gt;Separate the steps:&lt;/p&gt;

&lt;figure class=&quot;post-flow-wrap&quot; aria-label=&quot;Separated duties&quot;&gt;
&lt;ol class=&quot;post-flow&quot; style=&quot;--n: 4&quot;&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 0&quot;&gt;Detect&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 1&quot;&gt;Validate&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 2&quot;&gt;Approve&lt;/li&gt;
  &lt;li class=&quot;post-flow__step&quot; style=&quot;--i: 3&quot;&gt;Execute&lt;/li&gt;
&lt;/ol&gt;
&lt;/figure&gt;

&lt;p&gt;instead of one agent that decides, approves and executes on its own.&lt;/p&gt;

&lt;h2 id=&quot;security-and-governance-treat-external-data-as-hostile-input&quot;&gt;Security and governance: treat external data as hostile input&lt;/h2&gt;

&lt;p&gt;Financial automation expands the attack surface because the model may process documents and messages originating outside the organization.&lt;/p&gt;

&lt;p&gt;Supplier invoices, OCR output, emails, attachments, and retrieved text must be treated as untrusted data.&lt;/p&gt;

&lt;h3 id=&quot;required-controls&quot;&gt;Required controls&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Least privilege&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Give service identities only the permissions required for the specific workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt-injection resistance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do not allow instructions embedded in invoices, PDFs, or supplier notes to override system policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side authorization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never rely on the model to enforce access control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human approval&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Require explicit approval where policy or risk thresholds demand it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Immutable audit records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Capture the source document, relevant inputs, tool calls, policy decisions, resulting ERP transaction, and timestamps in a tamper-evident audit trail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sensitive-data minimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do not place payroll data, bank details, or unrelated customer information into model context merely because the agent technically can access it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Auditability rule&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;The system should be able to answer three questions after every material action:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;What did the agent see?&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Why was the action allowed?&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;What exactly changed in the ERP?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;failure-diagnosis-inspect-the-control-boundary-not-only-the-model&quot;&gt;Failure diagnosis: inspect the control boundary, not only the model&lt;/h2&gt;

&lt;p&gt;When an agent fails, the root cause is often not the language model itself.&lt;/p&gt;

&lt;h3 id=&quot;recursive-tool-loop&quot;&gt;Recursive tool loop&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workflow repeatedly retries the same operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical cause&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The service returns an error that is difficult to interpret or the controller has no explicit termination state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use bounded retries, structured error codes, and explicit terminal states.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Attempt 1
   |
   v
Structured error
   |
   v
Can retry safely?
   |
 +--+--+
 |     |
yes    no
 |     |
 v     v
Retry  Abort / Escalate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A retry limit should be derived from the operation and failure mode. A universal number such as “three attempts” is only a heuristic.&lt;/p&gt;

&lt;h3 id=&quot;parameter-hallucination&quot;&gt;Parameter hallucination&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model produces a plausible but nonexistent account, vendor, tax code, or transaction identifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical cause&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workflow allows free-form construction before authoritative lookup and validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Resolve identifiers against the ERP master data before mutation.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Free text
   |
   v
Candidate identifier
   |
   v
Authoritative lookup
   |
   +--&amp;gt; Not found ----&amp;gt; Stop
   |
   +--&amp;gt; Inactive -----&amp;gt; Stop
   |
   +--&amp;gt; Valid ---------&amp;gt; Continue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;context-rot&quot;&gt;Context rot&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent loses the original business objective after many tool calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical cause&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Large raw responses accumulate in the context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Store state outside the conversation and pass forward only the fields required for the next step.&lt;/p&gt;

&lt;h2 id=&quot;step-by-step-implementation-plan&quot;&gt;Step-by-step implementation plan&lt;/h2&gt;

&lt;h3 id=&quot;1-define-workflow-boundaries&quot;&gt;1. Define workflow boundaries&lt;/h3&gt;

&lt;p&gt;Choose one concrete ERP workflow.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;three-way invoice matching&lt;/li&gt;
  &lt;li&gt;vendor master validation&lt;/li&gt;
  &lt;li&gt;journal draft preparation&lt;/li&gt;
  &lt;li&gt;payment exception classification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not begin with “automate accounting” as a single agent scope.&lt;/p&gt;

&lt;h3 id=&quot;2-define-authoritative-systems&quot;&gt;2. Define authoritative systems&lt;/h3&gt;

&lt;p&gt;For every field the agent may use, document the authoritative source.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Field&lt;/th&gt;
      &lt;th&gt;Source of truth&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Vendor ID&lt;/td&gt;
      &lt;td&gt;Vendor master&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Account code&lt;/td&gt;
      &lt;td&gt;Chart of accounts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tax code&lt;/td&gt;
      &lt;td&gt;Tax configuration&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PO status&lt;/td&gt;
      &lt;td&gt;Purchasing system&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Approval state&lt;/td&gt;
      &lt;td&gt;Workflow engine&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;3-define-the-transaction-contract&quot;&gt;3. Define the transaction contract&lt;/h3&gt;

&lt;p&gt;Create a versioned schema for the structured object that moves between reasoning components and deterministic services.&lt;/p&gt;

&lt;p&gt;Treat this object as an API contract, not as free-form model output.&lt;/p&gt;

&lt;h3 id=&quot;4-build-the-read-only-path-first&quot;&gt;4. Build the read-only path first&lt;/h3&gt;

&lt;p&gt;Test retrieval, matching, validation, and explanations without allowing financial mutations.&lt;/p&gt;

&lt;p&gt;Create synthetic edge cases for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;inactive vendors&lt;/li&gt;
  &lt;li&gt;closed fiscal periods&lt;/li&gt;
  &lt;li&gt;currency mismatches&lt;/li&gt;
  &lt;li&gt;duplicate invoices&lt;/li&gt;
  &lt;li&gt;missing purchase orders&lt;/li&gt;
  &lt;li&gt;tax mismatches&lt;/li&gt;
  &lt;li&gt;partial receipts&lt;/li&gt;
  &lt;li&gt;stale master data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-add-policy-enforcement&quot;&gt;5. Add policy enforcement&lt;/h3&gt;

&lt;p&gt;Introduce RBAC, approval rules, segregation of duties, and monetary thresholds outside the model.&lt;/p&gt;

&lt;h3 id=&quot;6-enable-controlled-mutation&quot;&gt;6. Enable controlled mutation&lt;/h3&gt;

&lt;p&gt;Expose only the smallest deterministic mutation service required by the workflow.&lt;/p&gt;

&lt;p&gt;Add:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;idempotency&lt;/li&gt;
  &lt;li&gt;transaction handling&lt;/li&gt;
  &lt;li&gt;optimistic concurrency where appropriate&lt;/li&gt;
  &lt;li&gt;structured errors&lt;/li&gt;
  &lt;li&gt;audit events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;7-evaluate-the-complete-system&quot;&gt;7. Evaluate the complete system&lt;/h3&gt;

&lt;p&gt;Measure more than model accuracy.&lt;/p&gt;

&lt;p&gt;Track:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;retrieval precision&lt;/li&gt;
  &lt;li&gt;invalid identifier rate&lt;/li&gt;
  &lt;li&gt;unauthorized action rate&lt;/li&gt;
  &lt;li&gt;duplicate mutation rate&lt;/li&gt;
  &lt;li&gt;validation rejection rate&lt;/li&gt;
  &lt;li&gt;escalation rate&lt;/li&gt;
  &lt;li&gt;mean recovery time&lt;/li&gt;
  &lt;li&gt;audit completeness&lt;/li&gt;
  &lt;li&gt;end-to-end task success&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;quick-reference-checklist&quot;&gt;Quick reference checklist&lt;/h2&gt;

&lt;h3 id=&quot;architecture&quot;&gt;Architecture&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Narrow workflow scope&lt;/li&gt;
  &lt;li&gt;Scoped retrieval with authorization metadata&lt;/li&gt;
  &lt;li&gt;Small, typed tool surface&lt;/li&gt;
  &lt;li&gt;Deterministic validation before mutation&lt;/li&gt;
  &lt;li&gt;Explicit state transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;erp-correctness&quot;&gt;ERP correctness&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Authoritative identifier lookup&lt;/li&gt;
  &lt;li&gt;Account and tax validation&lt;/li&gt;
  &lt;li&gt;Fiscal-period checks&lt;/li&gt;
  &lt;li&gt;Balance validation&lt;/li&gt;
  &lt;li&gt;Idempotent mutations&lt;/li&gt;
  &lt;li&gt;Transaction verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;security&quot;&gt;Security&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Server-side RBAC&lt;/li&gt;
  &lt;li&gt;Segregation of duties&lt;/li&gt;
  &lt;li&gt;Prompt-injection defenses&lt;/li&gt;
  &lt;li&gt;Sensitive-data minimization&lt;/li&gt;
  &lt;li&gt;Approval thresholds&lt;/li&gt;
  &lt;li&gt;Tamper-evident audit logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;agent-operations&quot;&gt;Agent operations&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Bounded retries&lt;/li&gt;
  &lt;li&gt;Structured error handling&lt;/li&gt;
  &lt;li&gt;Externalized workflow state&lt;/li&gt;
  &lt;li&gt;Compact agent handoffs&lt;/li&gt;
  &lt;li&gt;Full observability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-short-version&quot;&gt;The short version&lt;/h2&gt;

&lt;p&gt;The strongest pattern for AI in ERP automation isn’t a more autonomous generalist agent. It’s a controlled system: the model handles language and bounded reasoning, and deterministic services keep authority over business rules, permissions, transaction integrity and stored state. That split is what makes the workflow testable and auditable.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Who does what&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;The model proposes. The system validates. The ERP stays the source of truth.&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name>MohammadMahdi Javid</name><uri>https://www.mmjavid.com/</uri></author><category term="AI agents" /><category term="ERP" /><category term="Automation" /><summary type="html">Let the model read, match and propose. Let deterministic services validate, authorize and post. How to keep an ERP agent auditable.</summary></entry><entry><title type="html">Soldering 101</title><link href="https://www.mmjavid.com/soldering-101/" rel="alternate" type="text/html" title="Soldering 101" /><published>2024-06-06T00:00:00+02:00</published><updated>2026-10-06T00:00:00+02:00</updated><id>https://www.mmjavid.com/soldering-101</id><content type="html" xml:base="https://www.mmjavid.com/soldering-101/">&lt;p&gt;Soldering is a small skill that opens up a lot: sensors, keyboards, robots, repairs and prototypes. This guide shows each step with a photo and covers the details that save beginners from burnt boards and joints that don’t hold.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;If you remember one thing&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Heat the metal parts first, then feed solder into the joint. Not onto the tip of the iron.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-a-good-joint-looks-like&quot;&gt;What a good joint looks like&lt;/h2&gt;

&lt;div class=&quot;post-pair&quot;&gt;
&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering-good.jpg?width=640&quot; alt=&quot;A good solder joint: smooth and shiny&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    &lt;strong&gt;Good:&lt;/strong&gt; smooth and shiny, solder has flowed into the joint.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering-good.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering-bad.jpg?width=640&quot; alt=&quot;A bad solder joint: dull and lumpy&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    &lt;strong&gt;Bad:&lt;/strong&gt; dull and lumpy, sitting on top instead of flowing in.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering-bad.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;/div&gt;

&lt;p&gt;A good joint has three things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The solder forms a small, shiny ramp between the pad and the wire or lead.&lt;/li&gt;
  &lt;li&gt;You can still see the shape of the lead under the solder. It isn’t buried in a blob.&lt;/li&gt;
  &lt;li&gt;Nothing moved while it cooled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;set-up-safely&quot;&gt;Set up safely&lt;/h2&gt;

&lt;h3 id=&quot;the-bench&quot;&gt;The bench&lt;/h3&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Setup%20for%20the%20soldering%20of%20electronic%20components.jpg?width=960&quot; alt=&quot;A soldering workstation with tools laid out on a bench&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    A steady surface, a clear bench, good light and tools within reach.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Setup_for_the_soldering_of_electronic_components.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Every time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Work on a steady table, never on your lap.&lt;/li&gt;
  &lt;li&gt;Use bright light, and a magnifier if you have one.&lt;/li&gt;
  &lt;li&gt;Keep the iron in a proper stand, on a heat-resistant mat.&lt;/li&gt;
  &lt;li&gt;Keep paper towels, alcohol and plastic bags away from the iron.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;fumes&quot;&gt;Fumes&lt;/h3&gt;

&lt;p&gt;You might not smell much, but the flux in solder gives off fumes. Pull them away from your face, close to where you work.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Woman%20soldering%20in%20front%20of%20fume%20extractor.png?width=960&quot; alt=&quot;Person soldering in front of a fume extractor&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    A fume extractor behind the work pulls the smoke away from you.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Woman_soldering_in_front_of_fume_extractor.png&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;A small extractor or fan behind the joint is enough. Keep the airflow gentle: a strong draft cools the joint and blows small parts away.&lt;/p&gt;

&lt;h3 id=&quot;eyes-burns-and-hands&quot;&gt;Eyes, burns and hands&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Wear safety glasses. Solder can spit, especially when you desolder.&lt;/li&gt;
  &lt;li&gt;The iron always goes back in the stand, never on the table.&lt;/li&gt;
  &lt;li&gt;Route the cable so you can’t catch it with your arm.&lt;/li&gt;
  &lt;li&gt;Have a heat-proof spot, like a ceramic tile, to drop hot parts on.&lt;/li&gt;
  &lt;li&gt;Wash your hands afterwards, especially with leaded solder.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;static-esd&quot;&gt;Static (ESD)&lt;/h3&gt;

&lt;p&gt;Not every project needs it, but an anti-static strap is cheap and protects modern chips.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/AntiStatic-Wrist-Guard.jpg?width=960&quot; alt=&quot;An anti-static wrist strap&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Clip the strap to a grounded point, like an ESD mat.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:AntiStatic-Wrist-Guard.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&quot;the-tools-that-matter&quot;&gt;The tools that matter&lt;/h2&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering%20workbench.jpg?width=960&quot; alt=&quot;A workbench with soldering tools&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    A few tools used well beat a big kit used at random.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering_workbench.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;You need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A temperature-controlled iron, or a good fixed-temperature one&lt;/li&gt;
  &lt;li&gt;A stand and a heat-resistant mat&lt;/li&gt;
  &lt;li&gt;Brass wool, or a damp sponge, to clean the tip&lt;/li&gt;
  &lt;li&gt;Electronics solder with a rosin (flux) core&lt;/li&gt;
  &lt;li&gt;Extra flux, as a pen or a small tub. It makes everything easier.&lt;/li&gt;
  &lt;li&gt;Helping hands or a PCB holder, so nothing moves while it cools&lt;/li&gt;
  &lt;li&gt;Flush cutters to trim leads&lt;/li&gt;
  &lt;li&gt;Isopropyl alcohol and a brush to clean up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Worth having:&lt;/strong&gt; a magnifier, tweezers for small parts, desoldering wick or a pump for mistakes, and a heat gun for heat-shrink tubing.&lt;/p&gt;

&lt;h2 id=&quot;clean-and-tin-the-tip&quot;&gt;Clean and tin the tip&lt;/h2&gt;

&lt;p&gt;Beginners skip this the most, and it matters the most. A dirty, oxidised tip passes heat badly, so you hold it on the pad longer, and that’s what damages boards.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Cleaning%20Soldering%20Iron.JPG?width=960&quot; alt=&quot;Cleaning a soldering iron tip&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    A quick wipe removes the oxide. A clean tip heats faster.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Cleaning_Soldering_Iron.JPG&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Wipe it quickly. If you use a sponge, it should be damp, not dripping.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Tinning%20a%20Soldering%20Iron.JPG?width=960&quot; alt=&quot;Melting solder onto a soldering iron tip&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Tinning: a thin coat of solder keeps the tip shiny.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Tinning_a_Soldering_Iron.JPG&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Repeat this rhythm all the time:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Heat the iron.&lt;/li&gt;
  &lt;li&gt;Clean the tip.&lt;/li&gt;
  &lt;li&gt;Melt a little solder on it, so it looks shiny.&lt;/li&gt;
  &lt;li&gt;Solder a joint.&lt;/li&gt;
  &lt;li&gt;Clean and re-tin every few joints, or whenever the tip looks dull.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Don’t keep the iron on the pad&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;If the solder takes more than a few seconds to melt, stop. Clean and tin the tip, add flux, and try again with better contact.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;the-basic-motion&quot;&gt;The basic motion&lt;/h2&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Circuit%20Soldering.gif?width=960&quot; alt=&quot;Animation: the iron heats a joint and solder is fed in&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    The iron heats the joint, solder goes into the joint, then everything holds still.    &lt;small&gt;Animation: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Circuit_Soldering.gif&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Position:&lt;/strong&gt; touch the iron to the pad and the lead at the same time.&lt;/p&gt;

    &lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering-step2a.jpg?width=960&quot; alt=&quot;The iron touching both the pad and the lead&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Both metals heat together.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering-step2a.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Feed:&lt;/strong&gt; touch the solder where the hot metals meet and let it flow. Don’t push.&lt;/p&gt;

    &lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering-step2c.jpg?width=960&quot; alt=&quot;Solder being fed into the joint&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    The solder melts on the joint, not on the iron.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering-step2c.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Finish:&lt;/strong&gt; take the solder away first, then the iron. Hold still while it cools.&lt;/p&gt;

    &lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering-step3.jpg?width=960&quot; alt=&quot;The finished joint cooling&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Solder away, then iron away.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering-step3.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A few details that help:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use the flat face of the tip. More contact means faster heat.&lt;/li&gt;
  &lt;li&gt;If the solder won’t flow, add flux.&lt;/li&gt;
  &lt;li&gt;Don’t blow on the joint. Let it cool by itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;through-hole-parts&quot;&gt;Through-hole parts&lt;/h2&gt;

&lt;p&gt;Headers, resistors, LEDs: anything with legs that go through the board.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering-PCB-a.jpg?width=960&quot; alt=&quot;Soldering a through-hole part on a PCB&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Heat pad and lead, feed solder, trim the lead.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering-PCB-a.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;ol&gt;
  &lt;li&gt;Push the part in fully, or to the height you want.&lt;/li&gt;
  &lt;li&gt;Bend the leads slightly so it doesn’t fall out.&lt;/li&gt;
  &lt;li&gt;Add flux if you have it.&lt;/li&gt;
  &lt;li&gt;Heat the pad and the lead together.&lt;/li&gt;
  &lt;li&gt;Feed solder into the joint.&lt;/li&gt;
  &lt;li&gt;Remove the solder, then the iron.&lt;/li&gt;
  &lt;li&gt;Trim the leads once it’s cool.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Watch the pad&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;If the pad starts to lift or the board around it changes colour, stop and let it cool. Holding the iron too long is the most common way to lift a pad.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;wires&quot;&gt;Wires&lt;/h2&gt;

&lt;p&gt;With wires you want a joint that is mechanically strong as well as soldered.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Good%20and%20bad%20soldering.jpeg?width=960&quot; alt=&quot;Good and bad soldered wire joints side by side&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Smooth flow is good. Bubbles and a rough surface usually mean too much heat or movement.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Good_and_bad_soldering.jpeg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Tin the wires first.&lt;/strong&gt; Strip them, twist the strands, add flux, then heat the wire and feed solder until the strands look silver. No blobs. Two tinned wires join in a second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add strain relief.&lt;/strong&gt; Solder isn’t glue. Put heat-shrink tubing over the joint, and tie the wire down so movement doesn’t pull on the joint.&lt;/p&gt;

&lt;h2 id=&quot;heat-guns&quot;&gt;Heat guns&lt;/h2&gt;

&lt;p&gt;A heat gun is great for heat-shrink tubing, but it doesn’t replace the iron on board joints.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Heatgun%20and%20shrinkwrap.JPG?width=960&quot; alt=&quot;A heat gun shrinking tubing over a wire&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    What a heat gun is for: heat-shrink.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Heatgun_and_shrinkwrap.JPG&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Keep the nozzle moving.&lt;/li&gt;
  &lt;li&gt;Use the lowest heat that shrinks the tubing.&lt;/li&gt;
  &lt;li&gt;Don’t point it at the board for long. It can warp plastic, loosen connectors and damage parts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For hot-air rework on electronics, use a hot-air rework station. Unlike a paint-stripping heat gun, it lets you set the temperature and airflow.&lt;/p&gt;

&lt;h2 id=&quot;check-your-work&quot;&gt;Check your work&lt;/h2&gt;

&lt;h3 id=&quot;cold-joints&quot;&gt;Cold joints&lt;/h3&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Cold%20solder%20joint2.jpg?width=960&quot; alt=&quot;A cold solder joint close up&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    A cold joint: dull, grainy, crusty.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Cold_solder_joint2.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add flux, reheat until it flows, add a little fresh solder if needed, and let it cool without moving.&lt;/p&gt;

&lt;h3 id=&quot;a-healthy-joint&quot;&gt;A healthy joint&lt;/h3&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Solderedjoint.jpg?width=960&quot; alt=&quot;A clean solder joint on a circuit board&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Smooth, with clean edges and no spikes or balls.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Solderedjoint.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;a-30-second-check&quot;&gt;A 30-second check&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Any &lt;strong&gt;bridges&lt;/strong&gt; between pins, especially on chips and headers?&lt;/li&gt;
  &lt;li&gt;Any &lt;strong&gt;dull or grainy&lt;/strong&gt; joints?&lt;/li&gt;
  &lt;li&gt;Any joints that look like a &lt;strong&gt;ball&lt;/strong&gt; sitting on top?&lt;/li&gt;
  &lt;li&gt;Any pads that are &lt;strong&gt;discoloured or lifting&lt;/strong&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;fixing-mistakes&quot;&gt;Fixing mistakes&lt;/h2&gt;

&lt;p&gt;Boards get damaged during rework more than anywhere else. Keep heat contact short and use plenty of flux.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Desoldering.jpg?width=960&quot; alt=&quot;Desoldering a joint&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Add flux, add fresh solder if needed, remove it cleanly, solder again.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Desoldering.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;solder-wick&quot;&gt;Solder wick&lt;/h3&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Solder%20wick%20rolled.jpg?width=960&quot; alt=&quot;A roll of solder wick&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Lay the braid on the solder, press the iron on top, lift both straight up.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Solder_wick_rolled.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Put flux on the wick before you heat it. It soaks up solder much better.&lt;/p&gt;

&lt;h3 id=&quot;save-the-pad&quot;&gt;Save the pad&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Don’t keep heating the same pad. Heat, try, let it cool, try again.&lt;/li&gt;
  &lt;li&gt;If the solder won’t melt quickly: clean and tin the tip, add flux, or use a bigger tip.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-first-look-at-smd&quot;&gt;A first look at SMD&lt;/h2&gt;

&lt;p&gt;You can learn surface-mount parts as a beginner. Start with larger sizes like 0805 before the tiny ones.&lt;/p&gt;

&lt;figure class=&quot;post-figure&quot;&gt;  &lt;img src=&quot;https://commons.wikimedia.org/wiki/Special:FilePath/Soldering%20a%200805.jpg?width=960&quot; alt=&quot;Soldering an 0805 surface-mount part&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;  &lt;figcaption&gt;    Tack one side, line it up, then solder the other side with flux.    &lt;small&gt;Photo: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Soldering_a_0805.jpg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/small&gt;  &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&quot;finish-cleanly&quot;&gt;Finish cleanly&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Inspect under bright light, magnified if you can.&lt;/li&gt;
  &lt;li&gt;Clean flux residue with isopropyl alcohol and a brush, and let the board dry before you power it.&lt;/li&gt;
  &lt;li&gt;Put the iron in its stand and unplug it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-30-minute-practice-plan&quot;&gt;A 30-minute practice plan&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Motion:&lt;/strong&gt; twist two scrap wires together and solder until you get three smooth joints in a row.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Through-hole:&lt;/strong&gt; solder a header onto a spare board or perfboard. Aim for joints that all look the same.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rework:&lt;/strong&gt; make a bad joint on purpose, then fix it with flux and heat, and clean it up.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;cheat-sheet&quot;&gt;Cheat sheet&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Before&lt;/th&gt;
      &lt;th&gt;During&lt;/th&gt;
      &lt;th&gt;After&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Bright light, steady bench&lt;/td&gt;
      &lt;td&gt;Heat pad and lead together&lt;/td&gt;
      &lt;td&gt;Look for bridges and cold joints&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Fumes pulled away&lt;/td&gt;
      &lt;td&gt;Feed solder into the joint&lt;/td&gt;
      &lt;td&gt;Clean off flux residue&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tip clean and tinned&lt;/td&gt;
      &lt;td&gt;Solder away, then iron&lt;/td&gt;
      &lt;td&gt;Iron in the stand, unplugged&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Hold still while it cools&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;</content><author><name>MohammadMahdi Javid</name><uri>https://www.mmjavid.com/</uri></author><category term="Hardware" /><category term="Soldering" /><category term="Electronics" /><summary type="html">How to make solder joints that hold: setting up safely, the basic motion, wires, inspection and fixing mistakes, with a photo for each step.</summary></entry><entry><title type="html">Let’s listen to music together</title><link href="https://www.mmjavid.com/Listen-to-Music-together/" rel="alternate" type="text/html" title="Let’s listen to music together" /><published>2022-11-08T00:00:00+01:00</published><updated>2026-10-06T00:00:00+02:00</updated><id>https://www.mmjavid.com/Listen%20to%20Music%20together</id><content type="html" xml:base="https://www.mmjavid.com/Listen-to-Music-together/">&lt;p&gt;I’m a big fan of music, and I’m sure many of you are too. Here are a few of my favourites to start with.&lt;/p&gt;

&lt;figure class=&quot;post-embed&quot; data-embed-src=&quot;https://open.spotify.com/embed/playlist/2XfMB8PX7vbrAeo3oOL2JT?utm_source=generator&amp;amp;theme=0&quot; data-embed-title=&quot;A few of my favourites&quot;&gt;
  &lt;div class=&quot;post-embed__face&quot;&gt;
    &lt;span class=&quot;post-embed__eq&quot; aria-hidden=&quot;true&quot;&gt;&lt;i&gt;&lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;
    &lt;span class=&quot;post-embed__text&quot;&gt;
      &lt;span class=&quot;post-embed__label&quot;&gt;Playlist on Spotify&lt;/span&gt;
      &lt;span class=&quot;post-embed__title&quot;&gt;A few of my favourites&lt;/span&gt;
    &lt;/span&gt;
    &lt;a class=&quot;post-embed__play&quot; href=&quot;https://open.spotify.com/playlist/2XfMB8PX7vbrAeo3oOL2JT&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Play here&lt;/a&gt;
  &lt;/div&gt;
&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Your turn&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;I’m not looking for anything in particular. If there’s a song you like and want to share, send it to me through any of the links on &lt;a href=&quot;/follow-me/&quot;&gt;Follow Me&lt;/a&gt;. Suggestions are always welcome.&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name>MohammadMahdi Javid</name><uri>https://www.mmjavid.com/</uri></author><category term="Music" /><summary type="html">A few of my favourite songs to start with. Send me yours.</summary></entry><entry><title type="html">A SOCKS5 proxy over SSH to get around censorship</title><link href="https://www.mmjavid.com/socks5-proxy-to-bypass-censorship/" rel="alternate" type="text/html" title="A SOCKS5 proxy over SSH to get around censorship" /><published>2022-10-02T00:00:00+02:00</published><updated>2026-10-06T00:00:00+02:00</updated><id>https://www.mmjavid.com/Post-Creating%20SOCKS5%20Proxy%20to%20bypass%20Censorship</id><content type="html" xml:base="https://www.mmjavid.com/socks5-proxy-to-bypass-censorship/">&lt;p&gt;This setup is for when your device can only reach the local network, a machine on that network can reach a filtered internet, and you have a server somewhere outside the filter. One SSH tunnel chains them together, and your device ends up with a normal SOCKS5 proxy.&lt;/p&gt;

&lt;figure class=&quot;post-hops&quot; aria-label=&quot;Client on the intranet, through a middle machine with filtered internet, to a server with open internet&quot;&gt;
  &lt;div class=&quot;post-hops__node&quot;&gt;
    &lt;span class=&quot;post-hops__name&quot;&gt;Client&lt;/span&gt;
    &lt;span class=&quot;post-hops__note&quot;&gt;phone or PC, intranet only&lt;/span&gt;
  &lt;/div&gt;
  &lt;span class=&quot;post-hops__link&quot; aria-hidden=&quot;true&quot;&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;
  &lt;div class=&quot;post-hops__node&quot;&gt;
    &lt;span class=&quot;post-hops__name&quot;&gt;Middle&lt;/span&gt;
    &lt;span class=&quot;post-hops__note&quot;&gt;filtered internet&lt;/span&gt;
  &lt;/div&gt;
  &lt;span class=&quot;post-hops__link&quot; aria-hidden=&quot;true&quot;&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;
  &lt;div class=&quot;post-hops__node&quot;&gt;
    &lt;span class=&quot;post-hops__name&quot;&gt;Server&lt;/span&gt;
    &lt;span class=&quot;post-hops__note&quot;&gt;open internet&lt;/span&gt;
  &lt;/div&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;what-you-need&quot;&gt;What you need&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Client:&lt;/strong&gt; the device you want to browse from. It only reaches the intranet.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Middle:&lt;/strong&gt; a Linux machine the client can reach, with internet access that is filtered.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Server:&lt;/strong&gt; a machine outside the filter that you can SSH into from the middle machine. A small VPS is enough.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SSH&lt;/strong&gt; on the middle machine and the server.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.inet.no/dante/&quot; title=&quot;Dante, a free SOCKS server&quot;&gt;&lt;strong&gt;Dante&lt;/strong&gt;&lt;/a&gt; on the server, only if you use option B.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.proxifier.com/&quot; title=&quot;Proxifier, a proxy client&quot;&gt;&lt;strong&gt;Proxifier&lt;/strong&gt;&lt;/a&gt;, or any app that can use a SOCKS5 proxy, on the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Anywhere below you can use a domain name instead of an IP address, and any free port instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1080&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;option-a-ssh-alone&quot;&gt;Option A: SSH alone&lt;/h2&gt;

&lt;p&gt;SSH can act as a SOCKS5 proxy by itself. Run this on the &lt;strong&gt;middle&lt;/strong&gt; machine:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-N&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt; 0.0.0.0:1080 user@server.example.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-D 0.0.0.0:1080&lt;/code&gt; opens a SOCKS5 proxy on port 1080 of the middle machine, on all its network interfaces, so the client can reach it.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-C&lt;/code&gt; compresses the traffic, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-N&lt;/code&gt; means “no shell, just the tunnel”.&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; while you test, to see each connection as it opens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every connection the client makes through the proxy now leaves from the server, outside the filter.&lt;/p&gt;

&lt;h2 id=&quot;option-b-dante-on-the-server&quot;&gt;Option B: Dante on the server&lt;/h2&gt;

&lt;p&gt;If you’d rather run a real SOCKS server on the outside machine, for example to share it between several tunnels, install Dante there and keep it on localhost, so only the tunnel can reach it:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dante-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A minimal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/danted.conf&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-conf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;logoutput&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;syslog&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;internal&lt;/span&gt;: &lt;span class=&quot;m&quot;&gt;127&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; = &lt;span class=&quot;m&quot;&gt;1080&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;external&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;socksmethod&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;clientmethod&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;privileged&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;unprivileged&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;nobody&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pass&lt;/span&gt; { &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;: &lt;span class=&quot;m&quot;&gt;127&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;/&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;: &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;/&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; }
&lt;span class=&quot;n&quot;&gt;socks&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pass&lt;/span&gt; { &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;: &lt;span class=&quot;m&quot;&gt;127&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;/&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;: &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;/&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; with the server’s network interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip a&lt;/code&gt; lists them), then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl restart danted&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now forward a port from the &lt;strong&gt;middle&lt;/strong&gt; machine to Dante:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-N&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt; 0.0.0.0:1080:127.0.0.1:1080 user@server.example.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The pattern is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-L [listen IP]:[listen port]:[destination IP]:[destination port]&lt;/code&gt;. Here the middle machine listens on port 1080 and sends everything to port 1080 on the server’s own localhost, where Dante is waiting.&lt;/p&gt;

&lt;h2 id=&quot;point-the-client-at-the-proxy&quot;&gt;Point the client at the proxy&lt;/h2&gt;

&lt;p&gt;On the client, add a proxy in Proxifier:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Profile → Proxy Servers → Add&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Address: the middle machine’s intranet IP. Port: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1080&lt;/code&gt;. Protocol: &lt;strong&gt;SOCKS Version 5&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Check&lt;/strong&gt; to test it, then let Proxifier send all traffic through it.&lt;/li&gt;
  &lt;li&gt;Under &lt;strong&gt;Profile → Name Resolution&lt;/strong&gt;, resolve hostnames through the proxy. Otherwise DNS lookups still go through the filtered network.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On a phone, use any app or browser setting that supports SOCKS5 with the same address and port. In Firefox it’s &lt;strong&gt;Settings → Network Settings → Manual proxy&lt;/strong&gt;, with &lt;strong&gt;Proxy DNS when using SOCKS v5&lt;/strong&gt; ticked.&lt;/p&gt;

&lt;h2 id=&quot;keep-it-running&quot;&gt;Keep it running&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Log in with an SSH key instead of a password, so the tunnel can start without you typing anything.&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-o ServerAliveInterval=30 -o ExitOnForwardFailure=yes&lt;/code&gt; so a dead connection is noticed quickly.&lt;/li&gt;
  &lt;li&gt;Wrap the command in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autossh&lt;/code&gt; or a systemd service, so it comes back by itself after a drop.&lt;/li&gt;
  &lt;li&gt;Anyone who can reach the middle machine’s port 1080 can use your proxy. If the intranet isn’t yours, bind to one interface instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0.0.0&lt;/code&gt;, or block the port for everyone but your client with a firewall rule.&lt;/li&gt;
&lt;/ul&gt;</content><author><name>MohammadMahdi Javid</name><uri>https://www.mmjavid.com/</uri></author><category term="SSH" /><category term="SOCKS5" /><category term="Networking" /><summary type="html">Three machines, one SSH tunnel: how to give a device that only reaches the intranet a clean internet connection.</summary></entry><entry><title type="html">How to make your own website on GitHub Pages</title><link href="https://www.mmjavid.com/how-to-create-a-github-website/" rel="alternate" type="text/html" title="How to make your own website on GitHub Pages" /><published>2019-10-08T00:00:00+02:00</published><updated>2026-10-06T00:00:00+02:00</updated><id>https://www.mmjavid.com/Post-How%20to%20create%20github%20websites</id><content type="html" xml:base="https://www.mmjavid.com/how-to-create-a-github-website/">&lt;p&gt;I wrote this guide in 2019 for a course where everyone had to put a personal website online. GitHub Pages hosts it for free, and you only need a browser, Git and a text editor. Every step has a screenshot, so you can check that your screen looks the same before you move on.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;GitHub has changed since 2019&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;The screenshots are from 2019 and some buttons have moved. The Pages settings now live under &lt;strong&gt;Settings → Pages&lt;/strong&gt;, new repositories use a branch called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;, and the green &lt;strong&gt;Code&lt;/strong&gt; button replaced “Clone or download”. The steps are otherwise the same.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;1-create-a-github-account&quot;&gt;1. Create a GitHub account&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Go to &lt;a href=&quot;https://github.com&quot;&gt;github.com&lt;/a&gt; and click &lt;strong&gt;Sign up&lt;/strong&gt; at the top right. Pick your username carefully: your site will live at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourusername.github.io&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/signup.webp&quot; alt=&quot;GitHub home page with the Sign up button marked&quot; width=&quot;1280&quot; height=&quot;689&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fill in the form.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/create-account.webp&quot; alt=&quot;The Create your account form&quot; width=&quot;1280&quot; height=&quot;660&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Choose the &lt;strong&gt;Free&lt;/strong&gt; plan.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/free-plan.webp&quot; alt=&quot;Plan selection with the free plan marked&quot; width=&quot;1280&quot; height=&quot;660&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The “customize” page is optional. You can skip it.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/skip-customize.webp&quot; alt=&quot;The optional customize page with the skip link marked&quot; width=&quot;1280&quot; height=&quot;657&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;GitHub sends you an email. If it isn’t in your inbox, check spam.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/verify-email-inbox.webp&quot; alt=&quot;Inbox with the GitHub verification email&quot; width=&quot;944&quot; height=&quot;176&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Open it and click &lt;strong&gt;Verify email address&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/verify-email-button.webp&quot; alt=&quot;Verification email with the Verify email address button marked&quot; width=&quot;680&quot; height=&quot;889&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;2-copy-the-website-template&quot;&gt;2. Copy the website template&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Open the &lt;a href=&quot;https://github.com/sauleh/personal_website_template&quot;&gt;personal website template&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/template-repo.webp&quot; alt=&quot;The personal_website_template repository&quot; width=&quot;1280&quot; height=&quot;653&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;strong&gt;Fork&lt;/strong&gt; at the top right. This copies the template into your own account.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/fork-button.webp&quot; alt=&quot;The Fork button marked&quot; width=&quot;1280&quot; height=&quot;655&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you see a “Forking…” page, wait a few seconds or refresh. If you don’t see it, that’s fine too.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/forking.webp&quot; alt=&quot;The Forking page&quot; width=&quot;1280&quot; height=&quot;649&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;When it’s done you’re on your own copy, and the Fork button is greyed out.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/forked.webp&quot; alt=&quot;Your forked copy with the Fork button greyed out&quot; width=&quot;1280&quot; height=&quot;652&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Open your repositories: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://github.com/yourusername?tab=repositories&lt;/code&gt; (use your username).&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/repositories-url.webp&quot; alt=&quot;The repositories URL in the address bar&quot; width=&quot;1280&quot; height=&quot;36&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;You should see the template in the list.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/repositories-list.webp&quot; alt=&quot;Your repository list with personal_website_template&quot; width=&quot;1280&quot; height=&quot;657&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;strong&gt;personal_website_template&lt;/strong&gt;, or go straight to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://github.com/yourusername/personal_website_template&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/open-repo.webp&quot; alt=&quot;The repository link marked in the list&quot; width=&quot;1280&quot; height=&quot;657&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/repo-page.webp&quot; alt=&quot;The repository page&quot; width=&quot;1280&quot; height=&quot;653&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/settings-tab.webp&quot; alt=&quot;The Settings tab marked&quot; width=&quot;1280&quot; height=&quot;655&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/settings-page.webp&quot; alt=&quot;The repository settings page&quot; width=&quot;1280&quot; height=&quot;657&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Rename the repository to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourusername.github.io&lt;/code&gt;. If your username is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ali&lt;/code&gt;, the name is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ali.github.io&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/rename-repo.webp&quot; alt=&quot;Repository name field set to yourusername.github.io&quot; width=&quot;1280&quot; height=&quot;660&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Scroll down to &lt;strong&gt;GitHub Pages&lt;/strong&gt; and set the source to the &lt;strong&gt;master branch&lt;/strong&gt; (on today’s GitHub: &lt;strong&gt;Settings → Pages&lt;/strong&gt;, branch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;). If it’s already set, leave it.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/pages-source.webp&quot; alt=&quot;GitHub Pages source set to master branch&quot; width=&quot;1280&quot; height=&quot;657&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourusername.github.io&lt;/code&gt; in your browser. You should see the template site. It can take a minute or two the first time.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/live-site.webp&quot; alt=&quot;The template website live on github.io&quot; width=&quot;1280&quot; height=&quot;658&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;3-install-git-and-download-your-site&quot;&gt;3. Install Git and download your site&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Go to &lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;git-scm.com/downloads&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-downloads.webp&quot; alt=&quot;The Git downloads page&quot; width=&quot;1280&quot; height=&quot;655&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click the button for your system. I used 64-bit Windows 10, so I clicked &lt;strong&gt;Windows&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-windows.webp&quot; alt=&quot;The Windows download button marked&quot; width=&quot;1280&quot; height=&quot;658&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The download should start on its own. If it doesn’t, go to &lt;a href=&quot;https://git-scm.com/download/win&quot;&gt;git-scm.com/download/win&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-download-page.webp&quot; alt=&quot;The Downloading Git page&quot; width=&quot;1280&quot; height=&quot;647&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;and pick the installer that matches your Windows (for most people, &lt;strong&gt;64-bit Git for Windows Setup&lt;/strong&gt;).&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-64bit.webp&quot; alt=&quot;The 64-bit installer link marked&quot; width=&quot;1280&quot; height=&quot;647&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run the downloaded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.exe&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-exe.webp&quot; alt=&quot;The downloaded Git installer file&quot; width=&quot;760&quot; height=&quot;38&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt; on every screen. The defaults are fine.&lt;/p&gt;

    &lt;details class=&quot;post-more&quot;&gt;
      &lt;summary&gt;Show all eight installer screens&lt;/summary&gt;

      &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/setup-license.webp&quot; alt=&quot;Installer: license&quot; width=&quot;584&quot; height=&quot;473&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-components.webp&quot; alt=&quot;Installer: components&quot; width=&quot;582&quot; height=&quot;475&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-editor.webp&quot; alt=&quot;Installer: default editor&quot; width=&quot;584&quot; height=&quot;476&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-path.webp&quot; alt=&quot;Installer: PATH environment&quot; width=&quot;582&quot; height=&quot;482&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-https.webp&quot; alt=&quot;Installer: HTTPS backend&quot; width=&quot;583&quot; height=&quot;478&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-line-endings.webp&quot; alt=&quot;Installer: line endings&quot; width=&quot;580&quot; height=&quot;479&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-terminal.webp&quot; alt=&quot;Installer: terminal emulator&quot; width=&quot;581&quot; height=&quot;477&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;
&lt;img src=&quot;/assets/images/posts/github-pages/setup-extras.webp&quot; alt=&quot;Installer: extra options&quot; width=&quot;583&quot; height=&quot;477&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;/details&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On the last screen click &lt;strong&gt;Install&lt;/strong&gt;,&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/setup-install.webp&quot; alt=&quot;The Install button marked&quot; width=&quot;582&quot; height=&quot;475&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;then &lt;strong&gt;Finish&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/setup-finish.webp&quot; alt=&quot;Completing the Git Setup Wizard&quot; width=&quot;505&quot; height=&quot;394&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To check that it worked, type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd&lt;/code&gt; in the Windows search box&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/search-cmd.webp&quot; alt=&quot;Windows search box&quot; width=&quot;493&quot; height=&quot;52&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;and open &lt;strong&gt;Command Prompt&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/open-cmd.webp&quot; alt=&quot;Command Prompt in the search results&quot; width=&quot;1043&quot; height=&quot;852&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; and press Enter. If you see a list of Git commands, Git is installed.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-help.webp&quot; alt=&quot;Git&apos;s help text in Command Prompt&quot; width=&quot;1280&quot; height=&quot;692&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Back on GitHub, open your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourusername.github.io&lt;/code&gt; repository.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/your-repo.webp&quot; alt=&quot;Your repository on GitHub&quot; width=&quot;1280&quot; height=&quot;686&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;strong&gt;Clone or download&lt;/strong&gt; (today: the green &lt;strong&gt;Code&lt;/strong&gt; button) and copy the URL.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/clone-url.webp&quot; alt=&quot;Clone or download with the copy button marked&quot; width=&quot;1280&quot; height=&quot;687&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In Command Prompt, make a folder for your projects and clone the site into it:&lt;/p&gt;

    &lt;div class=&quot;language-bat highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/d &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\
&lt;span class=&quot;nb&quot;&gt;md&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;dev&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;dev&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;https&lt;/span&gt;://github.com/yourusername/yourusername.github.io.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Git prints a few lines while it downloads. When it stops, the site is on your computer.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/git-clone.webp&quot; alt=&quot;Git clone output in Command Prompt&quot; width=&quot;1280&quot; height=&quot;297&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;4-open-the-site-in-vs-code&quot;&gt;4. Open the site in VS Code&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Open Visual Studio Code, then &lt;strong&gt;File → Open Folder&lt;/strong&gt; (&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;K&lt;/kbd&gt; &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;O&lt;/kbd&gt;).&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/vscode-open-folder.webp&quot; alt=&quot;The File menu in VS Code with Open Folder marked&quot; width=&quot;1280&quot; height=&quot;687&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Pick the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\dev\yourusername.github.io&lt;/code&gt; folder and click &lt;strong&gt;Select Folder&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/vscode-select-folder.webp&quot; alt=&quot;Folder picker with Select Folder marked&quot; width=&quot;1280&quot; height=&quot;690&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You should see the site’s files on the left.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/vscode-explorer.webp&quot; alt=&quot;The site&apos;s files in the VS Code explorer&quot; width=&quot;554&quot; height=&quot;1030&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;. Most of the site’s settings are in this one file.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/config-file.webp&quot; alt=&quot;_config.yml marked in the file list&quot; width=&quot;488&quot; height=&quot;582&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;5-make-it-yours&quot;&gt;5. Make it yours&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Change the title, your name, the description and the colour skin.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/config-site.webp&quot; alt=&quot;Site settings in _config.yml&quot; width=&quot;1237&quot; height=&quot;907&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;Further down, fill in your bio, email and social links.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/config-author.webp&quot; alt=&quot;Author settings in _config.yml&quot; width=&quot;1280&quot; height=&quot;859&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The pages themselves are Markdown files: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cv.md&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;courses.md&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog.md&lt;/code&gt; and so on. Edit them like any text file.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/posts/github-pages/pages-files.webp&quot; alt=&quot;The page files in the file list&quot; width=&quot;466&quot; height=&quot;713&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To put your changes online, save, then commit and push from the same folder:&lt;/p&gt;

    &lt;div class=&quot;language-bat highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;add&lt;/span&gt; .
&lt;span class=&quot;kd&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;commit&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-m &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;My first changes&quot;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;push&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;GitHub rebuilds the site after every push. Refresh &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourusername.github.io&lt;/code&gt; after a minute and you’ll see your changes.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;</content><author><name>MohammadMahdi Javid</name><uri>https://www.mmjavid.com/</uri></author><category term="GitHub Pages" /><category term="Git" /><category term="Jekyll" /><summary type="html">A step-by-step guide I wrote in 2019 for a course: a GitHub account, a site template, Git on Windows, and your first edits.</summary></entry></feed>