All posts by DAN - DBA

Mcp Integration

Welcome to the NZRT Wiki Podcast. Today we’re looking at MCP Integration.

So let’s start with why this matters. x402 — NZRT’s micropayment protocol — pairs naturally with MCP, which stands for Model Context Protocol. The idea is that the MCP server acts as the paying client, automatically handling payment responses in a way that’s completely transparent to the language model itself. The LLM never has to think about money — it just asks for something and gets an answer back.

Let’s walk through how that works in practice, starting with the architecture.

Picture Claude — the language model — calling a tool called search wiki, passing in a keyword like “kubernetes”. That call goes down to the MCP server, which is running locally on your machine. The MCP server then makes an HTTP request out to a resource server. The resource server comes back with a 402 status — that’s the standard HTTP code meaning payment required. The MCP server reads the payment requirements, signs a USDC authorization using its wallet, and retries the same request, this time including a payment header. The resource server accepts that, returns the actual content, and the MCP server passes the result back up to Claude as a normal tool result. Claude never sees the payment happen. It just gets its answer. That’s the elegance of this design.

Now let’s look at what the MCP server is actually responsible for. There are five main areas. First, tool exposure — it defines the tools that the language model can call. Second, it acts as the HTTP client, making requests to the resource server on Claude’s behalf. Third, it handles those 402 responses — detecting them, reading the payment requirements, signing the payment, and retrying. Fourth, it holds the payer wallet, meaning it stores the private key used to sign what’s called an EIP-712 payment authorization. And fifth, it manages nonces, generating a random value for each payment to prevent replay attacks.

Speaking of the tools the MCP server exposes, there are two defined in the schema. The first is called search wiki — its job is to search the NZRT knowledge vault by keyword, and it takes a single input, a search query string. The second tool is called get wiki note, which retrieves a specific note from the vault by its file path — something like Wiki slash Flarum slash HOME. Both tools require their input to be provided, so Claude always needs to pass either a query or a path when calling them.

Next, transport. You have two options for how an MCP server can run. The first is stdio, which is local — Claude Desktop spawns the MCP server process directly on the same machine. The second is SSE, which stands for Server-Sent Events and works over HTTP, letting a server run independently while Claude connects to it via a URL. For x402 MCP setups, stdio is the typical choice. The resource server itself can be remote or local, but the MCP server always runs local to wherever the LLM client is.

Now let’s talk about what actually triggers the automatic payment. When the MCP server makes a request and gets back that 402 status, it reads the payment requirements from the response body, signs a payment using the private key it holds, and retries the original request with a header called X-Payment carrying the encoded payment data. If everything checks out, the resource server responds with a 200 and the content, which flows back up to Claude as a normal result.

On the resource server side, you’ve got five responsibilities of its own. First, route protection — it returns 402 for any endpoint that requires payment. Second, payment verification — for each incoming payment header, it calls out to a facilitator service to verify the payment is valid. Third, settlement — once verified, it calls that same facilitator’s settle endpoint to actually execute the on-chain transfer. Fourth, content serving — after a verified payment, it returns the requested content. And fifth, there’s a free health check endpoint always available for monitoring, no payment needed.

So to pull it all together, MCP Integration with x402 creates a clean separation of concerns. Claude stays focused on reasoning and tool use. The MCP server handles all the payment plumbing invisibly in the background. And the resource server enforces access control and handles settlement. The whole thing is designed so that paid API access just works, without the LLM needing to understand anything about wallets, signatures, or blockchain transactions.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Protocol Reference

Welcome to the NZRT Wiki Podcast. Today we’re looking at Protocol Reference.

This episode walks you through the x402 payment protocol — the HTTP headers involved, what the payment objects contain, how the facilitator API works, and how to wire it all up with the official SDK. Let’s get into it.

Start with the headers, because they’re the backbone of the whole flow. When your client hits a protected endpoint without having paid, the server returns a 402 response. That response includes a header called X-Payment-Requirements, which carries a Base64-encoded block describing exactly what payment is needed. Once your client has sorted out the payment, it retries the original request with a header called X-Payment — also Base64-encoded — containing the payment details. If everything checks out, the server comes back with a 200, and includes a third header: X-Payment-Response, a Base64-encoded settlement confirmation. So to recap: requirements come down from the server, payment goes up from your client, and the settlement confirmation comes back down.

Now let’s look at what’s inside those Base64 blobs. The payment requirements object — the one you get in the 402 — contains a version number and an array of accepted payment options. Each option in that array tells you the payment scheme, which is called “exact”, the network identifier, the maximum amount required expressed in atomic units — and worth noting here, USDC uses six decimal places — the full URL of the protected endpoint, a human-readable description, the expected response type, the payee’s Ethereum address, a timeout window in seconds, the USDC contract address on that network, and optionally a name and version identifying the token. For USDC that version is two, following the EIP-3009 standard.

The payment payload — what your client sends back in that X-Payment header — also has a version number and a scheme field. The core of it is a payload section with two parts. First, a signature: a hex-encoded cryptographic signature proving you authorised the transfer. Second, an authorization block containing the payer’s address, the payee’s address, the amount in atomic units, two timestamps marking when the authorization becomes valid and when it expires, and finally a random nonce — a 32-byte hex value that ensures the same authorization can’t be replayed in a future request.

Moving on to the Facilitator API, hosted at x402.org/facilitator. This is the service that verifies and settles payments on your behalf, and it exposes two endpoints. The first is the verify endpoint. You post to it with the payment payload and the payment requirements, and it responds with a simple result: either the payment is valid, or it is not, along with a reason string if something went wrong. The second endpoint is settle. Same input structure, and if successful, the response tells you the transaction was successful, gives you the transaction hash, and confirms which network the settlement occurred on.

On the subject of error codes, here are the HTTP status codes you’ll encounter. A 402 with the requirements attached means you need to pay before proceeding. A 400 means your payment header was malformed and couldn’t be parsed at all. Another 402 can come back even after you’ve submitted payment, if the signature check failed. And a 200 means you’re good — content is returned. On the facilitator side, when a payment comes back as invalid, the reason will be one of five values: invalid signature, insufficient amount, expired, nonce already used, or invalid network.

Now for the cryptographic layer underneath all of this. x402 uses a standard called EIP-3009, specifically a typed data structure called TransferWithAuthorization. The signing domain is set up for USD Coin version two, on chain ID 8453 — that’s the Base network — pointing to the USDC contract address on that chain. The typed data itself defines six fields: the from and to addresses, the value as a large unsigned integer, two timestamp fields for the valid-after and valid-before window, and the nonce as a 32-byte value. This is the structure that the payer signs to produce the signature you saw in the payment payload object earlier.

Finally, if you’re working in TypeScript or JavaScript, there’s an official SDK available through npm. You import a middleware function from the x402 package, attach it to your Express application with your wallet address and a configuration object that maps your API routes to their prices and target networks, and the middleware handles everything from there — generating the 402 responses, calling the facilitator to verify incoming payments, and settling them automatically. You don’t need to manually wire up any of the facilitator calls if you’re using the SDK.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Base Network Reference

Welcome to the NZRT Wiki Podcast. Today we’re looking at Base Network Reference.

If you’re doing any blockchain development at NZRT, Base is one of the networks you’ll be working with regularly. This episode covers the key parameters, useful contracts, and how to configure your local Hardhat setup for both the mainnet and testnet environments.

So first, let’s talk about what Base actually is. Base is a Layer 2 blockchain network built on top of Ethereum, developed by Coinbase. Like Ethereum, it uses ETH as its currency and gas token. The important thing to understand is that NZRT works with two versions of the Base network. There’s Base Mainnet, which is the live production network where real transactions happen with real value. And then there’s Base Sepolia, which is the testnet — a sandbox environment where you can develop and test without spending real money.

Let’s go through the network parameters for both. Each network has what’s called a Chain ID, which is a unique number that identifies the network to your tools and wallets. For Base Mainnet, that Chain ID is 8453. For Base Sepolia testnet, it’s 84532. You’ll need these numbers when configuring wallets or development tools.

Both networks use ETH as the currency, though on the testnet it’s test ETH with no real-world value.

For connecting to the networks, you have two RPC endpoint options — think of these as the addresses your tools use to talk to the blockchain. The public RPC for Base Mainnet is mainnet.base.org, and for the testnet it’s sepolia.base.org. If you’re using Alchemy for a more reliable connection, there are dedicated Alchemy endpoints for both networks that include your API key as part of the URL.

For browsing transactions and contracts on-chain, the explorer for Base Mainnet is basescan.org, and for the testnet you’d go to sepolia.basescan.org. These are essentially search engines for the blockchain — you can look up wallet addresses, transactions, and contract details.

If you need to move ETH between Ethereum and Base, the bridge for mainnet is bridge.base.org. For the testnet, you’d use testnets.superbridge.app. And speaking of the testnet — if you need test ETH to actually run transactions in the Sepolia environment, you can get it free from the Coinbase faucet at coinbase.com/faucets. There’s no faucet needed for mainnet since that uses real ETH.

One more useful number: the block time on both networks is approximately 2 seconds. That means transactions confirm pretty quickly compared to Ethereum mainnet.

Now let’s look at two important contract addresses you’ll use frequently on Base Mainnet. A contract address is a fixed location on the blockchain where a specific token or smart contract lives. The first is USDC — the USD Coin stablecoin — which lives at an address starting with 0x8335. The second is WETH, which stands for Wrapped Ether, and that lives at an address starting with 0x4200. You’ll need these addresses any time you’re interacting with those tokens programmatically. Make sure you grab the exact full addresses from the wiki rather than typing them manually — contract addresses are case-sensitive and errors can be costly on mainnet.

Now let’s cover the Hardhat configuration. Hardhat is a development environment for building and deploying smart contracts. The code block in the wiki shows how to add Base Mainnet and Base Sepolia as named networks in your Hardhat config file. What it’s doing is defining two network entries. The first, called base-mainnet, points to your Alchemy Base Mainnet URL — which you store as an environment variable so it stays out of your code — and it uses your private key, also stored as an environment variable, to sign transactions. It also specifies that Chain ID of 8453 so Hardhat knows exactly which network it’s talking to. The second entry does the same thing for Base Sepolia, pointing to the Alchemy Sepolia URL and using Chain ID 84532. Once these entries are in your config, you can deploy or run scripts against either network just by passing the network name in your Hardhat command.

A quick note on environment variables — you’ll notice the config references things like your Alchemy URL and private key pulled from the environment rather than hardcoded. That’s intentional. Never put API keys or private keys directly in your config files. Store them in a dot-env file locally and make sure that file is excluded from version control.

For further reading, the wiki cross-references three related topics: the broader Ethereum and Base Network page, the Alchemy integration notes, and the Hardhat documentation. If you’re setting up a new project or onboarding to NZRT’s blockchain stack for the first time, those are worth working through in that order.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Blockchain Glossary

Welcome to the NZRT Wiki Podcast. Today we’re looking at Blockchain Glossary.

If you’re new to blockchain development or just need a quick reference for the terminology you’ll encounter working with NZRT’s stack, this episode walks you through the core terms. We’ve got a big list here, so let’s move through it in a way that actually makes sense.

Let’s start with the foundations. A blockchain transaction is a signed instruction sent to the network, whether that’s transferring tokens or calling a smart contract. A smart contract is a self-executing program stored directly on the blockchain, and it’s written in a language called Solidity, which is the primary language for Ethereum-based development. When you want to run that contract, it gets compiled down to bytecode, which is the machine-readable version that lives on-chain. The thing that actually runs it is called the EVM, or Ethereum Virtual Machine. Think of it as the engine underneath everything.

Now, every contract and wallet has an address, which is a twenty-byte hexadecimal identifier derived from a public key. You’ll see these starting with zero-x followed by a string of characters. There are two main account types. An EOA, or Externally Owned Account, is a wallet controlled by a private key, which is a two-hundred-and-fifty-six-bit secret that authorises your transactions. Then there are smart contract accounts, like a Multisig, which requires multiple approvals before anything executes. Gnosis Safe is a common example of that.

To deploy a contract means publishing it to the blockchain. Once it’s live, you interact with it through something called an ABI, which stands for Application Binary Interface. The ABI defines how to call the contract’s functions, what inputs they take and what they return. If you’re building tooling around a contract, you’ll be working with the ABI constantly.

Let’s talk about networks. NZRT’s primary network is Base, which is an Ethereum Layer 2 blockchain built by Coinbase. Layer 1, or L1, refers to the base blockchain itself, which in this context is Ethereum. Layer 2, or L2, is a scaling solution built on top of that. Base has two main environments you’ll use. Mainnet is the live production network, and it has a Chain ID of eight-four-five-three. Base Sepolia is the testnet, where you use fake ETH to test your work without spending real money, and it has a Chain ID of eighty-four-five-three-two. Always build and test on Sepolia first.

Now for the cost side of things. Gas is the unit that measures computational work on the network, and you pay for it in ETH. Gas prices are usually expressed in Gwei, which is one billion Wei. Wei is the smallest unit of ETH, and there are ten to the power of eighteen Wei in a single ETH. So Gwei sits in the middle, which makes it a convenient unit for talking about fees without writing out enormous numbers.

When a transaction goes through, you’ll hear about confirmations. Each confirmation is another block added to the chain after your transaction’s block. More confirmations means more finality, meaning it becomes increasingly unlikely the transaction will be reversed.

Every account has a nonce, which is a sequential number that prevents transaction replay attacks. Each transaction from an account increments the nonce, so the network knows the order and won’t let the same transaction be submitted twice.

For token standards, ERC-20 defines fungible tokens, meaning each token is identical and interchangeable. USDC is a great example, a stablecoin pegged one-to-one with the US dollar, and it’s widely used on Base. ERC-721 is the NFT standard, where each token is non-fungible, meaning unique and non-interchangeable.

A few more terms worth knowing. OpenZeppelin is a library of audited Solidity base contracts, a solid starting point for writing secure token and access control logic. Hardhat is the development environment used to compile, test, and deploy contracts. RPC stands for Remote Procedure Call, and it’s the API layer you use to talk to a blockchain node. Verification means publishing your contract’s source code on Basescan, the block explorer for Base, so anyone can inspect what the contract actually does.

DeFi is Decentralised Finance, financial services running on-chain without traditional intermediaries. RWA stands for Real-World Asset, which refers to a physical asset that’s been represented on-chain as a token.

On the security side, your seed phrase is the twelve or twenty-four words used to recover a wallet. These follow the BIP-39 standard. Treat it like a master password, because it is one. Ethereum itself runs on Proof-of-Stake, which has been its consensus mechanism since an upgrade known as The Merge.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Blockchain Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Blockchain Overview.

If you’ve been following along with NZRT’s work, you may have heard the name Iteasel come up — that’s our ITE product line. And one of the key things happening with Iteasel right now is real-world asset tokenization, built on the Ethereum and Base blockchain network. So today we’re going to walk you through what a blockchain actually is, how NZRT’s stack is set up, and why we’ve chosen the tools we have.

Let’s start from the top. A blockchain is a distributed, append-only ledger of transactions secured by cryptography. Break that down and you get four key properties. First, it’s decentralised — there’s no single point of control, and no one organisation owns it or can shut it down. Second, it’s immutable — once a transaction is confirmed, it cannot be altered. It’s written in stone, so to speak. Third, it’s transparent — all transactions are publicly visible on-chain, meaning anyone can look them up. And fourth, it’s trustless — smart contracts execute automatically without needing a middleman to oversee them. Those four properties are what make blockchain useful for things like tokenizing real-world assets.

Now let’s talk about NZRT’s specific blockchain stack, because this is where it gets practical for us.

At the foundation, you’ve got Layer 1, which is Ethereum. Think of this as the settlement layer — the bedrock that everything else is anchored to. On top of that, NZRT operates on Layer 2, which is Base. Base is built by Coinbase and it’s our primary network. It gives us low-cost, fast transactions while still inheriting Ethereum’s security underneath.

For development, we use a tool called Hardhat. That handles local development, testing, and deployment of smart contracts. When it comes to actually connecting to the Base network, we go through Alchemy, which is a managed node provider — essentially a reliable gateway to the blockchain so we don’t have to run our own node.

To verify contracts and look up transaction history, we use Basescan, which is Etherscan’s explorer built specifically for the Base network. Think of it like a public dashboard where you can inspect anything that’s happened on-chain.

For wallets, we use MetaMask as the deployer wallet during testnet work. For production contract ownership, we use Gnosis Safe, which is a multisignature wallet that requires two out of three approvals before any action can be taken — that’s a critical security layer. And the actual smart contracts themselves are built on top of OpenZeppelin’s ERC-20 base contracts, which are the industry standard for fungible token implementations on Ethereum-compatible networks.

So why Base specifically, rather than deploying straight to Ethereum Layer 1? The difference comes down to cost, speed, and practicality. On Ethereum Layer 1, a single transaction can cost you anywhere from five dollars to over fifty dollars in gas fees, and it takes around twelve seconds to confirm. On Base, that same transaction costs somewhere between one cent and ten cents, and confirms in about two seconds. Both networks offer strong security — Ethereum natively, and Base by inheriting that security from Ethereum underneath. The Base ecosystem is growing quickly, backed by Coinbase, making it a pragmatic choice for a project like NZRT’s ITE tokenization work.

Putting it all together — NZRT’s ITE product, Iteasel, tokenizes real-world hardware assets using an ERC-20 token called ITSL, deployed on Base Mainnet. The full architecture around that, including how the contracts are structured and how assets are represented on-chain, lives in the NZRT Blockchain Architecture documentation, which you can explore separately. There’s also deeper reading available in the Ethereum and Base Network doc, and the NZRT ITE Vault.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Chain Fusion Cross Chain

Welcome to the NZRT Wiki Podcast. Today we’re looking at Chain Fusion & Cross-Chain.

Let’s start by unpacking what chain fusion actually means, because the term gets used in a couple of different ways. At its most specific, Chain Fusion is a concept from a platform called the Internet Computer Protocol, or ICP. ICP’s version of chain fusion lets smart contracts running on its network directly read and write to other blockchains — like Bitcoin, Ethereum, or Base — without relying on external bridges or trusted third parties. More broadly though, you can think of chain fusion as the idea of weaving multiple independent blockchains into a single piece of smart contract logic. One contract, multiple chains, all coordinated simultaneously.

So how does ICP actually pull this off? There are two key technologies at play. The first is called Threshold ECDSA, or tECDSA. What this means in practice is that an ICP smart contract — called a canister — can hold and control cryptographic keys in a distributed way. No single node holds the complete private key. Instead, it’s split across many nodes using threshold cryptography, and they cooperate to sign transactions. The result is that an ICP canister can own and operate an Ethereum wallet natively — signing and broadcasting Ethereum transactions — without any centralised custodian or external bridge involved.

The second technology is HTTP Outcalls. This lets ICP canisters reach out to external web services directly from within the smart contract environment. That includes Ethereum and Base node endpoints. So a canister can check token balances, read contract events, and react to on-chain activity happening on those other networks.

If you picture the architecture, an ICP canister sits in the middle. On one side it’s reading state from Base or Ethereum through a service like Alchemy. On the other side it’s signing and broadcasting transactions back to those networks via the distributed key scheme. For NZRT, this is technically exciting because it could allow autonomous token operations without a centralised server or private key holder — but it’s pre-mainnet for our use case, so it stays on the radar rather than the immediate roadmap.

Now let’s talk about something more immediately relevant — the bridge between Base and Ethereum mainnet. NZRT currently deploys the ITSL token on Base, which is Coinbase’s Layer 2 network built on the OP Stack. Understanding how assets move between these two networks matters for things like migrating liquidity or accepting revenue payments from mainnet wallets.

The native connection is called the OP Stack Canonical Bridge. Moving assets from Ethereum mainnet down to Base takes roughly one to three minutes. Moving assets the other way — from Base back up to mainnet — takes seven days. That delay exists because OP Stack uses an optimistic rollup model, where transactions are assumed valid but anyone can challenge them during a fraud proof window. It is the core security model of the system.

If you need faster movement from Base back to mainnet, third-party bridges can help. There are three worth knowing about. Across Protocol uses an intent-based model and is known for being fast and low cost. Stargate, built on LayerZero, uses unified liquidity pools and supports many chains. Hop Protocol is purpose-built for OP Stack Layer 2 networks. The trade-off with any of these is that you’re accepting additional trust assumptions beyond the canonical bridge — the canonical bridge has no extra counterparty risk, while third-party bridges introduce their own smart contract risk.

For cross-chain messaging more broadly, there are three major protocols to understand. The first is Chainlink CCIP, the Cross-Chain Interoperability Protocol. It uses Chainlink’s oracle network to validate and relay messages across chains. It’s enterprise-grade and well-suited to regulated environments and high-value transfers, though it comes at a higher cost and requires paying fees in Chainlink’s own token.

The second is LayerZero. It supports over seventy chains and uses what are called Decentralised Verifier Networks to attest messages. One of its standout features is the Omnichain Fungible Token standard — OFT — which lets an ERC-20 token like ITSL exist natively on multiple chains at the same time rather than being wrapped or bridged. That makes it a compelling option if ITSL ever needs to expand beyond Base.

The third is Wormhole. It uses a network of nineteen validators and is particularly strong for projects that need to reach non-EVM chains like Solana, Sui, or Aptos. It had a significant exploit in 2022, though the funds were fully repaid.

So where does all of this leave NZRT? For ITSL, the current recommendation is to stay single-chain on Base until the mainnet launch is complete. If investor demand grows to the point where wholesale investors want to buy ITSL without bridging from Ethereum mainnet, LayerZero’s OFT standard is the leading candidate to evaluate after launch. On the consulting side, cross-chain architecture is a genuine billable advisory area for ICS — helping clients choose between these protocols, integrate CCIP for regulated token transfers, or design multi-chain token architectures from the ground up.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Ethereum Base Network

Welcome to the NZRT Wiki Podcast. Today we’re looking at Ethereum & Base Network.

So first, a quick framing note. NZRT deploys on Base, which is a low-cost Ethereum Layer 2 network built by Coinbase. Base settles back to Ethereum’s main chain, so you get the security of Ethereum with much lower costs. That’s the core reason NZRT uses it, and it’ll make more sense as we walk through the pieces.

Let’s start with some Ethereum basics, because you need these to understand everything that follows.

Ethereum is a proof-of-stake Layer 1 blockchain. Think of it as the foundation — it’s a smart contract platform, meaning you can deploy programs directly onto it that run without a central server. The thing that actually runs those programs is called the EVM, short for Ethereum Virtual Machine. The EVM executes the bytecode of smart contracts, and it runs the same way on every node in the network, which is how you get trustless, consistent execution.

The language most developers use to write those smart contracts is called Solidity. If you’ve seen any Ethereum development work, you’ve probably come across it.

Now, running code on Ethereum costs something. That cost is measured in units called gas. Gas is just the unit of computation — every operation in a smart contract uses a certain amount of it, and you pay for that gas in ETH, which is Ethereum’s native currency.

Speaking of ETH, it’s worth knowing the denominations. The smallest unit is called Wei. One ETH equals ten to the power of eighteen Wei — so an enormous number. In practice, when people talk about gas prices, they usually use Gwei, which is one billion Wei. You’ll see Gwei come up a lot in tooling and configuration, so it’s good to have that in your vocabulary.

Now let’s talk about Base specifically.

Base is what’s called an Optimistic Rollup. That’s a type of Layer 2 solution where transactions are processed off the main Ethereum chain, but the results are periodically posted back to Ethereum for final settlement. The “optimistic” part means transactions are assumed to be valid unless someone challenges them. This approach lets you dramatically reduce costs while still inheriting Ethereum’s security guarantees.

Base was built by Coinbase, its native token is ETH just like Ethereum, and its block time is around two seconds — much faster than Ethereum’s main chain. If you ever need to look up a transaction on Base, the block explorer is at basescan dot org. There’s also a testnet version of Base called Base Sepolia, which is where you’d go to test things without spending real money.

Two numbers worth memorising: the Chain ID for Base Mainnet is 8453, and for the Base Sepolia Testnet it’s 84532. You’ll need these when configuring wallets or developer tools.

On that note, let’s talk about how you actually configure your environment to connect to Base.

When you’re setting up a tool like MetaMask or a development framework like Hardhat to point at Base Mainnet, you’d provide a few key pieces of information. You’d name the network Base, point the connection URL to the mainnet Base endpoint — which you can get either directly from Base or through a provider like Alchemy — set the Chain ID to 8453, set the currency to ETH, and use basescan dot org as the explorer URL.

For the testnet setup — Base Sepolia — it’s the same idea. You’d name it Base Sepolia, point the connection URL to the Sepolia endpoint, use Chain ID 84532, and note that the ETH here is testnet ETH, meaning it has no real value. The explorer for testnet is at sepolia dot basescan dot org. And if you need testnet ETH to actually do anything, Coinbase runs a faucet where you can get some for free.

Faucets, by the way, are just web tools that send you small amounts of testnet currency so you can pay for gas while you’re developing and testing.

That covers the core concepts you need to work with Ethereum and Base at NZRT. To recap: Ethereum is the Layer 1 foundation, the EVM runs smart contracts written in Solidity, gas is what you pay in ETH for computation, and Wei and Gwei are the sub-units of ETH you’ll encounter in tooling. Base sits on top of Ethereum as an Optimistic Rollup — faster, cheaper, same ETH token, Chain ID 8453 for mainnet and 84532 for testnet. When you’re configuring your wallet or dev tools, those two Chain IDs are the key thing to get right.

If you want to go deeper, the wiki also links out to related topics on smart contracts, the Alchemy provider, and the contract deployment process — so those are good next reads if you’re getting hands-on with Base development at NZRT.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Erc 20 Tokens

Welcome to the NZRT Wiki Podcast. Today we’re looking at ERC-20 Tokens.

So, what exactly is ERC-20? It’s the standard that Ethereum uses for fungible tokens. Fungible just means that every token is identical and interchangeable — one ITSL token is worth exactly the same as any other ITSL token, the same way one dollar bill is the same as any other dollar bill. At NZRT, ERC-20 is directly relevant because our ITSL token is built on this standard and lives on the Base network, which is an Ethereum Layer 2 chain.

Now, the ERC-20 standard defines a set of functions that every compliant token contract must include. Think of these as a minimum feature set — a kind of API contract that lets wallets, exchanges, and other smart contracts interact with your token without needing to know anything else about it. Let’s walk through each one.

First, there’s total supply. This does exactly what it sounds like — it tells you how many tokens exist in total. Then there’s balance of, which takes an address and returns how many tokens that address holds. So if you want to check your own token balance, this is the function doing that work under the hood.

Next is transfer. This is how you send tokens directly from your own wallet to someone else’s. You provide the recipient’s address and the amount, and the tokens move. Then there’s approve, which is where things get a little more interesting. Approve lets you authorise a third party — called a spender — to use up to a certain number of your tokens on your behalf. This is critical for things like decentralised exchanges, where a smart contract needs permission to move your tokens without you handing over full control of your wallet.

Closely related to approve is allowance. This function lets you check how many tokens a spender is still authorised to use. So if you approved someone to spend a hundred tokens and they’ve already used thirty, allowance would come back with seventy.

Finally, there’s transfer from. This is the function that actually executes a third-party transfer — the spender you previously approved uses this to move tokens from your address to another one, within the limit you already set.

On top of these functions, ERC-20 also defines two events. An event called Transfer fires every time tokens move between addresses, and an event called Approval fires whenever an approval is granted. These events are how external tools like block explorers and wallets track activity on-chain without having to read every piece of contract state directly.

Now let’s look at how NZRT’s own ITSL token is implemented. The contract code shows a Solidity smart contract that extends two components from the OpenZeppelin library — the standard ERC-20 base contract, and an Ownable module that restricts certain privileged actions to the contract’s owner. The constructor sets the token’s name to Iteasel Token and its symbol to ITSL, assigns ownership to whoever deploys the contract, and immediately mints the full initial token supply to that same deployer address. The supply calculation accounts for decimals by multiplying the number you pass in by ten to the power of eighteen — which brings us neatly to the token’s properties.

The ITSL token has a name of Iteasel Token, a ticker symbol of ITSL, and uses eighteen decimal places. That’s the default for ERC-20 tokens, and it means one token is stored on-chain as a whole number with eighteen zeros behind it. This is how you get precise fractional amounts when transacting. The token lives on Base Mainnet and is built using the OpenZeppelin ERC-20 implementation, which is the industry-standard audited library for this kind of contract.

There is also a mint function in the contract. This is a post-deployment function that only the contract owner can call. It lets the owner create and send additional tokens to any address at any time after launch — useful if you need to distribute tokens in stages rather than all at once at deployment.

If you want to go deeper, the wiki cross-references three related topics: OpenZeppelin, which provides the base contract code; the ITSL Token page, which covers deployment specifics and tokenomics in more detail; and the Contract Deployment page, which walks you through the process of actually getting a contract live on the Base network.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Contract Verification

Welcome to the NZRT Wiki Podcast. Today we’re looking at Contract Verification.

So, what exactly is contract verification, and why should you care? When you deploy a smart contract to a blockchain network like Base, the code that actually lives on-chain is compiled bytecode — essentially machine-readable instructions that aren’t meant for human eyes. Verification is the process of publishing your original source code alongside that bytecode on a block explorer like Basescan. Once verified, anyone can read your contract logic directly in the browser, transactions become human-readable, and your project gains a visible marker of legitimacy — a green checkmark on Basescan that signals to investors and users that you have nothing to hide.

There are four main reasons you’d want to do this. First, investors can read your contract logic directly on Basescan — no guesswork, no blind trust. Second, decentralised applications and scripts can pull and use your public ABI, which is the interface definition that tells external tools how to interact with your contract. Third, if you’re running a public token offering, verification is essentially a requirement — it’s part of the trust baseline. And fourth, that green checkmark is a genuine signal of legitimacy to anyone looking at your project.

Now let’s talk about how you actually do it. There are two main methods: using the Hardhat Verify plugin from your command line, or doing it manually through the Basescan website.

The command-line approach is the easier and more reliable path. You run a command through Hardhat’s verify task, specifying which network you’re targeting along with the deployed contract’s address. If your contract was deployed with constructor arguments — for example, an initial token supply passed in as a number — you include that value at the end of the command. If your contract had no constructor arguments, you leave that part out entirely. And if it had multiple arguments, you pass each one in sequence. The plugin handles the rest, matching your source code to what’s on-chain and submitting it to Basescan automatically. You can target either a test network like Base Sepolia or the live mainnet, depending on what you’re verifying.

The second method is the manual fallback through the Basescan website. You navigate to the verify contract page, enter your contract’s address, and choose how you want to submit your source — either as a single flattened Solidity file or as a standard JSON input. If you go the flattened file route, you first run a flatten command through Hardhat, which combines your contract and all its imports into one big file, and then paste that into the Basescan form. Crucially, you also need to make sure the compiler version and optimisation settings you select in the form exactly match what’s in your Hardhat configuration file. If those don’t line up, verification will fail.

Speaking of failures — let’s walk through the most common issues you might run into. There are four worth knowing about. The first is seeing a message that says the contract is already verified. That one’s actually fine — it just means someone already submitted it, and no further action is needed. The second issue is a compiler version mismatch. The fix is straightforward: go back to your Hardhat config file and use the exact compiler version listed there — don’t guess or approximate. The third issue involves constructor argument mismatches. This happens when the arguments you provide during verification don’t match what was actually passed when the contract was deployed. You need to ABI-encode the exact same values that went in at deploy time. And the fourth issue is import errors that show up in your flattened file. This often happens because the flattening process can produce duplicate SPDX licence declarations when multiple source files each carry their own. The fix is to go through the flattened output and remove the duplicates, leaving only one licence header at the top.

So to recap: verification makes your contract readable, trustworthy, and usable by the broader ecosystem. The Hardhat plugin is your primary tool, and the Basescan UI is there as a backup when you need it. Watch your compiler version, match your constructor arguments exactly, and clean up any duplicate licence headers in flattened files — and you’ll get that green checkmark without much drama.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Contract Deployment

Welcome to the NZRT Wiki Podcast. Today we’re looking at Contract Deployment.

Specifically, we’re going to walk through how you compile, deploy, and verify a smart contract on the Base network using a tool called Hardhat. Whether you’re targeting the testnet or pushing to mainnet, this guide covers the full sequence from pre-flight checks all the way through to post-deployment confirmation.

Let’s start before you even touch a deploy command, because there are five things you need to have sorted first.

One, your contract needs to be compiled and come back clean — no warnings. Two, your tests need to be passing. You run those with the Hardhat test command. Three, you need a dot-env file populated with three specific values: your private key, your Alchemy Base Sepolia URL, and your Basescan API key. Four, your deployer wallet needs to actually have test ETH on Base Sepolia — no funds, no deployment. And five, your Hardhat config file needs to include both Base Sepolia and Base Mainnet as networks. Get all five of those sorted before you go any further.

