Skip to the content.

Bastion Label Format Specification

Version: 1.0
Date: 2025-11-30
Status: Stable

Note on Examples: Version numbers shown in examples (e.g., VERSION=0.3.0) are illustrative. Refer to VERSION for the canonical tool version used during label generation.

Overview

This specification defines the unified label format for Bastion. Labels serve two purposes:

  1. 1Password Tag Hierarchy — The hierarchy portion creates browsable folder structure in 1Password UI
  2. Reproducible Generation — The complete label contains all parameters needed to regenerate credentials

Design Principles

Principle Rationale
Front-loaded hierarchy Most important classification info appears first, creating useful 1P folder structure
Consistent separators / for hierarchy, : for metadata, # for params, \| for check digit
UPPERCASE attributes Visual consistency, easy parsing, follows cryptographic convention
URL query-string notation ATTR=value&ATTR=value pattern uses familiar URL conventions
Self-describing VERSION always required; label contains everything needed for regeneration
1P tag compatible Hierarchy portion (before first :) is a valid 1Password tag

Grammar

LABEL     = HIERARCHY ":" IDENT ":" DATE "#" PARAMS "|" CHECK
LABEL     = HIERARCHY ":" IDENT ":" DATE "#" PARAMS          (without check)

HIERARCHY = TOOL "/" TYPE "/" ALGO
ALGO      = ALGO_PART ("/" ALGO_PART)*
PARAMS    = "VERSION=" VERSION_NUM ("&" ATTR "=" VALUE)*

