Lays the storage and bus substrate for the "credential reuse patterns"
task in DEVELOPMENT.md and scaffolds decnet/vectorstore/ as the future
substrate for statistical attacker re-identification over behavioral
fingerprints. No correlator, profiler, API, or dashboard wiring in
this commit — see TODO.md for the handoff.
Schema:
- Credential.attacker_uuid (nullable FK to attackers.uuid),
backfilled by the profiler post-write to avoid coupling the
capture path to the profiler's ordering.
- CredentialReuse table — UUID PK, JSON list columns for the
accumulating attacker_uuids/ips/deckies/services, target_count
(the discriminative scalar), confidence reserved for a future
fuzzy-credential pass.
Repo:
- upsert_credential_reuse / list_credential_reuses /
get_credential_reuse_by_id / update_credential_attacker_uuid.
- Renamed pre-existing get_credential_reuse(secret_sha256) to
get_credential_attempts_for_secret(secret_sha256) — the new
findings table needs the cleaner name.
Bus topics:
- credential.captured (one per Credential upsert)
- credential.reuse.detected (correlator-emitted on insert/grow)
Vectorstore subpackage (decnet/vectorstore/, flat layout mirroring
decnet/bus/):
- BaseVectorStore ABC keyed by (kind, id) — kind discriminator
means new feature families are additive, no schema migration.
- FakeVectorStore (in-memory L2 KNN), NullVectorStore (no-op for
DECNET_VECTORSTORE_ENABLED=false), SqliteVecVectorStore (lazy
sqlite_vec extension load, one vec0 virtual table per kind).
- get_vectorstore() env-driven dispatch with graceful fallback
to FakeVectorStore when the sqlite-vec extension isn't on the
host, so workers don't crash on a missing optional dep.
Tests: 26 new (11 cred-reuse repo, 15 vectorstore). Existing
credentials and base-repo tests updated for the rename. Total: 34
passing on the touched files.
253 lines
5.3 KiB
Python
253 lines
5.3 KiB
Python
"""
|
|
Database tables (SQLModel) and HTTP request/response shapes (Pydantic).
|
|
|
|
Split into topical modules for readability, but every symbol is re-exported
|
|
from this package so ``from decnet.web.db.models import X`` keeps working
|
|
everywhere — no importer needs to know which submodule a class lives in.
|
|
"""
|
|
from ._base import (
|
|
NullableDatetime,
|
|
NullableString,
|
|
_BIG_TEXT,
|
|
_normalize_null,
|
|
)
|
|
from .common import (
|
|
MessageResponse,
|
|
)
|
|
from .auth import (
|
|
AdminConfigResponse,
|
|
ChangePasswordRequest,
|
|
ConfigResponse,
|
|
CreateUserRequest,
|
|
DeploymentLimitRequest,
|
|
GlobalMutationIntervalRequest,
|
|
LoginRequest,
|
|
ResetUserPasswordRequest,
|
|
Token,
|
|
UpdateUserRoleRequest,
|
|
User,
|
|
UserResponse,
|
|
)
|
|
from .attackers import (
|
|
Attacker,
|
|
AttackerBehavior,
|
|
AttackersResponse,
|
|
SessionProfile,
|
|
SmtpTarget,
|
|
)
|
|
from .deploy import (
|
|
DeployIniRequest,
|
|
DeployResponse,
|
|
MutateIntervalRequest,
|
|
PurgeResponse,
|
|
)
|
|
from .health import (
|
|
ComponentHealth,
|
|
HealthResponse,
|
|
)
|
|
from .logs import (
|
|
Bounty,
|
|
BountyResponse,
|
|
Credential,
|
|
CredentialReuse,
|
|
CredentialReuseResponse,
|
|
CredentialsResponse,
|
|
Log,
|
|
LogsResponse,
|
|
State,
|
|
StatsResponse,
|
|
)
|
|
from .swarm import (
|
|
DeckyShard,
|
|
DeckyShardView,
|
|
SwarmCheckResponse,
|
|
SwarmDeployRequest,
|
|
SwarmDeployResponse,
|
|
SwarmEnrolledBundle,
|
|
SwarmEnrollRequest,
|
|
SwarmHost,
|
|
SwarmHostHealth,
|
|
SwarmHostResult,
|
|
SwarmHostView,
|
|
SwarmTeardownRequest,
|
|
SwarmUpdaterBundle,
|
|
)
|
|
from .topology import (
|
|
LAN,
|
|
ArchetypeCatalogResponse,
|
|
ArchetypeEntry,
|
|
DeckyCreateRequest,
|
|
DeckyRow,
|
|
DeckyUpdateRequest,
|
|
DeployAcceptedResponse,
|
|
EdgeCreateRequest,
|
|
EdgeRow,
|
|
LANCreateRequest,
|
|
LANRow,
|
|
LANUpdateRequest,
|
|
MutationEnqueueRequest,
|
|
MutationEnqueueResponse,
|
|
MutationRow,
|
|
NextIPResponse,
|
|
NextSubnetResponse,
|
|
NotEditableResponse,
|
|
ReapReportResponse,
|
|
ServiceCatalogResponse,
|
|
Topology,
|
|
TopologyDecky,
|
|
TopologyDetail,
|
|
TopologyEdge,
|
|
TopologyGenerateRequest,
|
|
TopologyListResponse,
|
|
TopologyMutation,
|
|
TopologyStatusEvent,
|
|
TopologyStatusEventRow,
|
|
TopologySummary,
|
|
ValidationErrorResponse,
|
|
ValidationIssueResponse,
|
|
VersionConflictResponse,
|
|
)
|
|
from .updater import (
|
|
HostReleaseInfo,
|
|
HostReleasesResponse,
|
|
PushUpdateRequest,
|
|
PushUpdateResponse,
|
|
PushUpdateResult,
|
|
RollbackRequest,
|
|
RollbackResponse,
|
|
)
|
|
from .webhooks import (
|
|
SimpleEvent,
|
|
WebhookCreateRequest,
|
|
WebhookCreateResponse,
|
|
WebhookResponse,
|
|
WebhookSubscription,
|
|
WebhookTestResponse,
|
|
WebhookUpdateRequest,
|
|
)
|
|
from .workers import (
|
|
StartAllResponse,
|
|
StartFailure,
|
|
WorkerControlResponse,
|
|
WorkersResponse,
|
|
WorkerStatus,
|
|
)
|
|
|
|
__all__ = [
|
|
# _base
|
|
"NullableDatetime",
|
|
"NullableString",
|
|
"_BIG_TEXT",
|
|
"_normalize_null",
|
|
# common
|
|
"MessageResponse",
|
|
# auth
|
|
"AdminConfigResponse",
|
|
"ChangePasswordRequest",
|
|
"ConfigResponse",
|
|
"CreateUserRequest",
|
|
"DeploymentLimitRequest",
|
|
"GlobalMutationIntervalRequest",
|
|
"LoginRequest",
|
|
"ResetUserPasswordRequest",
|
|
"Token",
|
|
"UpdateUserRoleRequest",
|
|
"User",
|
|
"UserResponse",
|
|
# attackers
|
|
"Attacker",
|
|
"AttackerBehavior",
|
|
"AttackersResponse",
|
|
"SessionProfile",
|
|
"SmtpTarget",
|
|
# deploy
|
|
"DeployIniRequest",
|
|
"DeployResponse",
|
|
"MutateIntervalRequest",
|
|
"PurgeResponse",
|
|
# health
|
|
"ComponentHealth",
|
|
"HealthResponse",
|
|
# logs
|
|
"Bounty",
|
|
"BountyResponse",
|
|
"Credential",
|
|
"CredentialReuse",
|
|
"CredentialReuseResponse",
|
|
"CredentialsResponse",
|
|
"Log",
|
|
"LogsResponse",
|
|
"State",
|
|
"StatsResponse",
|
|
# swarm
|
|
"DeckyShard",
|
|
"DeckyShardView",
|
|
"SwarmCheckResponse",
|
|
"SwarmDeployRequest",
|
|
"SwarmDeployResponse",
|
|
"SwarmEnrolledBundle",
|
|
"SwarmEnrollRequest",
|
|
"SwarmHost",
|
|
"SwarmHostHealth",
|
|
"SwarmHostResult",
|
|
"SwarmHostView",
|
|
"SwarmTeardownRequest",
|
|
"SwarmUpdaterBundle",
|
|
# topology
|
|
"LAN",
|
|
"ArchetypeCatalogResponse",
|
|
"ArchetypeEntry",
|
|
"DeckyCreateRequest",
|
|
"DeckyRow",
|
|
"DeckyUpdateRequest",
|
|
"DeployAcceptedResponse",
|
|
"EdgeCreateRequest",
|
|
"EdgeRow",
|
|
"LANCreateRequest",
|
|
"LANRow",
|
|
"LANUpdateRequest",
|
|
"MutationEnqueueRequest",
|
|
"MutationEnqueueResponse",
|
|
"MutationRow",
|
|
"NextIPResponse",
|
|
"NextSubnetResponse",
|
|
"NotEditableResponse",
|
|
"ReapReportResponse",
|
|
"ServiceCatalogResponse",
|
|
"Topology",
|
|
"TopologyDecky",
|
|
"TopologyDetail",
|
|
"TopologyEdge",
|
|
"TopologyGenerateRequest",
|
|
"TopologyListResponse",
|
|
"TopologyMutation",
|
|
"TopologyStatusEvent",
|
|
"TopologyStatusEventRow",
|
|
"TopologySummary",
|
|
"ValidationErrorResponse",
|
|
"ValidationIssueResponse",
|
|
"VersionConflictResponse",
|
|
# updater
|
|
"HostReleaseInfo",
|
|
"HostReleasesResponse",
|
|
"PushUpdateRequest",
|
|
"PushUpdateResponse",
|
|
"PushUpdateResult",
|
|
"RollbackRequest",
|
|
"RollbackResponse",
|
|
# webhooks
|
|
"SimpleEvent",
|
|
"WebhookCreateRequest",
|
|
"WebhookCreateResponse",
|
|
"WebhookResponse",
|
|
"WebhookSubscription",
|
|
"WebhookTestResponse",
|
|
"WebhookUpdateRequest",
|
|
# workers
|
|
"StartAllResponse",
|
|
"StartFailure",
|
|
"WorkerControlResponse",
|
|
"WorkersResponse",
|
|
"WorkerStatus",
|
|
]
|