Now let’s talk about the deployment script. There’s a JavaScript file in the scripts folder called deploy dot js, and here’s what it does in plain terms. It starts by grabbing the first signer from your Hardhat environment — that’s your deployer wallet. It logs the wallet address and the current ETH balance to the console, so you can confirm you’re deploying from the right account with enough funds. Then it sets an initial supply of one million tokens, uses Hardhat’s contract factory to pull in a contract called ITSLToken, and deploys it with that initial supply. Once the deployment transaction is confirmed on-chain, it prints the deployed contract address to your console. There’s also basic error handling at the end — if anything goes wrong, it logs the error and exits with a failure code so you know the deployment didn’t succeed.

Once that script is in place, actually running a deployment is a single command. For testnet, you run Hardhat with the deploy script pointed at the base-sepolia network. That will execute everything in the script we just described and land your contract on Base Sepolia. For mainnet, the command is identical except you swap base-sepolia for base-mainnet. The wiki notes to use mainnet with caution — you’ll need a funded deployer wallet, and there’s no undoing a mainnet transaction.

After deployment, you’ll want to verify your contract on Basescan. Verification means the source code is publicly visible and readable on the block explorer, which is important for trust and auditability. You do this with a Hardhat verify command, passing in the network, the contract address you just deployed to, and the initial supply as arguments. So for example, you’d supply the contract address — a long hexadecimal string — and then the number one million as a quoted value. Hardhat handles the rest, submitting your source to Basescan and linking it to the deployed bytecode.

Once verification is done, there are four post-deployment steps to work through. First, go to the Basescan explorer — for testnet that’s sepolia dot basescan dot org, and for mainnet it’s basescan dot org — and confirm your contract actually shows up. Second, click into the Contract tab on that page and check that it shows verified source. If the verification step worked correctly, you should see your readable Solidity code right there in the browser. Third, if you’re on mainnet, transfer ownership of the contract to a Gnosis Safe. That’s a multi-signature wallet setup that protects against single points of failure — so no one person can unilaterally make changes to the contract after it’s live. And fourth, record the contract address in the Wiki under the Blockchain section, so the rest of the team knows where to find it.

That’s the full flow. Pre-deployment checklist, deploy script, run the deploy command for whichever network you’re targeting, verify on Basescan, then confirm, check the source tab, hand off ownership if you’re on mainnet, and document the address.

If you want to go deeper on any of the pieces here, the related topics in the wiki are Hardhat, Contract Verification, and Gnosis Safe Multisig — all worth reading alongside this guide.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Etherscan Basescan

Welcome to the NZRT Wiki Podcast. Today we’re looking at Etherscan & Basescan.

If you’ve ever deployed a smart contract and wondered how anyone is supposed to trust it, this is the tool that answers that question. Basescan is the block explorer for the Base network, and it’s one of the most important things you’ll use when working with blockchain development at NZRT. It lets you verify contracts, inspect transactions, and expose your contract’s ABI publicly so that other developers and users can see exactly what your contract does. The same family of tools includes Etherscan, which covers the Ethereum mainnet. Think of these explorers as a public ledger viewer — a window into everything that’s happening on the chain.

Let’s start with the three explorers you need to know about. For the Base mainnet, you head to basescan dot org. For Base Sepolia, which is the test network you’ll use during development, you go to sepolia dot basescan dot org. And for the Ethereum mainnet, the address is etherscan dot io. Those are your three primary URLs, and which one you use depends entirely on which network you’re working on.

Now let’s talk about contract verification, because this is probably the most important thing you’ll do with Basescan. When you verify a contract, you’re publishing its source code publicly on Basescan. This matters for two big reasons. First, it means the public ABI — that’s the application binary interface, essentially the list of functions your contract exposes — becomes accessible to anyone. Second, it makes transactions human-readable. Instead of seeing a wall of encoded hex data, people can actually see what a transaction is doing in plain terms.

To verify a contract, you use Hardhat from your terminal. The first code example shows the general command structure. You run Hardhat’s verify task, specify which network you’re targeting — in this case Base Sepolia — then provide the contract’s deployed address, followed by any constructor arguments your contract was deployed with. So if your contract took two arguments when it was first deployed, you pass both of those in order after the address.

The second example makes this concrete. It shows verifying the ITSL Token contract on Base Sepolia, where the contract was deployed with an initial supply of one million tokens. So the command passes the contract address and then that initial supply value as the constructor argument.

One thing to keep in mind before any of this works: you need a Basescan API key, and it needs to be stored in your dot env file under the name BASESCAN underscore API underscore KEY. At NZRT, you’ll find the API key stored under ITE slash Blockchain slash Etherscan in the credentials system — so check there first before going looking elsewhere.

Once your contract is verified, Basescan becomes a really powerful inspection tool. There are six key things you can check, and knowing where to find each one will save you a lot of time. First, to confirm a contract is actually verified, you go to the Contract tab and look for a green checkmark — that’s your confirmation. Second, if you want to read data from the contract, you go to Contract and then Read Contract — this shows you all the view functions and lets you call them directly from the browser without spending any gas. Third, if you need to interact with state-changing functions, you go to Contract and then Write Contract — this is where you can call functions that modify the blockchain state, and you’ll need a connected wallet to do it. Fourth, to see who holds tokens associated with the contract, you go to Token and then Holders. Fifth, for a full history of every transaction that’s touched the contract, you check the Transactions tab. And sixth, if you want to see event logs — things the contract has emitted during its lifetime — you go to the Events tab.

Those six checks cover the vast majority of what you’ll need day to day when monitoring or debugging a deployed contract. Whether you’re confirming a deployment went through, checking that a function returned the right value, or auditing token distribution, Basescan puts all of that in one place.

If you want to dig deeper into the workflow around deploying and verifying contracts, the related topics to look up in the wiki are Hardhat and Contract Verification — those will give you the full picture of the development and deployment pipeline that feeds into what you’ve heard today.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Hardhat

Welcome to the NZRT Wiki Podcast. Today we’re looking at Hardhat.

Hardhat is the development environment NZRT uses for compiling, testing, and deploying Solidity smart contracts to the Base network. If you’re working on blockchain development here, Hardhat is your main toolkit, and this episode will walk you through how it’s set up and used.

Let’s start with installation. The setup code block shows you a sequence of commands to get a new project going. You start by creating a new folder for your contract project and moving into it. Then you initialise a Node package, which sets up your project’s dependency management. After that you install Hardhat itself as a development dependency. Once that’s done, you run Hardhat’s init command and choose the JavaScript project option when it prompts you. Finally, you install two more packages — the Hardhat Toolbox, which bundles together a set of useful plugins, and the OpenZeppelin Contracts library, which gives you access to audited, battle-tested contract templates. That’s your full starting point — five steps and you’re ready to go.

Now let’s talk about how a Hardhat project is organised. The project structure block lays out five key locations. First, there’s a folder called contracts, which is where all your Solidity source files live. Second, there’s a scripts folder — this is where your deployment scripts go. Third, a test folder holds your test files, written using the Mocha and Chai testing frameworks. Fourth, a file called hardhat.config.js is your central configuration file — it handles network settings and plugins. And fifth, a dot-env file is where you store your private keys and API keys. That last one is critical — you never commit that file to version control. Keep it local and keep it secret.

The next section covers the Hardhat config file set up specifically for the Base network, and this is where things get a bit more detailed. The config file shown in the wiki does a few things. It pulls in the Hardhat Toolbox and the dotenv library, which lets you read those environment variables from your dot-env file. It then defines your Solidity compiler version as 0.8.20. After that it sets up two networks. The first is Base Sepolia, which is the testnet — it uses a chain ID of 84532 and pulls its RPC URL and your wallet’s private key from environment variables. The second is Base Mainnet, the live network, which uses chain ID 8453 and works the same way. On top of that, the config sets up contract verification through Basescan — again for both networks. For Base Sepolia, it points to the Sepolia-specific Basescan API and browser URLs. For Base Mainnet, it points to the standard Basescan URLs. Both read your Basescan API key from an environment variable. This setup means you can switch between testnet and mainnet deployments just by changing one flag in your command, without ever hardcoding sensitive credentials into your config.

Speaking of commands, let’s go through what’s in the common commands section. There are six you’ll reach for regularly. The first is the compile command, which processes your Solidity files and checks them for errors. The second is the test command, which runs your full test suite. The third starts a local network on your machine, useful for rapid development and iteration without touching any real or test network. The fourth and fifth are your deployment commands — you run the same deploy script but point it at either Base Sepolia or Base Mainnet by passing the network name as a flag. And the sixth is the verify command, which submits your deployed contract’s source code to Basescan so that anyone can inspect it. When you run verify, you pass the network, the deployed contract address, and any constructor arguments your contract was initialised with.

One thing worth noting from the wiki — the full NZRT-specific deployment guide lives in the ITE Blockchain section under Hardhat. If you’re doing an actual production deployment and want the NZRT-specific steps and conventions, that’s where to go. The wiki page you’re listening to now is the overview and reference — the deeper guide covers the full process end to end.

Related topics in the wiki include Alchemy, which is the RPC provider you saw referenced in the config file, and the Contract Deployment guide which covers the broader deployment workflow.

To summarise: Hardhat gives you a complete local environment for Solidity development. You install it with npm, organise your work across contracts, scripts, and test folders, configure your networks in the config file using environment variables for security, and then use a small set of commands to compile, test, deploy, and verify your contracts on Base.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Openzeppelin

Welcome to the NZRT Wiki Podcast. Today we’re looking at OpenZeppelin.

If you’ve been following NZRT’s blockchain work, you’ve probably heard the name OpenZeppelin come up. So let’s break down what it actually is and how we use it.

OpenZeppelin is a library of smart contracts written in Solidity, the programming language used to build on Ethereum and compatible blockchains. The key word here is battle-tested. These contracts have been audited, reviewed by thousands of developers, and used in production across the industry. At NZRT, we use OpenZeppelin as the foundation for our ITSL ERC-20 token.

Let’s start with getting it into your project. Installation is a single command — you’re essentially telling your Node project to pull in the OpenZeppelin contracts package. Once that’s done, you have access to the full library.

Now let’s talk about which contracts NZRT actually uses, because there are quite a few to choose from.

There are six key contracts worth knowing. First, ERC20 — this is the standard fungible token contract and it forms the base of the ITSL token. Second, Ownable, which gives a contract a single designated owner and a special modifier that restricts certain functions so only that owner can call them. Third, AccessControl — a more flexible alternative to Ownable. Instead of a single owner, it lets you define roles and assign permissions to multiple addresses. Fourth, Pausable, which is essentially an emergency stop button for your contract. If something goes wrong, you can pause all activity. Fifth, ERC20Burnable, which adds the ability to permanently destroy tokens, reducing the total supply. And sixth, ERC20Capped, which lets you set a hard maximum on how many tokens can ever exist.

Next, let’s look at how you bring these into your Solidity code. You’ll see five import statements, each one pointing into the OpenZeppelin contracts package. You’re pulling in the base ERC20 contract, the Burnable extension, the Capped extension, the Ownable access contract, and the Pausable utility. Think of these as building blocks you snap together before writing your own logic on top.

Now for the ITSL token itself — this is where it all comes together. The token contract is called ITSLToken, and it inherits from three of those building blocks: ERC20 for standard token behaviour, ERC20Burnable to allow burning, and Ownable to lock down admin functions to a single owner. Inside the constructor — the function that runs once when the contract is first deployed — you set the token’s full name as Iteasel Token, its ticker symbol as ITSL, and assign ownership to whoever deploys the contract. Then you mint the initial supply. The amount is calculated by taking the initial supply number and multiplying it by ten to the power of the token’s decimal places. This is standard practice in Solidity, since the language doesn’t support floating point numbers, so decimal precision has to be handled explicitly like this.

One more tool worth knowing about is the OpenZeppelin Contract Wizard. It’s a web-based interface where you tick checkboxes for the features you want — mintable, burnable, pausable, capped, and so on — and it generates clean, ready-to-use Solidity code. It’s a great starting point when you’re prototyping. You take the output, drop it into your project, and adjust from there rather than starting from scratch.

If you want to go deeper after this episode, the related topics to explore are Smart Contracts, ERC-20 Tokens, and the ITSL Token page in the wiki.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Nzrt Blockchain Architecture

Welcome to the NZRT Wiki Podcast. Today we’re looking at NZRT Blockchain Architecture.

If you’re working at NZRT or just trying to understand how the blockchain side of things is put together, this episode gives you a clear picture of the full stack, from where code gets written all the way through to live tokens on the blockchain.

Let’s start with the big picture. The architecture shows a pipeline that flows from development tools down through testing environments and into production. At the top you’ve got Hardhat, which is the development framework the team uses to write and test smart contracts locally. From there, code can be deployed to a local test network running on your own machine, then out to Base Sepolia which is a public testnet, and finally all the way to Base Mainnet, the live production blockchain where real tokens exist. Connecting the smart contracts to the outside world is Alchemy, which acts as the RPC provider, essentially the communication layer between your application and the blockchain network. The primary smart contract right now is the ITSL Token, which follows the ERC-20 standard. There are also future contracts planned for things like staking, vesting, and governance. Sitting above all of this is a Gnosis Safe multisig wallet that acts as the contract owner and administrator. And everything is publicly visible and verifiable on Basescan, which hosts the contract verification and the public ABI.

Now let’s walk through the three deployment environments. The first is your local development environment. This runs on the Hardhat Network with a Chain ID of 31337 and connects to your own machine. This is where you do fast iteration, testing changes quickly without spending any real money or waiting on a network. The second environment is the testnet. This connects to Base Sepolia with a Chain ID of 84532 and uses Alchemy as the RPC provider. This is where you test your contracts in a realistic network environment before going anywhere near real funds. The third and final environment is production, Base Mainnet with Chain ID 8453, also running through Alchemy. This is the live network where the ITSL token actually exists and operates.

Next, let’s look at how contract ownership is structured. There are three roles to understand. The contract owner role is held by a Gnosis Safe wallet, operating as a two-of-three multisig, meaning you need at least two out of three authorised signers to approve any administrative action. This is an important security measure for a live token. The deployer role for testnet is a standard MetaMask externally owned account, used for testnet only. And for mainnet deployment, the Gnosis Safe itself acts as the deployer, so even the initial deployment of a production contract requires that multisig approval.

Now for the star of the show, the ITSL Token itself. This is an ERC-20 token, the most widely used token standard on Ethereum-compatible blockchains. It’s built on top of OpenZeppelin’s ERC20 base contract, an industry-standard audited library that gives you safe and reliable token functionality out of the box. The token lives on Base Mainnet, carries the symbol ITSL, and uses 18 decimal places, which is standard for ERC-20 tokens and allows one token to be divided into incredibly small fractions for precise transactions. The total supply is defined separately in the tokenomics documentation, so the contract itself stays flexible on that front.

To tie it all together, you’ve got a clean layered architecture. Development starts locally with Hardhat, gets validated on Base Sepolia testnet, and graduates to Base Mainnet for production. Alchemy handles the RPC connectivity throughout. The ITSL ERC-20 token is the current live contract, secured by a Gnosis Safe multisig for all ownership and admin actions. And Basescan provides public transparency for anyone who wants to verify the contract code.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Metamask Setup

Welcome to the NZRT Wiki Podcast. Today we’re looking at MetaMask Setup.

MetaMask is NZRT’s browser-based crypto wallet, and it plays a specific role in the blockchain workflow — it’s used for testnet deployment and transaction signing. If you’re working on anything in the ITE or blockchain space, this is the tool you’ll need to get familiar with.

Let’s start with installation. Head over to metamask.io and install the MetaMask browser extension. Once that’s done, you’re not going to be creating a brand new wallet. Instead, you’ll be importing an existing one using a seed phrase. That seed phrase is stored securely in the vault under ITE, then Blockchain, then MM-Seed. Retrieve that phrase, use it to import the wallet, and you’re ready to move on to network configuration.

MetaMask doesn’t include the Base network out of the box, so you’ll probably need to add it manually. That said, it’s worth checking Settings, then Networks first, because MetaMask sometimes detects and auto-adds Base for you. Save yourself a step if it’s already there.

There are two Base networks to set up. The first is Base Mainnet. When you go to add a custom network in MetaMask, you’ll be filling in five fields. The network name is Base. The RPC URL is the mainnet.base.org address using HTTPS. The Chain ID is 8453. The currency symbol is ETH. And the block explorer URL is basescan.org, also using HTTPS.

The second network is Base Sepolia, which is the testnet environment. Same process, just different values. The network name is Base Sepolia. The RPC URL is sepolia.base.org using HTTPS. The Chain ID is 84532 — that’s eight, four, five, three, two. The currency symbol is still ETH. And the block explorer is sepolia.basescan.org using HTTPS.

With your networks in place, you may also need to export your private key, particularly when you’re working with Hardhat for smart contract deployment. To do that, open MetaMask, go to your Account, click the three-dot menu, then select Account Details, and choose Show Private Key. Copy that value and paste it into your dot-env file, using PRIVATE underscore KEY as the variable name, with the key value prefixed by zero x. And here’s a rule with no exceptions: never commit that dot-env file to Git. Your private key must stay out of version control at all times.

Now for security, and this part really matters. This MetaMask wallet is a testnet deployer only. You should never send mainnet funds to it. When you need to operate on mainnet, that goes through Gnosis Safe, which is NZRT’s multisig setup for production use. As for the seed phrase, it’s stored offline — the full details are in the Key Security documentation in the vault.

If you want to go deeper on any of this, there are three related pages in the wiki worth reading: Gnosis Safe Multisig, Wallets and Keys, and Key Security. Between those three and this guide, you’ll have a solid picture of how NZRT manages its blockchain signing infrastructure.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Smart Contracts

Welcome to the NZRT Wiki Podcast. Today we’re looking at Smart Contracts.

So, what exactly is a smart contract? Put simply, it’s a self-executing program that lives on a blockchain. Once you deploy it, its code is locked in place and it runs exactly as written — no exceptions, no overrides, no one in the middle to interfere. That permanence is actually one of its defining features.

Let’s talk about the key properties that make smart contracts tick, because there are four worth understanding. First, they’re immutable — meaning once that code is deployed, you cannot change it. If you want a contract that can be updated later, you need to build in something called a proxy pattern from the start, so keep that in mind during design. Second, they’re deterministic — the same inputs will always, every single time, produce the same outputs. There’s no randomness, no surprises. Third, they’re trustless — you don’t need a bank, a lawyer, or any other intermediary to enforce the agreement. The network itself does that. And fourth, there’s gas cost. Every operation your contract performs consumes gas, and that gas is paid by whoever is calling the contract. So efficiency in your code isn’t just good practice — it directly affects cost.

Now let’s look at a code example to make this concrete. The sample here is written in Solidity, which is the most common language for writing smart contracts on Ethereum-compatible blockchains. The contract is called ITSL Token — that’s NZRT’s Iteasel Token. At the top, you’ll see a license identifier and a version declaration, which tells the compiler exactly which version of Solidity to use. The contract then imports two pre-built building blocks from a library called OpenZeppelin — one handles the standard ERC-20 token behaviour, and the other handles ownership, so only the contract owner can perform certain privileged actions.

Inside the contract, there’s a constructor — think of this as the setup function that runs once when the contract is first deployed. It takes an initial supply as an input, sets the token’s name to “Iteasel Token” and its ticker symbol to ITSL, assigns ownership to whoever deployed the contract, and then mints the initial supply of tokens straight to that deployer’s wallet. Below that, there’s a mint function. This lets the owner create additional tokens later and send them to any address — but crucially, only the owner can call it, thanks to that Ownable pattern imported earlier.

So that’s the code. A relatively compact file, but it gives you a fully functional, ownable token on the blockchain.

Next, let’s walk through the contract lifecycle — what actually happens from writing code to getting it live in production. You write your Solidity source code first. Then you compile it using a tool called Hardhat, which produces two things: the ABI, which we’ll come back to in a moment, and the bytecode, which is what actually gets deployed to the chain. After compiling, you write and run unit tests — both Hardhat and Foundry are common frameworks for this. Once tests pass, you deploy to a testnet first. In NZRT’s case, that’s Base Sepolia, and you verify the contract on Basescan so anyone can inspect it. Then comes the security audit — a review specifically looking for vulnerabilities before you touch mainnet. Only after that do you deploy to Base Mainnet, and at NZRT that deployment goes through Gnosis Safe for an added layer of control.

That brings us to the ABI — the Application Binary Interface. This is essentially the instruction manual for your contract. It describes every function the contract exposes: what it’s called, what inputs it expects, and what it returns. Your frontend, your scripts, any external tool that needs to talk to the contract — they all need the ABI to do it. Without it, you can’t interact with the contract in any meaningful way.

And that’s the full picture of smart contracts: self-executing, immutable, trustless programs with a clear development pipeline from code to production.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Git Deployment Cpanel

Welcome to the NZRT Wiki Podcast. Today we’re looking at Git Deployment — cPanel.

If you’re working with NZRT’s hosting setup on Hoopla — that’s our cPanel server — you’ll want to know how code gets from GitHub onto the server. There are a few ways cPanel supports this. You can connect via SSH and run git commands directly on the server. You can clone private GitHub repositories using a deploy key. And there’s also an optional graphical interface inside cPanel called Git Version Control. But the primary method NZRT uses is GitHub Actions — an automated workflow that SSHes into cPanel and runs a git pull whenever code is pushed to the main branch.

Before we get into the automation, let’s talk about what’s actually being deployed. There’s one main repository deployed to the cPanel server: it’s called NZRT-Scripts, and it lives in a folder called scripts inside the server’s home directory. This repo contains scripts for deployment, backups, health checks, and a product sync script. Worth noting — WordPress itself isn’t git-deployed. That’s handled separately via WP-CLI and WebDAV. And Dolibarr doesn’t live on cPanel at all.

So let’s say you’re setting this up for the first time. You SSH into the server, then clone the NZRT-Scripts repository from GitHub into that scripts folder. After that, you verify it worked by checking the last few commit messages in the git log. Because the repo is private, the server needs a way to authenticate with GitHub. You do this with a deploy key. You generate a new SSH key pair on the server specifically for deployment, then take the public half of that key pair and add it to the repository’s settings on GitHub, under Deploy Keys, set to read-only. Finally, you add a small configuration entry on the server that tells SSH to use that deploy key whenever it connects to GitHub.

There’s also an optional GUI path through cPanel itself. You go to Files, then Git Version Control, and create a new entry pointing to your scripts folder and the GitHub clone URL. This gives you a dashboard to pull updates and check repo status. It also enables a special deployment configuration file you place in the root of your repository. When cPanel pulls new code through its Git Version Control interface, it automatically runs whatever tasks are listed in that file. In NZRT’s case, it runs a post-deploy shell script that flushes the WordPress cache, fixes file permissions on any shell scripts so they’re executable, and logs the date and time the deployment finished.

Now for the main event — the GitHub Actions workflow. This is triggered whenever someone pushes to the main branch of NZRT-Scripts. When that happens, GitHub spins up a temporary Linux environment and runs a series of steps. It sets up an SSH connection by writing the private deploy key to a file, locking down its permissions, and adding the server to a list of trusted hosts. Then it SSHes into the cPanel server, navigates to the scripts folder, pulls the latest code from main, and makes sure all shell scripts are executable. After a short pause, it runs a health check by SSHing back in and printing the most recent commit — just to confirm the deployment landed.

To make all this work, you need three secrets stored at the GitHub organisation level so they’re available across all repos. You need the SSH private key, the cPanel username, and the SSH port number. Set these once and they’re inherited automatically by any workflow that needs them.

Once your scripts are live on the server, you wire them up to cPanel’s cron job scheduler. You find that under Advanced, then Cron Jobs. There are three jobs configured. The first is a daily backup that runs at two in the morning, covering the database and file backups. The second is a health check that runs every fifteen minutes. The third is an hourly sync — a Python script that keeps WordPress products and Dolibarr in alignment.

Finally, how do you verify everything is working end to end? You can SSH in and check the git log to confirm the latest commits are present on the server. You can manually trigger one of the scripts to test it in isolation. And for the full end-to-end test, you push a commit to the main branch, watch the GitHub Actions run complete successfully, and then confirm that same commit appears in the server’s git log.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Logs Cache

Welcome to the NZRT Wiki Podcast. Today we’re looking at Logs & Cache.

This episode covers server log locations, how log rotation works, and how to manage the LiteSpeed cache in the NZRT hosting environment. Whether you’re troubleshooting an issue or just keeping things tidy, knowing where your logs live and how to clear your cache is essential.

Let’s start with log paths. There are four main types of logs you’ll deal with. First, access logs and error logs — both of these sit in the logs folder under DavWWWRoot. The access logs track per-subdomain HTTP requests, while the error logs capture Apache and LiteSpeed errors. If you’re seeing something broken on one of your subdomains, this is your first stop. Third, cPanel access logs are found directly inside cPanel under the Logs section — these cover WHM and cPanel login activity. And fourth, mail logs are accessible in cPanel under Email Deliverability — that’s where you’ll find the Exim mail log if you’re chasing a delivery issue.

How do you actually get to these log files? The full path goes through WebDAV — it’s a network drive connection to the hosting server over a secure SSL connection on port 2078, pointing to that DavWWWRoot logs folder. If you’ve already got the WebDAV drive mapped in Windows, navigating to the logs folder from there is the easiest approach.

Next up, log rotation. The good news here is that cPanel handles this automatically, so you don’t need to set anything up yourself. Rotation typically happens monthly. When logs rotate, they get compressed and held briefly before being deleted — so if you need an older log, don’t wait too long. If you want to download raw access logs, you can find them in cPanel under Metrics, then Raw Access.

Now let’s talk about LiteSpeed Cache. LiteSpeed is the web server NZRT runs, and it has its own caching layer that sits in front of WordPress. The cache files live in a folder called lscache under DavWWWRoot, and the behaviour of that cache is controlled by the LiteSpeed Cache WordPress plugin, configured separately per subdomain.

There are three ways to purge the cache when you need to. The first and most common is through the WordPress admin panel. You go into the LiteSpeed Cache menu, click Purge, then Purge All. Simple. The second way is through cPanel — look for the LiteSpeed Web Cache Manager and use Flush All from there. The third option is a direct delete — you go into the lscache folder via WebDAV and delete the contents manually. That one works, but use it carefully, since you’re touching the live file system directly.

It’s also worth understanding the cache rules in place for WordPress sites. There are four key behaviours to know. Static assets like images, scripts, and stylesheets are cached for one year — that’s a long time-to-live, intentionally set for performance since those files rarely change. Pages are cached for one hour by default, though that’s configurable if you need more or less freshness. WooCommerce pages and other dynamic pages are excluded from the cache entirely, because you don’t want users seeing stale cart or checkout data. And finally, if a user is logged in, the cache is bypassed completely for them — so admins and members always see live content.

Finally, there are some monitoring recommendations worth flagging. First, set up uptime monitoring for all four NZRT subdomains — tools like UptimeRobot can notify you immediately if something goes down. Second, make a habit of reviewing error logs weekly, specifically looking for PHP errors or any 500-level server errors that could indicate something quietly broken in the background. Third, consider archiving your access logs monthly — either to the wordpress-backups folder via WebDAV or to Nextcloud for longer-term storage. And fourth, if you want to make this a proper routine, add a log review task as a recurring item in your Dolibarr Projects so it doesn’t slip through the cracks.

Two quick related topics to keep in mind as well. The cPanel Overview wiki page covers the full Metrics and Logs section in more detail. And if you’re looking at security-related logs — specifically Imunify360 threat activity — that’s covered in the Security Overview wiki page.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Dolibarr Rest Api

Welcome to the NZRT Wiki Podcast. Today we’re looking at Dolibarr REST API.

If you’ve been working with Dolibarr and you want to connect it to other systems, pull data out programmatically, or automate repetitive tasks, the REST API is your main tool. It exposes pretty much every entity inside Dolibarr — customers, invoices, products, orders — all through standard web requests. Let’s walk through how it works.

First, the base URL. Every API call you make starts at the same address, which is your Dolibarr instance’s domain, followed by the path api/index.php and then a forward slash. So if your Dolibarr is hosted at erp.yourcompany.com, every endpoint you call will begin with erp.yourcompany.com/api/index.php/ and then whatever resource you’re targeting. That consistent pattern makes it easy to reason about — once you know the base, you just tack on the entity name you want to work with.

Now, authentication. Dolibarr’s REST API uses API key authentication. When you make a request, you include a special HTTP header called DOLAPIKEY, and the value is your personal API key. You don’t need to deal with OAuth flows or token exchanges — it’s a single header on every request. Your API key is something you generate inside the Dolibarr admin interface and then store somewhere secure, like a credentials file or an environment variable. If you’re working in an automated script or agent, you’ll want to pass that key in programmatically rather than hardcoding it anywhere that might end up in version control.

Let’s talk about the key endpoints, because this is where you’ll spend most of your time. Think of each endpoint as the address for a specific type of data in Dolibarr.

There are six main ones covered in the wiki. The first is thirdparties, which covers your customers and suppliers. You can send GET requests to retrieve records, POST to create new ones, and PUT to update existing ones. The second is products, which works the same way — GET, POST, and PUT for reading, creating, and updating your product catalogue.

Third is invoices. Again, GET, POST, and PUT. If you’re building any kind of billing automation — say pulling invoice data into a reporting tool or creating invoices from an external order system — this is the endpoint you’ll be hitting most. Fourth is orders, specifically customer orders, with the same three methods available.

The fifth endpoint is bankaccounts. This one is read-only, so you only have GET available here. It’s useful if you need to retrieve account details as part of a reconciliation process or a financial summary. And sixth is users, also GET only, which lets you pull user records from Dolibarr — handy if you’re syncing user data between systems or need to look up user IDs for assigning tickets or tasks.

So to summarise the pattern: most of the business-critical entities — thirdparties, products, invoices, orders — support full read-write access through GET, POST, and PUT. The more sensitive or administrative resources like bank accounts and users are restricted to read-only access through GET.

One more thing worth mentioning: Dolibarr ships with an interactive API explorer built on Swagger UI. You reach it by going to your instance’s API base path and then adding the word explorer at the end. So it would be your domain, then api/index.php/explorer. This gives you a browser-based interface where you can browse every available endpoint, see what parameters each one accepts, and even fire off test requests directly from the page. If you’re new to the API or you’re trying to figure out the shape of a response before writing code, the explorer is genuinely the fastest way to get familiar with it.

For bulk data needs — like exporting large sets of records for reporting — the wiki points you toward the Data Export feature as an alternative. The REST API is great for targeted, programmatic access and integration work, but if you need to dump thousands of rows at once, the built-in export tools might be a more practical starting point.

To pull it all together: when you’re integrating with Dolibarr, your request always goes to the api/index.php base path, you always include your DOLAPIKEY header for authentication, and you choose your endpoint based on the entity you’re working with. Whether you’re reading customers, creating invoices, updating products, or pulling order data, the structure is consistent and predictable across the board.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Integrations Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at 🔌 Integrations Overview.

So, if you’ve ever wondered how Dolibarr, our core ERP platform, actually talks to the rest of the world, this episode is for you. The short answer is that Dolibarr connects to external systems in three main ways: through a REST API, through webhooks, and through purpose-built connectors designed for specific platforms. Each of these gives you a different kind of bridge between Dolibarr and the tools you use every day.

Let’s walk through the full integration inventory, because there are eight systems worth knowing about here.

The first is WordPress. This is set up as a CMS and portal publishing integration. What that means for you is that Dolibarr can push content and data out to the NZRT WordPress site, so your ERP and your public-facing web presence can stay in sync rather than living in separate silos.

Second is the REST API itself. This is the programmatic access layer, and it’s arguably the most flexible integration of all. If you’re a developer or you’re working with automation scripts, the REST API is how you talk to Dolibarr directly from code. You can create records, query data, update statuses, and much more, all through standard HTTP requests.

Third, you have payment platforms. NZRT has connectors set up for Stripe, PayPal, and Paybox. So when money moves, Dolibarr knows about it. Whether you’re processing client invoices or handling online transactions, these integrations mean payment events can flow back into your ERP records automatically.

Fourth is LDAP, which stands for Lightweight Directory Access Protocol. This one is about directory and single sign-on synchronisation. In plain terms, it means you can connect Dolibarr to a centralised user directory so that staff accounts and permissions can be managed in one place and reflected across systems, rather than having to maintain separate user lists everywhere.

Fifth is Google Analytics. This is a web analytics integration, and it gives you the ability to connect Dolibarr’s web-facing activity to your Analytics reporting. So if you’re tracking portal traffic or user behaviour on pages that tie back to Dolibarr, this connector closes that loop.

Sixth is ClickToDial. This is a VoIP calling integration that works from within the CRM. What that means practically is that you can initiate phone calls directly from a contact or customer record in Dolibarr, which saves time and keeps your call activity tied to the right records without switching between applications.

Seventh is Gmail SMTP. This covers outbound email. When Dolibarr sends emails, whether that’s invoice notifications, automated messages, or system alerts, the Gmail SMTP connector is what routes those messages out through your Google mail infrastructure. It’s the plumbing behind every email Dolibarr sends.

And eighth is the Email Collector. This one works in the opposite direction to Gmail SMTP. Instead of sending email out, the Email Collector pulls inbound email in and converts it into Dolibarr records. So if a client replies to a message, or an email arrives at a monitored inbox, Dolibarr can pick that up and attach it to the right project, ticket, or contact automatically.

Now, one more thing to know about the way NZRT categorises all of this. There’s a service code used internally called 000INT, which stands for the Integration type. Whenever you’re documenting, ticketing, or tracking work that relates to any of these connectors or external system links, that’s the code you’ll see applied. It maps to a broader set of NZRT Service Codes that help keep everything organised across the business.

To summarise what you’ve just heard: Dolibarr sits at the centre of NZRT’s system architecture and reaches outward through eight key integrations. You’ve got WordPress for publishing, the REST API for developer access, three payment platforms covering Stripe, PayPal, and Paybox, LDAP for directory sync and single sign-on, Google Analytics for web reporting, ClickToDial for VoIP from the CRM, Gmail SMTP for outbound email, and the Email Collector for pulling inbound mail into records. All of this integration work falls under the 000INT service code.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Rest Api Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at REST API Overview.

So let’s talk about how Flarum’s API works and how you can interact with it. The first thing to know is that Flarum follows something called the JSON API specification. What that means for you in practice is that every request you send and every response you get back uses a standard content type called application JSON. The base URL you’ll be hitting for everything is your forum domain, followed by slash api.

Let’s start with authentication, because you need to get this right before anything else works. The recommended approach for write operations is to get a session token. You do this by sending a POST request to the token endpoint, passing in a JSON body with your username under the identification field and your password. The API comes back with a response containing two things: a token, which is a long string of characters, and your user ID. From that point on, you include that token in the Authorization header of every request, formatted as the word Token followed by your token string.

Here’s an important warning that will save you a lot of headaches. If you’re using pre-generated API tokens from the user settings panel in Flarum, those work fine for read-only GET requests, but they will fail on POST, PATCH, and DELETE operations because of CSRF validation. So always go through the token endpoint for anything that writes data.

Now let’s walk through what you can actually do with the API. There are three main groups of endpoints. For discussions, you can list all discussions, fetch a single discussion by its ID, create a new discussion, update an existing one, or delete one. For posts, meaning individual replies within a discussion, you can list posts, create a new reply, edit an existing post, or delete one. And for users, you can list all users or fetch a single user by ID. There’s also an endpoint to list all available tags.

Next, let’s talk about how you structure a request body when you’re creating or updating something. The JSON API format wraps everything inside a top-level data object. Inside that, you specify the type, for example the word discussions, then an attributes section where you put things like the title and the body content of your post. If you need to attach a tag, you add a relationships section, and inside that a tags object, which holds a data array. Each item in that array is an object with a type of tags and the numeric ID of the tag you want to apply.

For filtering and pagination, you append query parameters to your URL. To filter discussions by a tag, you use filter with tag as the key and the tag slug as the value. To control how many results come back, you use page limit and set a number. To offset into the results for pagination, you use page offset. You can also do text search across discussions or users by using filter with q as the key and your search term as the value.

Finally, a word on patching tags onto an existing discussion. The structure is similar to creation. You send a PATCH request with a data object specifying the type as discussions, the ID of the discussion you’re targeting, and then a relationships block with the tag you want to apply. One critical thing to be aware of here: you can only send one tag per PATCH request. If you try to send multiple tags in a single request, the API will return a 500 error. So if you need to apply multiple tags, make separate requests.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Branching Strategy

Welcome to the NZRT Wiki Podcast. Today we’re looking at Git Branching Strategy.

If you’ve ever worked on a codebase with more than one person — or even just juggled more than one task at the same time — you’ve probably felt the pain of keeping your code organised. That’s exactly what a branching strategy solves. It’s a set of conventions that define how your team handles parallel development, prepares releases, and deals with those urgent production fixes that always seem to arrive at the worst moment.

Let’s start with the building blocks. At the heart of most branching strategies is the main branch — sometimes called master. This is your production-ready code. Every commit on this branch should be something you could deploy right now. Then there’s the develop branch, which acts as an integration point for work in progress. Think of it as the staging area for your next release.

From there, you branch out — literally. Feature branches are where individual developers do their work. You create one from develop, build your feature, and merge it back in through a pull request. Release branches are for preparing a production release — final testing, version number bumps, and last-minute adjustments. And then there are hotfix branches, for those critical production issues that can’t wait for the next release cycle. You branch a hotfix directly from main, fix the issue, and merge it back into both main and develop so nothing gets lost.

NZRT also uses a consistent naming convention across all branches. You’ll see prefixes like feature, bugfix, hotfix, release, and chore — each followed by a slash and a descriptive name. This means you can tell exactly what a branch is for just by reading its name.

Now, NZRT doesn’t use a single branching model for everything. The approach depends on what you’re working on.

For Dolibarr — that’s the ERP platform — and for Dolibarr custom development, NZRT uses trunk-based development. In this model, the main branch is always production-ready, protected by feature flags that let you deploy code without activating it yet. Feature branches here are short-lived, with a maximum lifespan of three days. The idea is to keep branches small and merge them into main frequently, rather than letting long-running branches drift far from the rest of the codebase. There’s no separate develop branch in this model — everything flows directly to main in small, manageable increments. This suits rapid iteration on ERP features, which is exactly what NZRT needs for Dolibarr work.

One rule applies regardless of which model you’re in: all feature branches require a code review before merging. That means opening a pull request on GitHub and getting at least one reviewer to sign off.

So what does this look like in practice? Imagine you’re working on a WordPress upgrade. You start by switching to the develop branch and pulling down the latest changes, so you’re working from the most current version of the code. Then you create a new branch — something like feature slash wordpress-upgrade. You do your work, commit your changes, and push that branch up to the remote repository. From there, you open a pull request on GitHub so a colleague can review your changes before they’re merged in. That’s the full cycle: branch, build, push, review, merge.

Good branching habits keep your codebase clean, reduce merge conflicts, and make it much easier to trace when and why a change was introduced. Whether you’re in trunk-based development or a more structured flow with a develop branch, the underlying goal is the same — make parallel work manageable and releases predictable.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Agentic Cicd Pipelines

Welcome to the NZRT Wiki Podcast. Today we’re looking at Agentic CI/CD Pipelines.

So what makes these pipelines “agentic”? The short answer is that Claude-powered agents aren’t just passive observers in the pipeline — they actively analyse, verify, make decisions, and execute actions. They’re not scripts that blindly run commands. They think about what’s happening and respond accordingly.

The first thing worth knowing is the design philosophy here: less is best. All pipelines reuse the same three core files from the agents framework — the run script, the agent module, and the configs file. There are no new services to stand up, no new infrastructure to manage.

Now let’s talk about the two types of pipelines NZRT runs.

The first type is called Deploy and Agent Verify. This is triggered whenever code is pushed to the main branch on GitHub. After the deployment happens over SSH, a Claude Haiku model steps in to analyse the deploy state and returns either a pass or a fail result. The second type is called Business Event Pipelines. These are triggered either on a schedule via Windows Task Scheduler or by events coming in through a Nextcloud trigger bus. Named pipelines hand off plain-English goals to existing ReAct loop agents, and those agents figure out which tools to use to complete the goal.

Let’s look at the full pipeline catalogue. There are six pipelines in total. The deploy verify pipeline uses Haiku directly and fires on every push to main, annotating the GitHub Actions run with a pass or fail result. The product sync pipeline uses the pam agent, runs daily at two in the morning and also responds to Dolibarr events, and writes a sync log to the shared marketing folder. The backup verify pipeline uses the dan agent, runs every morning at six, and writes a backup verification file to the shared database folder. The daily finance pipeline uses the fin agent at seven in the morning, writing a finance summary to the shared finance folder. The weekly analytics pipeline uses the dai agent every Monday at eight and produces a weekly report in the shared analytics folder. And finally, the health diagnose pipeline also uses dan, but this one is event-driven — it fires when cPanel reports a failure and writes a diagnostics file to the shared database folder.

Let’s go deeper on the Deploy and Agent Verify flow, because it’s a neat piece of work. After your SSH deploy step completes, GitHub Actions sets up Python, installs the Anthropic library, and runs the verify deploy script from the agents framework folder. That script connects to cPanel over SSH, collects three things — the git log, file permissions, and script timestamps — then passes all of that to Claude Haiku in a single-turn call. The whole thing takes about two seconds and costs roughly a tenth of a cent per deploy. If Haiku is satisfied, the pipeline exits cleanly and GitHub shows a green notice annotation saying agent verification passed. If something looks wrong, it exits with a failure code and GitHub shows a red error annotation with Haiku’s diagnosis text. One thing you’ll need to add is a new secret in the NZRT-Scripts repository — the Anthropic API key — so the runner can authenticate.

Now for Business Event Pipelines. You run these by calling the pipeline runner script with the name of the pipeline you want. For example, you’d call it with product sync, or backup verify, or pass a list flag to see all available pipelines. This runner is a thin wrapper over the existing run script — it doesn’t modify the tested entry point, it just maps pipeline names to agent codes and goal templates, then hands off to the ReAct loop in the agent module.

Scheduling is handled by Windows Task Scheduler, not cPanel cron. The reason for this is that agents need access to the Anthropic API key and other system credentials, and those live locally on the laptop. The laptop is the convergence point. To get everything set up, you navigate to the agents framework folder and run the setup scheduled tasks PowerShell script once as Administrator. After that you can verify the tasks were registered by querying the task scheduler and filtering for NZRT entries — you’ll see the task name, its status, and when it’s next scheduled to run. If you want to test one immediately, you can trigger it manually by name without waiting for its scheduled time.

The last piece of the puzzle is the trigger bus. For events that don’t come from GitHub — things like cPanel failures, new products added in Dolibarr, or files uploaded to Nextcloud — NZRT uses a Nextcloud folder as a lightweight message bus. A polling script in the agents framework checks this folder every five minutes and watches for new trigger files. When it finds one, it fires the appropriate pipeline. No ports to open, no extra infrastructure — Nextcloud’s desktop client is already syncing, so the folder is always available.

To summarise the key files you care about: verify deploy lives in the agents framework folder and handles the GitHub Actions verification. Run pipeline also lives there and is your entry point for named pipelines. Poll triggers handles the Nextcloud event bus. Setup scheduled tasks is the one-time PowerShell setup script. And the deploy workflow file in the dot-github workflows folder is where the GitHub Actions changes live.

The key design decisions worth remembering: Haiku for deploy verification because it’s fast and cheap. The pipeline runner wraps but never modifies the tested run script. Windows Task Scheduler because credentials live locally. And Nextcloud as the message bus because the infrastructure is already there.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Automated Testing

Welcome to the NZRT Wiki Podcast. Today we’re looking at Automated Testing.

So what is automated testing and why does NZRT use it? Put simply, automated tests let you validate code changes quickly and reliably — without manually clicking through everything every time someone pushes an update. At NZRT, testing falls into three main categories, and it’s worth understanding the difference between them.

First, you have unit tests. These test individual functions in isolation, meaning any external dependencies like databases or APIs are swapped out for fakes — called mocks — so you’re only checking one small piece of logic at a time. Then there are integration tests, which go a level up and check how components actually interact with each other — think database calls and API connections working together. Finally, end-to-end tests, or E2E tests, simulate full user workflows either in a browser or a live environment. So where a unit test might check a single calculation, an E2E test walks through an entire user journey from start to finish.

A few other concepts you’ll hear mentioned. Test coverage is the percentage of your code paths that tests actually execute — NZRT aims for above eighty percent. CI integration means tests run automatically whenever you open a pull request or push a commit. And critically, if tests fail, that pull request is blocked — it cannot be merged until everything passes. Test results and coverage data are all visible through GitHub Actions.

Now, the tools NZRT uses depend on the language you’re working in. There are four main stacks. For PHP, the framework is PHPUnit and you run it with the phpunit command. For JavaScript, Jest handles both the assertions and the test run via npm test. For Python, pytest does everything — you just run pytest. And for Bash scripts, there’s a tool called bats, which works the same way — you run bats and it handles the rest.

Let’s talk about the Dolibarr custom test suite specifically. Tests live in a directory called tests, and the PHP file shown as an example is called WpSyncProductTest. It contains three test methods that give you a good picture of how tests are structured. The first checks that a product can be created — it inserts a test product and confirms the returned ID is greater than zero, meaning something was actually saved. The second test creates a product object using a specific ID, retrieves its SKU, and checks two things: that the SKU isn’t empty, and that it starts with the prefix NZRT-dash. The third test takes a product, calls a method that syncs it to Dolibarr, and then checks two outcomes — that the sync returned true, and that the product’s Dolibarr status in the database was set to the word “synced”. Together these three tests cover creation, data integrity, and a cross-system sync operation.

Running tests locally is straightforward. For PHP in the Dolibarr custom project, you change into that directory and run phpunit pointing at the tests folder. For JavaScript, it’s just npm test. For Python in the nzrt-scripts repo, you run pytest against the tests directory. And if you want a full coverage report generated as HTML, you add a coverage flag to the phpunit command and it outputs into a coverage folder you can open in a browser.

Coverage reporting is also wired into the CI pipeline. A workflow step runs phpunit with a flag that outputs coverage data in a format called Clover XML. That file is then uploaded to a service called Codecov using a GitHub Actions step — it’s tagged as unit tests and given the name codecov-umbrella. This means every time tests run in CI, coverage trends are tracked automatically over time.

If you want to dig deeper, the related topics to look at next are the CI CD Overview, GitHub Actions Workflows, and the Code Review Process — all linked from this wiki page.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Branch Protection

Welcome to the NZRT Wiki Podcast. Today we’re looking at ??? Branch Protection.

If you’ve ever pushed a change directly to your main branch and immediately regretted it, branch protection is the feature that’s going to save you from yourself. At NZRT, branch protection rules are applied to two critical branches across our repositories — main, which is production, and develop, which is staging. Let’s walk through what that actually means and why it matters.

The idea behind branch protection is straightforward. You’re preventing accidental or unauthorized changes from landing in places that really count. GitHub gives you a toolkit of rules you can switch on, and NZRT uses most of them. Let’s run through the key ones so you know what you’re working with.

First up, requiring pull request reviews. This means nobody can merge code into a protected branch without it going through a pull request and getting approved. Alongside that, there’s a dismiss stale reviews setting, which means if someone approves your pull request and you then push more commits, that approval gets wiped out. You need a fresh sign-off on what’s actually in the branch right now.

Then there’s requiring code owner review. NZRT uses code owner files to automatically assign the right people to review changes in specific parts of the codebase. If this rule is on, those assigned owners have to approve before anything merges.

Require status checks is another important one. This means your GitHub Actions workflows — things like build jobs, linting, security scans, and tests — all have to pass before the merge button becomes available. Combined with the require branches up to date rule, your branch also has to be current with the base branch before you can merge. There’s also the option to require signed commits, restrict who can push, and auto-delete head branches after a merge to keep things tidy.

Now let’s look at how NZRT actually configures these for a real repository. Taking the Dolibarr Custom repository as an example — for the main branch, which is production, the settings require two approvals before any pull request can merge. Stale reviews are dismissed, code owner review is required, and three status checks must pass: the build job, PHP linting, and a security scan. The branch must be up to date before merging. Only admins can push directly, and only admins can force push. Head branches are automatically deleted after merge.

For the develop branch on that same repository, the rules are a bit more relaxed. You only need one approval instead of two. Stale reviews are still dismissed, but code owner review is not required. All GitHub Actions must still pass and the branch must be up to date, but developers can push small fixes directly without needing admin rights. Auto-delete is still on.

A second repository configuration follows a similar pattern for its main branch — one approval required, stale reviews dismissed, code owner review on. It requires four status checks to pass: PHP linting, PHPStan static analysis, unit tests, and integration tests. Push access is restricted to admins, and head branches auto-delete on merge.

If you need to set up branch protection on a repository yourself, the process is simple. You go to repository settings, find the branches section, and click add rule. You enter the branch name pattern — so that would be something like main, develop, or a wildcard covering all release branches. Then you tick the boxes for the rules you want to apply and save.

Now, what happens if there’s an emergency and you genuinely need to bypass protection? NZRT has a defined hotfix process for exactly that. You create a hotfix branch directly from main, make your critical fix, then request a review from someone with admin rights — that’s the xc role at NZRT. You create a pull request, get at least one approval, and merge using a merge commit so the history is preserved. Then you deploy to production immediately, cherry-pick the fix across to develop, and follow up with a post-incident review.

If a full admin override is truly needed and someone with the xc role needs to push directly to main, they must log the reason in a GitHub issue, create a pull request afterward for the audit trail, and notify the team straight away.

Branch protection is one of those things that feels like overhead until the day it stops a bad deploy from happening. Getting it right across your repositories means your production code stays trustworthy and your team has a clear, consistent process to follow every time.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Cicd Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at CI/CD Overview.

If you’ve ever wondered how code goes from a developer’s laptop to a live production system without someone manually clicking through a checklist at midnight, that’s exactly what CI/CD is all about. CI stands for Continuous Integration, and CD stands for Continuous Deployment. Together, they automate the testing and deployment of your code so that every change is checked, built, and shipped in a reliable, repeatable way.

Let’s break down what each part means. Continuous Integration means that every time a developer pushes a code change or opens a pull request, automated tests kick in immediately. These tests might check code style with a linter, run unit tests to make sure nothing is broken, or even run security scans to catch vulnerabilities early. The idea is that you catch problems at the point of change, not three weeks later when someone is trying to track down a mystery bug.

Continuous Deployment takes it a step further. Once the tests pass, the code is automatically deployed to the right environment. At NZRT, the general pattern is that merging into the develop branch pushes to a staging environment, while merging into main or creating a release tag triggers a production deployment.

There are five main stages in a pipeline. First, lint — which checks that the code is written cleanly and follows style rules. Second, test — which runs all the automated tests. Third, build — which compiles or packages the code into a deployable artifact. Fourth, deploy — which sends that artifact to the target environment. And fifth, verify — which confirms the deployment actually worked, usually through health checks.

There are also a few important supporting concepts to know about. Status checks mean that a pull request is blocked from merging until every pipeline stage passes. Notifications send alerts to Slack or email so the team knows right away if something succeeds or fails. Rollback gives you a quick path back to the previous working version if a deployment goes wrong. And artifacts are the saved build outputs that can be used for release distribution.

Now let’s look at how NZRT specifically uses CI/CD across its two main code areas. For the dolibarr-custom project, which handles WordPress and Dolibarr sync, the pipeline triggers on any push to main or any pull request targeting main. The tests cover PHP linting, a WordPress security scan, and checks for module conflicts. A successful pipeline deploys to the dolibarr-custom staging domain, and when you create a versioned release tag, it goes to production but requires a manual approval step before it does.

For the core Dolibarr ERP project, the setup is similar in terms of triggers and PHP linting, but it also includes unit tests and integration tests. Deployments here go straight to production immediately, but with feature flags turned off by default so new functionality can be controlled after deployment. Health checks and error rate monitoring run continuously to catch anything that slips through.

Let’s walk through the pipeline execution flow, which is shown as a diagram in the wiki. Picture a developer pushing code. That push is detected by GitHub, which triggers GitHub Actions. At that point, the pipeline splits into two parallel tasks running at the same time — the lint check and the test suite. Once both of those finish, the pipeline checks whether everything passed. If yes, it moves on to deploy and then verify, ending in a success state. If either the lint or tests failed, the pipeline stops, marks the build as failed, and automatically posts a comment on the pull request showing what went wrong. That means you get immediate, specific feedback right where you’re working.

Finally, NZRT tracks five key metrics to measure how well the CI/CD system is performing. Build time should be under five minutes for pull request checks. Test coverage should be above eighty percent on any modified code. Deployment frequency for dolibarr-custom is targeted at one to two deployments per day. Lead time — meaning the total time from a commit to it being live in production — should be under thirty minutes. And mean time to recover, which is how long it takes to roll back a bad deployment, should be under five minutes.

Those five numbers give you a clear health picture of your delivery pipeline. If build times start creeping up, or rollback takes longer than expected, those are early signals that something needs attention.

If you want to go deeper on any of this, the wiki links out to three related notes covering GitHub Actions Workflows, Automated Testing, and Deployment Pipelines specifically. Those are worth reading alongside this overview.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Code Review Process

Welcome to the NZRT Wiki Podcast. Today we’re looking at the Code Review Process.

Code review is one of those practices that separates teams that ship quality software from teams that don’t. At NZRT, every code change goes through a structured review process using GitHub pull requests before anything gets merged. Let’s walk through how that works and what’s expected of you whether you’re the person writing the code or the person reviewing it.

First, let’s cover the key roles. There are two main players in every review. You’ve got the author, that’s the person who wrote the code and opened the pull request, and the reviewer, who examines the changes and gives feedback. Reviewers are looking at four main things: correctness, meaning does the code actually do what it’s supposed to do, style, meaning does it follow the team’s conventions, security, meaning does it introduce any vulnerabilities, and maintainability, meaning will future developers be able to understand and change this code without pain.

Now, not all feedback is created equal. Some comments are blocking issues, which are serious enough that the code cannot merge until they’re fixed. Others are suggestions, which are improvements the author can choose to address but aren’t required. Comments happen in threads attached to specific lines of code, so the discussion stays contextual and traceable.

So what does NZRT actually require before a pull request can merge? There are five things. The PR needs at least one approval if it’s targeting the develop branch, or two approvals if it’s going to main. All automated checks must pass, that includes linting, tests, and security scans run by GitHub Actions. The branch must be up to date with the base branch. Every conversation thread must be resolved. And if a CODEOWNER is assigned, they must have approved.

As a reviewer, your job is to genuinely understand the change, check the logic, look for security issues like exposed secrets or improper authentication, verify that edge cases and error handling are covered, confirm that new functionality has tests, and make sure documentation has been updated if needed. You should only approve if you’re actually confident in the quality of what you’re looking at.

As an author, your job starts before the review even begins. Write a clear title and description. Keep your PR focused, one feature or fix per pull request. When feedback comes in, respond to every comment, address it promptly, and request a re-review when you’re ready. Don’t merge without the required approvals, and don’t dismiss someone’s review without discussing it first.

Let’s talk through what good review communication looks like. Imagine a reviewer spots a SQL injection vulnerability. They’d explain the problem, show an example of the problematic pattern, which would be building a query by gluing a variable directly into a string, then show the correct pattern, which uses a parameterized query where a placeholder stands in for the user input and the actual value is passed separately. That separation is what prevents an attacker from manipulating the query.

When the author responds and fixes it, a good response acknowledges the catch, explains what was done to fix it, lists the specific changes made, such as switching to parameterized queries, adding input validation, and adding a unit test, and references the commit where those changes live.

Not every comment is a blocker. A reviewer might suggest extracting some logic into a separate method for reusability and explicitly flag it as not required for merge, just something worth considering for the long run.

The overall flow looks like this. You open a pull request. GitHub Actions run automatically to check lint, tests, and security. If they fail, you fix them before moving forward. If they pass, you request reviewers. Reviewers examine the code and either approve, leave comments, or request changes. If changes are requested, you address them and loop back. Once you have the required approvals and everything is green, a maintainer merges the PR and the branch gets cleaned up.

A few tips to make reviews work well for everyone. Be respectful and focus on the code, not the person. Ask questions instead of making accusations, something like “can you explain why this approach was chosen” is far more useful than just saying “this is wrong.” Suggest solutions, not just problems. Be timely, aim to review within twenty-four hours, same day if you can. Give context when you flag something, explain why it matters whether that’s security, performance, or readability. And don’t forget to call out good work when you see it. Reviews are as much about reinforcing strong patterns as they are about catching problems.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Git Commands Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at the Git Commands Cheatsheet.

Git is the version control system we use across all NZRT repositories, and this cheatsheet covers everything from first-time setup through to advanced recovery operations. Let’s walk through it section by section.

Starting with initial setup. Before you do anything else, you tell Git who you are. There are commands to set your global username, your email address, your preferred text editor, and to switch on coloured output in the terminal. Once those are set, you can list your current configuration at any time to confirm everything looks right.

Next up, creating and cloning repositories. If you’re starting from scratch on a local folder, you initialise Git in that directory, optionally giving the project a name. If you’re pulling down an existing NZRT repo from GitHub, you clone it using its URL. You can also clone into a custom-named folder, or do what’s called a shallow clone, which grabs only the most recent snapshot rather than the full history — useful when you just need the code fast and don’t need every past commit.

Moving on to checking status and history. This is where you spend a lot of your daily Git time. You can check what’s changed right now with the status command, including a short compact version that also shows your branch. For history, you can view the full commit log, or a condensed one-line-per-commit version, limit it to the last five commits, or draw a visual graph showing how branches have diverged and merged. You can filter history by author, by date range, by keywords in commit messages, or even view the actual code changes alongside each commit. You can also view differences — between your working files and the last commit, between staged changes and the last commit, or between any two branches or specific commits.

Branching is next, and it’s one of Git’s most powerful features. You can list all your local branches, all remote branches, or both together. You can create a new branch, switch to an existing one, or create and switch in a single step. Newer versions of Git introduced a cleaner switch command that does the same thing. You can rename branches and delete them — either safely, where Git stops you if the branch hasn’t been merged yet, or forcefully if you’re sure you want it gone. You can also delete branches that exist on the remote server.

Then there’s staging and committing — the core of your daily workflow. You check what’s changed, stage individual files or everything at once, or use interactive mode to choose exactly which chunks of changes to include. You can unstage things if you change your mind. When you commit, you write a short message describing what changed. You can amend your last commit if you forgot something, or add to it without even changing the message. If you want to throw away local changes entirely, there are restore commands that discard them from your working directory.

Merging and rebasing handle bringing branches back together. You can merge a feature branch into your current branch, with options to always create a merge commit or to squash all the feature branch’s commits into one clean commit. Rebasing replays your commits on top of another branch, keeping history linear. Interactive rebase lets you reorder, squash, or edit individual commits — very handy before pushing. Cherry-pick lets you grab one specific commit from anywhere and apply it to your current branch.

Pushing and pulling covers syncing with the remote. You push your local commits up to GitHub, specifying a branch name or using your current branch. The first time you push a new branch, you set the upstream tracking reference at the same time. Force pushing is available but flagged as dangerous — use it carefully. Pulling downloads and merges remote changes in one step, or you can use pull with rebase to keep history cleaner. Fetching downloads changes without merging them, giving you a chance to review before integrating.

Undoing changes is something you’ll need eventually. You can discard file changes, unstage files, or undo entire commits. Revert creates a new commit that cancels out a previous one — the safe option for shared branches. Reset lets you move your branch pointer back one or more commits, with three modes: soft keeps your changes staged, mixed unstages them but keeps the files, and hard discards everything. There’s also a clean command to remove untracked files, with a dry-run option to preview what would be deleted before you commit to it.

Stashing gives you a temporary holding area. If you need to switch context mid-work, you stash your changes, switch branch, do the other work, come back and pop the stash to restore where you were. You can name stashes, list them, apply specific ones, and clear them all at once.

Tags mark release points. Lightweight tags are just pointers to commits. Annotated tags include a message and are the recommended type for releases. You can push individual tags or all tags to the remote, and delete them locally or remotely.

Remote management lets you list, add, rename, or remove the remote servers your repo points to. You can also inspect a remote to see exactly what branches it’s tracking.

Finally, advanced operations. There’s bisect, which helps you find the exact commit that introduced a bug by binary-searching through history. Blame shows you who wrote each line of a file and when. Reflog is your safety net — it records every position your branch pointer has been, so you can recover from almost any mistake. There are also commands to search commit history for specific text, verify signed commits and tags, and view the full change history of a single file.

The cheatsheet ends with a set of useful aliases you can add to your Git configuration — shortcuts like using the letters s t for status, c o for checkout, and a visual alias that draws the full branch graph in one command.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Deployment Pipelines

Welcome to the NZRT Wiki Podcast. Today we’re looking at 🚀 Deployment Pipelines.

If you’ve ever wondered how code travels from a developer’s laptop to a live server without breaking everything, deployment pipelines are the answer. At NZRT, these pipelines automate the process of releasing code to staging and production environments. And depending on which system you’re working with, the approach is a little different.

Let’s start with some key concepts you’ll want to understand.

First, there’s the staging environment. Think of this as a rehearsal space — it mirrors your production server so you can test changes before real users ever see them. The production environment is the live server actually serving customers. A deployment script is an automated script that pulls the latest code and restarts any services that need it. Health checks verify that a deployment actually worked — essentially asking “did everything go okay?” Rollback is your safety net: if something breaks, you revert to the previous working version.

You might also hear about blue-green deployment, where you run two identical production environments and switch traffic between them when you’re ready. Or canary deployment, where you gradually roll out a change to just a percentage of your traffic before going all in. Zero-downtime deployment means handling things like database migrations and cache updates without ever interrupting the live service.

Now let’s look at how NZRT actually deploys its two main systems.

For Dolibarr custom, the process is continuous and triggered daily. A developer creates a feature branch, runs tests, then opens a pull request to the main branch. That pull request needs at least one code review approval before it can be merged. When it’s merged — using a squash merge to keep the history clean — GitHub Actions kicks in automatically. It runs a linter to check code quality, executes tests, performs a security scan, and then deploys directly to the Dolibarr custom environment. Health checks and smoke tests confirm everything is working, and a Slack notification goes out to the team.

The standard Dolibarr deployment follows a similar flow but adds an important safety mechanism: feature flags. A developer creates a short-lived branch, ideally lasting less than three days. Any new feature is wrapped behind a feature flag that starts switched off. After tests pass and the pull request is merged, GitHub Actions runs linting, unit tests, and integration tests, then automatically deploys to production — but the feature flag stays off. The code is live but dormant.

After deployment, the team monitors error logs and performance. When they’re confident everything looks good, they enable the feature flag and monitor again for regressions. If something goes wrong, they can disable the feature flag immediately — that’s the fastest fix — or revert the commit and redeploy.

Now let’s talk about what the actual deployment script does behind the scenes. The script takes two inputs: the environment you’re targeting, either production or staging, and the version number you want to deploy. Based on those inputs, it determines which server to connect to.

It then carries out six steps over a secure connection to the target server. First, it creates a timestamped database backup, so you always have a restore point before anything changes. Second, it checks out the specified version of the code and pulls the latest files. Third, it installs PHP dependencies, skipping anything only needed for development. Fourth, it runs any database migrations that have been queued. Fifth, it flushes the cache so the server isn’t serving stale data. And sixth, it runs a health check by requesting a specific page on the server — if that request fails, the script stops immediately and reports the failure.

If you ever need to roll back, the process is straightforward. You can list all tagged releases to find the version you want to return to, then run the deploy script again pointing at that earlier version tag. If things are really urgent, you can connect directly to the server and revert the most recent commit on the spot.

That covers the full deployment pipeline picture at NZRT — from branching strategies and feature flags all the way through to the step-by-step mechanics of what happens on the server during every release.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Git Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at ?? Git Cheatsheet.

This is a quick reference for the essential Git commands you’ll use day to day at NZRT. Whether you’re just getting started or need a reminder, let’s walk through the key areas together.

First up, setup and configuration. Before using Git on a new machine, you need to tell it who you are. You set your global username and email address — these get attached to every commit you make. Once that’s done, you can view all your configuration settings with a list command. When you’re ready to start a project, you either initialise a new repository from scratch, or clone an existing one from a URL.

Next, the basic daily workflow. You check the status of your working tree to see which files are modified, staged, or untracked. Then you stage the files you want in your next commit — either one at a time, or all changes at once. Once staged, you commit with a message describing what you did. After that, you push your commits up to the remote, and pull down changes your teammates have made.

Branching is how you work on features or fixes without touching the main codebase. You can list your local branches, or all branches including remote ones. You can create a branch and switch to it in one step, or do them separately. When you’re done, you delete it safely — Git warns you if it hasn’t been merged — or force delete if you’re sure. You can also delete a branch from the remote server directly.

For history and inspection, you can view the full commit log or a compact one-line version. You can limit it to the last ten commits, or filter by a specific file. You can show the details of any individual commit, see what’s changed but not yet staged, what’s staged and ready to commit, and even see who changed each line of a file — that last one is called blame.