TOOL      = "Bastion" (or other PascalCase tool name)
TYPE      = "USER" | "CARD" | "KEY"
ALGO_PART = [A-Z0-9]+
IDENT     = [a-z0-9._-]+
DATE      = YYYY-MM-DD | user-defined
VERSION_NUM = MAJOR ("." MINOR)?
ATTR      = [A-Z]+
VALUE     = [A-Za-z0-9._-]+   (non-empty, no &, =, |, :, #)
CHECK     = [0-9A-Z]  (single Luhn mod-36 character)

Field Definitions

Hierarchy Fields (1Password Tag)

Field Format Description Examples
TOOL PascalCase Tool that generated the credential Bastion
TYPE UPPERCASE Generator type USER, CARD, KEY
ALGO UPPERCASE, /-separated Algorithm family/variant SHA2/512, SLIP39/ARGON2ID

Metadata Fields

Field Format Description Examples
IDENT lowercase, allows . - _ Service or purpose identifier github.com, aws-prod, banking.a0
DATE ISO 8601 recommended Generation date or descriptor 2025-11-30, initial

Parameter Fields

Field Format Description Examples
VERSION VERSION={semver} Bastion tool version that generated the credential (always required) VERSION=0.3.0, VERSION=1.0.0
TIME TIME={n} Argon2 time cost (iterations) TIME=3, TIME=10
MEMORY MEMORY={n} Argon2 memory cost (KB) MEMORY=65536, MEMORY=2048
PARALLELISM PARALLELISM={n} Argon2 parallelism (threads) PARALLELISM=4, PARALLELISM=8
NONCE NONCE={value} Random nonce for non-recoverable generation (see Nonce Mode) NONCE=Kx7mQ9bL
LENGTH LENGTH={n} Output length (characters) LENGTH=16, LENGTH=24
ENCODING ENCODING={n} Output encoding alphabet size ENCODING=36, ENCODING=64, ENCODING=90

Note on VERSION parameter: The VERSION parameter records which version of Bastion generated the credential. This allows checking release history for potential breaking changes. For example, VERSION=0.3.0 indicates the credential was generated by Bastion v0.3.0. This replaces the previous dual-parameter system (BASTION + VERSION) used in earlier drafts.

Nonce Mode (Stolen Seed Protection)

The NONCE parameter enables non-recoverable usernames that provide protection against stolen seeds.

Threat Model: Without nonce mode, an attacker who compromises the secret salt can regenerate ALL usernames by iterating through common domains. Nonce mode prevents this attack by including a random value in the generation that only exists in the stored label.

How It Works:

  1. When --nonce flag is used, a random URL-safe string is generated
  2. The nonce is included in the HMAC message: HMAC(salt, label + ":" + nonce)
  3. The nonce is stored in the label’s PARAMS section: NONCE=Kx7mQ9bL
  4. To regenerate, you MUST have both the salt AND the original label with nonce

Security Trade-off:

Mode Recoverable? Stolen Seed Risk Dependency
Standard Yes, from salt + domain All usernames exposed Salt only
Nonce Only with original label Only stored usernames exposed Salt + 1Password

When to Use Nonce Mode:

When NOT to Use Nonce Mode:

Delimiters

Delimiter Purpose Example
/ Hierarchy separator (creates 1P folders) Bastion/USER/SHA2/512
: Metadata field separator :github.com:2025-11-30
# Params section marker #VERSION=1&LENGTH=16
& Param attribute separator (URL-style) TIME=3&MEMORY=65536&PARALLELISM=4
= Attribute name/value separator (URL-style) VERSION=1, LENGTH=16
\| Check digit separator \|K

Algorithm Naming Convention

Algorithms use hierarchical naming with / separator:

Hash Functions

Algorithm Label Format 1P Folder Path
SHA-256 (SHA-2) SHA2/256 …/SHA2/256
SHA-512 (SHA-2) SHA2/512 …/SHA2/512
SHA3-256 SHA3/256 …/SHA3/256
SHA3-512 SHA3/512 …/SHA3/512

Key Derivation Functions

Algorithm Label Format 1P Folder Path
Argon2id ARGON2ID …/ARGON2ID
SLIP39 + Argon2id SLIP39/ARGON2ID …/SLIP39/ARGON2ID
PBKDF2 + SHA-256 PBKDF2/SHA2/256 …/PBKDF2/SHA2/256

Key Types

Algorithm Label Format 1P Folder Path
X25519 X25519 …/X25519
Ed25519 ED25519 …/ED25519
RSA 2048-bit RSA/2048 …/RSA/2048
RSA 4096-bit RSA/4096 …/RSA/4096

Parameter Ordering

When multiple attributes are present, they MUST appear in this fixed order:

  1. VERSION — Bastion tool version (SemVer, always required)
  2. TIME — Argon2 time cost
  3. MEMORY — Argon2 memory cost
  4. PARALLELISM — Argon2 parallelism
  5. NONCE — Generation nonce
  6. LENGTH — Output length
  7. ENCODING — Output encoding

Only include attributes relevant to the algorithm. VERSION is always required.

Examples

Bastion Username (SHA2-512)

Bastion/USER/SHA2/512:github.com:2025-11-30#VERSION=0.3.0&LENGTH=16|K
Component Value Description
1P Tag Bastion/USER/SHA2/512 Browsable in 1Password
IDENT github.com Service identifier
DATE 2025-11-30 Generation date
VERSION 0.3.0 Bastion tool version
LENGTH 16 16-character username
CHECK K Luhn mod-36 check digit

Bastion Username with Nonce (SHA2-512, Stolen Seed Protection)

Bastion/USER/SHA2/512:github.com:2025-11-30#VERSION=0.3.0&NONCE=Kx7mQ9bL&LENGTH=16|M
Component Value Description
1P Tag Bastion/USER/SHA2/512 Browsable in 1Password
IDENT github.com Service identifier
DATE 2025-11-30 Generation date
VERSION 0.3.0 Bastion tool version
NONCE Kx7mQ9bL Random nonce (makes non-recoverable)
LENGTH 16 16-character username
CHECK M Luhn mod-36 check digit

Security Note: This username cannot be regenerated from salt + domain alone. The nonce value must be preserved in 1Password.

Bastion Username (SHA3-512, minor version)

Bastion/USER/SHA3/512:aws.amazon.com:2025-11-30#VERSION=1.1&LENGTH=24|M
Component Value Description
1P Tag Bastion/USER/SHA3/512 SHA3 variant
VERSION 1.1 Label format v1.1
LENGTH 24 24-character username

Bastion Card Token (SLIP39/ARGON2ID)

Bastion/CARD/SLIP39/ARGON2ID:banking.a0:2025-11-30#VERSION=1&TIME=3&MEMORY=2048&PARALLELISM=8&NONCE=Kx7mQ9bL&ENCODING=90|X
Component Value Description
1P Tag Bastion/CARD/SLIP39/ARGON2ID Full algo hierarchy
IDENT banking.a0 Card ID + index
TIME 3 Argon2 iterations
MEMORY 2048 Argon2 memory (KB)
PARALLELISM 8 Argon2 threads
NONCE Kx7mQ9bL Unique nonce
ENCODING 90 Base90 output

Bastion Key (X25519, minimal params)

Bastion/KEY/X25519:ssh-primary:2025-11-30#VERSION=1|J
Component Value Description
1P Tag Bastion/KEY/X25519 Key type
IDENT ssh-primary Key purpose
VERSION 1 Only required param

Bastion Key (RSA 4096)

Bastion/KEY/RSA/4096:signing-key:2025-11-30#VERSION=1|Q
Component Value Description
1P Tag Bastion/KEY/RSA/4096 RSA with key size in hierarchy

1Password Tag Extraction

To extract the 1Password tag from a label:

TAG = everything before the first ":"

Examples:

This creates a browsable folder hierarchy in 1Password:

Bastion/
├── USER/
│   ├── SHA2/
│   │   ├── 256/
│   │   └── 512/
│   └── SHA3/
│       └── 512/
├── CARD/
│   └── SLIP39/
│       └── ARGON2ID/
├── KEY/
│   ├── X25519/
│   ├── ED25519/
│   └── RSA/
│       ├── 2048/
│       └── 4096/
├── SALT/               # Salt items (no algorithm hierarchy)
├── ENTROPY/            # Entropy pool items
└── CONFIG/             # Configuration items

Luhn Mod-36 Check Digit

The optional check digit detects transcription errors:

The check digit catches:

Design Decisions

Decision Choice Rationale
Hierarchy separator / Native 1Password tag hierarchy support
VERSION location In PARAMS, not hierarchy Keeps 1P folder structure clean; version is parsing metadata
PARAMS at end After # Separates machine-parsed params from human-readable hierarchy/metadata
Attribute format ATTR.value Consistent dot notation; avoids ambiguity with - separator
UPPERCASE attributes TIME, MEMORY, etc. Visual consistency, cryptographic convention, easy parsing
Tool name case PascalCase Better UX in 1Password UI folder display
CARD type Bastion/CARD/... Seed card tokens unified under Bastion; distinguishes from usernames and keys
SHA2 vs SHA SHA2/512 Explicit SHA-2 family disambiguation from SHA-1 and SHA-3
RSA with size RSA/2048 Groups RSA keys; size in hierarchy for browsability
VERSION always required Yes Labels are fully self-describing; no implicit defaults
Fixed param order Yes Deterministic parsing; canonical form for comparison

Character Sets

Field Allowed Characters
TOOL [A-Za-z][A-Za-z0-9]* (PascalCase)
TYPE [A-Z]+
ALGO [A-Z0-9/]+
IDENT [a-z0-9._-]+
DATE [A-Za-z0-9-]+
PARAMS keys [A-Z]+ (UPPERCASE attribute names)
PARAMS values [A-Za-z0-9._-]+ (non-empty, no &, =, \|, :, #)
CHECK [0-9A-Z] (single character)

Validation Rules

  1. HIERARCHY must contain at least TOOL/TYPE/ALGO
  2. IDENT must not be empty
  3. DATE must not be empty
  4. PARAMS must start with VERSION={n}
  5. If | is present, exactly one CHECK character must follow
  6. All attributes in PARAMS must be UPPERCASE
  7. Attributes must appear in canonical order
  8. Parameter values must be non-empty and contain only [A-Za-z0-9._-]
  9. Parameter values cannot contain reserved characters: &, =, |, :, #

1Password Record Structure

When creating 1Password items, the label components are stored in structured sections for both human readability and machine recovery.

Tags

The HIERARCHY portion of the label becomes a 1Password tag, creating a browsable folder structure:

Tags: Bastion/USER/SHA2/512

The tag is extracted from the label hierarchy (everything before the first :).

Bastion Label Section

A dedicated section stores the complete label and its parsed components, in label order:

Field Type Description Example
Label text Complete label string Bastion/USER/SHA2/512:github.com:2025-11-30#VERSION=1&LENGTH=16\|K
Type text Generator type USER
Algorithm text Algorithm identifier SHA2/512
Identifier text Service/purpose github.com
Date text Generation date 2025-11-30
Version text Label format version 1
Length text Output length (USER) 16
Time text Argon2 time cost (CARD) 3
Memory text Argon2 memory KB (CARD) 2048
Parallelism text Argon2 threads (CARD) 8
Nonce text Generation nonce (CARD) Kx7mQ9bL
Encoding text Output encoding (CARD) 90

Only include fields relevant to the generator type.

Salt Section

A dedicated section stores the reference to the salt used for generation:

Field Type Description Example
UUID text Reference to salt item in 1Password abc123-def456-...

Example: Username Record

Title: Github
URL: https://github.com
Username: 10178t59dlzfkece

Tags:
  - Bastion/USER/SHA2/512

[Bastion Label]
  Label: Bastion/USER/SHA2/512:github.com:2025-11-30#VERSION=1&LENGTH=16|K
  Type: USER
  Algorithm: SHA2/512
  Identifier: github.com
  Date: 2025-11-30
  Version: 1
  Length: 16

[Salt]
  UUID: abc123-def456-...

Example: Card Token Record

Title: Banking Card A0
Category: Secure Note

Tags:
  - Bastion/CARD/SLIP39/ARGON2ID

[Bastion Label]
  Label: Bastion/CARD/SLIP39/ARGON2ID:banking.a0:2025-11-30#VERSION=1&TIME=3&MEMORY=2048&PARALLELISM=8&NONCE=Kx7mQ9bL&ENCODING=90|X
  Type: CARD
  Algorithm: SLIP39/ARGON2ID
  Identifier: banking.a0
  Date: 2025-11-30
  Version: 1
  Time: 3
  Memory: 2048
  Parallelism: 8
  Nonce: Kx7mQ9bL
  Encoding: 90

[Salt]
  UUID: xyz789-...

Example: Key Record

Title: SSH Primary Key
Category: Secure Note

Tags:
  - Bastion/KEY/X25519

[Bastion Label]
  Label: Bastion/KEY/X25519:ssh-primary:2025-11-30#VERSION=1|J
  Type: KEY
  Algorithm: X25519
  Identifier: ssh-primary
  Date: 2025-11-30
  Version: 1

[Salt]
  UUID: key456-...

Example: Entropy Source Record

Title: Bastion Entropy Source #3
Category: Secure Note

Tags:
  - Bastion/ENTROPY

[Pool Info]
  pool_type: source
  source: YubiKey OpenPGP
  source_type: hardware

[Size]
  bytes: 256
  bits: 2048

[Lifecycle]
  created: 2025-11-30T10:30:00Z
  status: active

Example: Entropy Source Record (Infinite Noise TRNG)

Title: Bastion Entropy Source #4
Category: Secure Note

Tags:
  - Bastion/ENTROPY

[Pool Info]
  pool_type: source
  source: Infinite Noise TRNG
  source_type: hardware

[Device Metadata]
  serial: ABC123XYZ
  whitened: true
  collection_method: infnoise CLI

[Size]
  bytes: 64
  bits: 512

[Lifecycle]
  created: 2025-11-30T11:00:00Z
  status: active

Example: Entropy Derived Record

Title: Bastion Entropy Derived #1
Category: Secure Note

Tags:
  - Bastion/ENTROPY

[Pool Info]
  pool_type: derived
  source: combine
  derivation_method: sha3_512

[Entropy Sources]
  Source 1: abc123-def456-...
  Source 2: xyz789-012345-...
  Source 3: ghi678-jkl901-...

[Size]
  bytes: 64
  bits: 512

[Lifecycle]
  created: 2025-11-30T10:35:00Z
  status: active

Entropy Sources Section

For derived entropy pools, source UUIDs are stored as individual fields rather than a CSV string. This provides:

Field naming: Source 1, Source 2, etc. (numbered sequentially)

Field Naming Convention

Following 1Password data model best practices: