Complete rebrand cleanup: remaining env vars and references
Fix STEGASOO_* env vars → FIELDWITNESS_* and VERISOO_* → FIELDWITNESS_* across stego module, attest module, and frontends. Wire format identifiers (VERISOO\x00 magic bytes, STEGASOO-Z: QR prefixes) intentionally preserved for backwards compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
490f9d4a1d
commit
88f5571bf9
10
CLAUDE.md
10
CLAUDE.md
@ -34,9 +34,9 @@ src/fieldwitness/ Core library
|
|||||||
__init__.py Package init, __version__ (0.2.0)
|
__init__.py Package init, __version__ (0.2.0)
|
||||||
_availability.py Runtime checks for optional subpackages (has_stego, has_attest)
|
_availability.py Runtime checks for optional subpackages (has_stego, has_attest)
|
||||||
api.py Optional unified FastAPI app (uvicorn fieldwitness.api:app)
|
api.py Optional unified FastAPI app (uvicorn fieldwitness.api:app)
|
||||||
audit.py Append-only JSON-lines audit log (~/.fieldwitness/audit.jsonl)
|
audit.py Append-only JSON-lines audit log (~/.fwmetadata/audit.jsonl)
|
||||||
cli.py Click CLI entry point (fieldwitness command)
|
cli.py Click CLI entry point (fieldwitness command)
|
||||||
paths.py All ~/.fieldwitness/* path constants (single source of truth, lazy resolution)
|
paths.py All ~/.fwmetadata/* path constants (single source of truth, lazy resolution)
|
||||||
config.py Unified config loader (FieldWitnessConfig dataclass + JSON)
|
config.py Unified config loader (FieldWitnessConfig dataclass + JSON)
|
||||||
exceptions.py FieldWitnessError, ChainError, ChainIntegrityError, ChainAppendError, KeystoreError
|
exceptions.py FieldWitnessError, ChainError, ChainIntegrityError, ChainAppendError, KeystoreError
|
||||||
metadata.py Extract-then-strip EXIF pipeline with field classification
|
metadata.py Extract-then-strip EXIF pipeline with field classification
|
||||||
@ -179,7 +179,7 @@ Stego and Attest are inlined subpackages, not separate pip packages:
|
|||||||
- **Extract-then-strip model**: Stego strips all EXIF (carrier is vessel); attestation
|
- **Extract-then-strip model**: Stego strips all EXIF (carrier is vessel); attestation
|
||||||
extracts evidentiary EXIF (GPS, timestamp) then strips dangerous fields (device serial)
|
extracts evidentiary EXIF (GPS, timestamp) then strips dangerous fields (device serial)
|
||||||
- **subprocess_stego.py copies verbatim** from fieldwitness.stego -- it's a crash-safety boundary
|
- **subprocess_stego.py copies verbatim** from fieldwitness.stego -- it's a crash-safety boundary
|
||||||
- **All state under ~/.fieldwitness/** -- one directory to back up, one to destroy.
|
- **All state under ~/.fwmetadata/** -- one directory to back up, one to destroy.
|
||||||
`FIELDWITNESS_DATA_DIR` env var relocates everything (cover mode, USB mode)
|
`FIELDWITNESS_DATA_DIR` env var relocates everything (cover mode, USB mode)
|
||||||
- **Offline-first**: All static assets vendored, no CDN. pip wheels bundled for airgap install
|
- **Offline-first**: All static assets vendored, no CDN. pip wheels bundled for airgap install
|
||||||
- **Flask blueprints**: stego, attest, fieldkit, keys, admin, dropbox, federation
|
- **Flask blueprints**: stego, attest, fieldkit, keys, admin, dropbox, federation
|
||||||
@ -208,10 +208,10 @@ Stego and Attest are inlined subpackages, not separate pip packages:
|
|||||||
- **Transport-aware stego**: --transport whatsapp|signal|telegram auto-selects DCT/JPEG
|
- **Transport-aware stego**: --transport whatsapp|signal|telegram auto-selects DCT/JPEG
|
||||||
and pre-resizes carrier for platform survival
|
and pre-resizes carrier for platform survival
|
||||||
|
|
||||||
## Data directory layout (`~/.fieldwitness/`)
|
## Data directory layout (`~/.fwmetadata/`)
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.fieldwitness/
|
~/.fwmetadata/
|
||||||
config.json Unified configuration (FieldWitnessConfig dataclass)
|
config.json Unified configuration (FieldWitnessConfig dataclass)
|
||||||
audit.jsonl Append-only audit trail (JSON-lines)
|
audit.jsonl Append-only audit trail (JSON-lines)
|
||||||
carrier_history.json Carrier reuse tracking database
|
carrier_history.json Carrier reuse tracking database
|
||||||
|
|||||||
18
README.md
18
README.md
@ -48,7 +48,7 @@ fieldwitness init
|
|||||||
fieldwitness serve
|
fieldwitness serve
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates the `~/.fieldwitness/` directory structure, generates an Ed25519 identity and
|
This creates the `~/.fwmetadata/` directory structure, generates an Ed25519 identity and
|
||||||
channel key, writes a default config, and starts an HTTPS web UI on
|
channel key, writes a default config, and starts an HTTPS web UI on
|
||||||
`https://127.0.0.1:5000`.
|
`https://127.0.0.1:5000`.
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ responses.
|
|||||||
|
|
||||||
### Field Security (Fieldkit)
|
### Field Security (Fieldkit)
|
||||||
|
|
||||||
- **Killswitch** -- emergency destruction of all data under `~/.fieldwitness/`, ordered by
|
- **Killswitch** -- emergency destruction of all data under `~/.fwmetadata/`, ordered by
|
||||||
sensitivity (keys first, then data, then logs). Includes:
|
sensitivity (keys first, then data, then logs). Includes:
|
||||||
- **Deep forensic scrub** -- removes `__pycache__`, `.pyc`, pip `dist-info`, pip
|
- **Deep forensic scrub** -- removes `__pycache__`, `.pyc`, pip `dist-info`, pip
|
||||||
download cache, and scrubs shell history entries containing "fieldwitness"
|
download cache, and scrubs shell history entries containing "fieldwitness"
|
||||||
@ -307,7 +307,7 @@ FieldWitness ships four configuration presets at `deploy/config-presets/`:
|
|||||||
| `critical-threat.json` | 3 min | On | 6h / 1h grace | "System Statistics" |
|
| `critical-threat.json` | 3 min | On | 6h / 1h grace | "System Statistics" |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp deploy/config-presets/high-threat.json ~/.fieldwitness/config.json
|
cp deploy/config-presets/high-threat.json ~/.fwmetadata/config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
See [docs/deployment.md](docs/deployment.md) for the full deployment guide including
|
See [docs/deployment.md](docs/deployment.md) for the full deployment guide including
|
||||||
@ -317,7 +317,7 @@ security hardening, Kubernetes manifests, systemd services, and operational secu
|
|||||||
|
|
||||||
## CLI Reference
|
## CLI Reference
|
||||||
|
|
||||||
All commands accept `--data-dir PATH` to override the default `~/.fieldwitness` directory,
|
All commands accept `--data-dir PATH` to override the default `~/.fwmetadata` directory,
|
||||||
and `--json` for machine-readable output.
|
and `--json` for machine-readable output.
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -488,7 +488,7 @@ through Flask blueprints. Served by **Waitress** (production WSGI server) by def
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
FieldWitness loads configuration from `~/.fieldwitness/config.json`. All fields have sensible defaults.
|
FieldWitness loads configuration from `~/.fwmetadata/config.json`. All fields have sensible defaults.
|
||||||
`fieldwitness init` writes the default config file.
|
`fieldwitness init` writes the default config file.
|
||||||
|
|
||||||
### Config fields
|
### Config fields
|
||||||
@ -522,7 +522,7 @@ FieldWitness loads configuration from `~/.fieldwitness/config.json`. All fields
|
|||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `FIELDWITNESS_DATA_DIR` | Override the data directory (default: `~/.fieldwitness`). Enables portable USB mode and cover/duress directory naming |
|
| `FIELDWITNESS_DATA_DIR` | Override the data directory (default: `~/.fwmetadata`). Enables portable USB mode and cover/duress directory naming |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -582,10 +582,10 @@ deploy/ Deployment artifacts
|
|||||||
config-presets/ Threat level presets (low/medium/high/critical)
|
config-presets/ Threat level presets (low/medium/high/critical)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Data directory (`~/.fieldwitness/`)
|
### Data directory (`~/.fwmetadata/`)
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.fieldwitness/
|
~/.fwmetadata/
|
||||||
config.json Unified configuration
|
config.json Unified configuration
|
||||||
audit.jsonl Append-only audit trail
|
audit.jsonl Append-only audit trail
|
||||||
carrier_history.json Carrier reuse tracking database
|
carrier_history.json Carrier reuse tracking database
|
||||||
@ -612,7 +612,7 @@ Sensitive directories (`identity/`, `auth/`, `certs/`, and the root) are created
|
|||||||
Argon2id from user-supplied factors. Attest uses Ed25519 for signing. These serve
|
Argon2id from user-supplied factors. Attest uses Ed25519 for signing. These serve
|
||||||
different security purposes and are kept strictly separate.
|
different security purposes and are kept strictly separate.
|
||||||
|
|
||||||
**Killswitch priority.** The killswitch destroys all data under `~/.fieldwitness/`, including
|
**Killswitch priority.** The killswitch destroys all data under `~/.fwmetadata/`, including
|
||||||
the audit log. This is intentional -- in a field compromise scenario, data destruction
|
the audit log. This is intentional -- in a field compromise scenario, data destruction
|
||||||
takes precedence over audit trail preservation. The deep forensic scrub extends beyond
|
takes precedence over audit trail preservation. The deep forensic scrub extends beyond
|
||||||
the data directory to remove Python bytecache, pip metadata, pip download cache, shell
|
the data directory to remove Python bytecache, pip metadata, pip download cache, shell
|
||||||
|
|||||||
@ -593,7 +593,7 @@ RestartSec=5
|
|||||||
NoNewPrivileges=yes
|
NoNewPrivileges=yes
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
ProtectHome=read-only
|
ProtectHome=read-only
|
||||||
ReadWritePaths=/home/fieldwitness/.fieldwitness
|
ReadWritePaths=/home/fieldwitness/.fwmetadata
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|||||||
@ -1602,7 +1602,7 @@ const Stego = {
|
|||||||
// Webcam QR scanning for RSA key (v4.1.5)
|
// Webcam QR scanning for RSA key (v4.1.5)
|
||||||
document.getElementById('rsaQrWebcam')?.addEventListener('click', () => {
|
document.getElementById('rsaQrWebcam')?.addEventListener('click', () => {
|
||||||
this.showQrScanner((text) => {
|
this.showQrScanner((text) => {
|
||||||
// Check for raw PEM or compressed format (STEGASOO-Z: prefix)
|
// Check for raw PEM or compressed format (legacy STEGASOO-Z: prefix)
|
||||||
const isRawPem = text.includes('-----BEGIN') && text.includes('KEY-----');
|
const isRawPem = text.includes('-----BEGIN') && text.includes('KEY-----');
|
||||||
const isCompressed = text.startsWith('STEGASOO-Z:');
|
const isCompressed = text.startsWith('STEGASOO-Z:');
|
||||||
if (isRawPem || isCompressed) {
|
if (isRawPem || isCompressed) {
|
||||||
@ -1672,7 +1672,7 @@ const Stego = {
|
|||||||
// Webcam QR scanning for RSA key (v4.1.5)
|
// Webcam QR scanning for RSA key (v4.1.5)
|
||||||
document.getElementById('rsaQrWebcam')?.addEventListener('click', () => {
|
document.getElementById('rsaQrWebcam')?.addEventListener('click', () => {
|
||||||
this.showQrScanner((text) => {
|
this.showQrScanner((text) => {
|
||||||
// Check for raw PEM or compressed format (STEGASOO-Z: prefix)
|
// Check for raw PEM or compressed format (legacy STEGASOO-Z: prefix)
|
||||||
const isRawPem = text.includes('-----BEGIN') && text.includes('KEY-----');
|
const isRawPem = text.includes('-----BEGIN') && text.includes('KEY-----');
|
||||||
const isCompressed = text.startsWith('STEGASOO-Z:');
|
const isCompressed = text.startsWith('STEGASOO-Z:');
|
||||||
if (isRawPem || isCompressed) {
|
if (isRawPem || isCompressed) {
|
||||||
|
|||||||
@ -325,7 +325,7 @@
|
|||||||
<div class="alert alert-info mt-3 mb-0">
|
<div class="alert alert-info mt-3 mb-0">
|
||||||
<i class="bi bi-info-circle me-2"></i>
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
This server is running in <strong>public mode</strong>.
|
This server is running in <strong>public mode</strong>.
|
||||||
Set <code>STEGASOO_CHANNEL_KEY</code> to enable server-wide channel isolation.
|
Set <code>FIELDWITNESS_CHANNEL_KEY</code> to enable server-wide channel isolation.
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -33,7 +33,7 @@ from .crypto import verify_signature, load_public_key_from_bytes
|
|||||||
|
|
||||||
# Configuration via environment
|
# Configuration via environment
|
||||||
DATA_DIR = Path(os.environ.get("FIELDWITNESS_DATA_DIR", Path.home() / ".fieldwitness"))
|
DATA_DIR = Path(os.environ.get("FIELDWITNESS_DATA_DIR", Path.home() / ".fieldwitness"))
|
||||||
BASE_URL = os.environ.get("VERISOO_BASE_URL", "https://attest.io")
|
BASE_URL = os.environ.get("FIELDWITNESS_BASE_URL", "https://attest.io")
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Attest",
|
title="Attest",
|
||||||
|
|||||||
@ -686,8 +686,8 @@ def serve(host: str, port: int) -> None:
|
|||||||
|
|
||||||
\b
|
\b
|
||||||
ENVIRONMENT VARIABLES:
|
ENVIRONMENT VARIABLES:
|
||||||
VERISOO_DATA_DIR Override data directory
|
FIELDWITNESS_DATA_DIR Override data directory
|
||||||
VERISOO_BASE_URL Base URL for proof links (default: https://attest.io)
|
FIELDWITNESS_BASE_URL Base URL for proof links (default: https://attest.io)
|
||||||
|
|
||||||
\b
|
\b
|
||||||
SECURITY NOTES:
|
SECURITY NOTES:
|
||||||
|
|||||||
@ -37,9 +37,9 @@ try:
|
|||||||
has_jpegio_support,
|
has_jpegio_support,
|
||||||
calculate_dct_capacity,
|
calculate_dct_capacity,
|
||||||
)
|
)
|
||||||
HAS_STEGASOO = True
|
HAS_STEGO = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_STEGASOO = False
|
HAS_STEGO = False
|
||||||
has_dct_support = lambda: False
|
has_dct_support = lambda: False
|
||||||
has_jpegio_support = lambda: False
|
has_jpegio_support = lambda: False
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ except ImportError:
|
|||||||
|
|
||||||
# Fixed public seed for Attest proof links
|
# Fixed public seed for Attest proof links
|
||||||
# This is intentionally public - anyone should be able to extract the proof link
|
# This is intentionally public - anyone should be able to extract the proof link
|
||||||
VERISOO_SEED = b"attest"
|
ATTEST_SEED = b"attest"
|
||||||
|
|
||||||
# Base URL for proof links
|
# Base URL for proof links
|
||||||
DEFAULT_BASE_URL = "https://attest.io"
|
DEFAULT_BASE_URL = "https://attest.io"
|
||||||
@ -250,7 +250,7 @@ def embed_proof_in_jpeg(
|
|||||||
ImportError: If stego is not available
|
ImportError: If stego is not available
|
||||||
ValueError: If image is too small or embedding fails
|
ValueError: If image is too small or embedding fails
|
||||||
"""
|
"""
|
||||||
if not HAS_STEGASOO:
|
if not HAS_STEGO:
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"DCT embedding requires stego. "
|
"DCT embedding requires stego. "
|
||||||
"Ensure stego is installed or available at ../stego"
|
"Ensure stego is installed or available at ../stego"
|
||||||
@ -268,7 +268,7 @@ def embed_proof_in_jpeg(
|
|||||||
stego_bytes, stats = embed_in_dct(
|
stego_bytes, stats = embed_in_dct(
|
||||||
data=payload,
|
data=payload,
|
||||||
carrier_image=image_data,
|
carrier_image=image_data,
|
||||||
seed=VERISOO_SEED,
|
seed=ATTEST_SEED,
|
||||||
output_format="jpeg",
|
output_format="jpeg",
|
||||||
color_mode="color",
|
color_mode="color",
|
||||||
)
|
)
|
||||||
@ -291,13 +291,13 @@ def extract_proof_from_jpeg(image_data: bytes) -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
Proof URL string or None if not found/invalid
|
Proof URL string or None if not found/invalid
|
||||||
"""
|
"""
|
||||||
if not HAS_STEGASOO:
|
if not HAS_STEGO:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payload = extract_from_dct(
|
payload = extract_from_dct(
|
||||||
stego_image=image_data,
|
stego_image=image_data,
|
||||||
seed=VERISOO_SEED,
|
seed=ATTEST_SEED,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Validate it looks like a proof link
|
# Validate it looks like a proof link
|
||||||
@ -327,7 +327,7 @@ def get_embed_method(image_path: Path) -> str:
|
|||||||
suffix = image_path.suffix.lower()
|
suffix = image_path.suffix.lower()
|
||||||
|
|
||||||
if suffix in DCT_FORMATS:
|
if suffix in DCT_FORMATS:
|
||||||
if HAS_STEGASOO and has_jpegio_support():
|
if HAS_STEGO and has_jpegio_support():
|
||||||
return "dct"
|
return "dct"
|
||||||
else:
|
else:
|
||||||
return "xmp" # Fallback to XMP if stego unavailable
|
return "xmp" # Fallback to XMP if stego unavailable
|
||||||
@ -485,7 +485,7 @@ def extract_proof_link(image_path: Path) -> ExtractResult:
|
|||||||
suffix = image_path.suffix.lower()
|
suffix = image_path.suffix.lower()
|
||||||
|
|
||||||
# Try DCT for JPEG
|
# Try DCT for JPEG
|
||||||
if suffix in DCT_FORMATS and HAS_STEGASOO:
|
if suffix in DCT_FORMATS and HAS_STEGO:
|
||||||
try:
|
try:
|
||||||
image_data = image_path.read_bytes()
|
image_data = image_path.read_bytes()
|
||||||
proof_link = extract_proof_from_jpeg(image_data)
|
proof_link = extract_proof_from_jpeg(image_data)
|
||||||
@ -523,14 +523,14 @@ def extract_proof_link(image_path: Path) -> ExtractResult:
|
|||||||
|
|
||||||
def can_embed_dct() -> bool:
|
def can_embed_dct() -> bool:
|
||||||
"""Check if DCT embedding is available."""
|
"""Check if DCT embedding is available."""
|
||||||
return HAS_STEGASOO and has_jpegio_support()
|
return HAS_STEGO and has_jpegio_support()
|
||||||
|
|
||||||
|
|
||||||
def get_embed_capabilities() -> dict[str, Any]:
|
def get_embed_capabilities() -> dict[str, Any]:
|
||||||
"""Get information about available embedding capabilities."""
|
"""Get information about available embedding capabilities."""
|
||||||
return {
|
return {
|
||||||
"dct_available": HAS_STEGASOO and has_dct_support(),
|
"dct_available": HAS_STEGO and has_dct_support(),
|
||||||
"jpeg_native": HAS_STEGASOO and has_jpegio_support(),
|
"jpeg_native": HAS_STEGO and has_jpegio_support(),
|
||||||
"xmp_available": True, # Always available
|
"xmp_available": True, # Always available
|
||||||
"supported_dct_formats": list(DCT_FORMATS) if can_embed_dct() else [],
|
"supported_dct_formats": list(DCT_FORMATS) if can_embed_dct() else [],
|
||||||
"supported_xmp_formats": list(XMP_FORMATS | RAW_FORMATS),
|
"supported_xmp_formats": list(XMP_FORMATS | RAW_FORMATS),
|
||||||
|
|||||||
@ -199,11 +199,11 @@ class KeystoreManager:
|
|||||||
|
|
||||||
def has_channel_key(self) -> bool:
|
def has_channel_key(self) -> bool:
|
||||||
"""Check if a channel key is configured."""
|
"""Check if a channel key is configured."""
|
||||||
return bool(os.environ.get("STEGASOO_CHANNEL_KEY")) or self._channel_key_file.exists()
|
return bool(os.environ.get("FIELDWITNESS_CHANNEL_KEY")) or self._channel_key_file.exists()
|
||||||
|
|
||||||
def get_channel_key(self) -> str | None:
|
def get_channel_key(self) -> str | None:
|
||||||
"""Get the channel key, or None if not configured."""
|
"""Get the channel key, or None if not configured."""
|
||||||
env_key = os.environ.get("STEGASOO_CHANNEL_KEY")
|
env_key = os.environ.get("FIELDWITNESS_CHANNEL_KEY")
|
||||||
if env_key:
|
if env_key:
|
||||||
return env_key
|
return env_key
|
||||||
if self._channel_key_file.exists():
|
if self._channel_key_file.exists():
|
||||||
@ -246,7 +246,7 @@ class KeystoreManager:
|
|||||||
# representation to back up, so we refuse rather than silently skip.
|
# representation to back up, so we refuse rather than silently skip.
|
||||||
if not self._channel_key_file.exists():
|
if not self._channel_key_file.exists():
|
||||||
raise KeystoreError(
|
raise KeystoreError(
|
||||||
"Channel key is set via STEGASOO_CHANNEL_KEY environment variable "
|
"Channel key is set via FIELDWITNESS_CHANNEL_KEY environment variable "
|
||||||
"and cannot be rotated through fieldwitness. Unset the variable and store "
|
"and cannot be rotated through fieldwitness. Unset the variable and store "
|
||||||
"the key in the keystore first."
|
"the key in the keystore first."
|
||||||
)
|
)
|
||||||
|
|||||||
@ -30,7 +30,7 @@ _PATH_DEFS: dict[str, tuple[str, ...]] = {
|
|||||||
# on fragile filesystem mtime.
|
# on fragile filesystem mtime.
|
||||||
"IDENTITY_META": ("identity", "identity.meta.json"),
|
"IDENTITY_META": ("identity", "identity.meta.json"),
|
||||||
# Stego state
|
# Stego state
|
||||||
"STEGASOO_DIR": ("stego",),
|
"FIELDWITNESS_DIR": ("stego",),
|
||||||
"CHANNEL_KEY_FILE": ("stego", "channel.key"),
|
"CHANNEL_KEY_FILE": ("stego", "channel.key"),
|
||||||
# Attest attestation storage
|
# Attest attestation storage
|
||||||
"ATTESTATIONS_DIR": ("attestations",),
|
"ATTESTATIONS_DIR": ("attestations",),
|
||||||
@ -84,7 +84,7 @@ def ensure_dirs() -> None:
|
|||||||
dirs = [
|
dirs = [
|
||||||
BASE_DIR,
|
BASE_DIR,
|
||||||
__getattr__("IDENTITY_DIR"),
|
__getattr__("IDENTITY_DIR"),
|
||||||
__getattr__("STEGASOO_DIR"),
|
__getattr__("FIELDWITNESS_DIR"),
|
||||||
__getattr__("ATTESTATIONS_DIR"),
|
__getattr__("ATTESTATIONS_DIR"),
|
||||||
__getattr__("CHAIN_DIR"),
|
__getattr__("CHAIN_DIR"),
|
||||||
__getattr__("AUTH_DIR"),
|
__getattr__("AUTH_DIR"),
|
||||||
|
|||||||
@ -22,7 +22,7 @@ from .channel import (
|
|||||||
validate_channel_key,
|
validate_channel_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Audio support — gated by STEGASOO_AUDIO env var and dependency availability
|
# Audio support — gated by FIELDWITNESS_AUDIO env var and dependency availability
|
||||||
from .constants import AUDIO_ENABLED, VIDEO_ENABLED
|
from .constants import AUDIO_ENABLED, VIDEO_ENABLED
|
||||||
|
|
||||||
# Crypto functions
|
# Crypto functions
|
||||||
@ -87,7 +87,7 @@ else:
|
|||||||
encode_audio = None
|
encode_audio = None
|
||||||
decode_audio = None
|
decode_audio = None
|
||||||
|
|
||||||
# Video support — gated by STEGASOO_VIDEO env var and ffmpeg + audio deps
|
# Video support — gated by FIELDWITNESS_VIDEO env var and ffmpeg + audio deps
|
||||||
if VIDEO_ENABLED:
|
if VIDEO_ENABLED:
|
||||||
from .decode import decode_video
|
from .decode import decode_video
|
||||||
from .encode import encode_video
|
from .encode import encode_video
|
||||||
|
|||||||
@ -45,7 +45,7 @@ from pathlib import Path
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
# Configure logging for API frontend
|
# Configure logging for API frontend
|
||||||
_log_level = os.environ.get("STEGASOO_LOG_LEVEL", "").strip().upper()
|
_log_level = os.environ.get("FIELDWITNESS_LOG_LEVEL", "").strip().upper()
|
||||||
if _log_level and hasattr(logging, _log_level):
|
if _log_level and hasattr(logging, _log_level):
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=getattr(logging, _log_level),
|
level=getattr(logging, _log_level),
|
||||||
@ -53,7 +53,7 @@ if _log_level and hasattr(logging, _log_level):
|
|||||||
datefmt="%H:%M:%S",
|
datefmt="%H:%M:%S",
|
||||||
stream=sys.stderr,
|
stream=sys.stderr,
|
||||||
)
|
)
|
||||||
elif os.environ.get("STEGASOO_DEBUG", "").strip() in ("1", "true", "yes"):
|
elif os.environ.get("FIELDWITNESS_DEBUG", "").strip() in ("1", "true", "yes"):
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format="[%(asctime)s.%(msecs)03d] [%(levelname)s] [%(name)s] %(message)s",
|
format="[%(asctime)s.%(msecs)03d] [%(levelname)s] [%(name)s] %(message)s",
|
||||||
|
|||||||
@ -32,7 +32,7 @@ PROJECT_CONFIG_DIR = Path("./config")
|
|||||||
API_KEYS_FILE = "api_keys.json"
|
API_KEYS_FILE = "api_keys.json"
|
||||||
|
|
||||||
# Environment variable for API key (alternative to file)
|
# Environment variable for API key (alternative to file)
|
||||||
API_KEY_ENV_VAR = "STEGASOO_API_KEY"
|
API_KEY_ENV_VAR = "FIELDWITNESS_API_KEY"
|
||||||
|
|
||||||
|
|
||||||
def _hash_key(key: str) -> str:
|
def _hash_key(key: str) -> str:
|
||||||
|
|||||||
@ -12,7 +12,7 @@ Use cases:
|
|||||||
- Public instances: No channel key = compatible with any instance without a channel key
|
- Public instances: No channel key = compatible with any instance without a channel key
|
||||||
|
|
||||||
Storage priority:
|
Storage priority:
|
||||||
1. Environment variable: STEGASOO_CHANNEL_KEY
|
1. Environment variable: FIELDWITNESS_CHANNEL_KEY
|
||||||
2. Config file: ~/.stego/channel.key or ./config/channel.key
|
2. Config file: ~/.stego/channel.key or ./config/channel.key
|
||||||
3. None (public mode - compatible with any instance without a channel key)
|
3. None (public mode - compatible with any instance without a channel key)
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ CHANNEL_KEY_LENGTH = 32 # Characters (excluding dashes)
|
|||||||
CHANNEL_KEY_FORMATTED_LENGTH = 39 # With dashes
|
CHANNEL_KEY_FORMATTED_LENGTH = 39 # With dashes
|
||||||
|
|
||||||
# Environment variable name
|
# Environment variable name
|
||||||
CHANNEL_KEY_ENV_VAR = "STEGASOO_CHANNEL_KEY"
|
CHANNEL_KEY_ENV_VAR = "FIELDWITNESS_CHANNEL_KEY"
|
||||||
|
|
||||||
# Config locations (in priority order)
|
# Config locations (in priority order)
|
||||||
CONFIG_LOCATIONS = [
|
CONFIG_LOCATIONS = [
|
||||||
@ -200,7 +200,7 @@ def get_channel_key() -> str | None:
|
|||||||
Get the current channel key from environment or config.
|
Get the current channel key from environment or config.
|
||||||
|
|
||||||
Checks in order:
|
Checks in order:
|
||||||
1. STEGASOO_CHANNEL_KEY environment variable
|
1. FIELDWITNESS_CHANNEL_KEY environment variable
|
||||||
2. ./config/channel.key file
|
2. ./config/channel.key file
|
||||||
3. ~/.stego/channel.key file
|
3. ~/.stego/channel.key file
|
||||||
|
|
||||||
|
|||||||
@ -533,7 +533,7 @@ def audio_encode(
|
|||||||
if not AUDIO_ENABLED:
|
if not AUDIO_ENABLED:
|
||||||
raise click.UsageError(
|
raise click.UsageError(
|
||||||
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
||||||
"or set STEGASOO_AUDIO=1 to force enable."
|
"or set FIELDWITNESS_AUDIO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .audio_steganography import calculate_audio_lsb_capacity
|
from .audio_steganography import calculate_audio_lsb_capacity
|
||||||
@ -723,7 +723,7 @@ def audio_decode(
|
|||||||
if not AUDIO_ENABLED:
|
if not AUDIO_ENABLED:
|
||||||
raise click.UsageError(
|
raise click.UsageError(
|
||||||
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
||||||
"or set STEGASOO_AUDIO=1 to force enable."
|
"or set FIELDWITNESS_AUDIO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .decode import decode_audio
|
from .decode import decode_audio
|
||||||
@ -819,7 +819,7 @@ def audio_info(ctx, audio):
|
|||||||
if not AUDIO_ENABLED:
|
if not AUDIO_ENABLED:
|
||||||
raise click.UsageError(
|
raise click.UsageError(
|
||||||
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
||||||
"or set STEGASOO_AUDIO=1 to force enable."
|
"or set FIELDWITNESS_AUDIO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .audio_steganography import calculate_audio_lsb_capacity
|
from .audio_steganography import calculate_audio_lsb_capacity
|
||||||
@ -964,7 +964,7 @@ def video_encode(
|
|||||||
if not VIDEO_ENABLED:
|
if not VIDEO_ENABLED:
|
||||||
raise click.UsageError(
|
raise click.UsageError(
|
||||||
"Video support is disabled. Install ffmpeg and audio extras, "
|
"Video support is disabled. Install ffmpeg and audio extras, "
|
||||||
"or set STEGASOO_VIDEO=1 to force enable."
|
"or set FIELDWITNESS_VIDEO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .encode import encode_video
|
from .encode import encode_video
|
||||||
@ -1137,7 +1137,7 @@ def video_decode(
|
|||||||
if not VIDEO_ENABLED:
|
if not VIDEO_ENABLED:
|
||||||
raise click.UsageError(
|
raise click.UsageError(
|
||||||
"Video support is disabled. Install ffmpeg and audio extras, "
|
"Video support is disabled. Install ffmpeg and audio extras, "
|
||||||
"or set STEGASOO_VIDEO=1 to force enable."
|
"or set FIELDWITNESS_VIDEO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .decode import decode_video
|
from .decode import decode_video
|
||||||
@ -1232,7 +1232,7 @@ def video_info(ctx, video):
|
|||||||
if not VIDEO_ENABLED:
|
if not VIDEO_ENABLED:
|
||||||
raise click.UsageError(
|
raise click.UsageError(
|
||||||
"Video support is disabled. Install ffmpeg and audio extras, "
|
"Video support is disabled. Install ffmpeg and audio extras, "
|
||||||
"or set STEGASOO_VIDEO=1 to force enable."
|
"or set FIELDWITNESS_VIDEO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .video_utils import calculate_video_capacity, get_video_info
|
from .video_utils import calculate_video_capacity, get_video_info
|
||||||
@ -1713,7 +1713,7 @@ def info(ctx, full):
|
|||||||
click.echo(json.dumps(info_data, indent=2))
|
click.echo(json.dumps(info_data, indent=2))
|
||||||
else:
|
else:
|
||||||
# Fastfetch-style output
|
# Fastfetch-style output
|
||||||
click.echo(f"\033[1mSTEGASOO\033[0m v{__version__}")
|
click.echo(f"\033[1mFIELDWITNESS\033[0m v{__version__}")
|
||||||
click.echo("─" * 36)
|
click.echo("─" * 36)
|
||||||
|
|
||||||
# Service status
|
# Service status
|
||||||
@ -1819,7 +1819,7 @@ def channel_generate(ctx, save, save_user):
|
|||||||
click.echo(f"Saved to: {path}")
|
click.echo(f"Saved to: {path}")
|
||||||
else:
|
else:
|
||||||
click.echo("To use this key:")
|
click.echo("To use this key:")
|
||||||
click.echo(f' export STEGASOO_CHANNEL_KEY="{key}"')
|
click.echo(f' export FIELDWITNESS_CHANNEL_KEY="{key}"')
|
||||||
click.echo()
|
click.echo()
|
||||||
click.echo("Or save to config:")
|
click.echo("Or save to config:")
|
||||||
click.echo(" stego channel generate --save")
|
click.echo(" stego channel generate --save")
|
||||||
|
|||||||
@ -320,7 +320,7 @@ def detect_stego_mode(encrypted_data: bytes) -> str:
|
|||||||
# Environment variables to enable/disable optional feature families.
|
# Environment variables to enable/disable optional feature families.
|
||||||
# Values: "auto" (default — detect dependencies), "1"/"true" (force on),
|
# Values: "auto" (default — detect dependencies), "1"/"true" (force on),
|
||||||
# "0"/"false" (force off even if deps are installed).
|
# "0"/"false" (force off even if deps are installed).
|
||||||
# Pi builds or minimal installs can set STEGASOO_AUDIO=0 to stay image-only.
|
# Pi builds or minimal installs can set FIELDWITNESS_AUDIO=0 to stay image-only.
|
||||||
|
|
||||||
import os as _os
|
import os as _os
|
||||||
|
|
||||||
@ -370,8 +370,8 @@ def _resolve_feature(toggle: str | bool, dep_check: callable) -> bool:
|
|||||||
return dep_check()
|
return dep_check()
|
||||||
|
|
||||||
|
|
||||||
_audio_toggle = _parse_feature_toggle("STEGASOO_AUDIO")
|
_audio_toggle = _parse_feature_toggle("FIELDWITNESS_AUDIO")
|
||||||
_video_toggle = _parse_feature_toggle("STEGASOO_VIDEO")
|
_video_toggle = _parse_feature_toggle("FIELDWITNESS_VIDEO")
|
||||||
|
|
||||||
AUDIO_ENABLED: bool = _resolve_feature(_audio_toggle, _check_audio_deps)
|
AUDIO_ENABLED: bool = _resolve_feature(_audio_toggle, _check_audio_deps)
|
||||||
VIDEO_ENABLED: bool = _resolve_feature(_video_toggle, _check_video_deps)
|
VIDEO_ENABLED: bool = _resolve_feature(_video_toggle, _check_video_deps)
|
||||||
|
|||||||
@ -4,14 +4,14 @@ Stego Debugging Utilities
|
|||||||
Debugging, logging, and performance monitoring tools.
|
Debugging, logging, and performance monitoring tools.
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
STEGASOO_LOG_LEVEL env var controls log level:
|
FIELDWITNESS_LOG_LEVEL env var controls log level:
|
||||||
- Not set or empty: logging disabled (production default)
|
- Not set or empty: logging disabled (production default)
|
||||||
- DEBUG: verbose debug output (encode/decode flow, crypto params, etc.)
|
- DEBUG: verbose debug output (encode/decode flow, crypto params, etc.)
|
||||||
- INFO: operational messages (format detection, mode selection)
|
- INFO: operational messages (format detection, mode selection)
|
||||||
- WARNING: potential issues (fallback KDF, format transcoding)
|
- WARNING: potential issues (fallback KDF, format transcoding)
|
||||||
- ERROR: operation failures
|
- ERROR: operation failures
|
||||||
|
|
||||||
STEGASOO_DEBUG=1 is a shorthand for STEGASOO_LOG_LEVEL=DEBUG
|
FIELDWITNESS_DEBUG=1 is a shorthand for FIELDWITNESS_LOG_LEVEL=DEBUG
|
||||||
|
|
||||||
CLI: stego --debug encode ... (sets DEBUG level for that invocation)
|
CLI: stego --debug encode ... (sets DEBUG level for that invocation)
|
||||||
|
|
||||||
@ -47,12 +47,12 @@ VALIDATION_ASSERTIONS = True # Enable runtime validation assertions
|
|||||||
|
|
||||||
def _configure_from_env() -> bool:
|
def _configure_from_env() -> bool:
|
||||||
"""Configure logging from environment variables. Returns True if debug enabled."""
|
"""Configure logging from environment variables. Returns True if debug enabled."""
|
||||||
# STEGASOO_DEBUG=1 is shorthand for DEBUG level
|
# FIELDWITNESS_DEBUG=1 is shorthand for DEBUG level
|
||||||
if os.environ.get("STEGASOO_DEBUG", "").strip() in ("1", "true", "yes"):
|
if os.environ.get("FIELDWITNESS_DEBUG", "").strip() in ("1", "true", "yes"):
|
||||||
_setup_logging(logging.DEBUG)
|
_setup_logging(logging.DEBUG)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
level_str = os.environ.get("STEGASOO_LOG_LEVEL", "").strip().upper()
|
level_str = os.environ.get("FIELDWITNESS_LOG_LEVEL", "").strip().upper()
|
||||||
if level_str and level_str in _LEVEL_MAP:
|
if level_str and level_str in _LEVEL_MAP:
|
||||||
_setup_logging(_LEVEL_MAP[level_str])
|
_setup_logging(_LEVEL_MAP[level_str])
|
||||||
return level_str == "DEBUG"
|
return level_str == "DEBUG"
|
||||||
|
|||||||
@ -314,7 +314,7 @@ def decode_audio(
|
|||||||
if not AUDIO_ENABLED:
|
if not AUDIO_ENABLED:
|
||||||
raise ExtractionError(
|
raise ExtractionError(
|
||||||
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
||||||
"or set STEGASOO_AUDIO=1 to force enable."
|
"or set FIELDWITNESS_AUDIO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .audio_utils import detect_audio_format, transcode_to_wav
|
from .audio_utils import detect_audio_format, transcode_to_wav
|
||||||
@ -434,7 +434,7 @@ def decode_video(
|
|||||||
if not VIDEO_ENABLED:
|
if not VIDEO_ENABLED:
|
||||||
raise ExtractionError(
|
raise ExtractionError(
|
||||||
"Video support is disabled. Install video extras and ffmpeg, "
|
"Video support is disabled. Install video extras and ffmpeg, "
|
||||||
"or set STEGASOO_VIDEO=1 to force enable."
|
"or set FIELDWITNESS_VIDEO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .video_utils import detect_video_format
|
from .video_utils import detect_video_format
|
||||||
|
|||||||
@ -382,7 +382,7 @@ def encode_audio(
|
|||||||
if not AUDIO_ENABLED:
|
if not AUDIO_ENABLED:
|
||||||
raise AudioError(
|
raise AudioError(
|
||||||
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
"Audio support is disabled. Install audio extras (pip install stego[audio]) "
|
||||||
"or set STEGASOO_AUDIO=1 to force enable."
|
"or set FIELDWITNESS_AUDIO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .audio_utils import detect_audio_format, transcode_to_wav
|
from .audio_utils import detect_audio_format, transcode_to_wav
|
||||||
@ -485,7 +485,7 @@ def encode_video(
|
|||||||
if not VIDEO_ENABLED:
|
if not VIDEO_ENABLED:
|
||||||
raise VideoError(
|
raise VideoError(
|
||||||
"Video support is disabled. Install video extras and ffmpeg, "
|
"Video support is disabled. Install video extras and ffmpeg, "
|
||||||
"or set STEGASOO_VIDEO=1 to force enable."
|
"or set FIELDWITNESS_VIDEO=1 to force enable."
|
||||||
)
|
)
|
||||||
|
|
||||||
from .video_utils import detect_video_format
|
from .video_utils import detect_video_format
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user