Merging and rebasing are how you bring branches together. A regular merge combines another branch into your current one. A squash merge does the same but collapses all the incoming commits into a single one, keeping history tidy. Rebasing replays your commits on top of another branch for a clean linear history. Interactive rebase lets you edit, squash, or reorder your recent commits. And if a merge goes wrong mid-way, you can abort it and get back to where you started.

Undoing changes — you’ll need this sooner or later. You can discard changes to a specific file, unstage something you accidentally added, or throw away all local changes with a hard reset back to your last commit. If you need to undo a commit that’s already been pushed, revert is the safe option — it creates a new commit that cancels the old one out. You can also clean out untracked files and directories when your working directory gets messy.

Stashing is your save point for work in progress. If you need to switch context without committing half-finished work, you stash your changes and they’re set aside safely. You can list all your stashes, restore the most recent one with pop, or apply a specific stash by its index number. When you’re done with a stash, you drop it.

Tags and releases give you named markers in your commit history. You can list existing tags, create a lightweight one, or create an annotated tag with a message — annotated tags are the better choice for marking releases. You can push a specific tag to the remote, or push all tags at once.

Remote management covers your connections to remote repositories. You can list your remotes and their URLs, add a new remote, or remove one. Fetch downloads changes from the remote without merging them — handy when you want to inspect before integrating. Fetch with prune also cleans up any remote branches that have been deleted on the server.

And finally, a few advanced commands worth knowing. Cherry-pick lets you apply one specific commit from another branch onto your current one — great for pulling in a single fix. Bisect helps you hunt down which commit introduced a bug by doing a binary search through your history. Reflog shows you a log of everywhere your HEAD has pointed — it’s your safety net when something goes wrong and you need to recover. And filter-branch can rewrite commit history entirely, but that one should be used carefully and deliberately.

That covers the full Git cheatsheet — from first-time setup through to recovery tools for tricky situations.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Commits History

Welcome to the NZRT Wiki Podcast. Today we’re looking at Git Commits and History.

If you’ve ever needed to track down exactly when a bug was introduced, or figure out why a piece of code changed three months ago, you already know why commits matter. Commits are the fundamental building blocks of version control. Every time you commit, you’re capturing a snapshot of your project at that moment in time, paired with a descriptive message that explains what changed and why. Get this right consistently, and your commit history becomes a powerful audit trail. Get it wrong, and you’re left staring at a list of messages like “fix stuff” and “more changes” with no idea what happened.

Let’s walk through the key concepts you need to understand.

First, atomic commits. The idea here is simple — one logical change per commit. Not a dozen things bundled together, just one. That way, if something breaks, you can reverse that single commit without pulling apart unrelated work at the same time.

Next, the staging area. Before you commit anything, you use git add to select exactly which changes go into that next commit. Think of it like packing a box before sealing it — you choose what goes in before you close the lid.

Then there’s amending. If you’ve just made a commit and you realise you forgot something or your message has a typo, you can amend that last commit to fix it. Important caveat though — you can only safely do this before you’ve pushed the commit to a shared remote. Once it’s out there, amending rewrites history and causes problems for everyone else on the team.

When you want to look back at what’s happened, you use git log. This shows you all your commits in chronological order, and every single commit has a unique identifier — a SHA-1 hash — which is that long string of letters and numbers you see next to each entry. That hash is how git tracks and references specific points in your history.

Finally, rebasing. This is a more advanced technique that lets you reorder or combine commits. It’s useful for cleaning up a messy local history before you share your work, so the final record looks logical and easy to follow.

Now let’s talk about the NZRT commit message format, because at NZRT this isn’t optional — every commit needs to follow the standard structure.

The format has four parts. You start with a type and a scope in parentheses, followed by a colon and a short subject line. Then you leave a blank line and write a body with more detail. Finally, you add a footer for things like issue references or breaking change notices.

For the type, you choose from a fixed list: feat for a new feature, fix for a bug fix, docs for documentation changes, style for formatting that doesn’t affect logic, refactor for code restructuring, perf for performance improvements, test for adding or updating tests, and chore for maintenance tasks like dependency updates.

The scope is the area of the codebase you’re touching — so something like dolibarr, wp-sync, or scripts depending on what you’re working on.

The subject line should be written in present tense and kept under fifty characters. Think of it like the subject line of an email — short, specific, and descriptive.

To make this concrete, here’s what a real NZRT commit message looks like in plain language. The type is feat, the scope is wp-sync, and the subject says “add product comparison module.” The body then explains that this implements a side-by-side product comparison feature for customers, adds a new shortcode for easy integration, and includes the CSS and JavaScript needed to make it work. The footer closes issue number forty-two.

That structure means anyone reading the history six months from now immediately knows what changed, why it changed, and which issue it was tied to.

In the NZRT context specifically, the Dolibarr custom repository maintains a detailed commit history and every commit needs to be production-ready before it’s pushed. Daily pushes are the norm, so keeping each commit clean and well-described is critical for the team to audit who changed what and when.

There are three git commands worth knowing for navigating that history. The first shows you the last twenty commits in a compact, one-line-per-commit view. The second lets you look at the full details of any specific commit by passing in its hash. The third shows you the full change history for a single file — so if you want to trace every modification ever made to a particular script or config file, that’s the command you reach for.

Taken together, clean atomic commits with descriptive messages, the NZRT type-scope-subject format, and regular use of git log turn your repository’s history into something genuinely useful — not just a backup, but a clear record of your project’s evolution that anyone on the team can read and learn from.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Commit Message Standards

Welcome to the NZRT Wiki Podcast. Today we’re looking at 💬 Commit Message Standards.

If you’ve ever looked back through a project’s git history and had no idea what anyone was actually doing, this episode is for you. At NZRT, we follow a format called Conventional Commits, and once you get used to it, you’ll wonder how you ever lived without it. It makes your history scannable, your changelogs automatable, and your teammates much happier.

So let’s start with the structure. Every commit message you write follows the same basic shape. You open with a type, then optionally a scope in parentheses, then a colon and a short subject line. Below that, separated by a blank line, you can add a body. And below that, a footer for referencing issues or flagging breaking changes. Think of it as: what kind of change, where it happened, what it does, why it was needed, and what it affects.

Let’s talk about types first. There are ten of them. “feat” is for a new feature. “fix” is a bug fix. “docs” means you’re only touching documentation. “style” is for code formatting changes with no logic impact. “refactor” means you restructured code without changing its behaviour or fixing a bug. “perf” is a performance improvement. “test” is for adding or updating tests. “chore” covers maintenance work like updating dependencies or build config. “ci” is for changes to your continuous integration or deployment setup. And “revert” is for undoing a previous commit.

Now for scope. The scope is optional, but recommended. It goes inside parentheses right after the type, and it tells you which part of the codebase was touched. At NZRT, the approved scopes are: wp-sync for the WordPress and Dolibarr sync module, dolibarr for the ERP integration, api for the REST API, db for database work and migrations, sync for product or data sync, deploy for deployment processes, test for testing infrastructure, and docs for documentation.

Your subject line is the short description that follows the colon. Keep it under fifty characters, write it in imperative mood — so “add” not “added” or “adds” — start it lowercase, and don’t put a period at the end. So you’d write something like: feat wp-sync, colon, add product comparison widget.

The body is where you explain the why. Not what the code does — the code shows that. But why this change was necessary, what problem it solves, or what decision was made. Wrap each line at around seventy-two characters, and leave that blank line between the subject and body.

The footer is for linking to issues. You use “Closes” followed by a hash and issue number to auto-close that issue when the commit merges. “Refs” references an issue without closing it. And if your change breaks something for existing users — like renaming a field in an API response — you add a line starting with BREAKING CHANGE followed by a colon and an explanation. That signals a major version bump is needed.

Let me walk you through a few examples in plain language.

The first example is a feature commit for the wp-sync scope. The subject says “add product comparison widget.” The body explains that a side-by-side comparison feature was implemented for customers, introduces a new shortcode and page template, and includes styling and interactivity. The footer closes issue 42.

The second is a bug fix in the sync scope. The subject is “prevent duplicate product sync.” The body explains that a webhook was firing twice because there was no idempotency check, and that the fix adds signature validation and idempotency key tracking. It closes issue 89 and references issue 87.

There’s also a performance example in the db scope. The subject is “optimize product list query.” The body explains that indexed lookups replace a full table scan for category filtering, cutting query time from five hundred milliseconds down to fifty milliseconds on large catalogs.

And a breaking change example in the api scope. The subject includes an exclamation mark after the scope to flag it as breaking. The footer uses BREAKING CHANGE to explain that a field called product underscore id has been renamed to just id in the response, and tells clients exactly what to update.

Before you commit, run through a quick checklist. Is the type one of the ten approved types? Is the scope lowercase and from the approved list? Is the subject in imperative mood? Is it under fifty characters with no trailing period? Does the body explain why, not what? Are issues referenced properly? And are breaking changes called out in the footer?

Finally, you can enforce all of this automatically with a git hook. You’d create a file called commit-msg inside your dot git hooks folder. The script reads your commit message and checks it against a pattern matching the type, scope, and subject format. If the message doesn’t match, it prints an error, shows you what the expected format looks like, and blocks the commit from going through. You then make the file executable with a chmod command. One-time setup, saves a lot of back-and-forth in code review.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Github Packages

Welcome to the NZRT Wiki Podcast. Today we’re looking at GitHub Packages.

So what is GitHub Packages? Put simply, it’s a package registry built right into GitHub. Instead of relying on a completely separate service to host your libraries and containers, you can publish and install packages directly from the same platform where your code already lives. That tight integration with GitHub repositories, GitHub Actions, and GitHub authentication is what makes it particularly useful for teams already working in that ecosystem.

Let’s talk about what kinds of packages GitHub Packages actually supports, because it covers a lot of ground. If you’re working with JavaScript, you can use it as an npm registry. If you’re in the Java world, it supports Maven and Gradle. For dotnet developers, there’s NuGet. If you’re building and deploying containers, Docker images are supported. And if you’re in the Ruby ecosystem, RubyGems are covered too. So whatever stack you’re working with, there’s a good chance GitHub Packages has you covered.

Now, a few key concepts worth understanding before you dive in. First is scoping. When you publish a package, you namespace it to either your organisation or your user account. For NZRT, that means packages are published under the nzrtnetwork organisation scope. This keeps things organised and makes it clear who owns and maintains a given package.

Next is visibility. Packages can be either public or private, just like repositories. Public packages can be installed by anyone, while private packages require authentication and appropriate access.

Speaking of authentication, you’ll need a GitHub token to both publish and install packages. This ties into GitHub’s permissions model, where package access is inherited from repository access. If you have read access to a repository, you can install its packages. If you have write access, you can publish.

And then there’s CI and CD integration, which is where things get really powerful. GitHub Actions can automatically publish packages as part of your workflow, so every time you push a release, your package is built and published without any manual steps.

Let me walk you through how NZRT handles npm package publishing via GitHub Actions. The workflow step is straightforward. There’s an action step that runs npm publish, and it passes in an environment variable called NODE AUTH TOKEN, which is automatically set to the built-in GitHub token that Actions provides. You don’t need to manually create or store that token — GitHub injects it securely at runtime. That’s the publish side sorted.

On the install side, it’s just as clean. If you want to install one of NZRT’s npm packages from the command line, you run npm install followed by the package name under the nzrtnetwork scope. For example, there’s a package called wp-sync-sdk published under that scope, and installing it looks exactly like any other npm install command — the only difference is the scoped name tells npm to look at the GitHub registry rather than the default public registry.

Now let’s go through the four supported registry types NZRT is using, because it’s worth knowing what’s available. First, Docker, which is used for container images. The NZRT Docker images are hosted on the GitHub Container Registry, and there’s a WordPress base image there as an example. Second, Maven, which handles Java libraries. NZRT uses this for a Dolibarr API library under the co.nzrt group. Third, npm, which as we just covered is for JavaScript libraries. The nzrt-utils package is one example published under the nzrtnetwork scope. And fourth, NuGet, which is for dotnet packages. NZRT has a Dolibarr integration package published there.

So to bring it all together — GitHub Packages gives you a single, integrated place to host your libraries and containers, authenticate using the same tokens and permissions you already have set up in GitHub, and automate publishing through Actions. For a team like NZRT that’s already leaning heavily on the GitHub ecosystem, it removes a lot of friction that comes with managing separate registries for different package types.

If you want to go deeper, the related wiki notes on GitHub Overview and GitHub Actions are worth checking out — they’ll give you the broader picture of how all these pieces connect.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Git Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at 📚 Git Overview.

So, what is Git? At its core, Git is a distributed version control system. What that means in practice is that it tracks changes to your files over time, lets you collaborate with other developers, and keeps a complete history of your entire project. Every time something changes, Git knows about it. And at NZRT, Git is the foundation that holds all of our development workflows together, particularly around Dolibarr customisations and the supporting tools that sit alongside them.

Let’s start with the key concepts you need to understand before anything else.

First, distributed version control. Because Git is distributed, every developer working on a project has a full copy of the repository, including its entire history. You’re not dependent on a central server being online to work. You have everything you need locally.

Next up are commits. Think of a commit as a snapshot of your code at a specific point in time. Each one comes with a descriptive message so that you and your teammates know exactly what changed and why. Good commit messages matter a lot at NZRT, and there are specific standards for how they should be written.

Then there are branches. Branches let you work on multiple features at the same time without stepping on each other’s toes. You spin up a new branch, do your work in isolation, and bring it back into the main codebase when it’s ready.

The staging area is something that trips people up early on. Before you actually commit your changes, you stage them. This is your chance to review what you’re about to lock in. You choose what goes into the commit rather than just bundling everything together automatically.

You’ll also encounter merge conflicts at some point. These happen when two branches have both made changes to the same piece of code. Git can’t automatically decide which version wins, so it flags it and asks you to resolve it manually.

And finally, tags. Tags are named snapshots that mark important moments in a project’s life, things like a version one release or a stable milestone. You might see labels like v one point zero point zero attached to specific commits.

Now, how does all of this work specifically at NZRT? The main repository you’ll be working with is Dolibarr-Custom, which lives on the NZRT GitHub account and contains all of the ERP customisations. The team uses trunk-based development with feature flags, which means the main branch is always the source of truth, and new work branches off it, gets reviewed, and comes back in cleanly.

Every commit must follow the NZRT Commit Message Standards format, so make sure you’re familiar with that before you start pushing code. Once your changes are ready, you push your branch to GitHub, open a pull request, and a peer reviews it before anything gets merged. GitHub Actions then automatically runs linting and tests on every pull request, so quality checks happen without anyone having to think about it.

For your local development environment, NZRT uses Laragon, which gives you PHP version eight and above, MySQL, and Python three point thirteen all running together. Deployment flows from GitHub through GitHub Actions and out to the production server.

Now let’s walk through what a typical Git workflow looks like at NZRT. There’s a code example in the wiki that shows the full sequence, so let me walk you through what it’s doing in plain English.

You start by cloning the Dolibarr-Custom repository from GitHub down to your local machine. That pulls down all the code and history. Then you navigate into that project folder. From there, you switch to the main branch and pull the very latest changes from the remote, making sure you’re starting from an up-to-date base.

Next, you create a new branch for the feature you’re about to work on. The branch name starts with the word feature followed by a short description of what you’re building. That naming convention is intentional and makes it easy for the team to see what’s in progress at a glance.

After you’ve made your changes, you stage everything you want to include in your commit. Then you write a commit message. In the example, the message starts with the word feat followed by a colon and a short description. That’s the conventional commits format that NZRT follows. Finally, you push that feature branch back up to GitHub, where it becomes available for review.

And that’s the loop. Clone, branch, code, stage, commit, push, review, merge. Once you’re comfortable with that rhythm, Git starts to feel like a very natural part of how you work rather than an extra layer of complexity.

If you want to go deeper, the wiki has related notes on Repository Structure, Branching Strategy, Commits and History, and a Git Commands Cheatsheet. Those are great next stops after this overview.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Github Actions Workflows

Welcome to the NZRT Wiki Podcast. Today we’re looking at ⚙️ GitHub Actions Workflows.

If you’ve ever pushed code to a GitHub repository and wondered how tests run automatically, or how your changes end up on a server without anyone manually doing it — that’s GitHub Actions at work. At NZRT, we use it across our repositories to handle linting, testing, and deployment in a consistent, automated way.

So let’s start with the basics. A GitHub Actions workflow is a configuration file written in YAML, and it lives inside a folder called dot-github slash workflows in your repository. The structure of one of these files is straightforward. You give the workflow a name, you tell it what events should trigger it — things like a push to a branch, a pull request, or a scheduled time — and then you define one or more jobs. Each job runs on a virtual machine, typically the latest version of Ubuntu, and contains a series of steps that run in order.

Now let’s look at the main workflows NZRT uses.

The first is the deploy workflow for the Dolibarr custom project. This one triggers in two situations: when you push to either the develop or main branches, or when a release is published. It has three jobs that can run depending on the context.

The first job is called lint-and-test, and it runs every time. It sets up a PHP 8.1 environment with the extensions needed for the project, installs dependencies using Composer, then runs three checks in sequence. First it scans every PHP file in the WordPress content folder to make sure the syntax is valid. Then it runs a security scan. Then it runs the full test suite using PHPUnit. If any of those steps fail, the whole workflow stops there.

The second job, deploy to staging, only runs if the branch being pushed is the develop branch, and it only starts if lint-and-test passed. It connects to the staging server over SSH using a deploy key stored as a secret, pulls the latest code, and flushes the WordPress cache. When it’s done — whether it succeeded or failed — it sends a Slack notification with the status, who triggered it, and what repository it was for.

The third job, deploy to production, works differently. It only runs when you push a version tag — something like v one-point-two-point-zero. It uses GitHub’s deployment API to register a formal deployment event, then connects to the production server via SSH, checks out the tagged version, pulls it down, flushes the cache, and exports a database backup before anything changes. After deploying, it runs two health checks against the live site to confirm it’s responding. Then it sends a Slack notification with the version number and status.

The second workflow is the Dolibarr CI workflow. This one runs on every push and every pull request — so it’s always watching. It has two jobs. The first is a linting job that checks your PHP code against the PSR12 coding standard and runs static analysis to catch type errors and other issues before they become bugs. The second job is a test job, and this one spins up an actual MySQL 8 database as part of the runner environment — it even has a health check to make sure the database is ready before the tests start. Then it installs dependencies and runs the test suite against that live database connection.

The third workflow is the NZRT Scripts deploy workflow. This one is simpler. Any time you push to the main branch of the NZRT-Scripts repository, it SSHes into the cPanel server, goes to the scripts folder, pulls the latest code from the main branch, and makes sure all shell scripts are marked as executable. Then it verifies the deploy worked by logging the most recent commit on the server.

Now, all three of these workflows rely on secrets — sensitive values like SSH keys, usernames, and ports that you never want to hard-code into a file. NZRT manages these at the organisation level in GitHub, which means they’re available to every repository without you needing to set them up repo by repo. There are three org-level secrets you need to know about. The deploy key is the SSH private key used to authenticate with the server. The deploy user is the cPanel SSH username. And the deploy port is the port number used for SSH connections. All three are stored in the organisation secrets settings page on GitHub.

If you want to dig deeper, the wiki has related notes on CI CD Overview, Automated Testing, Deployment Pipelines, and the Git Deployment page which covers cPanel-specific git setup.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Github Actions

Welcome to the NZRT Wiki Podcast. Today we’re looking at ⚙️ GitHub Actions.

So what is GitHub Actions? Put simply, it’s the built-in automation platform that lives right inside GitHub. When you push code, open a pull request, cut a release, or even just trigger something manually, GitHub Actions can spring into life and do things for you automatically. We’re talking running tests, checking code quality, building your project, and deploying it to a server — all without you having to lift a finger after the initial setup. At NZRT, we use it across our repositories to keep things consistent and reduce the risk of human error slipping into production.

Let’s walk through the core concepts you need to understand before diving into any of this.

First, there’s the workflow. A workflow is a YAML configuration file that you place inside a special folder in your repository — specifically a folder called dot-github, then workflows inside that. Each file in there defines a piece of automation: what triggers it, what it does, and how it does it.

Speaking of triggers — those are the events that kick a workflow off. You can trigger on a push to a branch, on a pull request being opened, on a schedule like a nightly cron job, on a release being published, or manually via something called a workflow dispatch. That last one is useful when you want a button you can press yourself from the GitHub interface.

Inside a workflow, you have jobs. Jobs are the big chunks of work. By default they run in parallel, but you can chain them so one waits for another to finish. Inside each job, you have steps — these are the individual commands or actions that run in sequence. Think of a job as a recipe, and the steps as each line in that recipe.

Now, actions themselves — with a lowercase a — are reusable blocks of code you can drop into your steps. A huge library of these exists on the GitHub Marketplace. Instead of writing a script to install a specific version of PHP or Node, you just pull in an action someone else has already written and maintained. It saves a lot of time.

Jobs run on runners. A runner is basically a server — GitHub provides hosted ones running Ubuntu, Windows, or macOS, or you can bring your own self-hosted runner if you need more control or specific hardware.

Two more important concepts: artifacts and secrets. Artifacts are outputs from your build — compiled files, test reports, things like that — which you can save and either download later or pass between jobs. Secrets are encrypted environment variables where you store sensitive things like SSH keys, API tokens, and passwords. You configure these in your repository settings, and then reference them safely inside your workflows without ever exposing the actual values in your code.

Now let’s look at two real examples from NZRT’s own workflows.

The first is a PHP lint check that runs on every pull request. When someone opens a pull request, GitHub spins up a fresh Ubuntu server, checks out the code from the repository, installs PHP version 8.1, and then runs a syntax check across all PHP files in the source folder. If any file has a syntax error, the job fails and the pull request gets flagged. This is a simple but powerful quality gate — it stops broken PHP from ever making it into the main branch through a pull request.

The second example is a deployment workflow that fires when a release is published. Again it starts on Ubuntu, checks out the code, and then runs a deployment step. That step does a few things in sequence: it creates an SSH configuration directory, writes a private deploy key — pulled securely from GitHub Secrets, so it’s never visible in the code — into a key file, locks down the file permissions on that key so SSH will accept it, and then connects to the production server via SSH. Once connected, it navigates to the Dolibarr custom code directory on that server and pulls the latest changes from the main branch. So publishing a release in GitHub automatically ships the code to production. Clean, auditable, and no manual SSH session required.

Those two examples give you a good feel for the range GitHub Actions covers — from lightweight code checks all the way through to full deployments. The same mental model applies regardless of complexity: define your trigger, define your jobs, break each job into steps, and use secrets to handle anything sensitive.

If you want to go deeper, the NZRT wiki has related notes on GitHub Actions Workflows, Automated Testing, and Deployment Pipelines — worth a read once you’ve got the fundamentals down.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Github Cli Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at 🖥️ GitHub CLI Cheatsheet.

If you’ve ever wished you could do everything GitHub-related without leaving your terminal, the GitHub CLI tool, known as gh, is exactly what you need. It lets you manage repositories, pull requests, issues, releases, and more, all from the command line.

Let’s start with getting it installed. On Windows, you can install it using either Chocolatey or winget by running the install command for GitHub dot cli. On a Mac, Homebrew handles it with a simple brew install. On Linux, you can use apt. Once installed, you can verify everything worked by checking the version. Then you authenticate by running gh auth login, which walks you through an interactive setup where you choose GitHub dot com, pick SSH or HTTPS, and generate an access token. If you already have a token in a file, you can pipe that file directly into the login command to skip the prompts.

To check your current login state, you run gh auth status. To log out, gh auth logout. Simple.

Now, working with repositories. You can clone any repo by passing the owner and repo name to gh repo clone. You can even specify a destination folder as a second argument. To view information about a repo, you use gh repo view, either for your current directory or by naming the owner and repo. Tacking on the web flag opens it straight in your browser. You can list all repos under an organisation, filter for archived ones, or cap the results with a limit flag. Creating a new repo is just gh repo create, with options to make it private and add a description. Forking works similarly with gh repo fork, and adding the clone flag pulls it down to your machine at the same time.

Pull requests are where the CLI really shines. You create one with gh pr create, and you can specify a title, a body description, a base branch, a head branch, or mark it as a draft, all as flags on that same command. To see what’s open, gh pr list, filtered by state, author, or base branch as needed. You can view a specific PR by number, check its CI status with gh pr checks, request a review, approve it, request changes with a comment, or leave a comment yourself. Merging is flexible too: you can squash, rebase, or create a standard merge commit, and you can automatically delete the branch after merging. Closing or reopening a PR is just one command each.

Issues follow the same pattern. You create them with a title, body, and labels. You list them filtered by state, assignee, or label. You view, close with an optional comment, reopen, comment on, or edit them to change titles or add and remove labels.

For GitHub Actions, gh run list shows your recent workflow runs. You can view details and logs for a specific run by its ID, download artifacts, trigger a workflow manually if it supports that, and list all available workflows in your repository.

Releases are just as straightforward. You can list them, view a specific one by tag, create a new release with a title, or let GitHub auto-generate release notes from your merged pull requests. You can mark a release as a draft or a pre-release, upload assets like zip files to an existing release, edit it later, or delete it entirely.

Branch operations in the CLI lean on the gh api command, which lets you talk directly to the GitHub REST API. You can list branches, create one by passing a reference name and a commit hash, or delete one by sending a delete request to the reference endpoint.

For discussions, you can create one with a title, body, and category, then list discussions filtering for answered or unanswered ones, or view a specific discussion by its ID.

Label management is clean: list labels, create one with a name, a hex colour code, and a description, edit an existing label, or delete it.

You can also set up aliases in your git config to shortcut common gh commands. For example, mapping pr-create to gh pr create, or status to gh pr checks, so your muscle memory works even faster.

Finally, a few handy one-liners worth knowing. You can open your current repo in the browser with a single command. You can push a branch and immediately create a pull request in one chained command. You can merge and delete the branch in one go. You can create a release from your latest git tag and auto-generate the notes. And you can list all pull requests waiting specifically for your review by searching for your username in the reviewer-requested filter.

That covers the full GitHub CLI toolkit, from setup and authentication through to repos, pull requests, issues, actions, releases, branches, labels, and discussions.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Github Discussions

Welcome to the NZRT Wiki Podcast. Today we’re looking at ?? GitHub Discussions.

If you’ve spent any time in GitHub, you’re probably familiar with issues and pull requests. But there’s a third space in GitHub that NZRT uses just as actively, and that’s Discussions. Think of it as the team’s open conversation floor, a place where you can ask questions, float ideas, share news, or just check in with each other, all without cluttering up the issue tracker.

So let’s talk about what GitHub Discussions actually are and how NZRT uses them.

At the heart of it, Discussions are organised into categories. NZRT uses four main ones. There’s Questions, for when you need an answer from the team. There’s Ideas, for proposing features or improvements. There’s Announcements, for sharing important updates like releases or policy changes. And there’s General, for everything else, think project updates, team celebrations, onboarding, retrospectives, that kind of thing.

One of the most useful features in the Questions category is solution marking. When someone asks a question and another team member gives a helpful answer, the original author can mark that reply as the solution. This means the next person with the same question can find the answer instantly, without reading through a whole thread.

You can also upvote discussions and individual answers. So if someone asks a question you’ve been wondering about too, or if an idea resonates with you, a quick upvote lets the team know without needing a reply.

Now let’s look at some real examples of how NZRT actually formats these discussions, and I’ll walk you through what they look like without reading out any of the formatting characters.

The first example is a question post. It opens with a clear heading asking how to run tests locally. The person explains they’re trying to set up tests for a new WordPress plugin and want to know what command to use. Then they list their environment details, which include Windows 10 with Laragon, PHP version 8.1, and WordPress version 6.1. That’s a great example of a well-structured question. You’re telling the team what you’re trying to do, why you’re stuck, and exactly what your setup looks like. That context makes it much easier for someone to give you a useful answer.

The second example is an announcement post. The heading identifies it as a release, specifically version 2.1.0 of the dolibarr-custom package going to production. It then lists what’s new, in this case a product comparison widget, an advanced reporting dashboard, and a Dolibarr inventory sync. It notes the exact date and time of deployment, names the person who deployed it, and links to the full changelog on GitHub. That’s the kind of announcement that keeps everyone aligned. Anyone who needs to know what changed and when can find it right there in the discussion.

The third example is much simpler. It shows how you’d reference a discussion from inside a pull request or an issue comment. You simply write something like “Related discussion, number 5, how to set up the development environment.” GitHub will turn that into a clickable link automatically. This is how you connect conversations across the different parts of GitHub without losing context.

Now let’s talk about best practices, because how you use Discussions matters just as much as what you post.

The most important rule is to keep Discussions and Issues separate. Discussions are for conversation. Issues are for tracked work with assignees, labels, and milestones. If an idea in Discussions gains enough traction and the team reaches consensus, you convert it into an issue so it can be planned and implemented properly. That’s the intended flow: discuss, agree, track.

Before you post a question, do a quick search. Duplicate questions slow the team down, and the answer might already be there waiting for you. When you do post, give as much context as you can, including your environment, any error messages you’ve seen, and what you’ve already tried.

Keep the tone professional. GitHub Discussions are company communication, so treat them that way. That said, you can still use emoji reactions for quick feedback. A thumbs up, a rocket, a heart, these are all valid ways to respond when you don’t need to add words.

And finally, make sure you mark solutions when you get a good answer. It’s a small action that has a big payoff for everyone who comes after you.

Discussions also connect to the broader GitHub ecosystem. They’re referenced in the Collaboration Overview and the Issues and Projects notes in the wiki, so if you want to understand how all these pieces fit together, those are worth a look.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Github Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at the GitHub Overview.

If you’ve spent any time in software development, you’ve probably heard of GitHub. But let’s make sure we’re all on the same page about what it actually is and how NZRT uses it day to day.

GitHub is a cloud-based platform built on top of Git, which is a version control system. In plain terms, that means GitHub gives you a place to store your code, track every change ever made to it, and collaborate with other people without stepping on each other’s work. It also adds a layer of DevOps tooling on top, so you can automate testing, deployment, and all sorts of other workflows right from the same platform.

Now let’s walk through the key concepts you’ll encounter when working with GitHub.

First up, you have repositories. A repository is essentially a storage container for a project. It holds all the code, all the files, and the entire history of every change ever made. Repositories can be public, meaning anyone on the internet can see them, or private, meaning only people you grant access to can view them.

Next, there are forks. When you fork a repository, you create your own independent copy of it. This is really useful if you want to experiment with changes without affecting the original project, or if you’re contributing to someone else’s work and need your own space to develop.

Then you have pull requests. This is the formal process for getting your changes reviewed before they get merged into the main codebase. You make your changes on a separate branch, open a pull request, and other team members can review the code, leave comments, and approve or request changes before anything goes live.

GitHub Actions is the built-in automation engine. You can set up workflows that trigger automatically based on events, for example when someone pushes new code, opens a pull request, or on a regular schedule. This is how you handle things like running automated tests or deploying to a server without doing it manually every time.

Issues are how GitHub handles bug tracking, feature requests, and project planning. You can label issues, assign them to people, and group them into milestones to track progress toward a goal.

Releases let you package up specific versions of your software with release notes and downloadable files, so users or other systems always know exactly what version they’re working with.

GitHub Pages is a handy feature that lets you host static websites directly from a repository. It’s commonly used for documentation sites, project landing pages, or blogs.

And finally, webhooks. A webhook is a way for GitHub to send a real-time notification to another service whenever something happens in your repository. For example, you could have GitHub notify Slack every time a pull request is opened.

Now let’s talk about how NZRT specifically uses GitHub. All NZRT repositories live under the organisation at github dot com slash nzrtnetwork. The general approach is to keep documentation public so it’s accessible, while production code stays in private repositories.

For authentication, NZRT uses SSH keys alongside personal access tokens for command-line work. The preferred tool for local automation and CLI tasks is the GitHub CLI, known as gh. So if you’re scripting something GitHub-related at NZRT, that’s the channel to use.

GitHub Secrets are used to store sensitive information like API tokens and deployment credentials, keeping them out of the codebase but still available to automated workflows.

On the integration side, NZRT has connected GitHub to Slack notifications and email alerts, so the right people hear about pull request reviews and other key events without having to check GitHub manually.

One workflow worth highlighting is the Dolibarr-Custom repository, which is actively monitored by GitHub Actions. That means changes there can trigger automated processes, which is a good example of how GitHub sits at the centre of NZRT’s development pipeline rather than just being a place to store code.

So to pull it all together, GitHub at NZRT is not just a code host. It’s the central coordination point for version control, code review, automation, and integration with the wider toolset. Whether you’re pushing a small fix or managing a full deployment pipeline, GitHub is where it all comes together.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Merging Rebasing

Welcome to the NZRT Wiki Podcast. Today we’re looking at Git Merging and Rebasing.

If you’ve ever worked on a team codebase, you’ve probably hit the moment where two people have been working on the same project at the same time, and now you need to bring that work together. That’s exactly what merging and rebasing are for — and understanding when to use each one makes a real difference to how clean and manageable your code history stays.

