Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,5 @@ bindu/penguin/.bindu/public.pem
.bindu/

postman/*

AI-Agents/
5 changes: 5 additions & 0 deletions agentmesh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
AgentMesh – Hybrid Multi-Agent AI Infrastructure Platform

Goal:
Design and evaluate cost-aware, GPU-enabled, multi-agent NLP systems
with distributed scheduling, persistent storage, and observability.
25 changes: 25 additions & 0 deletions agentmesh/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: "3.8"

services:
postgres:
image: postgres:15
container_name: agentmesh-postgres
restart: always
environment:
POSTGRES_USER: bindu_user
POSTGRES_PASSWORD: bindu_pass
POSTGRES_DB: bindu_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data

redis:
image: redis:7
container_name: agentmesh-redis
restart: always
ports:
- "6379:6379"

volumes:
postgres_data:
170 changes: 170 additions & 0 deletions alembic/versions/20260322_0001_add_mtls_certificate_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
"""Add mTLS certificate tables for agent identity and audit logging.

Revision ID: 20260322_0001
Revises: 20250614_0001
Create Date: 2026-03-22 00:00:00.000000

This migration adds two tables to support the mTLS transport layer security
feature (Issue #146):

- agent_certificates: Stores CA-signed certificates tied to agent DIDs,
with lifecycle status (active/expired/revoked) and SHA-256 fingerprints
for zero-trust freshness checks.

- certificate_audit_log: Immutable event log for all certificate issuance,
renewal, and revocation events. Suitable for SIEM ingestion.
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = "20260322_0001"
down_revision: Union[str, None] = "20260119_0001"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Add agent_certificates and certificate_audit_log tables."""

# ------------------------------------------------------------------
# agent_certificates
# ------------------------------------------------------------------
op.create_table(
"agent_certificates",
sa.Column(
"id",
postgresql.UUID(as_uuid=True),
primary_key=True,
nullable=False,
),
sa.Column("agent_did", sa.String(255), nullable=False),
sa.Column("cert_fingerprint", sa.String(255), nullable=False, unique=True),
sa.Column("status", sa.String(50), nullable=False, server_default="active"),
sa.Column(
"issued_at",
sa.TIMESTAMP(timezone=True),
nullable=False,
server_default=sa.text("NOW()"),
),
sa.Column(
"expires_at",
sa.TIMESTAMP(timezone=True),
nullable=False,
),
sa.Column(
"created_at",
sa.TIMESTAMP(timezone=True),
nullable=False,
server_default=sa.text("NOW()"),
),
sa.Column(
"updated_at",
sa.TIMESTAMP(timezone=True),
nullable=False,
server_default=sa.text("NOW()"),
),
comment="mTLS agent certificates tied to DIDs",
)

# Indexes for zero-trust freshness checks
op.create_index(
"idx_agent_certs_fingerprint",
"agent_certificates",
["cert_fingerprint"],
)
op.create_index(
"idx_agent_certs_status",
"agent_certificates",
["status"],
)
op.create_index(
"idx_agent_certs_agent_did",
"agent_certificates",
["agent_did"],
)

# Auto-update trigger for updated_at
op.execute("""
CREATE TRIGGER update_agent_certificates_updated_at
BEFORE UPDATE ON agent_certificates
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
""")

# ------------------------------------------------------------------
# certificate_audit_log
# ------------------------------------------------------------------
op.create_table(
"certificate_audit_log",
sa.Column(
"id",
postgresql.UUID(as_uuid=True),
primary_key=True,
nullable=False,
),
sa.Column("event_type", sa.String(50), nullable=False),
sa.Column("agent_did", sa.String(255), nullable=False),
sa.Column("cert_fingerprint", sa.String(255), nullable=False),
sa.Column("performed_by", sa.String(255), nullable=True),
sa.Column(
"event_data",
postgresql.JSONB(astext_type=sa.Text()),
nullable=True,
server_default=sa.text("'{}'::jsonb"),
),
sa.Column(
"created_at",
sa.TIMESTAMP(timezone=True),
nullable=False,
server_default=sa.text("NOW()"),
),
comment="Immutable audit log for certificate lifecycle events (SIEM)",
)

# Indexes for audit queries
op.create_index(
"idx_cert_audit_agent_did",
"certificate_audit_log",
["agent_did"],
)
op.create_index(
"idx_cert_audit_fingerprint",
"certificate_audit_log",
["cert_fingerprint"],
)
op.create_index(
"idx_cert_audit_event_type",
"certificate_audit_log",
["event_type"],
)
op.create_index(
"idx_cert_audit_created_at",
"certificate_audit_log",
["created_at"],
postgresql_ops={"created_at": "DESC"},
)


def downgrade() -> None:
"""Remove mTLS certificate tables."""

# Drop certificate_audit_log
op.drop_index("idx_cert_audit_created_at", table_name="certificate_audit_log")
op.drop_index("idx_cert_audit_event_type", table_name="certificate_audit_log")
op.drop_index("idx_cert_audit_fingerprint", table_name="certificate_audit_log")
op.drop_index("idx_cert_audit_agent_did", table_name="certificate_audit_log")
op.drop_table("certificate_audit_log")

# Drop agent_certificates
op.execute(
"DROP TRIGGER IF EXISTS update_agent_certificates_updated_at ON agent_certificates"
)
op.drop_index("idx_agent_certs_agent_did", table_name="agent_certificates")
op.drop_index("idx_agent_certs_status", table_name="agent_certificates")
op.drop_index("idx_agent_certs_fingerprint", table_name="agent_certificates")
op.drop_table("agent_certificates")
78 changes: 78 additions & 0 deletions bindu/common/protocol/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,84 @@ class AgentTrust(TypedDict):
allowed_operations: Dict[str, TrustLevel]


# -----------------------------------------------------------------------------
# Certificate Lifecycle Types (mTLS) <NotPartOfA2A>
# -----------------------------------------------------------------------------


@pydantic.with_config(ConfigDict(alias_generator=to_camel))
class CertificateIssueParams(TypedDict):
"""Parameters for issuing a new mTLS certificate for an agent."""

agent_did: Required[str]
"""The DID of the agent requesting the certificate."""

csr: Required[str]
"""PEM-encoded Certificate Signing Request."""


@pydantic.with_config(ConfigDict(alias_generator=to_camel))
class CertificateRenewParams(TypedDict):
"""Parameters for renewing an existing mTLS certificate."""

agent_did: Required[str]
"""The DID of the agent renewing the certificate."""

csr: Required[str]
"""PEM-encoded Certificate Signing Request for the new certificate."""

current_fingerprint: Required[str]
"""SHA-256 fingerprint of the currently active certificate."""


@pydantic.with_config(ConfigDict(alias_generator=to_camel))
class CertificateRevokeParams(TypedDict):
"""Parameters for revoking an mTLS certificate."""

agent_did: Required[str]
"""The DID of the agent whose certificate is being revoked."""

cert_fingerprint: Required[str]
"""SHA-256 fingerprint of the certificate to revoke."""

reason: NotRequired[str]
"""Optional reason for revocation (for audit log)."""


@pydantic.with_config(ConfigDict(alias_generator=to_camel))
class CertificateData(TypedDict):
"""Response data after a certificate is issued or renewed."""

certificate_pem: Required[str]
"""PEM-encoded signed certificate."""

cert_fingerprint: Required[str]
"""SHA-256 fingerprint of the issued certificate."""

status: Required[Literal["issued", "active", "revoked", "expired"]]
"""Current lifecycle status of the certificate."""

issued_at: Required[str]
"""ISO 8601 timestamp of issuance."""

expires_at: Required[str]
"""ISO 8601 timestamp of expiry."""

agent_did: Required[str]
"""The DID this certificate is bound to."""


cert_issue_params_ta: TypeAdapter[CertificateIssueParams] = TypeAdapter(
CertificateIssueParams
)
cert_renew_params_ta: TypeAdapter[CertificateRenewParams] = TypeAdapter(
CertificateRenewParams
)
cert_revoke_params_ta: TypeAdapter[CertificateRevokeParams] = TypeAdapter(
CertificateRevokeParams
)
cert_data_ta: TypeAdapter[CertificateData] = TypeAdapter(CertificateData)

# -----------------------------------------------------------------------------
# Agent
# -----------------------------------------------------------------------------
Expand Down
42 changes: 35 additions & 7 deletions bindu/extensions/did/did_agent_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,17 +214,45 @@ def generate_and_save_key_pair(self) -> dict[str, str]:

private_pem, public_pem = self._generate_key_pair_data()

# Write keys using Path methods
self.private_key_path.write_bytes(private_pem)
self.public_key_path.write_bytes(public_pem)

# Set appropriate file permissions (owner read/write only for private key)
self.private_key_path.chmod(0o600)
self.public_key_path.chmod(0o644)

import os
import stat

# Create private key
fd = os.open(self.private_key_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "wb") as f:
f.write(private_pem)

try:
os.chmod(self.private_key_path, 0o600)
except Exception:
pass


# Public key
fd_pub = os.open(self.public_key_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644)
with os.fdopen(fd_pub, "wb") as f:
f.write(public_pem)

try:
os.chmod(self.public_key_path, 0o644)
except Exception:
pass



# Validation
if not self.private_key_path.exists():
raise OSError("Failed to create private key file")

if not self.public_key_path.exists():
raise OSError("Failed to create public key file")


return {
"private_key_path": str(self.private_key_path),
"public_key_path": str(self.public_key_path),
"public_key_path": str(self.public_key_path),
}

def _load_key_from_file(self, key_path: Path, key_type: str) -> bytes:
Expand Down
Empty file.
36 changes: 36 additions & 0 deletions bindu/extensions/semantic_memory/embeddings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
from typing import List

from openai import OpenAI

# Initialize client only if API key exists
_api_key = os.getenv("OPENROUTER_API_KEY")

client = OpenAI(
api_key=_api_key,
base_url="https://openrouter.ai/api/v1",
) if _api_key else None


def get_embedding(text: str) -> List[float]:
"""
Generate embedding for given text.

- Uses OpenRouter/OpenAI if API key is available
- Falls back to dummy embedding in test environments
"""

# 🔥 TEST-SAFE FALLBACK
if not client:
return [0.0] * 1536 # matches embedding size

try:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text,
)
return response.data[0].embedding

except Exception:
# 🔥 FAIL-SAFE (network/API issues)
return [0.0] * 1536
Loading