Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

All notable changes to Archon are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.1] - 2026-01-23

### Added
- Changelog for release tracking

### Changed
- Version metadata bumped to 0.1.1 across backend, UI, and agent work orders
- README now shows the current release

---

[Unreleased]: https://github.com/AeyeOps/archon/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/AeyeOps/archon/compare/v0.1.0...v0.1.1
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

---

**Current Release**: v0.1.1

## 🎯 What is Archon?

> Archon is currently in beta! Expect things to not work 100%, and please feel free to share any feedback and contribute with fixes/new features! Thank you to everyone for all the excitement we have for Archon already, as well as the bug reports, PRs, and discussions. It's a lot for our small team to get through but we're committed to addressing everything and making Archon into the best tool it possibly can be!
Expand Down
2 changes: 1 addition & 1 deletion archon-ui-main/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "archon-ui",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"type": "module",
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions archon-ui-main/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const AppContent = () => {

return (
<>
<Router>
<Router future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
<ErrorBoundaryWithBugReport>
<MainLayout>
{/* Migration Banner - shows when backend is up but DB schema needs work */}
Expand Down Expand Up @@ -141,4 +141,4 @@ export function App() {
)}
</QueryClientProvider>
);
}
}
143 changes: 143 additions & 0 deletions migration/cleanup_stale_credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env python3
"""
Cleanup stale encrypted credentials that can't be decrypted with current SUPABASE_SERVICE_KEY.

This script runs during bootstrap to prevent silent decryption failures when the
encryption key changes (e.g., fresh Supabase init, key rotation).

Usage:
python cleanup_stale_credentials.py

Environment variables:
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME - Database connection
SUPABASE_SERVICE_KEY - Used to derive encryption key
"""

import base64
import os
import sys

import psycopg2

# Encryption imports - same as credential_service.py
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC


def get_encryption_key() -> bytes:
"""Generate encryption key from SUPABASE_SERVICE_KEY - mirrors credential_service.py"""
service_key = os.getenv("SUPABASE_SERVICE_KEY", "default-key-for-development")

kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=b"static_salt_for_credentials", # Must match credential_service.py
iterations=100000,
)
key = base64.urlsafe_b64encode(kdf.derive(service_key.encode()))
return key


def try_decrypt(encrypted_value: str, fernet: Fernet) -> bool:
"""Attempt to decrypt a value, return True if successful."""
if not encrypted_value:
return True # Empty values are fine

try:
encrypted_bytes = base64.urlsafe_b64decode(encrypted_value.encode("utf-8"))
fernet.decrypt(encrypted_bytes)
return True
except (InvalidToken, ValueError, Exception):
return False


def main():
# Database connection from environment
db_config = {
"host": os.getenv("DB_HOST", "localhost"),
"port": int(os.getenv("DB_PORT", "5432")),
"user": os.getenv("DB_USER", "postgres"),
"password": os.getenv("DB_PASSWORD", "postgres"),
"dbname": os.getenv("DB_NAME", "postgres"),
}

service_key = os.getenv("SUPABASE_SERVICE_KEY")
if not service_key:
print("⚠ SUPABASE_SERVICE_KEY not set, skipping credential cleanup")
return 0

try:
conn = psycopg2.connect(**db_config)
conn.autocommit = True
cursor = conn.cursor()
except Exception as e:
print(f"⚠ Could not connect to database: {e}")
return 0 # Non-fatal - migrations may not have run yet

# Check if table exists
cursor.execute("""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = 'archon_settings'
)
""")
if not cursor.fetchone()[0]:
print("✓ archon_settings table doesn't exist yet, skipping credential cleanup")
conn.close()
return 0

# Get all encrypted credentials
cursor.execute("""
SELECT key, encrypted_value
FROM archon_settings
WHERE is_encrypted = true AND encrypted_value IS NOT NULL AND encrypted_value != ''
""")
encrypted_credentials = cursor.fetchall()

if not encrypted_credentials:
print("✓ No encrypted credentials to validate")
conn.close()
return 0

# Create Fernet cipher with current key
try:
encryption_key = get_encryption_key()
fernet = Fernet(encryption_key)
except Exception as e:
print(f"⚠ Could not create encryption cipher: {e}")
conn.close()
return 0

# Test each credential
stale_keys = []
valid_count = 0

for key, encrypted_value in encrypted_credentials:
if try_decrypt(encrypted_value, fernet):
valid_count += 1
else:
stale_keys.append(key)

# Delete stale credentials
if stale_keys:
print(f"⚠ Found {len(stale_keys)} stale encrypted credential(s) that cannot be decrypted:")
for key in stale_keys:
print(f" - {key}")

# Delete them
cursor.execute(
"DELETE FROM archon_settings WHERE key = ANY(%s)",
(stale_keys,)
)
print(f"✓ Deleted {len(stale_keys)} stale credential(s). Re-enter them in the Settings UI.")

if valid_count > 0:
print(f"✓ Validated {valid_count} encrypted credential(s)")

conn.close()
return 0


if __name__ == "__main__":
sys.exit(main())
25 changes: 22 additions & 3 deletions migration/complete_setup.sql
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ BEGIN
END;
$$ language 'plpgsql';

DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM pg_trigger
WHERE tgname = 'update_archon_settings_updated_at'
AND tgrelid = 'archon_settings'::regclass
) THEN
RAISE NOTICE 'Trigger update_archon_settings_updated_at already exists, dropping before recreation.';
EXECUTE 'DROP TRIGGER update_archon_settings_updated_at ON archon_settings';
ELSE
RAISE NOTICE 'Creating trigger update_archon_settings_updated_at.';
END IF;
END
$$;

CREATE TRIGGER update_archon_settings_updated_at
BEFORE UPDATE ON archon_settings
FOR EACH ROW
Expand All @@ -72,20 +88,23 @@ INSERT INTO archon_settings (key, value, is_encrypted, category, description) VA
('MCP_TRANSPORT', 'dual', false, 'server_config', 'MCP server transport mode - sse (web clients), stdio (IDE clients), or dual (both)'),
('HOST', 'localhost', false, 'server_config', 'Host to bind to if using sse as the transport (leave empty if using stdio)'),
('PORT', '8051', false, 'server_config', 'Port to listen on if using sse as the transport (leave empty if using stdio)'),
('MODEL_CHOICE', 'gpt-4.1-nano', false, 'rag_strategy', 'The LLM you want to use for summaries and contextual embeddings. Generally this is a very cheap and fast LLM like gpt-4.1-nano');
('MODEL_CHOICE', 'gpt-4.1-nano', false, 'rag_strategy', 'The LLM you want to use for summaries and contextual embeddings. Generally this is a very cheap and fast LLM like gpt-4.1-nano')
ON CONFLICT (key) DO NOTHING;

-- RAG Strategy Configuration (all default to true)
INSERT INTO archon_settings (key, value, is_encrypted, category, description) VALUES
('USE_CONTEXTUAL_EMBEDDINGS', 'false', false, 'rag_strategy', 'Enhances embeddings with contextual information for better retrieval'),
('CONTEXTUAL_EMBEDDINGS_MAX_WORKERS', '3', false, 'rag_strategy', 'Maximum parallel workers for contextual embedding generation (1-10)'),
('USE_HYBRID_SEARCH', 'true', false, 'rag_strategy', 'Combines vector similarity search with keyword search for better results'),
('USE_AGENTIC_RAG', 'true', false, 'rag_strategy', 'Enables code example extraction, storage, and specialized code search functionality'),
('USE_RERANKING', 'true', false, 'rag_strategy', 'Applies cross-encoder reranking to improve search result relevance');
('USE_RERANKING', 'true', false, 'rag_strategy', 'Applies cross-encoder reranking to improve search result relevance')
ON CONFLICT (key) DO NOTHING;

-- Monitoring Configuration
INSERT INTO archon_settings (key, value, is_encrypted, category, description) VALUES
('LOGFIRE_ENABLED', 'true', false, 'monitoring', 'Enable or disable Pydantic Logfire logging and observability platform'),
('PROJECTS_ENABLED', 'true', false, 'features', 'Enable or disable Projects and Tasks functionality');
('PROJECTS_ENABLED', 'true', false, 'features', 'Enable or disable Projects and Tasks functionality')
ON CONFLICT (key) DO NOTHING;

-- Placeholder for sensitive credentials (to be added via Settings UI)
INSERT INTO archon_settings (key, encrypted_value, is_encrypted, category, description) VALUES
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "archon"
version = "0.1.0"
version = "0.1.1"
description = "Archon - the command center for AI coding assistants."
readme = "README.md"
requires-python = ">=3.12"
Expand Down
2 changes: 1 addition & 1 deletion python/src/agent_work_orders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
Provides workflow-based agent execution in isolated sandboxes.
"""

__version__ = "0.1.0"
__version__ = "0.1.1"
6 changes: 3 additions & 3 deletions python/src/agent_work_orders/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
app = FastAPI(
title="Agent Work Orders API",
description="Independent agent work order service for workflow-based agent execution",
version="0.1.0",
version="0.1.1",
lifespan=lifespan,
)

Expand All @@ -125,7 +125,7 @@ async def health_check() -> dict[str, Any]:
health_status: dict[str, Any] = {
"status": "healthy",
"service": "agent-work-orders",
"version": "0.1.0",
"version": "0.1.1",
"enabled": config.ENABLED,
"dependencies": {},
}
Expand Down Expand Up @@ -260,7 +260,7 @@ async def root() -> dict:
"""Root endpoint with service information"""
return {
"service": "agent-work-orders",
"version": "0.1.0",
"version": "0.1.1",
"description": "Independent agent work order service",
"docs": "/docs",
"health": "/health",
Expand Down
2 changes: 1 addition & 1 deletion python/src/agents/document_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _create_agent(self, **kwargs) -> Agent:
agent = Agent(
model=self.model,
deps_type=DocumentDependencies,
result_type=DocumentOperation,
output_type=DocumentOperation,
system_prompt="""You are a Document Management Assistant that helps users create, update, and modify project documents through conversation.

**Your Capabilities:**
Expand Down
Loading