Let’s start with the basics. A merge is when Git takes two branches and combines them into one. The most common type is called a merge commit — Git creates a brand new commit that joins the two branches together, and crucially, it keeps the full history of both. You can see exactly what happened and when. There’s also something called a fast-forward merge, which only happens when there’s no divergence between branches — Git just moves the pointer forward in a straight line, keeping everything linear without needing an extra commit.

Rebasing works differently. Instead of joining branches together, rebasing takes your commits and replays them on top of a new base point. The result is a much cleaner, linear history — it looks like everything happened in a straight line, even if the work was happening in parallel. The trade-off is that rebasing rewrites history, which means you need to be careful about using it on shared branches.

Then there are merge conflicts. These happen when the same section of code has been changed in both branches, and Git can’t figure out which version to keep. When that happens, Git adds conflict markers directly into the file. You’ll see a section marked as coming from your current branch, a dividing line, and then the incoming changes below. You have to open the file, read both versions, decide what the final code should look like, and save it manually.

Another strategy worth knowing is a squash merge. Instead of bringing in every individual commit from a feature branch, squash merge collapses all of those commits into a single one. This keeps your main branch history very tidy — one feature, one commit.

Now, how does NZRT actually handle this? For the Dolibarr Custom project, the approach is squash merges into main, following what’s called a trunk-based development model. Before any merge happens, the branch gets rebased so that the history is perfectly linear. This makes rollbacks much simpler — if something goes wrong after a release, you can pinpoint exactly which change to undo.

Importantly, all of this happens through GitHub’s pull request interface, not the command line. You raise a PR, get it approved, and hit the merge button. The squash and rebase settings are configured at the repository level, so the right strategy is applied automatically.

Let’s talk through what that looks like in practice. Imagine you’ve finished a feature branch. First, you switch to the develop branch and run a squash merge of your feature branch into it. That collapses all your feature commits into one clean commit, which you then label with a meaningful message like “feat: my feature.” Before that merge, if you want to avoid any merge commits at all, you rebase your branch on top of develop first, then use a fast-forward-only merge. Fast-forward-only means Git will refuse to merge if it can’t do it in a straight line — a useful safety check.

To bring it all together: use merge commits when you want to preserve the full history of how branches came together. Use rebase when you want a clean, linear history before merging. Use squash merge when you want one tidy commit per feature on your main branch. And always resolve conflicts carefully, reading both sides before deciding what stays.

At NZRT, the decision is already made for you on Dolibarr Custom — squash and linear, always via GitHub. But understanding the mechanics underneath means you’ll know exactly what’s happening when you click that merge button.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Issues Projects

Welcome to the NZRT Wiki Podcast. Today we’re looking at ?? Issues & Projects.

If you’ve ever wanted a way to track bugs, plan features, and keep your development work organised in one place, GitHub Issues and Projects are exactly what you need. Let’s walk through how they work and how NZRT uses them day to day.

So, what is a GitHub Issue? Think of it as a conversation-backed task card. You create one with a title, a description, and a few extra details, and from that point on it becomes the single place where everything related to that task lives. Issues come in a few flavours at NZRT. You might open one for a bug someone found, a new feature the team wants to build, or a piece of documentation that needs writing. The type of issue is communicated through labels. Labels are just tags you attach to an issue to categorise it. You might have labels for priority, for which system is affected, for which team owns it, or for where it sits in the process.

Alongside labels, you have milestones. A milestone groups a set of issues together under a shared goal, like a release or a sprint. When you look at a milestone, you can see at a glance how many issues are closed versus still open, which gives you a quick health check on whether you’re on track. Then there are assignees, which is simply the person or people responsible for getting that issue done.

Now, one of the most useful things you can do with issues is link them directly to your code. When you’re writing a commit message or describing a pull request, you can reference an issue by its number, and if you use the word “Closes” before that number, GitHub will automatically close the issue the moment that pull request is merged. This keeps your board clean without any manual housekeeping.

To make sure issues are reported consistently, NZRT uses issue templates. Templates are pre-filled forms that guide whoever is opening the issue through what information to include. Let’s look at the two main ones.

The bug report template asks you to fill in four things. First, a brief description of what the bug is. Second, the steps to reproduce it, written out as a numbered sequence so someone else can follow along and see the same problem. Third, what you expected to happen, and fourth, what actually happened instead. There’s also a section for environment details, things like which browser you’re using, which version of PHP is running, what your Laragon environment looks like, and which versions of WordPress or Dolibarr are involved. That context is often what makes the difference between a bug that gets fixed quickly and one that takes days to pin down.

The feature request template is a bit different. It starts with a description of the feature itself, then asks you to explain the use case, meaning why this feature is actually needed and what problem it solves. Finally, there’s an acceptance criteria section where you list out the specific conditions that need to be true for the feature to be considered complete. This is really valuable because it means everyone, developers, testers, and stakeholders, agrees upfront on what done actually looks like.

Now let’s talk about GitHub Projects. Where issues are the individual task cards, a Project is the board that holds them all. NZRT uses a board called the Dolibarr-Custom Board, and it’s organised into six columns that represent the life of a piece of work from start to finish. Something lands in Backlog when it’s identified but not yet started. It moves to Design when it’s in the design review stage. Once someone picks it up and starts building, it moves to Development. From there it goes to Testing, which is your QA phase. Once testing passes, it moves to Deployment, meaning it’s ready to go live. And finally, when it’s out in the world, it lands in Done.

Projects also support automation, so you can set rules that move cards between columns automatically based on events, like an issue being closed or a pull request being merged. That saves a lot of manual drag-and-drop work and keeps the board accurate without relying on everyone to remember to update it.

If you want to dig deeper into the GitHub ecosystem, this topic connects closely with the GitHub Overview, Pull Requests, and Collaboration Overview pages in the wiki. Those will give you more context on how issues and projects fit into the wider workflow of code review and team collaboration at NZRT.

In short, Issues give you a structured, consistent way to capture and track work, and Projects give you the visual layer to see how all that work is progressing. Together they make it a lot easier to stay on top of what’s happening across your repositories without things slipping through the cracks.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Integrations Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at GitHub Integrations Overview.

If you’ve ever wondered how GitHub connects to all the other tools in your development workflow, this episode is for you. GitHub doesn’t just sit on its own as a code repository. It’s designed to talk to a wide range of external services, and understanding how those connections work will help you make sense of how NZRT’s systems stay in sync.

Let’s start with the big picture. There are a few different ways GitHub can integrate with other tools, and each one works a little differently depending on what you need.

First up, you have webhooks. Think of a webhook as GitHub tapping another service on the shoulder the moment something happens. When a developer pushes code, opens a pull request, or publishes a release, GitHub sends a real-time notification to an external service of your choice. That could be Slack, Discord, or even a custom server you’ve built yourself. The notification arrives as an HTTP POST request, which means GitHub is essentially packaging up the event details and delivering them straight to wherever you’re listening.

Next, there are OAuth Apps. These are third-party applications that can access your GitHub data, but only after a user explicitly authorises them. You’ve probably seen this pattern before when a website asks you to sign in with GitHub and then lists what permissions it’s requesting. That’s OAuth in action.

Then you have GitHub Apps, which are a more modern and fine-grained option. Instead of acting on behalf of a user, a GitHub App operates with its own identity and can be granted very specific permissions over your repositories. This makes them more suitable for automation and backend tooling.

There’s also the GitHub Marketplace, which is an official catalogue of integrations covering things like continuous integration and deployment, security scanning, and project management. And finally, GitHub supports status checks, which let external services report back on whether a pull request is safe to merge. If your test suite fails, for example, the status check can actually block the merge until things are fixed.

Now let’s talk about what NZRT is actually using. There are six services in the current integration picture. Starting with the active ones, you have the WordPress GitHub Integration, which handles product synchronisation and deployment. Alongside that is the Dolibarr GitHub Integration, which keeps the ERP system in sync and also handles deployment. Slack is also active, receiving build notifications and alerts. And VS Code connects directly to GitHub, giving developers an IDE integration that lets them work with repositories without leaving their editor.

Two more services are planned but not yet active. Codecov will handle code coverage reporting, giving the team visibility into how much of the codebase is covered by tests. And DataDog will come in for performance monitoring once it’s set up.

Now let’s look at the types of events that GitHub sends out via webhooks, because understanding these helps you know what can trigger activity in those connected services.

There are six common event types NZRT works with. The first is a push event, which fires whenever new commits land on any branch. The second is a pull request event, and this one is quite broad. It triggers when a pull request is opened, when new commits are added to it, when it’s reopened after being closed, when it’s closed without merging, and when it’s actually merged. So there’s a lot of useful information flowing through that single event type.

The third event is a release event, which fires when a release is published. This is useful for triggering deployment pipelines or notifying the team that a new version is available. Fourth is the issues event, which covers when an issue is opened, closed, or labelled. Fifth is the issue comment event, which triggers whenever a comment is added to an issue. And sixth is the workflow run event, which fires when a GitHub Actions workflow finishes. This is particularly useful if you want to notify Slack, for example, whether your automated build passed or failed.

If you want to go deeper on any of the specific integrations mentioned today, there are related notes covering the WordPress GitHub Integration, the Dolibarr GitHub Integration, the VS Code and GitHub setup, and GitHub Actions. Each of those gives you a much more detailed look at how those individual connections are configured and maintained within the NZRT environment.

So to bring it all together, GitHub’s integration layer is what turns a code repository into a connected part of your broader development and operations ecosystem. Webhooks push events out in real time, OAuth and GitHub Apps control how external tools access your data, the Marketplace gives you off-the-shelf options, and status checks help enforce quality gates on your pull requests. NZRT is actively using several of these today and has more on the roadmap.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Gitignore Templates

Welcome to the NZRT Wiki Podcast. Today we’re looking at .gitignore Templates.

So, what is a .gitignore file? Put simply, it is a file you place in your Git repository that tells Git which files and folders to leave alone — to never track, never commit, and never push. At NZRT, every repository uses a .gitignore tailored to the language or framework being used, and today we are going to walk through all of them.

Let us start with WordPress. The WordPress .gitignore is one of the more detailed ones, because WordPress installations have a lot of moving parts you do not want in version control. It excludes the WordPress core folders and files — the admin panel, the includes directory, and the main PHP files — since those are installed separately. It also excludes your WordPress configuration files, including any environment files, because those contain sensitive local settings. User uploads are excluded too, since binary media files have no place in a code repository. Here is the interesting part: all plugins and all themes are excluded by default, except for the ones NZRT actually builds and maintains — the custom sync plugin and the custom NZRT theme are specifically allowed back in. Third-party packages, IDE configuration folders, and common operating system files like the Mac finder metadata file and the Windows thumbnail database file are also excluded.

Next up is PHP. The PHP template is simpler. It excludes your vendor dependencies folder, environment files, IDE settings, test coverage reports, log files, temporary files, and your build output folders. If you are building a standalone PHP project or a Dolibarr module, this is your starting point.

For Node.js and JavaScript projects, the template covers the node modules folder — which you never want in Git because it can contain thousands of files — along with lock files for npm, Yarn, and pnpm. Build output directories, test coverage, log files, caches, and framework-specific folders like the Next.js output directory are all excluded.

For Python, the list is longer because Python generates quite a few artefacts. Virtual environment folders are excluded — these are your isolated Python installations and should never be committed. Compiled Python files and bytecode caches are excluded. Distribution and packaging folders are excluded. Test tooling outputs, IDE settings, environment files, log files, and local database files like SQLite databases round out the list.

The Docker template is short and focused. It excludes Docker secret files, any override files for Docker Compose — which are typically used for local development overrides — and log files.

There are also two cross-language sections. One covers compiled binaries and executables: things like Windows DLL files, Mac dynamic libraries, Linux shared object files, and compiled object files. These are outputs of a build process and should never live in source control. The other covers database exports: SQL dump files, compressed SQL files, and SQLite database files. Importantly, there is an exception carved out — SQL files inside a migrations folder are allowed, because those are intentional schema version files you do want to track.

The secrets and credentials section is one of the most important. It excludes entire folders like a secrets directory or private directory, SSH key files and certificate files, credential JSON files, token files, and all environment configuration files. There is one deliberate exception: files named with the word example are allowed through, so you can commit an example key file as a template for other developers to follow.

All of these patterns are combined into the NZRT Standard .gitignore, which is the catch-all template used across repositories. It brings together environment and secrets exclusions, dependency folders for both PHP and Node, build and distribution output, logs, test coverage, IDE files, operating system noise, WordPress-specific paths, Python virtual environments, and temporary files. If you are starting a new NZRT repository and you are not sure which template to use, start here.

Finally, let us talk about verifying and maintaining your .gitignore. There are a few commands worth knowing. You can ask Git to explain exactly why a particular file is being ignored, and it will tell you which rule in which .gitignore file is responsible. You can also do a dry run that lists everything Git would remove if you cleaned untracked files — useful for checking your rules are working without actually deleting anything. If you need to force-add a file that is being ignored, you can override the ignore rule for that one file. And if a file is already being ignored but it is already tracked by Git — meaning it was committed before the rule existed — you can remove it from Git’s tracking without deleting it from disk.

On that last point: if you add new rules to an existing repository, files that were already committed will not automatically be excluded. You need to remove everything from Git’s cache, re-add all files so Git picks up the new rules, and then commit that change. A commit message like “chore: update .gitignore” is the conventional way to label that housekeeping commit.

That covers the full set of .gitignore templates NZRT uses across its repositories, why each section exists, and how to verify things are working correctly.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Pull Requests

Welcome to the NZRT Wiki Podcast. Today we’re looking at Pull Requests.

If you’ve ever worked on a codebase with other developers, you’ve probably come across pull requests. But what exactly are they, and how do they work at NZRT? Let’s walk through everything you need to know.

A pull request — or PR for short — is the main way code changes get reviewed and merged into a shared codebase on GitHub. When you’ve been working on a feature or a fix in your own branch, a pull request is how you say “hey, I’ve got some changes here — can someone check them over before we bring them in?” It creates a structured, documented process around that conversation.

When you open a PR, a few things happen automatically. First, GitHub shows a visual diff — that’s a line-by-line comparison of everything you’ve changed. Anyone reviewing can see exactly what was added, removed, or modified. Second, all the commits you made on your feature branch are listed, so reviewers can follow the history of your work. Third, GitHub Actions kicks off — those are automated workflows that run checks like linting, testing, and security scans. These have to pass before the PR can be merged.

Then comes the human part. One or two of your peers — depending on how the repository is set up — will review your code. They can leave comments, ask questions, or request changes. If changes are needed, you push additional commits to address the feedback, and those appear in the PR automatically. Once a reviewer is satisfied, they approve, and a maintainer can merge it in.

Let’s talk about how NZRT specifically handles this. It follows a ten-step flow. You start by creating a feature branch with a descriptive name. You make your changes and commit them with clear messages. Then you push that branch up to GitHub and open the pull request with a title and description. You wait for GitHub Actions to pass — that covers linting, testing, and a security scan. Then you request a review from one or two developers. You address any feedback, push more commits if needed, get an approval, and finally a maintainer merges the PR — usually using a squash merge into the develop branch. After that, the branch is automatically deleted, which keeps things tidy.

Now let’s cover PR title format. The title follows a short, structured pattern. You start with a type — for example, feat for a new feature, fix for a bug fix, docs for documentation, style for formatting changes, refactor for restructuring code, perf for performance improvements, test for anything test-related, or chore for maintenance tasks. After the type, you put the scope in parentheses — that’s the area of the codebase you’re touching, such as dolibarr, wp-sync, or scripts. Then a colon, followed by a brief description. So as a spoken example, a PR adding a product comparison widget to the WordPress sync area would read something like: feat, wp-sync scope, add product comparison widget.

PR descriptions follow a standard template with four sections. The first is a plain description of what the PR does. The second links to a related issue — if the work closes a tracked issue, you reference it there. The third section covers testing — you explain how someone can verify the changes work correctly. And the fourth is a checklist: does the code follow the style guide, were tests added or updated, was documentation updated, and are there any breaking changes? Having this template means every PR carries the same core information, making review faster and more consistent.

One more thing worth knowing: NZRT supports draft PRs. If you’re still working on something and not ready for review, you can open it as a draft. That signals to the team it’s a work in progress, so nobody jumps in to review before you’re ready.

There are also three ways a PR can actually be merged. A merge commit preserves all your individual commits as-is. A squash merge combines everything into one clean commit — this is the most common approach at NZRT. And a rebase replays your commits on top of the target branch. Each repository can be configured to support one or more of these strategies depending on what works best for that codebase.

Pull requests aren’t just a gate before merging — they’re a collaboration tool. They create a record of why changes were made, who approved them, and what feedback was exchanged. Over time, that history becomes genuinely valuable for anyone working in the same codebase.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Repositories

Welcome to the NZRT Wiki Podcast. Today we’re looking at 📦 Repositories.

So let’s start with the basics. A GitHub repository is essentially a project container. Think of it as a single organised folder that holds everything related to a project — your code, your documentation, your issue tracker, and the full history of every change ever made. Whether you’re building a web app, managing config files, or storing deployment scripts, the repository is where it all lives.

Now, repositories come with a lot of settings, and understanding those settings is really what separates a well-managed project from a chaotic one. Let’s walk through the key concepts you need to know.

First up is visibility. A repository is either public or private. Public means anyone on the internet can view it — that’s typical for open source projects. Private means only people you’ve explicitly invited can see it. At NZRT, most repositories are private, which makes sense for business tooling and client work.

Next is the default branch. This is the branch that GitHub treats as the source of truth for the project. You’ll often see it called main or develop. It’s also usually the branch that gets deployed, so it matters a lot. Whatever goes into the default branch is what ends up in production.

Then there’s branch protection. This is a set of rules you apply to important branches — usually the default branch — to stop people from accidentally pushing directly to it. You might require that all changes come in through a pull request, that at least one other person reviews and approves that pull request, and that any automated checks or tests pass before the merge goes through. Branch protection is one of those things that feels like overhead until the day it saves you from a very bad situation.

Moving on — collaborators and teams. Collaborators are individual users you’ve given explicit access to a repository. There are five permission levels to know about. Pull access means someone can read and clone the repo. Triage lets them manage issues and pull requests. Write means they can push code. Maintain gives broader project management control. And Admin is full control — settings, access, the works. Teams work similarly, but instead of assigning permissions to individuals one by one, you group users into a team and assign permissions to the whole group. Much easier to manage at scale.

You should also know about the README file. When someone lands on your repository’s home page on GitHub, the first thing they see is the README. It’s your project’s front door — it should explain what the project is, how to set it up, and how to use it. A good README saves everyone time.

And finally, the license. If your repository is public and you want to let others use your code, you need a license. The most common ones at NZRT are MIT, Apache, and GPL. Each has different rules about how people can use, modify, and distribute your work. Without a license, legally speaking, no one has permission to use your code at all.

Now let’s look at a real example from NZRT. There’s a repository called Dolibarr-Custom. It’s private, which makes sense because it holds ERP customisations and modules — internal business logic you wouldn’t want exposed publicly. The default branch is main, and the main branch has protection rules in place: you need at least one review before anything merges, and all status checks must pass. Two collaborators are set up — fin with Maintain access, and xc with Admin access.

If you’re ever setting up a new repository at NZRT, there’s a handy checklist to work through. You want to confirm the visibility is correct, the default branch is set, and branch protection is enabled. Make sure collaborators are invited with the right permission level — not too much, not too little. Set up any webhooks you need for notifications. Add secrets for deployments so credentials never end up in the codebase. Write a README, add a license if it’s open source, and configure a gitignore file so the right files get excluded from version control. And if the project needs a public-facing site, consider whether GitHub Pages should be enabled.

If you want to go deeper, the related wiki notes on GitHub Overview, Branch Protection, and Secrets and Tokens are good next reads.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Vs Code Github Integration

Welcome to the NZRT Wiki Podcast. Today we’re looking at VS Code and GitHub Integration.

If you work at NZRT as a developer, VS Code is your home base. And when you pair it with GitHub, you get a setup that handles everything from writing code to reviewing pull requests to spinning up full cloud environments. Let’s walk through how it all fits together.

First, the key concepts. VS Code has a built-in Source Control panel. Think of it as a visual git client sitting right inside your editor. It shows you what files have changed, lets you compare versions side by side, and gives you a full commit history. Alongside that, you authenticate with your GitHub account directly inside VS Code, so all your repository access flows through securely without juggling tokens manually.

A handful of extensions make this really powerful. The GitHub Pull Requests and Issues extension lets you review, comment on, and approve pull requests without ever leaving your editor. GitHub Copilot gives you AI-powered code suggestions as you type, though that one requires a subscription. GitLens supercharges your git history view, showing you who changed what and when, right inline in your code. Then there are practical ones like Markdown All in One for writing notes and docs with live preview, PHP Intelephense and the PHP Extension Pack for server-side work, and the Remote SSH extension, which we’ll come back to in a moment.

To install these, you run a command in your terminal starting with the word “code” followed by “install-extension” and the extension identifier. You do that once for each extension. The wiki shows eight of them lined up in sequence.

Now let’s talk about the day-to-day workflow when you’re working on a feature. You start by cloning a repository. In VS Code, you press Control, Shift, and P to open the command palette, search for Git Clone, and paste in the repository URL. Once it’s open, you create a feature branch from the integrated terminal. As you make changes, the modified files are highlighted automatically in the file explorer. When you’re ready to commit, you open the Source Control panel, click the plus icon next to each file to stage it, type your commit message, and press Control and Enter. Then you click Publish Branch to push it up to GitHub. Back in the command palette, you search for GitHub: Create Pull Request, pick your base branch, add a title and description, and you’re done.

Reviewing pull requests is just as smooth. You open the command palette, search for the GitHub PR option to fetch a pull request, find the one you want by title or number, and it opens in diff view right inside the editor. You can click any line to leave a comment, start a thread, request changes, or approve. Everything you’d normally do on the GitHub website, you’re doing without leaving VS Code.

If you want a full cloud-based development environment, GitHub Codespaces is worth knowing about. You go to the repository on github.com, click the Code dropdown, and choose to create a codespace on the main branch. VS Code opens connected to a cloud machine that already has PHP, Composer, git, npm, and everything else ready to go.

For NZRT’s Dolibarr custom repository, there’s a configuration file in the devcontainer folder that tells GitHub exactly what environment to set up. It specifies a PHP 8.1 base image, adds Docker and Node version 18 as features, forwards ports for the web server and database so they’re accessible from your browser, runs Composer install and npm install automatically once the environment is created, and pre-installs the Pull Requests, PHP Intelephense, and GitLens extensions. You don’t configure any of that by hand. It just happens when the Codespace starts.

The last piece is SSH Remote development, which lets you connect VS Code directly to your local Laragon dev environment over your network. Your SSH settings point to the machine’s local IP address, specify the username, port 22, and the path to your SSH key file. Then in the command palette, you search for Remote-SSH: Connect to Host, select your Laragon Dev entry, and VS Code connects. From that point on, you’re editing files on the remote machine with the full IDE experience. Syntax highlighting, autocomplete, debugging, all of it works exactly as if the files were sitting on your own drive.

That covers the full picture: local development, cloud Codespaces, remote SSH connections, pull request reviews inside the editor, and the extension stack that ties it all together. Whether you’re committing a quick fix or spinning up an entire cloud environment, VS Code and GitHub have you covered at every step.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Helm Reference

Welcome to the NZRT Wiki Podcast. Today we’re looking at Helm Reference.

So, what is Helm? Put simply, Helm is the package manager for Kubernetes. If you’ve used something like npm for Node or pip for Python, Helm plays a similar role — but for Kubernetes applications. It takes all the Kubernetes manifest files your application needs and bundles them into a single versioned, configurable unit called a chart. That makes it much easier to deploy, upgrade, and manage complex applications on your cluster.

Before we jump into commands, let’s cover four core concepts you’ll hear come up again and again. First, there’s the chart itself — that’s the package containing all your Kubernetes manifest templates. Second is a release, which is what you get when you actually run a chart in a cluster — it’s the live, running instance. Third is a repository, which is just a collection of published charts you can browse and pull from, kind of like a package registry. And fourth are values — these are the configuration overrides you supply to customise how a chart behaves when it’s deployed. Those four — chart, release, repository, values — are the building blocks of everything Helm does.

Now let’s walk through the common commands you’ll use day to day. The first thing you typically do is add a repository and update it. Think of this as registering a source so Helm knows where to find charts. Once you’ve added a repo, you can search it to find specific charts by name.

When you’re ready to deploy something, you use the install command. You give your release a name, point it at the chart you want from your repo, tell it which namespace to deploy into, and optionally tell Helm to create that namespace if it doesn’t already exist. You can also pass in a values file at this point to customise the deployment.

Once something is running, you’ll eventually want to update it — that’s where the upgrade command comes in. The syntax is very similar to install: you reference the release name, the chart, the namespace, and your values file. Helm handles rolling out the changes.

To see what’s currently deployed, you use the list command. You can scope it to a specific namespace, or ask Helm to show releases across all namespaces at once.

When you need to remove something, uninstall does the job — just give it the release name and namespace and Helm tears it all down cleanly.

One of the most useful features Helm offers is rollback. If an upgrade goes wrong, you can roll back to a previous revision — just reference the release name and the revision number you want to return to. To find out what revision numbers are available, the history command shows you a full list of past deployments for any given release.

Finally, there’s the template command. This is a dry-run tool that renders all the Kubernetes manifests Helm would generate — but doesn’t actually apply them to the cluster. It’s incredibly useful for debugging or reviewing exactly what Helm is going to do before you commit to it.

Now let’s talk about the values file, because this is where a lot of the real configuration lives. A typical values file is written in YAML and covers several key areas. You set a replica count — for example, two — to control how many instances of your application run. You define your image settings: the repository to pull from, the specific tag or version you want, and the pull policy, such as only pulling if the image isn’t already present locally. You configure your service — specifying the type, like ClusterIP for internal-only access, and the port it listens on. And you set resource constraints, which is important for cluster stability. On the requests side you declare the minimum CPU and memory your container needs to start. On the limits side you set the ceiling — the maximum it’s allowed to consume. CPU is expressed in millicores, so one hundred m is a tenth of a CPU core, and memory is in mebibytes.

Together, these values give you fine-grained control over your deployment without ever touching the chart templates themselves. You just override what you need, and Helm does the rest.

If you want to go deeper on the cluster side of things, check out the related wiki pages on kubectl and the NZRT Kubernetes Setup — both will give you context that pairs well with what you’ve just heard.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Deployments

Welcome to the NZRT Wiki Podcast. Today we’re looking at Deployments.

So, what exactly is a Deployment in Kubernetes? Think of it as a manager for your running application. A Deployment makes sure a set number of copies of your app — called pod replicas — are up and running at all times. It also handles the tricky business of updating your app and rolling back if something goes wrong.

Let’s talk about what makes Deployments so useful. First, they work declaratively. That means you describe the state you want — say, two copies of your app running the latest image — and Kubernetes figures out how to get there. You don’t have to script every step yourself.

Second, Deployments support rolling updates. When you push a new version of your app, Kubernetes starts new pods before it stops the old ones. That way, your app stays available to users throughout the whole update process, with no downtime.

Third, if something goes wrong with an update, you can roll back. There’s a single command that reverts your Deployment to its previous state, and Kubernetes handles the rest.

And fourth, scaling. If you need more capacity, you can simply adjust the number of replicas — either directly in your configuration, or automatically using something called a Horizontal Pod Autoscaler.

Now let’s look at what a real Deployment definition looks like. The example in the wiki defines a Deployment for a WordPress application running in the production environment. Here’s what it sets up in plain terms.

It tells Kubernetes this is a Deployment object, gives it the name wordpress, and places it in the nzrt-prod namespace. It then says: keep two replicas running at all times. It uses a label — in this case, app equals wordpress — so Kubernetes knows which pods belong to this Deployment.

Inside the pod template, it defines one container, also called wordpress, running version six point five of the official WordPress image. That container listens on port 80, and it’s given an environment variable pointing it to the database — specifically, a service called mysql-service.

The definition also sets resource boundaries. Each pod requests a quarter of a CPU core and 256 megabytes of memory to start. But if it needs more headroom, it can scale up to one full CPU core and 512 megabytes of memory. This stops any single pod from overwhelming your cluster.

Finally, the update strategy is set to rolling update. The configuration allows one pod to be temporarily unavailable during a rollout, and one extra pod to come up above your normal replica count. So during an update, you might briefly have three pods running — two old, one new — before the old ones are taken down.

Now let’s go through the day-to-day commands you’ll use to manage Deployments.

To see all your Deployments in the production namespace, you run a get-deployments command scoped to that namespace. To get detailed information about the WordPress Deployment specifically, you use describe-deployment and give it the name.

Scaling is simple — there’s a scale-deployment command where you specify the new number of replicas. So if you want three copies instead of two, you set replicas to three and you’re done.

Updating the container image is done with a set-image command. You name the Deployment, name the container inside it, and provide the new image version — for example, switching from WordPress six point five to six point six.

To check on a rollout while it’s in progress, there’s a rollout-status command that gives you live feedback on how the update is going.

If something goes wrong, rollout-undo is your safety net. It reverts the Deployment to whatever it was running before, immediately.

And finally, rollout-history shows you the list of previous versions on record, so you know exactly what you can roll back to.

If you want to explore further, the wiki also points to related topics covering Pods and Containers, how Deployments relate to ReplicaSets under the hood, and how Services connect your running Deployment to the outside world.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Deployments Replicasets

Welcome to the NZRT Wiki Podcast. Today we’re looking at Deployments & ReplicaSets.

Let’s start with the big picture. In Kubernetes, a Deployment is the object you use to manage your applications. But it doesn’t directly manage pods — it does so through something called a ReplicaSet. Think of it as a chain of command. Your Deployment sits at the top, and underneath it creates a ReplicaSet. That ReplicaSet is what actually ensures the right number of pod replicas are running at any given time.

When you update a Deployment, Kubernetes creates a new ReplicaSet to represent the new version of your application. The structure looks something like this: at the top you have your Deployment. Below that, you have your current ReplicaSet, which holds all your running pods. And separately, you also have your previous ReplicaSet — still there, not actively running pods, but kept around so that if something goes wrong, you can roll back to it.

This is an important point. You should never manage ReplicaSets directly. Always go through the Deployment. Kubernetes keeps those old ReplicaSets around for rollback history, and how many are kept is controlled by a setting called revision history limit. If you start manually changing ReplicaSets, you risk breaking that rollback chain.

Now let’s talk about how Deployments handle updates. There are two strategies available to you.

The first is called Rolling Update, and it’s the default. With a rolling update, Kubernetes gradually replaces your old pods with new ones. It doesn’t take everything down at once — it brings new pods up while slowly removing the old ones, so your application stays available throughout the process. Two parameters fine-tune this behaviour. The first is max unavailable, which sets the maximum number of pods that can be offline during the update — by default, twenty-five percent of your total pod count. The second is max surge, which controls how many extra pods can be created above your desired count during the update — also twenty-five percent by default. Together these give you a smooth, controlled rollout.

The second strategy is called Recreate. This one is much more direct. Kubernetes terminates all your existing pods first, and only then starts creating the new ones. That means there will be a period of downtime. You’d use this if your application can’t safely run two versions simultaneously — for example, if it has strict database migration requirements that would break under a mixed-version environment.

Now let’s cover scaling. You have two options: manual and automatic.

For manual scaling, you issue a command that tells Kubernetes to set the replica count for your deployment to a specific number. As a concrete example, you could scale your wordpress deployment in the nzrt-prod namespace to three replicas with a single terminal command.

For automatic scaling, you use something called a Horizontal Pod Autoscaler, or HPA. This watches a metric — typically CPU usage — and adds or removes pods automatically to keep that metric within a target range. You can set this up from the command line by specifying the deployment you want to target, a minimum and maximum replica count, and the CPU utilisation percentage that should trigger scaling. For the wordpress deployment, for example, you might say: keep at least two replicas, scale up to a maximum of five, and aim to stay at or below seventy percent CPU.

You can also define the HPA as a configuration manifest — a structured file that describes the same settings. That file would name the autoscaler, point it at your wordpress deployment, set the replica boundaries, and define the CPU metric target. Storing your HPA as a file is useful because you can check it into version control and apply it consistently across environments.

To check what your autoscaler is currently doing, you can run a command that lists all HPAs in a given namespace. That gives you a live view of current versus desired replica counts and what the resource utilisation looks like right now.

One final thing worth noting — Deployments aren’t the right tool for every workload. If you’re running stateful applications like databases, you’ll want to look at StatefulSets instead. StatefulSets give each pod a stable identity and persistent storage, which is something that ReplicaSet-backed Deployments simply don’t provide.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Cluster Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Cluster Overview.

This episode covers the day-to-day management of the NZRT Kubernetes cluster — how you access it, how you check on its health, and how you handle common maintenance tasks. Whether you’re new to the cluster or just need a quick refresher, this should give you a solid grounding.

Let’s start with cluster access. Before you can do anything useful, you need to know which cluster you’re talking to. Kubernetes uses something called contexts to keep track of this. Think of a context as a saved profile — it knows which cluster, which user, and which namespace you want to work with.

To see all the contexts you have available, you run a command that lists them out for you. Once you know the name of the context you want, you can switch to it with a single command — you just tell kubectl, which is the main Kubernetes command-line tool, to use that context by name. If you want to double-check which context is currently active, there’s a command for that too — it simply prints the name of the one you’re on right now.

One more handy access tip: you can set a default namespace so you don’t have to keep specifying it on every command. For NZRT’s setup, the main production namespace is called nzrt-prod, and you can lock your current context to that namespace with a config command. After that, any kubectl command you run will automatically target nzrt-prod unless you say otherwise.

Now let’s talk about health checks, because knowing whether your cluster is actually healthy is one of the most important things you’ll do as an operator.

First, you’ll want to check on your nodes. Nodes are the machines that actually run your workloads — think of them as the physical or virtual servers underneath Kubernetes. A quick command gives you a summary list of all nodes and whether they’re in a ready state. If you need more detail on a specific node — things like resource capacity, conditions, and recent events — you can describe that node by name to get a full breakdown.

Next, you can check the health of the core Kubernetes components themselves, like the scheduler and the controller manager. There’s a command that queries the component statuses and gives you a simple healthy or unhealthy result for each one.

If you want a broad picture of everything running across all namespaces at once, there’s a command that pulls all resources cluster-wide — pods, services, deployments, the works. This is great for a quick sanity check.

And when something seems off, events are your best friend. Kubernetes logs events whenever something notable happens — a pod fails to schedule, a container crashes, a resource limit is hit. You can pull the events from nzrt-prod sorted by the most recent timestamp, which means the freshest and most relevant information floats to the top.

Moving on to cluster info. There are three quick commands worth knowing here. The first gives you a summary of where the key cluster endpoints are running — your API server, your DNS service. The second tells you the version of both the kubectl client and the server-side Kubernetes API, which matters a lot when you’re thinking about upgrades or compatibility. The third lists all the API resource types your cluster supports — handy when you’re trying to figure out what kinds of objects you can create or query.

Now let’s go through the common management tasks. There are five main ones you’ll reach for regularly.

First is draining a node. When you need to take a node offline for maintenance — maybe to patch the operating system or swap out hardware — you drain it first. Draining safely evicts all the pods running on that node and marks it as unschedulable, so nothing new lands on it while you’re working. You use a flag to tell Kubernetes to ignore daemonsets, which are special system pods that run on every node and don’t need to be moved.

Second is uncordoning a node. Once your maintenance is done and the node is back in service, you uncordon it. This removes the unschedulable mark and lets the scheduler start placing pods on it again.

Third is cordoning a node. This is like a softer version of draining — it marks the node unschedulable so no new pods get assigned to it, but it doesn’t touch the pods that are already running there. Useful if you want to quietly wind down a node over time.

Fourth and fifth are resource monitoring commands. You can check the live CPU and memory usage of pods in nzrt-prod, or check the same metrics at the node level across the whole cluster. These are invaluable for spotting which workloads are running hot or which nodes are under pressure before things become a problem.

That covers the essentials of your cluster day-to-day — getting in, checking the health, pulling the right info, and handling maintenance safely. If you want to go deeper, the related wiki pages on Nodes, the kubectl Reference, and the NZRT Kubernetes Architecture are great next steps.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Ingress

Welcome to the NZRT Wiki Podcast. Today we’re looking at Ingress.

If you’ve been working with Kubernetes, you’ve probably run into the question of how external traffic actually gets into your cluster. That’s where Ingress comes in. At its core, Ingress is a Kubernetes resource that routes incoming HTTP and HTTPS traffic to the right internal services, based on things like the hostname in the request or the URL path. One important thing to know upfront is that Ingress doesn’t work on its own. You need something called an Ingress Controller running in your cluster, and at NZRT we use the nginx-ingress controller for that.

So why use Ingress at all? To understand that, it helps to compare it with the alternative, which is a LoadBalancer Service. If you compare the two side by side, you see some important differences across five areas. First, protocol support. A LoadBalancer Service works at the TCP and UDP level, meaning it can handle almost any kind of traffic. Ingress, on the other hand, is HTTP and HTTPS only. Second, SSL termination. A LoadBalancer Service doesn’t handle SSL for you, but Ingress does. Third and fourth, routing. A LoadBalancer Service has no concept of path-based or host-based routing, meaning it just forwards everything. Ingress gives you both, so you can send traffic to different services depending on the URL path or the hostname. And fifth, cost. This is a big practical one. With LoadBalancer Services you need one load balancer per service, which adds up quickly in cloud environments. With Ingress, you only need one load balancer for everything, and Ingress handles the routing internally. That’s a significant saving.

Now let’s look at what an actual Ingress configuration looks like at NZRT. The manifest defines an Ingress resource called nzrt-ingress, sitting in the nzrt-prod namespace. It includes a couple of annotations, which are basically extra instructions for the nginx controller. One tells nginx to rewrite the request path to a forward slash, and the other tells cert-manager, which is the tool we use for SSL certificates, to use our production Let’s Encrypt issuer when generating a certificate.

The spec section is where the real routing logic lives. It starts by declaring that this Ingress uses the nginx ingress class. Then it sets up TLS, specifying that the hostname app dot nzrtnetwork dot com should be secured, and that the certificate should be stored in a Kubernetes secret called nzrt-tls. Finally, there’s a rules section. It says that any request arriving at app dot nzrtnetwork dot com, regardless of the path, should be forwarded to a service called wordpress-service on port 80. So in plain terms, you hit the NZRT app URL in your browser, Ingress intercepts that, terminates the SSL, and hands the request off to the WordPress service inside the cluster.

That brings us to cert-manager, which handles the TLS certificate side of things. You install cert-manager by applying a single manifest file from the cert-manager releases page. Once it’s running, it watches for Ingress resources that reference a cluster issuer, and it automatically requests and renews certificates from Let’s Encrypt on your behalf. You don’t have to touch the certificate manually.

To check on the status of a certificate, you run a command that lists all certificates in the nzrt-prod namespace. If you want more detail on a specific one, you describe the nzrt-tls certificate in that same namespace, and Kubernetes gives you a full breakdown including whether the certificate was issued successfully, when it expires, and any events or errors that occurred during the issuance process. If something goes wrong with your SSL setup, that describe output is usually the first place you look.

To tie this all together, the flow works like this. A user visits app dot nzrtnetwork dot com. DNS resolves that to the single load balancer sitting in front of your cluster. The load balancer passes the request to your nginx Ingress controller. The controller checks its rules, sees that the hostname matches, terminates the TLS using the certificate stored in the nzrt-tls secret, and forwards the plain HTTP request to the wordpress-service inside the cluster. All of that happens transparently, and you only needed one load balancer to make it work across however many services you want to add in future.

If you want to go deeper on any of this, the related topics in the wiki cover Networking Overview, Services, and SSL and DNS, which will give you the full picture of how all these pieces connect.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Kagent Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Kagent Overview.

So, what exactly is Kagent? At its core, Kagent is a Kubernetes-native AI agent framework. It was contributed to the Cloud Native Computing Foundation — the CNCF — by Solo.io, and it made its debut at KubeCon EU in 2025. The idea is straightforward but powerful: you define AI agents in YAML, deploy them as Kubernetes workloads, and those agents use a large language model — like Claude or one of several other options — to automate cluster operations, troubleshoot issues, and handle DevOps workflows directly inside your cluster.

What makes this interesting is that your agents aren’t some external service bolted onto your cluster. They run as standard Kubernetes Pods. That means they’re fully observable, they can be restarted, they can be scaled, and they fit right into your existing operational patterns. You get multi-LLM provider support out of the box — Anthropic, OpenAI, Azure OpenAI, Google Vertex AI, and even Ollama for local models. You also get a built-in tool ecosystem using the MCP protocol for Kubernetes operations, plus a web UI, a command-line tool called kagent, and of course the YAML-based interface you’d expect in any Kubernetes-native project.

Now let’s talk about how the architecture hangs together. Picture your Kubernetes cluster with four main moving parts working in concert.

First, you have the Controller. This is a Go deployment that sits and watches for changes to the custom resource definitions — the CRDs — that Kagent introduces. When you create or update an agent definition, the Controller is what responds and creates the underlying Pods and Services.

Second, there’s the ModelConfig. This is a CRD that defines your LLM provider — so, say, Anthropic — along with the specific model name you want to use and a reference to a Kubernetes Secret that holds your API key. This keeps your credentials managed the Kubernetes way, which is exactly what you’d want.

Third, you have the MCPServer CRD. This is what exposes the tools your agent can actually call. Think of things like kubectl operations, Helm chart management, Argo workflows, and so on. These tools follow the MCP protocol, and they’re what give your agent its ability to actually do things in the cluster rather than just talk about them.

And fourth, bringing it all together, you have the Agent CRD itself. This combines your ModelConfig, your MCPServer tool definitions, and a system prompt into a single running Pod. That’s your agent — live, running, ready to take requests.

So how does it actually work when you send a message? The flow goes like this. You send a message to the agent — through the web UI, the CLI, or the API. The agent takes your message along with its configured system prompt and sends it to Claude, or whichever LLM you’ve set up. Claude then looks at the available tools from the MCPServer and picks the right ones to use. Those tools execute against the Kubernetes API — so actions like getting pod status, reading logs, applying a manifest, or checking service connectivity happen in your actual cluster. Claude then takes those results, synthesises them, and sends back a coherent response. And throughout all of this, every action is traced via OpenTelemetry, so you have full observability into what your agent did and why.

Now, thinking about how NZRT would actually use this — there are four key patterns worth knowing about.

The first is an SRE troubleshooter agent. This is a read-only diagnostics setup, using tools to get resources, read pod logs, pull events, and describe resources. Safe, observational, great for first-line investigation.

The second is a deployment helper. This one steps it up a notch — it can generate manifests and apply them, patch resources, and check service connectivity. Your agent becomes an active participant in your deployment process.

The third is a Helm operator. Using the Helm MCP server, your agent can manage charts — installing, upgrading, rolling back — all driven by natural language instructions.

And the fourth is a monitoring analyst. Hook your agent up to the Prometheus MCP server and it can run metrics queries, surface anomalies, and give you plain-language summaries of what your cluster is doing.

If you want to go deeper from here, there are a few related wiki pages to check out. Kagent Setup covers the step-by-step install and configuration for NZRT specifically. Kagent RBAC and Security goes into service accounts, roles, and how to restrict what tools an agent is allowed to use — important for any production setup. And Kagent Agents and Tools gives you the full MCP tool reference along with example agent CRD definitions you can adapt.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Logging

Welcome to the NZRT Wiki Podcast. Today we’re looking at Logging.

Let’s start with the big picture. In Kubernetes, when your containers run, they write output to two standard streams — standard output and standard error. That output doesn’t just disappear. The kubelet, which is the agent running on each node, captures it and writes it to the node’s file system. From there, a log shipper picks it up and sends it on to a centralised log storage system called Loki. And then Grafana sits on top of Loki so you can actually query and visualise everything. That’s the full chain — container output, to kubelet, to log shipper, to Loki, to Grafana.

Now let’s talk about how you access logs directly using kubectl. This is your first line of investigation when something looks wrong.

The most basic command gets the current logs from a pod. You specify the pod name and tell it which namespace to look in — in NZRT’s case that’s nzrt-prod. This gives you a snapshot of what that pod has written to its output streams up to this point.

If you want to watch logs as they come in live, you can follow them. This keeps the stream open and prints new lines as they arrive — useful when you’re actively watching a deployment or waiting for a specific event to show up.

Now, what if a pod has crashed and restarted? That’s where the previous flag comes in. When a container crashes and Kubernetes restarts it, the logs from the crashed container are gone from the current session — but you can still retrieve them by asking for the previous instance. This is often the most useful thing to look at when you’re debugging a crash.

If you’re working with a pod that runs more than one container inside it, you need to also specify which container you want logs from. You give both the pod name and the container name, and kubectl knows exactly where to look.

And finally, if you don’t know the exact pod name but you know the application label, you can ask for logs from all pods matching that label at once. For example, you could ask for all pods labelled as WordPress in nzrt-prod, and you’ll get output from every one of them in one go.

Now let’s go deeper into the log architecture — the pipeline that makes centralised logging work.

First, your pod writes to standard output or standard error. The kubelet on that node picks it up and writes it to a path on the node’s local file system, under a directory called var log pods.

From there, a DaemonSet runs on every node in the cluster. A DaemonSet means one copy of a pod runs on every single node automatically. At NZRT, that’s either Promtail or Grafana Alloy — both are log shippers that read the files the kubelet wrote and forward them on.

The destination is Loki, a log storage system designed specifically for Kubernetes environments. Loki stores your logs and indexes them by labels like namespace and app name, rather than indexing the full text of every log line. This keeps storage lean and efficient.

Finally, Grafana sits in front of Loki. This is where you go to actually query your logs, build dashboards, or set up alerts based on log content.

So the flow is: pod, to kubelet, to Promtail or Alloy, to Loki, to Grafana. Each step has a clear role.

Once you’re in Grafana, you query Loki using a language called LogQL. Let me walk you through a few examples of what those queries do in plain terms.

The most basic query selects all logs from a specific namespace and application — for example, show me everything from the nzrt-prod namespace where the app is WordPress.

You can filter further by looking for specific text in the log lines. For example, you might ask for all logs in the nzrt-prod namespace that contain the word error somewhere in the line. That’s a simple text filter and a quick way to spot problems.

If your logs are structured as JSON, which is common with modern applications, you can parse them and then filter on specific fields. So you could ask for logs where the parsed level field equals error — which is more precise than just searching for the word error anywhere in the raw text.

And if you want to understand log volume rather than read individual lines, you can use a rate query. This tells you how many log lines are being produced per second or per minute, averaged over a time window like five minutes. This is useful for spotting sudden spikes in log output, which often signals something going wrong upstream.

That’s the core of Kubernetes logging at NZRT — kubectl for direct pod access, a DaemonSet-based pipeline to ship logs into Loki, and Grafana with LogQL for querying and visualising everything. If you want to go further, check out the Monitoring Overview and the kubectl Reference pages in the wiki.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Kubernetes Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Kubernetes Overview.

So, what exactly is Kubernetes? You might have seen it written as K8s, which is just a shorthand where the eight stands for the eight letters between the K and the s. At its core, Kubernetes is an open-source platform for container orchestration. That’s a fancy way of saying it automates the deployment, scaling, and management of containerised applications. Rather than you manually keeping track of which containers are running where, Kubernetes handles all of that on your behalf.

The way it works is by grouping containers into units called Pods, and then managing those pods across a cluster of machines called Nodes. Let’s talk about what Kubernetes actually does for you. First, it handles scheduling, which means it decides which pod gets placed on which node based on available resources. Second, it takes care of self-healing. If a container crashes or a node stops responding, Kubernetes automatically restarts or replaces it without you having to intervene. Third, it supports scaling. If your application suddenly needs to handle more traffic, Kubernetes can automatically spin up more instances based on things like CPU or memory usage. Fourth, it supports rolling updates, meaning you can push a new version of your application with zero downtime. Fifth, it provides service discovery, using internal DNS and load balancing so your services can find each other. And sixth, it manages secrets, storing sensitive information like passwords and API keys in encrypted storage.

Now let’s talk about how Kubernetes is actually structured. The architecture has two main sides: the Control Plane and the Worker Nodes.

The Control Plane is the brain of the operation. It contains four key components. The first is the API server, which is the central REST endpoint that every Kubernetes operation goes through. Think of it as the front door to the entire cluster. The second is etcd, which is a distributed key-value store that holds the entire state of your cluster. If Kubernetes needs to know what’s running and where, it looks here. The third is the scheduler, which is responsible for deciding where new pods should run based on what resources are available across your nodes. The fourth is the controller manager, which runs a collection of controllers that handle things like deployments, node management, and network endpoints, making sure the actual state of the cluster matches what you’ve asked for.

On the other side, you have the Worker Nodes. These are the machines where your actual application containers run. Each worker node has three components. The kubelet is an agent that sits on the node and makes sure the containers assigned to it are actually running properly. The kube-proxy manages the network rules on each node so that traffic gets routed correctly to the right services. And finally there’s the container runtime, which is the software that actually runs your containers, things like containerd or Docker.

So when you ask Kubernetes to run your application, your request goes through the API server, the scheduler figures out the best node for it, the controller manager keeps an eye on it, and the kubelet on the chosen node makes sure the container stays up and running.

Now, Kubernetes uses a set of objects to represent everything in your cluster. There are eight key ones worth knowing. A Pod is the smallest deployable unit and can contain one or more containers that share the same network and storage. A Deployment manages groups of pods and handles things like rolling updates and maintaining a certain number of running replicas. A Service gives you a stable network endpoint for a set of pods, so even if individual pods come and go, traffic can still reach your application reliably. An Ingress lets you define HTTP and HTTPS routing rules for traffic coming into the cluster from outside. A ConfigMap lets you store non-sensitive configuration data separately from your application code. A Secret is similar but designed for sensitive data like passwords, tokens, and keys. A Namespace gives you a way to create virtual clusters within your physical cluster, which is useful for separating teams, environments, or projects. And finally, a PersistentVolume represents a storage resource at the cluster level, so your data can survive beyond the life of any individual pod.

Together these objects give you a powerful, declarative way to describe exactly how your applications should run, and Kubernetes takes care of making that reality happen and keeping it that way.

If you want to go deeper, the NZRT wiki has a page on the specific cluster design used here at NZRT, a page on core concepts covering pods, deployments, services, and namespaces in more detail, and a kubectl cheatsheet with the most common commands you’ll reach for day to day.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Kubectl Reference

Welcome to the NZRT Wiki Podcast. Today we’re looking at kubectl Reference.

If you’re working with the NZRT Kubernetes cluster, kubectl is your main command-line tool. It’s how you talk to the cluster — listing what’s running, checking logs, deploying changes, and fixing problems. This episode walks you through the key commands grouped by what you’re trying to do.

Let’s start with getting and listing resources. This is probably the first thing you do when you sit down to check on the cluster. You can list pods, deployments, services, ingresses, config maps, secrets, persistent volume claims, and nodes — all within the nzrt-prod namespace. There’s also a shortcut to get everything in that namespace in one go, or if you need a full picture, you can pull everything across all namespaces at once. Think of these commands as your dashboard — a quick way to see what’s alive and what’s there.

Once you spot something you want to know more about, you move into the describe commands. Describing a resource gives you the detailed view — events, conditions, configuration specifics. You can describe a pod by name, a deployment, a node, or a service. This is especially useful when something isn’t behaving as expected and you need to dig past the surface-level status.

Next up is logs. When something goes wrong, logs are usually your first port of call. The basic version pulls logs from a named pod in nzrt-prod. If your pod is running multiple containers, you can specify which container you want logs from. If you want to watch logs live as they come in, there’s a follow mode — it streams new output to your terminal in real time, similar to tailing a file. And if a pod has already crashed, there’s a flag to pull the logs from the previous run, which is invaluable for diagnosing what went wrong before a restart.

Now for applying and deleting resources. When you have a manifest file — that’s a YAML file describing your Kubernetes resources — you can apply it to the cluster. Kubernetes will create or update whatever the file describes. You can also point at an entire directory of manifest files and apply them all at once. On the flip side, you can delete resources using the same manifest file, or delete a specific pod or deployment by name directly. Be careful with deletes — there’s no confirmation prompt, so make sure you know what you’re targeting.

The exec and port-forward commands are your tools for getting hands-on with a running pod. Exec lets you open an interactive shell session directly inside a container — either a basic shell or bash, depending on what the container has available. This is handy for inspecting files, running quick diagnostics, or testing connectivity from inside the cluster. Port-forward is different — it creates a tunnel from your local machine into the cluster. So if you specify local port 8080 mapping to port 80 on a pod or service, you can open your browser and hit that service directly from your laptop without exposing it externally. Really useful for testing.

Rollouts are how you manage deployments over time. You can check the status of a rollout to see if a deployment is progressing or stuck. You can view the rollout history to see previous versions. If something goes wrong after a deploy, you can undo it — this rolls the deployment back to its previous state. And if you want to update which container image a deployment is running, you can set that directly by specifying the deployment name, the container name, and the new image and tag.

Finally, output formats. By default kubectl gives you a summarised table view, but sometimes you need more. You can output a pod’s full configuration as YAML or JSON — useful if you want to inspect every field or save a copy. The wide output format adds extra columns to the default table, like which node a pod is running on. And you can show labels on your pods, which is helpful when you’re working with selectors or trying to understand how services are routing traffic.

That covers the full kubectl reference for NZRT cluster management. If you want to go deeper, the related wiki pages on the kubectl Cheatsheet and Cluster Overview are good next reads.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Kubectl Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at kubectl Cheatsheet.

This episode is your quick-reference guide for daily kubectl usage in the NZRT cluster. Whether you’re just getting started or need a fast reminder of the commands you reach for most often, we’re going to walk through everything section by section.

Let’s start with context and namespace. Before you do anything else in kubectl, you need to make sure you’re talking to the right cluster and working in the right namespace. There’s a command that lists all the contexts you have configured, so you can see what’s available. Then there’s a command to switch to a specific context by name. Finally, you can pin your current context to a specific namespace — in NZRT’s case, that’s the nzrt-prod namespace — so you don’t have to type it out every single time. Getting these three things right at the start saves you a lot of headaches later.

Next up is getting resources. Once you’re in the right context, you’ll want to see what’s running. You can pull a combined view of your pods, services, deployments, and ingresses all in one go, scoped to nzrt-prod. If you want everything in that namespace, there’s a command that literally gets all resources at once. You can also go wider and get everything across all namespaces in the cluster. And if you want to see your nodes with extra detail like IP addresses and which zone they’re in, there’s a wide-output option for that too.

Now let’s talk about inspecting things more deeply. This is where you go when something looks off. You can describe a specific pod by name, which gives you a detailed breakdown of its current state, events, and configuration — really useful for diagnosing problems. You can also stream the live logs from a pod, which is handy when you’re watching something in real time. If you need to actually get inside a running container and poke around, there’s an interactive shell command that drops you straight into a shell session inside the pod. And if you need to hit a service locally without exposing it externally, port forwarding lets you map a local port on your machine to a port on the service.

Moving on to applying and deleting resources. The bread and butter of day-to-day Kubernetes work. You can apply a manifest file to create or update resources defined in it. The same file can be used to delete those resources cleanly. And if you need to force-delete a pod immediately without waiting for a graceful shutdown, there’s a variant of the delete command that skips the grace period entirely — useful when a pod is stuck terminating.

Now for scaling and updating deployments. If you need to scale a deployment up or down, you specify the deployment name and the number of replicas you want, and Kubernetes handles the rest. Updating the container image inside a deployment is just as straightforward — you name the deployment, the container, and the new image with its tag. After triggering a rollout, you can watch its progress with the rollout status command, which tells you when it’s done or if something went wrong. And if the new version causes problems, there’s a rollout undo command that takes you straight back to the previous version with a single command. That one’s a lifesaver.

Let’s cover debugging next, because things do go wrong. The events command gives you a chronological list of what’s been happening in the namespace, sorted by the most recent timestamp — great for piecing together what just broke and when. You can also check resource consumption with the top commands, which show you CPU and memory usage for pods and nodes respectively. If you’re unsure whether your current permissions allow a certain action, there’s a command to check whether you’re allowed to do something like create pods in a namespace. And finally, you can spin up a temporary debug pod running a minimal image, drop into a shell, do your troubleshooting, and have it automatically cleaned up when you exit. That one’s great for checking network connectivity or DNS from inside the cluster.

The last section is about output formatting. Sometimes you need the raw data in a specific format. You can get the full YAML definition of any pod, which is useful when you want to see exactly how it’s configured or copy it as a base for a new manifest. You can also extract a specific field — like just the pod’s IP address — using a path expression that navigates the JSON structure. If you want to see which labels are attached to your pods, there’s a flag that adds a labels column to the output. And you can filter pods by label too, so for example you could list only the pods belonging to the WordPress app.

That covers the full kubectl cheatsheet for the NZRT cluster. Context first, then inspect, apply, scale, debug, and format your output. Keep these patterns in mind and you’ll move through cluster work quickly and confidently.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Pods Containers

Welcome to the NZRT Wiki Podcast. Today we’re looking at Pods & Containers.

If you’re working with Kubernetes, the first concept you need to get comfortable with is the Pod. A Pod is the smallest deployable unit in Kubernetes. Think of it as a wrapper that holds one or more containers together, letting them share the same network and storage resources.

So what does that actually mean in practice? Every container inside a Pod shares the same IP address and port space. That means if you have two containers running side by side in the same Pod, they can talk to each other just by using localhost — no complicated networking required. It’s as if they’re running on the same machine.

One thing that’s really important to understand about Pods is that they are ephemeral. They’re not designed to be repaired when something goes wrong — they’re replaced. If a Pod fails, the system spins up a new one. Because of this, you never create Pods directly in a production environment. Instead, you manage them through something called a Deployment, which handles the lifecycle for you.

Now let’s talk about the Pod lifecycle. A Pod moves through a series of phases depending on what’s happening with its containers. There are five phases to know. First is Pending — this means the cluster has accepted the Pod, but the containers haven’t started yet. Next is Running, which means at least one container is currently active. Then there’s Succeeded, which means all containers finished their work and exited cleanly with a success code. After that is Failed — meaning at least one container exited with an error code, signalling something went wrong. And finally there’s Unknown, which means the system simply can’t determine the current state of the Pod. That one’s usually a sign of a communication issue between the node and the control plane.

Now let’s look at what a Pod definition actually looks like. If you were to write a basic Pod manifest — that’s the configuration file that tells Kubernetes what to create — it would specify a few key things. You’d declare the type of object you’re creating, in this case a Pod, and give it a name. In this example it’s called example-pod, placed in a namespace called nzrt-prod, with a label identifying it as part of an app. Then under the specification section, you’d define the container itself. Here the container is named app, it’s running an nginx web server at version 1.25, and it’s listening on port 80. You’d also set resource boundaries. In this example, the container requests a minimum of 100 millicores of CPU and 128 megabytes of memory, with a hard ceiling of 500 millicores of CPU and 256 megabytes of memory. Those limits make sure one misbehaving container can’t consume all the resources on your node.

Finally, let’s cover the commands you’ll actually use day to day when working with Pods. There are five core ones to know. The first lists all Pods running in your production namespace, giving you a quick overview of what’s up and what isn’t. The second lets you describe a specific Pod in detail — this is your go-to when something looks wrong, because it surfaces events, container statuses, and resource usage all in one place. Third is the logs command, which streams output from a named Pod so you can see exactly what the application inside is doing, or where it crashed. Fourth is the exec command, which drops you into an interactive shell inside a running Pod — think of it like connecting directly into the container to poke around. And fifth is the delete command, which removes a Pod by name. Just keep in mind that if that Pod is managed by a Deployment, a replacement will start up automatically right after you delete it.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Persistent Volumes

Welcome to the NZRT Wiki Podcast. Today we’re looking at Persistent Volumes.

If you’ve worked with Kubernetes at all, you’ve probably run into the question of where your data actually lives. Containers are ephemeral by design — when one stops, its local data disappears with it. Persistent Volumes solve that problem, and that’s exactly what we’re covering today.

Let’s start with the two core concepts. A PersistentVolume, or PV, is a storage resource that exists at the cluster level. Think of it as a chunk of storage that Kubernetes knows about and can hand out. A PersistentVolumeClaim, or PVC, is how a workload actually requests that storage. Claims are scoped to a namespace, meaning they live within a specific environment in your cluster, and when you create one, Kubernetes binds it to an available PV that matches what you asked for.

Before you can claim storage, you usually need a StorageClass. This is where dynamic provisioning comes in. The first example in the wiki shows a StorageClass definition. It sets a name — in NZRT’s case that’s called nzrt-standard — and points to a provisioner, which is the component that actually goes and creates the underlying storage. The example uses a placeholder provisioner that you would swap out for your real cloud provider’s provisioner in a live deployment. Two other settings matter here: the reclaim policy is set to Retain, which we’ll come back to shortly, and the volume binding mode is set to wait for the first consumer, meaning storage isn’t actually provisioned until something tries to use it.

Next up is the PersistentVolumeClaim itself. The example shows a claim named nextcloud-pvc, sitting in the nzrt-prod namespace. It requests twenty gigabytes of storage, references the nzrt-standard StorageClass you just defined, and specifies an access mode of ReadWriteOnce. That access mode means only one node in the cluster can mount this volume for reading and writing at a time. That’s fine for something like a Nextcloud instance where you have one primary pod handling file access.

Once you have your claim, you need to attach it to a pod. The third example shows how that works inside a pod specification. You define a volume entry that references your claim by name — nextcloud-pvc in this case — and then inside your container definition, you mount that volume at a specific directory path. Here it’s mounted at the path where Nextcloud expects its web files to live. From the container’s point of view, it just sees a regular folder. The fact that it’s backed by persistent storage is completely transparent to the application.

Now let’s talk about the commands you’ll use day to day. You can list all persistent volumes across the cluster, list all claims within the nzrt-prod namespace specifically, pull up a detailed description of a particular claim to check its status or spot any error messages, and list all available StorageClasses. Those four commands cover most of what you need for routine inspection and troubleshooting.

Finally, reclaim policies — and this one is worth paying close attention to before you start deleting things. The wiki covers three options. Retain means that when you delete a PVC, the underlying PV stays intact and its data is preserved, but you are responsible for cleaning it up manually. Delete means that when the claim is removed, Kubernetes automatically removes the PV and all the backing storage with it. And Recycle is a third option that exists mostly as a historical footnote — it is deprecated, so don’t use it.

For most production workloads at NZRT, Retain is the safer default because it protects you from accidental data loss. Delete makes sense only when you are confident that automatically removing the data is acceptable, such as with temporary or test environments.

If you want to go deeper, the related topics in the wiki are Storage Overview and StatefulSets. StatefulSets in particular pair closely with Persistent Volumes when you need stable, predictable storage for applications that need to remember their state across restarts.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Nodes

Welcome to the NZRT Wiki Podcast. Today we’re looking at Nodes.

So, what is a node? In a Kubernetes cluster, a node is essentially a worker machine. Think of the cluster as a factory, and nodes are the individual workstations where the actual work gets done. Every node runs three key pieces of software to make that happen.

The first is the kubelet. This is an agent that sits on the node and keeps in constant communication with the Kubernetes API server. Its main job is managing the lifecycle of pods — making sure the right containers are running, healthy, and doing what they’re supposed to be doing.

The second component is kube-proxy. This one handles networking. It maintains the rules that allow network traffic to flow correctly to your services — so when something inside the cluster needs to talk to something else, kube-proxy makes sure that traffic lands in the right place.

The third component is the container runtime. For most setups you’ll see at NZRT, that’s containerd. This is what actually pulls and runs your containers at the low level.

Now, how do you check on your nodes? There are a few commands worth knowing. The first lets you list all nodes in your cluster — you get a quick overview of their names and current status. If you want more detail, you can run a wider version of that command, which also shows you IP addresses and operating system information. And if you really need to dig into a specific node — say you’re troubleshooting or checking recent events — you can describe that node by name and get a full breakdown of everything happening on it.

When you look at node status, you’ll see what are called conditions. There are five to be aware of. Ready is the one you want to see — it means the node is healthy and able to accept new pods. If you see MemoryPressure, the node is running low on memory. DiskPressure means disk space is getting tight. PIDPressure tells you there are too many processes running on that node. And NetworkUnavailable means the network isn’t configured correctly on that node. Any condition other than Ready being active is worth investigating.

The last area covered in this doc is labels and taints — two ways you can control how pods get scheduled onto nodes.

Labels are straightforward. You can tag a node with a key-value pair — for example, marking a node as belonging to a production environment. Once a node has a label, you can use that label in your pod configurations to tell Kubernetes where you want things to run.

Taints are a bit more powerful. When you taint a node, you’re essentially putting up a sign that says “pods can’t schedule here unless they explicitly say they’re okay with this.” You set a taint with a key, a value, and an effect. The effect used in this example is NoSchedule, which means Kubernetes will not place any new pods onto that node unless those pods have a matching toleration defined. This is useful for reserving nodes for specific workloads — for example, keeping a node dedicated to production traffic.

And when you’re done with a taint and want to remove it, you run the same command but add a minus sign at the end to strip it off.

So to recap — nodes are your worker machines, made up of the kubelet, kube-proxy, and a container runtime. You can inspect them with a handful of commands, watch their conditions to catch health issues early, and use labels and taints to control exactly what runs where.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Networking Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Networking Overview.

Kubernetes networking is the system that connects everything inside your cluster — pods talking to other pods, services providing stable endpoints, and external traffic finding its way in. If you’ve ever wondered how all those moving pieces actually reach each other, this episode walks you through it.

Let’s start with the core networking model, because Kubernetes makes some strong guarantees here that are worth understanding upfront. First, every pod gets its own unique IP address that is valid across the entire cluster. Not just within a node — across the whole cluster. Second, any pod can communicate directly with any other pod without going through network address translation, which you might know as NAT. Third, nodes can also reach any pod directly without NAT. And finally, pod IP addresses are never masqueraded — meaning what you see is what you get, no hidden remapping happening underneath. These four rules together form what’s called the Kubernetes network model, and every networking plugin you install has to honour them.

Now, let’s talk about how traffic actually flows from the outside world into your application. Picture this as a journey with a few stops. It starts with an external client — that’s someone’s browser, a mobile app, whatever is making a request from outside the cluster. That request first hits what’s called an Ingress Controller. In this setup the Ingress Controller is running nginx. The Ingress Controller is the gatekeeper — it reads your routing rules and decides where to send the traffic next. From there, the request moves to a Service. Services in Kubernetes have what’s called a ClusterIP, which is a stable internal address that doesn’t change even if the pods behind it do. Think of the Service as a reliable middleman. Finally, the request lands at the actual Pod — the container running your application. So the journey is: external client, then Ingress Controller, then Service, then Pod. Four hops, clean and predictable.

Next up is the CNI, which stands for Container Network Interface. This is the plugin layer that actually implements the network model we just described. Kubernetes doesn’t hard-code a single networking solution — instead it lets you choose a CNI plugin that suits your needs. There are four common options you’ll encounter. The first is Flannel, which is simple to set up and uses an overlay network — a good starting point if you want something that just works without a lot of configuration. The second is Calico, which gives you support for network policies and BGP routing, making it a strong choice when you need fine-grained control over which pods can talk to which. The third is Cilium, which is eBPF-based and gives you advanced observability — if you need deep visibility into your network traffic and performance, Cilium is worth a look. The fourth is Weave Net, which is known for easy setup and comes with encrypted overlay networking out of the box, so your pod-to-pod traffic is protected without extra configuration.

Choosing the right CNI depends on your priorities — simplicity, policy control, observability, or security. Most production clusters end up on Calico or Cilium, but Flannel and Weave Net are perfectly valid for smaller or simpler environments.

Now let’s cover DNS, because once pods and services exist, you need a way to find them by name rather than memorising IP addresses. Kubernetes handles this through CoreDNS, which runs inside the cluster and answers name lookups automatically. Services get a DNS name that follows a pattern: the service name, then the namespace it lives in, then the suffix svc.cluster.local. So if you have a service called api in a namespace called production, you can reach it at api.production.svc.cluster.local. Pods also get DNS names, though they’re used less often. A pod’s DNS name uses its IP address with dashes instead of dots, followed by the namespace, then pod.cluster.local. The important thing to know is that CoreDNS makes service discovery automatic — your application code can use a stable DNS name rather than hardcoding IPs that might change.

To wrap up, here’s the big picture. Every pod has its own IP, pods talk to each other freely without NAT, and the network model is enforced by whichever CNI plugin you choose. External traffic enters through an Ingress Controller, passes through a Service, and reaches your Pod. CoreDNS keeps name resolution working so everything can find everything else by name. If you want to go deeper, the related topics to explore next are Services, Ingress, and Services and DNS — each of those builds directly on what we’ve covered here.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Namespaces

Welcome to the NZRT Wiki Podcast. Today we’re looking at Namespaces.

So, what is a namespace? Think of it as a virtual cluster living inside your actual Kubernetes cluster. Instead of one big shared space where all your workloads, configs, and resources sit together, namespaces let you carve things up into isolated sections. Each section can have its own access controls, its own resource limits, and its own set of rules. It’s a clean way to separate concerns — especially when you’re running multiple environments on the same cluster.

Let’s talk about how NZRT uses namespaces specifically. There are six you need to know about. First is nzrt-prod, which is your production environment. This one is locked down — only xc and dan have access, so treat it with care. Then there’s nzrt-staging, which mirrors the production config and is used for pre-production testing and UAT work. Below that sits nzrt-dev, a more relaxed space for development and experimentation — resource limits are looser here, so you’ve got room to move.

Beyond those three environment namespaces, there are a few infrastructure ones. The monitoring namespace is home to the shared observability stack — that’s Prometheus, Grafana, and Loki all running together. Then there’s ingress-nginx, which handles the Nginx ingress controller for the whole cluster. And finally, kube-system — this is where Kubernetes puts its own system components. Leave this one alone. Don’t modify it.

Now, Kubernetes also comes with a set of default namespaces out of the box. The one simply called default catches any resource that doesn’t have a namespace explicitly assigned to it. Kube-system, as mentioned, is for the core Kubernetes internals. Kube-public is readable by everyone in the cluster and holds general cluster information. And kube-node-lease is used internally for node heartbeat tracking — you won’t need to touch that one directly either.

Let’s walk through some of the common commands you’ll use day to day. If you want to see all the namespaces currently in your cluster, you run a get namespaces command with kubectl. To create a new namespace — say, nzrt-staging — you use kubectl create namespace followed by the name. If you want to see all pods running across every namespace at once, you pass the all-namespaces flag to a get pods command. To narrow it down to just one namespace, like nzrt-prod, you use the n flag followed by the namespace name. And if you want to set a default namespace for your current context so you don’t have to type it every time, there’s a config set-context command where you pass the current flag and then specify the namespace you want to default to.

Now let’s look at resource quotas, because this is where namespaces really earn their keep in a shared cluster. A resource quota is a Kubernetes object that caps how much compute a namespace can consume. In the example for nzrt-prod, the quota is named nzrt-prod-quota and it’s scoped to the nzrt-prod namespace. It sets a minimum CPU request of four cores, a minimum memory request of four gigabytes, a maximum CPU limit of eight cores, a maximum memory limit of eight gigabytes, and a hard cap of twenty pods running at any one time. This means no matter what gets deployed into production, it can never exceed those boundaries — which protects the rest of the cluster from being starved of resources by a runaway workload.

If you want to go deeper on access control within namespaces, check out the RBAC wiki page, which covers role-based access policies. And for the broader picture of how all of this fits together, the NZRT Kubernetes Architecture page is your next stop.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Services Dns

Welcome to the NZRT Wiki Podcast. Today we’re looking at Services & DNS.

Let’s start with the basics. Inside a Kubernetes cluster, CoreDNS is the engine that handles automatic DNS resolution. That means when a service or a pod needs to find another service or pod by name, CoreDNS is the one doing the lookup behind the scenes.

Now, DNS names in the cluster follow a predictable pattern depending on what you’re trying to reach and where you’re reaching from. There are four main formats to know. First, if you’re looking up a service from within the same namespace, you can just use the service name on its own — short and simple. Second, if you’re crossing into a different namespace, you add the namespace after the service name, separated by a dot. Third, if you want the full, unambiguous address — what’s called a Fully Qualified Domain Name — you build it out as the service name, then the namespace, then the suffix “svc.cluster.local”. And fourth, pods themselves get a DNS name too, built from the pod’s IP address with dashes instead of dots, followed by the namespace and then “pod.cluster.local”. A real example of a fully qualified service name would look like: wordpress-service dot nzrt-prod dot svc dot cluster dot local.

Next up, testing DNS resolution. The wiki shows two ways to do this. The first approach launches a temporary debug pod using a minimal image called busybox, runs an nslookup command against the wordpress-service inside the nzrt-prod namespace, and then automatically removes itself when done. The second approach skips creating a new pod altogether — instead, you jump directly into a pod that’s already running and fire the nslookup from inside it, this time using the cross-namespace format with the namespace included in the name.

Now let’s talk about headless services, because these work a little differently. A normal service gives you a single stable IP address that load-balances traffic across your pods. A headless service, by contrast, has no cluster IP at all — and that’s intentional. When you query a headless service, you get back the actual IP addresses of the individual pods behind it. This is particularly useful for StatefulSets, where each pod needs its own stable, predictable DNS name.

In the Nextcloud example from the wiki, you’d have two pods addressable individually. The first would be nextcloud-0 dot nextcloud dot nzrt-prod dot svc dot cluster dot local, and the second would be nextcloud-1 with the same suffix. Each pod is reachable directly by name, which matters a lot for stateful applications where pod identity is important.

The configuration for a headless service is straightforward. You define it as a standard Kubernetes service, give it the name “nextcloud” in the nzrt-prod namespace, set the cluster IP field explicitly to the word “None” — that’s what makes it headless — point the selector at pods with the app label of nextcloud, and expose port 80.

Finally, if you ever need to inspect or troubleshoot the CoreDNS configuration itself, the wiki shows a command that retrieves the CoreDNS config map from the kube-system namespace and prints it out in full. This is handy if you’re chasing a DNS issue at the cluster level and need to see exactly how CoreDNS is configured.

For more context, the related topics in the wiki are Networking Overview, Services, and StatefulSets — worth reading alongside this one if you want the full picture.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Services

Welcome to the NZRT Wiki Podcast. Today we’re looking at Services.

So, what is a Service in Kubernetes? At its core, a Service gives you a stable network endpoint — think of it as a fixed address made up of a DNS name and an IP — for a group of pods. Here’s the thing about pods: they come and go. They spin up, they restart, they get replaced. But the Service address stays constant. That’s the whole point. You point your traffic at the Service, and Kubernetes takes care of routing it to whichever pods are actually running behind the scenes.

Now, not all Services work the same way. There are four types, and choosing the right one depends on what you’re trying to do.

The first type is called ClusterIP, and it’s the default. This one is for internal access only — pod-to-pod communication inside your cluster. Nothing outside the cluster can reach it directly.

The second type is NodePort. This exposes your service on each node’s IP address at a fixed port number. It’s a step up from ClusterIP in that you can reach it from outside the cluster, but it’s fairly manual and better suited for testing or simple setups.

Third is LoadBalancer. This one provisions an actual cloud load balancer — so if you’re running on a cloud provider, Kubernetes will automatically create a load balancer and wire it up to your service. This is your go-to for proper external access in production.

And the fourth type is ExternalName. Rather than routing to pods inside your cluster, this maps your service to an external DNS name. Useful when you need your cluster workloads to talk to something living entirely outside Kubernetes.

Let’s look at what a Service definition actually looks like. The example we have is a ClusterIP service for WordPress, sitting in the nzrt-prod namespace. The definition tells Kubernetes which pods to route traffic to — in this case, any pod labelled as a WordPress app. It then maps port 80 on the Service itself through to port 80 on those pods, using standard TCP. And since no type is explicitly set to something else, it defaults to ClusterIP, meaning internal only.

That’s a pretty typical pattern: you define which pods to target, you declare the port mapping, and you pick your type. Kubernetes handles the rest.

Next up, DNS. Once your Service is running, how do other things in the cluster actually talk to it? Kubernetes gives every service a DNS name that follows a predictable pattern. You take the service name, add the namespace, and then the suffix svc dot cluster dot local. So for the WordPress service in the nzrt-prod namespace, the full address would be wordpress-service dot nzrt-prod dot svc dot cluster dot local. That’s the fully qualified form.

If you’re working from within the same namespace, you can skip most of that and just use the service name on its own. Kubernetes DNS figures out the rest automatically.

Finally, let’s talk about the commands you’ll use most often when working with Services. There are three key ones. The first retrieves a list of all services running in the nzrt-prod namespace — handy for a quick overview of what’s live. The second gives you a detailed description of a specific service — in this case the WordPress service — including its selector, ports, and current state. And the third shows you the endpoints for that service, meaning the actual IP addresses and ports of the pods currently backing it. That last one is especially useful when you’re troubleshooting and you want to confirm that traffic actually has somewhere to go.

So to tie it all together: Services are how Kubernetes gives you a stable, reliable way to reach your pods no matter how many times they restart or get rescheduled. You pick the type that matches your use case — ClusterIP for internal, NodePort or LoadBalancer for external, ExternalName for mapping to outside systems. You write a short manifest, apply it, and from that point on your cluster has a consistent address to work with. DNS handles discovery automatically, and a handful of commands let you inspect and debug everything when you need to.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Storage Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Storage Overview.

If you’ve worked with Kubernetes before, you’ve probably noticed that it goes out of its way to separate your applications from the details of where their data actually lives. That’s the core idea behind Kubernetes storage — it abstracts the underlying storage system away from your workloads, so your pods don’t need to know whether their data is sitting on a local disk, a network drive, or a cloud volume. They just ask for storage, and Kubernetes handles the rest.

Let’s walk through how that actually works, because there’s a clear hierarchy to understand here. Think of it as a chain of four layers, each one sitting on top of the next.

At the top of that chain is something called a StorageClass. This is essentially a template or a definition that says “here’s the type of storage we can provision, and here’s how to provision it.” It points to a provisioner — the thing that actually knows how to create storage on your underlying infrastructure.

Beneath the StorageClass sits the PersistentVolume, or PV. This is the actual storage resource — the real chunk of space that exists in your cluster. It can be created manually by an administrator, or it can be created automatically and dynamically by Kubernetes using the StorageClass you just defined.

Below the PersistentVolume is the PersistentVolumeClaim, or PVC. This is how a pod makes a request for storage. Instead of pointing directly at a specific volume, a pod says “I need some storage with these characteristics” — and the PVC is that request. Kubernetes then matches the claim to an appropriate PersistentVolume.

And finally, at the bottom of the chain, you have the Pod itself. The pod mounts the PVC as a volume, and from that point on it just reads and writes files like normal. It never needs to know anything about the StorageClass or the underlying PersistentVolume that made it all possible.

So to put it simply — StorageClass defines how to make storage, PersistentVolumes are the actual storage, PersistentVolumeClaims are how pods ask for it, and Pods consume it.

Now let’s look at the five key storage objects you’ll encounter in Kubernetes. There are five of them, each with a distinct purpose.

First, the PersistentVolume — this is a cluster-level resource. It represents actual storage, and it can be provisioned manually by an admin or created dynamically when a claim comes in.

Second, the PersistentVolumeClaim — this operates at the namespace level. It’s the pod’s way of saying “I need storage,” and Kubernetes binds it to a matching PersistentVolume.

Third, the StorageClass — this is the provisioner definition. When dynamic provisioning is enabled, Kubernetes uses the StorageClass as a blueprint to create new PersistentVolumes on demand.

Fourth, the ConfigMap — this is how you inject non-sensitive configuration data into your pods. Think of it as key-value pairs that your application can read at runtime, without baking that configuration into your container image.

And fifth, the Secret — this is similar to a ConfigMap, but intended for sensitive data like passwords, tokens, and certificates. One important thing to know here: Secrets are base64-encoded by default, but they are not encrypted. Base64 is an encoding format, not a security measure — so if you need proper encryption at rest, that requires additional configuration in your cluster.

The last thing to understand is access modes — these define how a volume can be mounted across nodes in your cluster. There are three modes to know about.

ReadWriteOnce means only one node can mount that volume with read-write access at a time. This is the most common mode and suits most standard workloads.

ReadOnlyMany means the volume can be mounted as read-only across many nodes simultaneously. Useful when multiple pods need to read shared data but none of them need to write.

ReadWriteMany means many nodes can mount the volume with full read-write access at the same time. This requires storage backends that explicitly support it — not all do — so check your provider’s documentation before assuming this is available.

If you want to dive deeper, the related topics from this wiki page include Persistent Volumes, which goes into the full lifecycle of PV creation and binding, ConfigMaps and Secrets, which covers how to use those objects in practice, and StatefulSets, which is where storage gets especially interesting because StatefulSets are designed for workloads that need stable, persistent storage across restarts.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Architecture Tech Stack

Welcome to the NZRT Wiki Podcast. Today we’re looking at Architecture & Tech Stack.

So let’s start with what we’re actually talking about here. NZRT runs its own instance of Nextcloud, which is a self-hosted file sync and collaboration platform. You can reach it online at cloud dot nzrtnetwork dot com slash nextcloud. If you’re working locally on Nathan’s laptop, everything syncs down automatically through the Nextcloud desktop client into a folder called Nextcloud2 under the main user directory. So when you’re offline, your files are still right there on disk.

Now let’s walk through the tech stack — in other words, what’s actually powering this thing under the hood. There are seven components worth knowing about. First, hosting. Nextcloud lives on Hoopla, which is a shared Linux hosting environment managed through cPanel. Second, the web server is Apache, and cPanel handles its configuration. Third, PHP — again cPanel-managed, running version 8 or higher. Fourth, the database is MySQL version 8, and the specific database name is nzrtnetw underscore nc. Fifth, storage lives on the cPanel filesystem under the home directory for the nzrtnetw account. Sixth, caching — and this is worth noting — there’s no caching layer available here because shared hosting doesn’t support it. And seventh, search uses Nextcloud’s own built-in search engine. There’s no Elasticsearch, again because shared hosting doesn’t give you that option.

Now let’s talk about the system architecture — how all the pieces connect. Picture it as three layers stacked on top of each other. At the top you have the client layer, which is the Nextcloud desktop client running on the laptop. It connects over a secure HTTPS connection down to the middle layer, which is the Nextcloud web application itself — Apache and PHP running on Hoopla’s shared hosting at that cloud dot nzrtnetwork dot com address. From there, Nextcloud splits into two directions. On one side it talks to the MySQL database to store metadata and application data. On the other side it reads and writes actual files to the cPanel filesystem. And here’s the interesting part — sitting beneath that same database layer, sharing the same cPanel hosting account, you also have Dolibarr and WordPress. So all three applications — Nextcloud, Dolibarr, and WordPress — are running as neighbours on the same Hoopla account.

That shared hosting arrangement is actually central to how NZRT’s systems fit together, so it’s worth keeping in mind when you’re thinking about integrations or troubleshooting.

Speaking of integrations, there are four key ones to know. First, Dolibarr — that’s the ERP system used for document management and business workflows, and it sits right there on the same cPanel instance. Second, WordPress handles the public-facing website and content management, also on the same instance. Third, the desktop client keeps files synced to the laptop at that Nextcloud2 folder path, giving you offline access to the vault and shared folders. And fourth, WebDAV — this is the protocol that agents and automation scripts use to access files programmatically. The WebDAV endpoint lives at cloud dot nzrtnetwork dot com slash nextcloud slash remote dot php slash dav slash.

To wrap it all up — NZRT’s Nextcloud setup is deliberately lean. It’s shared hosting, so you don’t get extras like caching or Elasticsearch, but what you do get is tight co-location with Dolibarr and WordPress, solid file sync to the laptop, and a clean WebDAV interface for automated access. Everything runs under one cPanel account, which keeps the infrastructure simple and in one place.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

External Storage

Welcome to the NZRT Wiki Podcast. Today we’re looking at Nextcloud External Storage.

So what exactly is external storage in the context of Nextcloud? In simple terms, it is a feature that lets Nextcloud connect to remote file systems and present them to you as regular folders inside the Nextcloud interface. Think of it like a bridge between Nextcloud and storage that lives elsewhere on your network or out in the cloud. Instead of switching between different tools to reach a NAS drive, an FTP server, or a cloud storage bucket, you can see it all in one place through Nextcloud. For NZRT, this is particularly useful because it means the team can access legacy storage and backups without ever leaving the Nextcloud environment.

Let’s talk about what kinds of storage Nextcloud can connect to, because the list is pretty broad. You have SMB and CIFS, which are the protocols used by Windows file shares and popular NAS devices like Synology, QNAP, and TrueNAS. You have NFS, which stands for Network File System and is commonly used with Linux-based NAS setups. There is FTP and SFTP for connecting to remote servers. You have S3 and Swift for cloud object storage. And finally, WebDAV, which lets you connect to another Nextcloud instance or any other WebDAV-compatible server. So whatever storage you are working with, there is a good chance Nextcloud can mount it.

Now let’s look at how NZRT has this set up in practice. The main example is a NAS mount using the SMB protocol. To configure this, you would go into the Settings area and navigate to External Storage. From there, you define a mount point. In NZRT’s case, that mount point is a folder called NAS Archive. You select SMB as the connection type, then provide the host address, which points to the NAS device on the local network. You specify which share on that NAS you want to mount, in this case the archive share. You enter a username and a securely stored password for authentication. And finally, you control which users or groups can see this mount. For NZRT, it is made visible to the finance team, the EDM group, and the Dan user role. That is the entire configuration in a nutshell. A handful of fields, and the storage appears as a folder inside Nextcloud.

Now why would you bother doing this instead of just accessing the NAS directly? There are some solid practical benefits. First, centralized access. Your team does not need separate tools or credentials to get into the NAS. They reach it through Nextcloud just like any other folder. Second, transparent versioning. Nextcloud’s versioning features still apply to files on the NAS, so you get that version history even for files that physically live on external storage. Third, unified sharing. You can use Nextcloud’s permission system to control who sees what on the NAS, rather than managing access separately at the NAS level. And fourth, archive integration. For NZRT specifically, old Dolibarr documents stored on the NAS become accessible through Nextcloud without anyone needing to know where those files physically sit.

In terms of who manages all of this at NZRT, that responsibility falls to the DBA role, which maps to the Dan role on the team. Dan is the person configuring and maintaining these external storage connections.

The bigger picture here is that External Storage is one of the key ways NZRT bridges its newer cloud-first infrastructure with older legacy systems. Not everything lives in Nextcloud natively, and not everything needs to be migrated. External Storage lets you meet your existing storage where it is and bring it into a unified interface, without disrupting anything that is already working.

If you want to go deeper, the Files App documentation, the Administration Overview, and the WebDAV Cheatsheet are all related wiki notes worth exploring.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Integrations Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at Nextcloud Integrations Overview.

If you’ve ever wondered how Nextcloud fits into the broader NZRT tech stack, this is the episode for you. Nextcloud doesn’t just sit on its own — it connects to a whole range of other systems, and today we’re going to walk you through what those connections are, how they work, and why they matter.

Let’s start with the big picture. Nextcloud integrates with four main categories of systems: Dolibarr, which is our ERP platform; WordPress, our content management system; email systems for calendar and contact syncing; and external storage like network-attached storage devices for backup and archiving. On top of those, you can also build custom integrations using Nextcloud’s REST API and webhook support.

Now let’s talk about the integration types in a bit more detail. There are five main ones to know about.

First, the Dolibarr integration. This uses a combination of REST API calls and file synchronisation. The main use case here is electronic document management — what we often call EDM — along with document versioning. So when documents are created or updated in Dolibarr, they can flow into Nextcloud and be properly version-controlled.

Second, the WordPress integration. This also uses the REST API, plus LDAP for directory services. The use cases are user synchronisation — so your user accounts stay consistent across both platforms — and media asset management.

Third, email integration. This one uses the CalDAV and CardDAV protocols. If those sound unfamiliar, think of CalDAV as the standard way calendar data is shared over the internet, and CardDAV as the same idea but for contact information. So through this integration, your calendar events and contacts can stay in sync between Nextcloud and your email system.

Fourth, external NAS integration. NAS stands for Network-Attached Storage — basically a dedicated file server on your network. Nextcloud connects to these using SMB or NFS protocols, which are standard network file-sharing protocols. The use case is backup and archive storage, so older or less-accessed data can be offloaded to external storage while still being reachable through Nextcloud.

Fifth and finally, custom app integrations. These use the REST API combined with webhooks. Webhooks are a way for one system to notify another in real time when something happens — think of them as event triggers. This category is for building your own automation workflows on top of Nextcloud.

Now let’s look at how all of this fits together architecturally. The code block in the wiki shows a hub-and-spoke diagram. Picture Nextcloud at the centre. Radiating out from that hub, you have five connections. Dolibarr sits on one branch, handling ERP data and electronic document management. WordPress is on another, covering user sync and media. Email connects for calendar and contacts. External NAS branches off for archive backup. And custom webhooks extend outward for workflow automation. Nextcloud is the connective tissue holding all of these systems together.

So how does authentication work across all these integrations? There are four methods you might encounter. The first is LDAP and single sign-on, which gives you a unified user directory — one set of credentials that works across systems. The second is API tokens, used by service accounts for automated processes. If a script or agent needs to talk to Nextcloud without a human logging in, it uses a token. The third is OAuth2, the standard for letting third-party apps authenticate securely without exposing passwords. And the fourth is basic authentication — plain username and password — which is considered a legacy approach and is not recommended for new integrations.

Why does all of this matter for NZRT specifically? The core goal is eliminating data silos. Without these integrations, documents in Dolibarr wouldn’t talk to storage in Nextcloud, and media in WordPress would be disconnected from everything else. With these connections in place, an invoice generated in Dolibarr can be stored and versioned in Nextcloud. Media assets in WordPress can be sourced directly from Nextcloud. And custom automation via the API and WebDAV layer means your EDM workflows can be built to fit exactly how NZRT operates, rather than forcing you to work around the tools.

If you want to go deeper, the related docs to look up are the Dolibarr Integration note, the WordPress Integration note, the Email and CalDAV CardDAV note, and the REST API and WebDAV note. Each of those covers the specifics of how those individual connections are configured and maintained.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Nextcloud Cli Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at 🎯 Nextcloud CLI Cheatsheet.

If you manage a Nextcloud installation, you’ll quickly discover that the command line is your best friend. Nextcloud ships with a built-in tool called occ — short for ownCloud Console — and it handles just about everything you’d ever need to do from the server side. Let’s walk through the key things you can do with it.

Starting with user management. You can create a new user, delete one, or just pull up a list of everyone on the system. If someone forgets their password, there’s a command to reset it. You can also temporarily block a user from logging in by disabling their account, then re-enable them when needed. Setting storage quotas is straightforward too — you specify the user and a size value, and Nextcloud enforces that limit. You can also check when a user last logged in, which is handy for auditing inactive accounts.

Group management works in a similar way. You create a group by name, add users to it, remove users from it, and delete the group when it’s no longer needed. There are commands to list every user inside a specific group, and also to look up which groups a particular user belongs to — both directions covered.

For app management, occ lets you install, enable, disable, update, and remove apps without touching the web interface. You can list everything currently installed, check info on a specific app, or update all apps in one go. This is especially useful when you’re doing maintenance windows and want to script the whole thing.

File management is where things get really practical. If you’ve added files directly to the server — bypassing the web upload — Nextcloud won’t know they exist until you run a file scan. You can scan all users at once or target a specific user. There’s also a cleanup command that permanently removes trash items older than thirty days, a cache check for flagging file database inconsistencies, and commands to transfer all of one user’s files to another user, or to clear the file cache entirely.

On the database side, there are a few important housekeeping commands. One converts the file cache table to support larger integer values, which is something you’ll need on big installations. Another checks for and adds missing primary keys. You can also run raw database queries directly through occ if you need to inspect table sizes or check on the database state.

Configuration management is powerful. You can get or set configuration values for any app, delete config keys you no longer need, or list all configuration currently active on the system. System-wide settings work the same way — for example, you can read or update the list of trusted domains that Nextcloud will respond to.

Maintenance mode is something you’ll use before any major update or restore. One command turns it on, blocking all user access, and another turns it off again. You can also check the current system status, find out where the data directory lives, and get help on any specific command if you’re unsure of its options.

For sharing, you can list all active shares on the system, create a new share pointing to a file or folder, delete a share by its ID, or transfer share ownership from one user to another. This is useful when staff members leave and you need to reassign their shared content cleanly.

Logging and monitoring are covered too. Rather than using occ directly for logs, you’ll tail the main Nextcloud log file — you can view the last fifty lines, watch it in real time as events come in, or search it for error-level entries specifically. Background jobs have their own set of commands: you can check the queue status, list pending jobs, or manually trigger one job to run immediately.

Backup and restore rely on standard Linux tools alongside occ. For the database, you use mysqldump to export everything to a SQL file, and then restore by piping that file back into MySQL. For the Nextcloud application directory itself, you compress it into a tar archive for backup and extract it to restore. Always combine both steps — database and files — for a complete backup.

Finally, if you’re running LDAP for centralised user authentication, occ has you covered there too. You can test whether your LDAP connection is working, view the current LDAP configuration, sync users and groups from your directory, and inspect the mapping between LDAP entries and Nextcloud accounts.

At NZRT, both xc as the admin and dan as the DBA work with these commands regularly — for provisioning new users, managing groups, running maintenance, and troubleshooting issues as they come up. If you want to go deeper, the related notes in the wiki cover the full occ command reference and the administration overview.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Occ Command Reference

Welcome to the NZRT Wiki Podcast. Today we’re looking at occ Command Reference.

If you’ve ever needed to manage Nextcloud from the command line, the occ tool is your best friend. occ stands for ownCloud Console, and it’s Nextcloud’s built-in command-line interface. You’ll find it sitting in the Nextcloud root directory, and you run it either directly as occ or as php occ depending on your server setup. Let’s walk through the main command categories and what you can do with each one.

Starting with user management, which is probably where you’ll spend the most time. You can create a new user, delete one, list all users or filter that list by a search term, reset passwords, disable or enable login access, check when someone last logged in, and pull a full user report showing their email, login history, and quota. You can also get or set individual user preferences. For example, there’s a command specifically for setting a user’s storage quota, like capping someone at a hundred gigabytes. And if you want everything about a specific user in one go, the info command gives you a detailed breakdown.

Groups work in a very similar pattern. You can create and delete groups, list them or search for one, see who’s in a group, add or remove individual users, and pull group details.

Next up is app management. You can list every app installed on your Nextcloud instance, filter that list to show only enabled or only disabled apps, install new apps by name, enable or disable them without uninstalling, update individual apps or update everything at once, and completely remove an app. There’s also a code check command that’s handy for development work.

For file management, the scan commands are particularly important. You can trigger a full index of all user files across the entire instance, or narrow it down to a single user or a specific path. There’s a cleanup command that removes files sitting in the trash for more than thirty days, and a dry-run option that shows you what would be deleted before you commit. You can also transfer all of a user’s files to another user, which is useful for offboarding, or limit that transfer to a specific folder.

Database commands tend to be occasional maintenance operations. These include converting the file cache to support larger sixty-four-bit IDs for big file sets, adding missing primary keys or indices that the database might have lost, converting between database types, and even running a raw SQL query directly if you need to drop down to that level.

Configuration is handled through a consistent set of commands. You can get or set values at the app level or the system level, delete config keys, list the entire configuration, and import or export config as a file. This makes it straightforward to move settings between environments or back things up before a change.

If your Nextcloud is connected to an LDAP directory, there’s a dedicated category for that too. You can test the connection, view or update LDAP settings, sync users and groups from your directory into Nextcloud, list users not yet confirmed, and clear cached mappings when you need a fresh start.

Maintenance commands are ones you’ll want to know before doing any upgrade or significant change. You can check the overall system status, toggle maintenance mode on or off to block or allow user access, and switch to single-user mode for admin-only access. There are also commands for refreshing the Apache config file, upgrading themes, and updating the MIME type database.

Background jobs have their own section too. You can check the status of the pending queue, list everything in it, execute the next job in line, or target a specific job by its ID. For monitoring more broadly, you can tail the Nextcloud log in real time, set a size limit on it, and find out exactly where the log file lives on disk.

There’s also two-factor authentication management, where you can enforce or disable two-factor auth for individual users and check the current state across the system. Sharing commands let you list all shares, filter by user, create a new share, or transfer share ownership between users.

Now, the wiki includes some practical NZRT examples worth explaining. The first walks through creating a new finance team user by reading the password from an environment variable rather than exposing it in plain text, then adding that user to the finance group, and setting their quota to two hundred gigabytes. The second example is a scheduled overnight task set to run at two in the morning, running as the web server user, to automatically sync LDAP users into Nextcloud. The third schedules a three-in-the-morning daily job that first cleans up old trash and then rescans all files. And the fourth runs every five minutes to check the background job queue and append status output to a log file for monitoring.

In the NZRT environment specifically, it’s the database administrator and system administrator roles that rely on these commands for day-to-day maintenance, user provisioning, and troubleshooting.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Performance Tuning

Welcome to the NZRT Wiki Podcast. Today we’re looking at Nextcloud Performance Tuning.

If you’ve ever noticed Nextcloud feeling sluggish when a lot of people are using it at once, or when you’re dealing with large files, this episode is for you. At NZRT, performance tuning comes down to three main areas: caching, PHP configuration, and database optimisation. Let’s walk through each one.

First up is caching, and the tool NZRT uses here is Redis. Redis is an in-memory data store, and Nextcloud integrates with it really well. The configuration lives in Nextcloud’s main config file, and what it does is tell Nextcloud to use Redis for three things. Local memory caching, which is the everyday object cache that reduces how often Nextcloud has to hit the database. Distributed locking, which prevents conflicts when multiple users access or modify the same files at the same time. And session storage, which is what makes logins feel fast. You point Nextcloud at Redis running on localhost, on port 6379, and give it a password. That’s the core setup. The payoff is fewer database queries, faster page loads, and much smoother performance for things like Nextcloud Talk and the Calendar app, which are especially chatty with the cache.

Next is PHP-FPM tuning, and this one is about making sure PHP can handle many users at once without running out of workers. The configuration file for Nextcloud’s PHP pool sets a dynamic process model, meaning the number of running PHP processes scales up and down with demand. The key numbers here are: a maximum of 100 child processes so you can handle heavy load, 20 processes started at boot so you’re ready to go immediately, a minimum of 10 spare servers sitting idle waiting for requests, and a maximum of 50 spare servers at any given time. Each worker also resets itself after 500 requests, which keeps memory usage healthy over time. On top of that, the PHP settings themselves are tuned for large files. Upload limits and the maximum post size are both set to 2 gigabytes, and the memory limit per PHP process is set to 512 megabytes. If you’re handling Dolibarr invoice exports or large WordPress media uploads through Nextcloud, these limits matter.

Now let’s talk about database optimisation. NZRT runs MySQL, and there are a couple of things you can do here. First, you can enable query caching through Nextcloud’s own command-line tool, which tells Nextcloud to cache the results of repeated database queries rather than running them every time. Second, you can run an optimise command directly against the file cache table in MySQL. This table can grow quite large over time and optimising it reclaims space and improves read performance. Third, you can use Nextcloud’s system info command to pull diagnostic output and check database health at a glance.

For file input and output, the recommendation is to use an SSD for your primary Nextcloud data directory, because fast random write performance makes a noticeable difference when many users are uploading files. If you’re mounting network storage, like a NAS, you want to use asynchronous writes and tune your SMB mount options. The mount command you’d use includes flags like async to avoid blocking on writes, hard and intr so the system handles network interruptions gracefully, and you can specify the protocol version to match your NAS setup. NZRT uses this approach for archival storage mounted from a local network path.

Finally, monitoring. You want to keep an eye on three things. Redis cache hit rate, which you can check using the Redis command-line tool’s stats output. This tells you how often Redis is actually serving data versus falling through to the database. PHP-FPM pool status, where a simple process list filtered to your Nextcloud pool shows you how many workers are active. And slow query logging in MySQL, where you can turn on a slow query log and set a threshold of two seconds, so any database query taking longer than that gets flagged for review.

To put this all in context, NZRT’s Nextcloud instance supports over 50 concurrent users who regularly work with large files. Redis caching is considered essential, particularly because Talk and Calendar generate a lot of short, repeated calls that would otherwise hammer the database. Getting these settings right means the system stays responsive even during busy periods.

If you want to go deeper, the related wiki notes on Administration Overview, Architecture and Tech Stack, and Logging and Monitoring are good next stops.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Rest Api Webdav

Welcome to the NZRT Wiki Podcast. Today we’re looking at Nextcloud REST API and WebDAV.

Nextcloud gives you two different ways to interact with it programmatically. The first is WebDAV, which stands for Web Distributed Authoring and Versioning. The second is a REST API. Both of these let you automate tasks, integrate third-party tools, and perform bulk operations without ever having to touch the web interface manually. Let’s walk through what each one does and how you’d actually use them.

Starting with WebDAV. This is the protocol you use for file operations — uploading, downloading, listing, and deleting files. Every WebDAV request you make goes to a base URL on your Nextcloud server, followed by a path that includes the username of the account you’re working with and then the folder path inside that account.

So, uploading a file — let’s say a PDF into a Finance folder — you’d make a PUT request, authenticate with a username and password, and point it at the exact path where you want the file to land. Nextcloud places it right there.

Downloading works the opposite way. You make a standard GET request to that same file path, authenticate, and tell your tool where to save the output locally. The file comes back to you.

Listing the contents of a folder is a little different. WebDAV uses a special request type called PROPFIND for this. You point it at a folder path, include a small XML body that tells Nextcloud you want the properties of everything inside, and it returns an XML response with details about every file and subfolder in that location.

Deleting a file is the simplest of all — a DELETE request to the file’s path, with your credentials, and it’s gone.

Now let’s move on to the REST API side of things. This is what you use for user management, sharing, and app-level operations. These requests go to a different base endpoint — the OCS API path on your Nextcloud server.

Creating a new user requires admin credentials. You send a POST request, include a special header that tells Nextcloud this is an API request rather than a browser session, and pass the new username and a temporary password. That’s it — the account exists.

Adding a user to a group follows a similar pattern. Another POST request, again with admin credentials and that same API header, but this time you target a URL that includes the username you want to update, and you pass the group name you want to add them to.

If you want to list all the shares belonging to a particular user, you send a GET request to the file-sharing endpoint with that user’s credentials and the API header. You get back a list of everything they’ve shared.

Creating a share link — the kind where anyone with the link can access a file — is also a POST request to the file-sharing endpoint. You pass the file path, a share type value of three which means public link, and a permissions value of one which means read-only. Nextcloud returns a share URL you can distribute.

Now, a quick note on authentication. There are three ways to authenticate with Nextcloud. The first is basic auth, which is just a username and password combination. This works well for development and scripting but isn’t ideal for production. The second is app tokens — these are long strings you generate inside Nextcloud and use in place of your actual password. They’re more secure and the right choice for production automation. The third is OAuth2, which is the three-step flow you’d use for integrating third-party applications that need to act on behalf of a user.

Let’s look at how NZRT actually uses these capabilities in practice. There are two main automation examples worth knowing about.

The first is a nightly invoice sync. This script queries the Dolibarr database for any invoices created in the last day, then loops through each one and uploads the corresponding PDF file into a Finance folder on Nextcloud under the ema account. This runs automatically each night, keeping Nextcloud in sync with what Dolibarr knows about.

The second is bulk user provisioning. This script reads from a CSV file — a spreadsheet with columns for name, email, and role. For each row, it makes two API calls. The first creates the user account in Nextcloud. The second adds that user to the appropriate group based on their role. So you could spin up ten new user accounts in the time it takes to run a single script.

In terms of which NZRT agents use these capabilities — dan, who handles database and backend work, uses this for automating backups and bulk provisioning. Dai, the data agent, uses the API for extracting reports and making analytics calls. And ema, who handles document and records management, uses WebDAV to sync documents from Dolibarr and run archiving workflows.

If you want to dig deeper, the related wiki articles on Integrations Overview, Dolibarr Integration, and the Nextcloud CLI Cheatsheet are good next steps.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Webdav Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at WebDAV Cheatsheet.

WebDAV is a protocol that lets you interact with files stored on a server over HTTP. For NZRT, that means you can upload, download, move, copy, and delete files on Nextcloud programmatically, without ever opening a browser. Every operation you’ll see today targets a base endpoint on the Nextcloud server, structured around a path that includes the word “dav”, then “files”, then your username. That username part is important — every command you run needs to be tailored to the account you’re working with.

Let’s start with the basics. To upload a file, you send a PUT request to Nextcloud using curl. You authenticate with your username and password, point curl at the file on your local machine, and specify the destination path on Nextcloud where you want it to land. That’s it — one command, file is uploaded.

Downloading is the reverse. You send a GET-style request with your credentials, provide the remote file path, and tell curl where to save it locally. Simple and reliable.

Now, listing files works a little differently. WebDAV uses a special request type called PROPFIND, which asks the server to return properties about files and folders. You add a header that tells the server you want to look one level deep — so you see the contents of a folder without recursing into subfolders. You also pass a small XML body that asks for file properties. The server responds with an XML document describing everything in that folder.

Deleting a file is straightforward — you send a DELETE request with your credentials and the path to the file. No confirmation prompt, so be deliberate.

Creating a folder uses another WebDAV-specific request type called MKCOL, short for “make collection.” You point it at the path where you want the new folder, authenticate, and Nextcloud creates it.

Copying a file involves a COPY request. You point curl at the source file, and you add a Destination header that specifies where the copy should end up. Both paths need to be full URLs on the server.

Moving or renaming a file works the same way — you use a MOVE request instead, with a Destination header pointing to the new path or new name. One command handles both renaming and relocating.

If you need file metadata — things like content type, file size, last modified date, or an internal file ID — you use PROPFIND again, but this time against a specific file rather than a folder, and you pass a more detailed XML body listing exactly which properties you want back.

Now for the more powerful stuff. There’s a bulk upload script that loops through all PDF files in your current directory and uploads each one to a specific remote folder on Nextcloud. You set four variables at the top: the server URL, your username, your password, and the remote path — in the example that’s a Finance invoices folder for April 2026. The script then iterates over every PDF it finds and fires off a PUT request for each one, printing a confirmation line after each upload.

The bulk download script does the opposite. It first runs a PROPFIND to list files in a remote folder, extracts the file paths from the XML response, then loops through them and downloads each one to your current local directory.

You can also mount Nextcloud as a drive on Linux. You install a package called davfs2, then run a mount command pointing at the WebDAV URL, specifying a local mount point and your username. You’ll be prompted for your password. Once mounted, you interact with Nextcloud files exactly like a local folder. To disconnect, you run the unmount command.

On Windows, it’s even simpler. Open File Explorer, choose Map Network Drive, paste in the WebDAV URL with your username at the end, check the option to connect with different credentials, and enter your Nextcloud username and password. Nextcloud appears as a mapped drive in Explorer.

There’s also a practical NZRT example in the cheatsheet — a nightly invoice sync. This script queries the Dolibarr database for invoices created in the last day, then uploads each matching PDF from the local Dolibarr invoices folder to Nextcloud under the Finance invoices path. This is the kind of automation WebDAV was built for: bridging your ERP system and your file server without any manual steps.

To put it in context for NZRT — WebDAV is the glue that connects Nextcloud to your other systems. Whether you’re syncing Dolibarr invoices, running nightly backups, or building integrations with third-party tools, WebDAV gives you a consistent, scriptable interface to your Nextcloud storage. Related topics worth reading alongside this one include the REST API and WebDAV overview, the Nextcloud CLI Cheatsheet, and the Dolibarr Integration notes.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Hooks Filters

Welcome to the NZRT Wiki Podcast. Today we’re looking at Hooks & Filters.

If you’ve spent any time working with WordPress, you’ve probably heard the word “hooks” thrown around. But what exactly are they, and why do they matter? Let’s break it down in a way that actually makes sense.

At its core, WordPress hooks are a system that lets you — as a plugin or theme developer — step into WordPress at specific moments and either do something extra, or change something that’s already happening. The key idea is that you never have to touch the WordPress core files themselves. Instead, WordPress gives you these designated spots where you can attach your own code, and it handles the rest.

There are two types of hooks you need to know about: actions and filters. They sound similar but they do different things.

Let’s start with actions. An action hook is about doing something at a specific event. Think of it like an alarm going off — when WordPress reaches a certain point in its process, it fires that alarm, and any code you’ve attached to it wakes up and runs.

Here’s a simple example to picture. Say you want to add some custom text to the footer of every page on your site. You’d tell WordPress: “Hey, when you get to the footer moment, run my function.” The code example in the docs shows exactly that — you register a function called something like “my custom footer,” attach it to the footer event, and whenever WordPress builds that part of the page, it calls your function and outputs a paragraph of custom footer text. You didn’t touch a single core file. WordPress just calls your code at the right moment.

Now let’s talk about filters. Filters are a little different. Instead of just doing something, they’re about modifying data before it gets used or displayed. WordPress passes a piece of content to your function, you change it, and you hand it back.

Picture the post content on a blog. WordPress grabs your post text from the database, and before it shows it on screen, it runs it through a series of filters. The example in the docs shows a filter hooked into the content pipeline — your function receives the raw content, wraps it inside a div element, and returns that wrapped version. WordPress then uses that modified version for display. You’ve changed the output without ever touching how WordPress stores or retrieves it. Clean and surgical.

Now here’s where it gets a bit more nuanced — hook priority. When multiple functions are attached to the same hook, WordPress needs to know what order to run them in. That’s where priority comes in. Every hook has a default priority of ten. Lower numbers run earlier, higher numbers run later.

So if you have two filters both attached to the post title hook, and one has a priority of five while the other has a priority of fifteen, the one set to five runs first. It gets to modify the title before the second function even sees it. This matters a lot when the output of one function is meant to feed into another. Getting the order wrong can produce unexpected results, so it’s worth being deliberate about priority when you’re stacking multiple hooks.

Now let’s bring this back to NZRT specifically. If you’re working on anything inside the WP-Script plugin, you’ll see hooks used heavily to integrate Dolibarr REST API calls directly into the WordPress request cycle. Rather than building bespoke page loads or custom endpoints from scratch every time, hooks let the plugin slot into exactly the right moment in WordPress’s flow to make those API calls happen transparently.

SmartSlider 3 Pro, which you might be working with for visual content, also hooks into the theme templates — meaning it can inject slider content at template-level hook points without requiring manual template edits.

And if you’ve been involved in the agent role-based content filtering work, that’s another place hooks shine. Custom hooks allow content to be shown or hidden based on a user’s role, all handled through filter logic rather than conditional spaghetti scattered through templates.

So to pull it all together: actions let you run code at events, filters let you modify data in transit, priority controls the order everything fires in, and NZRT uses all of this to keep WordPress integrations modular and maintainable. If you want to dig deeper, the related topics to check out are Plugin Development, the WordPress REST API, and the WP-Script Core documentation.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Jc Importer

Welcome to the NZRT Wiki Podcast. Today we’re looking at JC Importer.

JC Importer is a utility plugin for WordPress, and at NZRT we use it whenever we need to bring in large amounts of data without doing it all by hand. Specifically, it gives you the ability to import posts from CSV files, create users in bulk, map your data columns to the right WordPress fields, preview what you’re about to import before you commit to it, and catch duplicate entries before they cause problems. Those five capabilities cover most of the heavy lifting you’d ever need to do when populating or updating a WordPress site with external data.

So how does it actually work in practice? The process runs in five steps, and they’re pretty straightforward. First, you upload your CSV file into the plugin. Second, you tell the plugin how your columns match up to WordPress fields — so for example, which column in your spreadsheet is the post title, which one is the content body, which one is the author, and which one holds the category. Third, and this is the part that saves a lot of headaches, you run a preview. It shows you a sample of five rows so you can confirm everything is mapped correctly before anything actually gets written to the database. Fourth, once you’re happy with how it looks, you execute the full import. And fifth, you go through the results and check the error log to see if anything didn’t make it through cleanly.

Now, why does NZRT actually reach for this tool? There are four main situations where it comes in handy for us. The first is when we’re pulling product listings out of Dolibarr — our ERP system — and we need to get those into WordPress in one go rather than entering them one by one. The second is when we need to create a whole set of pages for module categories in bulk. The third is when HR data needs to come across into WordPress, like employee information that originates in Dolibarr’s HR module. And the fourth is when we need to update pricing and product descriptions across a large number of entries at once — again, doing that manually would take far too long.

The common thread across all of those use cases is that you’ve got structured data living somewhere outside WordPress — usually in a spreadsheet or a Dolibarr export — and you need a reliable, repeatable way to get it into the site without risking errors from manual entry or wasting time on repetitive copy-paste work.

If you want to dig deeper into the broader context around this plugin, there are a few related wiki notes worth checking out: the Plugins Overview, the WordPress Overview, and the NZRT Site Overview. Those will give you a fuller picture of how JC Importer fits into the wider stack.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Plugin Development

Welcome to the NZRT Wiki Podcast. Today we’re looking at 🔧 Plugin Development.

So let’s start with the big picture. WordPress plugins are how you extend what WordPress can do without ever touching the core files. That’s an important distinction — you’re not hacking into WordPress itself, you’re adding on top of it. And the way WordPress knows something is a plugin is surprisingly simple: it just needs to be a PHP file with a special comment block at the top.

That comment block is called the plugin header, and it’s basically the plugin’s ID card. Picture a block of text near the top of a PHP file. It contains a few labelled fields — things like Plugin Name, which is just what you want to call it, a Description explaining what it does, a Version number like one point zero point zero, the Author’s name, and a License — in this case GPL version two or later. WordPress reads that block and goes “yep, that’s a plugin, I know what to do with this.” Without it, WordPress won’t recognise the file as a plugin at all. So that header is non-negotiable.

Now, once your plugin is recognised, how do you actually make it do things? That’s where hooks come in, and there are two types you need to know about.

The first type is called Actions. An action lets you run your own code at a specific moment in the WordPress lifecycle. You register an action by calling the add action function. You give it two things: the name of the hook — which represents a moment in time, like when the site footer loads — and the name of your own function that you want to run at that moment. So if you want something to happen right as WordPress is wrapping up the bottom of the page, you’d hook into the footer event and point it at your function.

The second type is called Filters. Filters are similar, but instead of just running code, you’re intercepting data and modifying it before WordPress uses it. You use the add filter function in the same way — give it the name of a hook and your function name. A good example from the documentation is filtering the title of a post. You can grab that title as it’s being processed and change it before it ever appears on the screen.

Speaking of hooks, there are a handful of common ones worth keeping in mind. The init hook fires once WordPress has fully initialised — it’s a good general-purpose starting point for a lot of plugin logic. The wp enqueue scripts hook is where you’re supposed to register and load your CSS stylesheets and JavaScript files. The wp footer hook runs when the footer of the site is being output. The rest api init hook is where you’d set up any custom REST API endpoints, and save post fires whenever a post gets saved — which is handy if you need to trigger something in response to content being created or updated.

And just to bring this into the NZRT context — if you’ve worked with the NZRT stack, you’ll know that we have a custom plugin called WP-Script, which uses exactly this kind of plugin architecture to connect WordPress to the Dolibarr REST API. That integration relies on these same hook patterns to run at the right moments and pass data between the two systems. Beyond that, the WordPress setup here also uses plugins like SmartSlider3 for media, JC Importer for bringing in content, and others that extend navigation and core features — all built on the same foundation you’ve just heard about.

If you want to dig deeper, the related areas to explore are Hooks and Filters in more detail, the WP-Script Core documentation, and the broader Plugins Overview in the wiki.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Wp Cli Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at 💻 WP CLI Cheatsheet.

So let’s start with the basics. WP-CLI is the command-line interface for WordPress. What that means for you is that instead of logging into the WordPress dashboard in your browser and clicking through menus, you can type commands directly into a terminal and get things done faster. It’s especially useful when you’re managing sites remotely, running bulk operations, or automating repetitive tasks.

Let’s walk through the main command groups you’ll use day to day.

First up, posts. There are four key things you can do here. You can list all posts currently in the system, you can pull up a specific post by its numeric ID, you can create a brand new post by providing a title, and you can delete a post by its ID. Simple, direct, and no browser required.

Next, plugins. This is where WP-CLI really shines. You can list all installed plugins so you can see what’s active and what’s not. You can activate a specific plugin by name, deactivate it, or install a new one straight from the WordPress plugin repository. And if you want to keep everything up to date in one go, there’s a command to update all plugins at once. That last one is a real time-saver on sites with a lot of plugins.

Then there’s user management. You can list all users on the site, create a new user by supplying a username, an email address, and a role like editor or administrator, delete a user by their ID, and even add specific capabilities to a user account. That last one is handy when you need to grant someone a particular permission without changing their entire role.

Moving on to the database. WP-CLI gives you three essential database commands. You can export the entire database to a SQL file, which is great for backups before you make any big changes. You can import a SQL file back in, which is useful for restores or migrations. And you can run an optimize command, which cleans up the database tables and can help with performance on older or high-traffic sites.

Finally, options. WordPress stores a lot of site configuration in what it calls options. You can retrieve the value of any option by name, update it with a new value, or list everything in one go. A common use case here is updating the site URL when you’re migrating a site from one domain to another.

Now, an important practical note. WP-CLI availability on your Hoopla shared cPanel hosting is not guaranteed. It depends on what the host has installed. The best way to check is to open an SSH terminal session through cPanel and see if the command is recognised. If it’s not available, don’t worry. You can fall back to the WordPress admin interface or cPanel’s built-in tools to accomplish the same things.

If you do have SSH access and WP-CLI is not installed, you can install it yourself. The process involves four steps. You download the WP-CLI package file using curl, you make that file executable, you move it to a system location so it’s available as a global command, and then you verify the installation by checking the version number. Once that’s done, you’re good to go.

One thing to keep in mind when using WP-CLI: you generally need to run commands from inside the WordPress installation directory, or point the tool at the correct path. If you’re getting errors about WordPress not being found, that’s usually the first thing to check.

For anyone at NZRT working on WordPress projects, WP-CLI pairs well with the broader WordPress workflow. The wiki has related notes covering WordPress as a platform, plugin development, and theme development, so if you want to go deeper on any of those areas, those pages are worth a look.

To recap what we covered today: WP-CLI lets you manage WordPress entirely from the command line. You can handle posts, plugins, users, the database, and site options all without touching the browser. On shared hosting like Hoopla, check SSH first to confirm availability. And if you need to install it yourself, it’s a four-step process that takes only a few minutes.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

WordPress Functions Cheatsheet

Welcome to the NZRT Wiki Podcast. Today we’re looking at 📚 WordPress Functions Cheatsheet.

If you’ve ever built a WordPress plugin or theme, you know how quickly you end up hunting through documentation just to remember the exact name of a function. This cheatsheet is your quick reference for the most common WordPress PHP functions — the ones you’ll reach for again and again. Let’s walk through them group by group.

We’ll start with posts, because that’s the heart of almost everything in WordPress. The first function lets you fetch multiple posts at once by passing in an array of arguments — things like post type, category, number of results, and ordering. You get back an array of post objects to loop through. If you only need one specific post and you already know its ID, there’s a simpler function that takes just that ID and hands you back a single post object. From there, if you’re inside the Loop — WordPress’s standard way of rendering content — you can call a function that just outputs the post title directly to the page. Same idea for the content. Another function handles creating or updating posts: you pass it an array of data describing the post, and WordPress takes care of inserting it into the database. And when you need to remove a post entirely, you pass its ID to the delete function and it’s gone.

Next up are hooks, which are one of the most powerful concepts in WordPress. Think of hooks as event listeners. There are two flavours: actions and filters. An action hook lets you attach your own function to a specific moment in WordPress’s execution — for example, right after a post is saved, or when the page header loads. You register your callback with the action hook name, and WordPress calls it at the right time. Filters are similar, but instead of just running code at a moment, they let you intercept a value, modify it, and pass it back. So if WordPress is about to output a piece of text, a filter lets you jump in and change what gets displayed. On the firing side, there are two corresponding functions — one that triggers an action hook so all attached callbacks run, and one that runs a value through all attached filters and returns the result. Understanding this four-function pattern — add action, add filter, do action, apply filters — gives you enormous flexibility in both plugins and themes.

Now let’s talk about the database layer. WordPress has its own options system for storing simple key-value settings, and there are two functions for it. One retrieves a stored option by name, and the other saves or updates a value against that name. These are perfect for plugin settings. For anything more complex, WordPress gives you access to a global database object. You can pass a raw SQL query string to it and execute it directly — useful for custom tables or complex queries that the higher-level functions don’t cover. Just be careful to sanitise your inputs properly when going this route.

Finally, we have user functions. If you need to know who’s currently logged in, there’s a function that returns their user ID — simple as that. Once you have that context, you’ll often want to check whether they’re allowed to do something before you let them. There’s a function for that too: you pass it a capability string — things like “edit posts” or “manage options” — and it returns true or false based on the current user’s role and permissions. This is the right way to gate functionality in WordPress rather than checking roles directly. And if you need to pull extra data stored against a user — things beyond their name and email, like custom profile fields — there’s a function that takes the user ID and a meta key and gives you back that stored value.

Tying it all together, this cheatsheet maps to three broader areas you’ll want to explore in the related notes: Plugin Development, Theme Development, and Hooks and Filters. The functions here are the building blocks that appear across all three. Whether you’re registering a custom post type, building a settings page, or intercepting content before it renders, you’ll almost certainly be combining functions from these four groups.

Keep this reference handy, and you’ll spend a lot less time digging through the WordPress developer docs and a lot more time actually building.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

WordPress Rest Api

Welcome to the NZRT Wiki Podcast. Today we’re looking at 🔌 WordPress REST API.

So what is the WordPress REST API? Put simply, it’s a way for other software to talk to your WordPress site using standard web requests. Instead of logging into a dashboard and clicking around, you can send a request to a specific web address and get data back in a format called JSON — which is just structured text that machines are very good at reading. Plugins, mobile apps, and external services all use this approach to read and write things like posts, users, and custom data.

Let’s talk about the basic building blocks. There are five core actions you’ll use constantly. First, you can fetch a list of all your posts by hitting the posts endpoint on your site’s API path. Second, if you want just one specific post, you add its ID to the end of that same address. Those two are read-only, so no credentials needed. But the next three — creating a new post, updating an existing one, or deleting one — all require you to be authenticated first. WordPress won’t let just anyone write or delete content through the API, which makes sense.

Speaking of authentication, how do you prove who you are? The most common approach at NZRT is application passwords. Think of it like a special one-time key you generate in your WordPress user profile — separate from your main login password. When you make a request, you pass your username and that application password together. There’s a command-line example that shows this in action: you send a request to the users endpoint asking for your own profile, and you pass your credentials alongside it. If everything checks out, WordPress returns your user data as JSON. OAuth2 is another option if you’re building something more complex that needs delegated access.

Now here’s where things get really powerful — custom endpoints. WordPress lets you register your own API routes, not just the built-in ones for posts and pages. There’s a PHP snippet that shows exactly how this works. You hook into WordPress at the point where the REST API is being set up, then you declare a new route — in this example it lives under a namespace called myapp version one, and the path is slash data. You tell WordPress it should respond to GET requests, point it to a callback function that actually handles the logic, and set a permission callback. In this case the permission is set to always return true, meaning anyone can call it — though in a real-world scenario you’d want to lock that down based on user roles.

So why does all of this matter at NZRT specifically? Your WP-Script plugin is the practical answer. It uses the WordPress REST API as a bridge between WordPress and Dolibarr — your ERP system. When data changes in Dolibarr, whether that’s customer records, invoices, or HR data, WP-Script can pull that through via REST API calls and keep things in sync. External integrations also rely on this same mechanism for data exchange, so anything talking to your WordPress site from the outside is almost certainly going through these endpoints.

If you want to dig deeper, check out the related notes on WordPress Overview, WP-Script Core, and Dolibarr REST API Setup — they’ll give you the full picture of how these pieces connect.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

X402 Overview

Welcome to the NZRT Wiki Podcast. Today we’re looking at x402 Overview.

So let’s start with the basics. x402 is an open protocol designed for machine-to-machine HTTP micropayments. The name comes from HTTP status code 402, which is called Payment Required — a code that’s been sitting in the HTTP specification for decades but was never seriously put to use. Until now.

The protocol was specified by Coinbase and lives on GitHub, and there’s a facilitator service running at x402.org that handles the verification side of things. We’ll get to what a facilitator does in a moment.

First, why does this exist? Think about how APIs are monetised today. You need to create an account, manage API keys, set up a billing system, choose subscription tiers, and build rate limiting infrastructure. That’s a lot of overhead — especially for automated systems or AI agents that just want to call an endpoint and get a result.

x402 strips all of that away. There are no accounts, because the payment itself acts as your authentication. There are no API keys, because your wallet signature proves you’ve paid. There’s no billing system, because payments happen per request on-chain. And crucially, it’s native to AI agents — machines can pay other machines automatically, with no human in the loop.

So how does it actually work? Here’s the flow. A client makes a request to a paid endpoint. The server responds with that HTTP 402 Payment Required status, and includes a set of payment requirements — how much to pay, which network to use, and which wallet address receives the funds. The client then creates a signed USDC transfer using a standard called EIP-712, which is a typed signature format for Ethereum-based transactions. The client attaches that signed authorisation to a second request and retries the call. The server takes that payment header and forwards it to a facilitator, which is an off-chain service that checks the signature. Once the facilitator confirms everything is valid, the server delivers the content.

The important thing to note is that verification happens off-chain, so there is no gas cost just to verify a payment. And there’s only one extra round-trip per uncached request, which keeps latency manageable.

Now let’s talk about where you’d actually use this. There are five key use cases. First, API monetisation — any HTTP endpoint can become a pay-per-request service. Second, AI agent tooling — agents can automatically pay for the tools and data they need. Third, knowledge base access — you could charge a small amount per document retrieval, like a wiki search. Fourth, metered compute — paying per AI inference, per render, or per export. And fifth, content paywalls — charging a micropayment for access to an article, a report, or a dataset.

The protocol is built on two specific technologies that make micropayments practical. The blockchain is Base, an Ethereum Layer 2 network backed by Coinbase. Gas fees on Base are fractions of a cent, which is what makes tiny payments economically viable. The currency is USDC, Circle’s stablecoin that tracks one-to-one with the US dollar and is native to EVM-compatible chains. On Ethereum mainnet, a payment of a fraction of a cent would cost more in gas than the payment itself — Base solves that entirely.

Finally, there are six key concepts worth locking in. The resource server is the HTTP server that enforces payment — it responds with the 402 and ultimately delivers your content. The facilitator is the off-chain service that verifies payment signatures, acting as the trust layer between client and server. Payment requirements are the details returned in that 402 response — the amount, network, asset type, and payee address. The payment authorisation is the signed transfer the client attaches to its retry request. The payee is the EVM wallet address receiving the USDC. And the payer is the EVM wallet address doing the paying — it signs the transfer to authorise the whole thing.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.

Wp Script Core

Welcome to the NZRT Wiki Podcast. Today we’re looking at WP-Script Core.

So let’s start with the big picture. WP-Script Core is a custom WordPress plugin built by NZRT. Its job is to extend the RetroTube theme — that’s the theme running on nzrtnetwork.com — with frontend user interface scripting capabilities. In plain terms, it’s a tool that sits on top of the theme and gives it extra powers on the browser side of things, the part that visitors actually see and interact with.

Now, before we go any further, there’s one thing worth calling out clearly, because it’s a common point of confusion. WP-Script Core is not a Dolibarr integration bridge. You might be wondering what that means. Dolibarr is the ERP system NZRT uses — it handles things like projects, tickets, invoices, and agent workflows. You might assume that because WP-Script Core is a WordPress plugin and NZRT connects WordPress to Dolibarr, this plugin must be the thing doing that connection work. It isn’t. When WordPress agents need to talk to Dolibarr, they call the Dolibarr REST API directly, using their own per-agent API keys. That happens completely separately from this plugin. WP-Script Core has nothing to do with that pipeline.

So what does WP-Script Core actually do? Its purpose breaks down into three main areas.

The first is theme UI extension. This is the core reason the plugin exists. It adds frontend scripting and layout features to the RetroTube theme that the theme doesn’t provide out of the box. Think of it as a companion layer that sits alongside the theme and enhances what it can do visually and behaviourally.

The second area is custom scripts. The plugin handles the injection of page-specific scripts and styles. What this means in practice is that certain pages on nzrtnetwork.com can have their own scripts loaded precisely when and where they’re needed, rather than having everything loaded globally all the time. This keeps things clean, targeted, and performant.

The third area is NZRT branding. Beyond generic layout features, the plugin implements custom theme behaviours that are specific to nzrtnetwork.com. These are the little things that make the site feel distinctly NZRT — behaviours, interactions, and styling choices that reflect the brand and wouldn’t belong in a general-purpose theme.

Putting it all together, WP-Script Core is really about making the RetroTube theme work exactly the way NZRT needs it to for their specific site. The theme provides the foundation, and WP-Script Core builds on top of that foundation on the frontend.

If you’re working in this area and you need to understand how Dolibarr connects to WordPress more broadly, the wiki has related notes you can follow up on. There’s a Dolibarr REST API Setup note, a WP-Dolibarr Integration Overview note, a WordPress REST API note, and a Plugins Overview note. Those will give you the full picture of how the different moving parts connect at the system level. But just remember — WP-Script Core isn’t part of that integration chain. Keep it in its lane: frontend, theme, UI scripting, branding.

One last thing worth reinforcing as you work with this plugin. Because it operates at the theme UI layer, changes you make here will have direct visible effects on the site for anyone visiting nzrtnetwork.com. It’s not a backend utility running quietly in the background. It touches what users see. So treat modifications carefully, test in context, and be mindful that the RetroTube theme and WP-Script Core are working as a team — changes to one can interact with the other.

That’s the essence of WP-Script Core. A custom plugin, tightly scoped to frontend UI work on the RetroTube theme, with no role in Dolibarr integration, and three clear jobs to do: extend the theme’s layout capabilities, inject page-specific scripts and styles where needed, and deliver the custom behaviours that make nzrtnetwork.com feel like NZRT.

That’s it for this episode of the NZRT Wiki Podcast. Thanks for listening.