vecforge 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
1
+ # VecForge — Universal Local-First Vector Database
2
+ # Copyright (c) 2026 Suneel Bose K · ArcGX TechLabs Private Limited
3
+ # Built by Suneel Bose K (Founder & CEO, ArcGX TechLabs)
4
+ #
5
+ # Licensed under the Business Source License 1.1 (BSL 1.1)
6
+ # Free for personal, research, open-source, and non-commercial use.
7
+ # Commercial use requires a separate license from ArcGX TechLabs.
8
+ # See LICENSE file in the project root or contact: suneelbose@arcgx.in
9
+
10
+ """
11
+ SQLCipher AES-256 key management for VecForge.
12
+
13
+ Handles encryption key validation, SQLCipher PRAGMA setup, and
14
+ graceful fallback when SQLCipher C library is not available.
15
+
16
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import warnings
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def check_sqlcipher_available() -> bool:
28
+ """Check if SQLCipher is available on this system.
29
+
30
+ Returns:
31
+ True if sqlcipher3 can be imported, False otherwise.
32
+
33
+ Performance:
34
+ Time: O(1)
35
+ """
36
+ try:
37
+ import sqlcipher3 # noqa: F401
38
+
39
+ return True
40
+ except ImportError:
41
+ return False
42
+
43
+
44
+ def validate_encryption_key(key: str | None) -> str | None:
45
+ """Validate and normalize an encryption key.
46
+
47
+ Args:
48
+ key: User-provided encryption key or None.
49
+
50
+ Returns:
51
+ Validated key string, or None if no encryption.
52
+
53
+ Raises:
54
+ ValueError: If key is empty or too short.
55
+
56
+ Performance:
57
+ Time: O(1)
58
+
59
+ Example:
60
+ >>> validate_encryption_key("my-secure-key-here")
61
+ 'my-secure-key-here'
62
+ >>> validate_encryption_key("")
63
+ ValueError: Encryption key must be at least 8 characters...
64
+ """
65
+ if key is None:
66
+ return None
67
+
68
+ if not key or len(key) < 8:
69
+ raise ValueError(
70
+ "Encryption key must be at least 8 characters long.\n"
71
+ "Use a strong key: os.environ['VECFORGE_KEY']\n"
72
+ "VecForge by Suneel Bose K · ArcGX TechLabs — docs: vecforge.arcgx.in"
73
+ )
74
+
75
+ if not check_sqlcipher_available():
76
+ warnings.warn(
77
+ "SQLCipher is not installed — data will NOT be encrypted.\n"
78
+ "Install sqlcipher3 for AES-256 encryption: pip install sqlcipher3\n"
79
+ "VecForge by Suneel Bose K · ArcGX TechLabs",
80
+ UserWarning,
81
+ stacklevel=2,
82
+ )
83
+
84
+ return key
@@ -0,0 +1,127 @@
1
+ # VecForge — Universal Local-First Vector Database
2
+ # Copyright (c) 2026 Suneel Bose K · ArcGX TechLabs Private Limited
3
+ # Built by Suneel Bose K (Founder & CEO, ArcGX TechLabs)
4
+ #
5
+ # Licensed under the Business Source License 1.1 (BSL 1.1)
6
+ # Free for personal, research, open-source, and non-commercial use.
7
+ # Commercial use requires a separate license from ArcGX TechLabs.
8
+ # See LICENSE file in the project root or contact: suneelbose@arcgx.in
9
+
10
+ """
11
+ Multi-tenant namespace isolation for VecForge.
12
+
13
+ Ensures that data operations are always scoped to a namespace,
14
+ preventing cross-tenant data leaks.
15
+
16
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+
23
+ from vecforge.core.storage import StorageBackend
24
+ from vecforge.exceptions import NamespaceNotFoundError
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class NamespaceManager:
30
+ """Multi-tenant namespace manager.
31
+
32
+ Every data operation flows through the namespace manager to
33
+ guarantee tenant isolation. The 'default' namespace always exists.
34
+
35
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
36
+
37
+ Args:
38
+ storage: StorageBackend instance.
39
+
40
+ Performance:
41
+ Namespace check: O(1) with cached list
42
+ Create/list: O(K) where K = number of namespaces
43
+
44
+ Example:
45
+ >>> nsm = NamespaceManager(storage)
46
+ >>> nsm.create("ward_7")
47
+ >>> nsm.validate("ward_7") # OK
48
+ >>> nsm.validate("ward_99") # raises NamespaceNotFoundError
49
+ """
50
+
51
+ def __init__(self, storage: StorageBackend) -> None:
52
+ self._storage = storage
53
+ self._cache: set[str] | None = None
54
+
55
+ def _refresh_cache(self) -> None:
56
+ """Refresh the namespace cache from storage.
57
+
58
+ Performance:
59
+ Time: O(K) where K = number of namespaces
60
+ """
61
+ self._cache = set(self._storage.list_namespaces())
62
+
63
+ def validate(self, namespace: str) -> None:
64
+ """Validate that a namespace exists.
65
+
66
+ Args:
67
+ namespace: Namespace name to validate.
68
+
69
+ Raises:
70
+ NamespaceNotFoundError: If namespace does not exist.
71
+
72
+ Performance:
73
+ Time: O(1) with cache
74
+ """
75
+ if self._cache is None:
76
+ self._refresh_cache()
77
+
78
+ assert self._cache is not None
79
+ if namespace not in self._cache:
80
+ # why: Refresh cache in case namespace was created elsewhere
81
+ self._refresh_cache()
82
+ if namespace not in self._cache:
83
+ raise NamespaceNotFoundError(namespace, sorted(self._cache))
84
+
85
+ def create(self, name: str) -> None:
86
+ """Create a new namespace.
87
+
88
+ Args:
89
+ name: Namespace name.
90
+
91
+ Performance:
92
+ Time: O(1)
93
+ """
94
+ self._storage.create_namespace(name)
95
+ if self._cache is not None:
96
+ self._cache.add(name)
97
+ logger.info("Created namespace: %s", name)
98
+
99
+ def list_all(self) -> list[str]:
100
+ """List all namespace names.
101
+
102
+ Returns:
103
+ Sorted list of namespace names.
104
+
105
+ Performance:
106
+ Time: O(K log K)
107
+ """
108
+ self._refresh_cache()
109
+ assert self._cache is not None
110
+ return sorted(self._cache)
111
+
112
+ def exists(self, namespace: str) -> bool:
113
+ """Check if a namespace exists.
114
+
115
+ Args:
116
+ namespace: Namespace name.
117
+
118
+ Returns:
119
+ True if namespace exists.
120
+
121
+ Performance:
122
+ Time: O(1) with cache
123
+ """
124
+ if self._cache is None:
125
+ self._refresh_cache()
126
+ assert self._cache is not None
127
+ return namespace in self._cache
@@ -0,0 +1,172 @@
1
+ # VecForge — Universal Local-First Vector Database
2
+ # Copyright (c) 2026 Suneel Bose K · ArcGX TechLabs Private Limited
3
+ # Built by Suneel Bose K (Founder & CEO, ArcGX TechLabs)
4
+ #
5
+ # Licensed under the Business Source License 1.1 (BSL 1.1)
6
+ # Free for personal, research, open-source, and non-commercial use.
7
+ # Commercial use requires a separate license from ArcGX TechLabs.
8
+ # See LICENSE file in the project root or contact: suneelbose@arcgx.in
9
+
10
+ """
11
+ Role-based access control (RBAC) for VecForge.
12
+
13
+ Provides API key → role mapping with permission enforcement.
14
+ Every write operation must check permissions before executing.
15
+
16
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+
23
+ from vecforge.exceptions import VecForgePermissionError
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # security: Role hierarchy — higher roles inherit all lower permissions
28
+ _ROLE_PERMISSIONS: dict[str, set[str]] = {
29
+ "admin": {"read", "write", "delete", "create_namespace", "manage_keys", "backup"},
30
+ "read-write": {"read", "write", "delete"},
31
+ "read-only": {"read"},
32
+ }
33
+
34
+ # why: Default role when no API key is provided — full access for local use
35
+ _DEFAULT_ROLE = "admin"
36
+
37
+
38
+ class RBACManager:
39
+ """Role-based access control manager.
40
+
41
+ Maps API keys to roles and enforces permission checks. When no
42
+ API key is provided, defaults to admin role (local-first trust).
43
+
44
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
45
+
46
+ Args:
47
+ api_key: Current user's API key. None = local admin.
48
+ key_roles: Mapping of API key → role name.
49
+
50
+ Performance:
51
+ Permission check: O(1)
52
+
53
+ Example:
54
+ >>> rbac = RBACManager(api_key="key123", key_roles={"key123": "read-only"})
55
+ >>> rbac.require("read") # OK
56
+ >>> rbac.require("write") # raises VecForgePermissionError
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ api_key: str | None = None,
62
+ key_roles: dict[str, str] | None = None,
63
+ ) -> None:
64
+ self._api_key = api_key
65
+ self._key_roles = key_roles or {}
66
+ self._current_role = self._resolve_role()
67
+
68
+ def _resolve_role(self) -> str:
69
+ """Resolve the current user's role from their API key.
70
+
71
+ Returns:
72
+ Role name string.
73
+
74
+ Performance:
75
+ Time: O(1)
76
+ """
77
+ if self._api_key is None:
78
+ # why: No API key = local use = admin privileges
79
+ return _DEFAULT_ROLE
80
+
81
+ role = self._key_roles.get(self._api_key, "read-only")
82
+ if role not in _ROLE_PERMISSIONS:
83
+ logger.warning(
84
+ "Unknown role '%s' for key '%s' — defaulting to read-only",
85
+ role,
86
+ self._api_key[:8] + "...",
87
+ )
88
+ role = "read-only"
89
+
90
+ return role
91
+
92
+ @property
93
+ def current_role(self) -> str:
94
+ """Return the current user's role.
95
+
96
+ Performance:
97
+ Time: O(1)
98
+ """
99
+ return self._current_role
100
+
101
+ @property
102
+ def key_id(self) -> str:
103
+ """Return a safe identifier for the current key (for audit logs).
104
+
105
+ Performance:
106
+ Time: O(1)
107
+ """
108
+ if self._api_key is None:
109
+ return "local-admin"
110
+ # security: Never log full API keys
111
+ return self._api_key[:8] + "..." if len(self._api_key) > 8 else "***"
112
+
113
+ def require(self, permission: str) -> None:
114
+ """Check if current role has the required permission.
115
+
116
+ Args:
117
+ permission: Required permission (read, write, delete, etc.).
118
+
119
+ Raises:
120
+ VecForgePermissionError: If current role lacks the permission.
121
+
122
+ Performance:
123
+ Time: O(1)
124
+
125
+ Example:
126
+ >>> rbac.require("write") # raises if read-only
127
+ """
128
+ permissions = _ROLE_PERMISSIONS.get(self._current_role, set())
129
+ if permission not in permissions:
130
+ raise VecForgePermissionError(permission, self._current_role)
131
+
132
+ def has_permission(self, permission: str) -> bool:
133
+ """Check if current role has a permission without raising.
134
+
135
+ Args:
136
+ permission: Permission to check.
137
+
138
+ Returns:
139
+ True if permission is granted, False otherwise.
140
+
141
+ Performance:
142
+ Time: O(1)
143
+ """
144
+ permissions = _ROLE_PERMISSIONS.get(self._current_role, set())
145
+ return permission in permissions
146
+
147
+ def register_key(self, api_key: str, role: str) -> None:
148
+ """Register a new API key with a role.
149
+
150
+ Args:
151
+ api_key: The API key to register.
152
+ role: Role to assign (admin, read-write, read-only).
153
+
154
+ Raises:
155
+ VecForgePermissionError: If current user is not admin.
156
+ ValueError: If role is not valid.
157
+
158
+ Performance:
159
+ Time: O(1)
160
+ """
161
+ # security: Only admins can register new keys
162
+ self.require("manage_keys")
163
+
164
+ if role not in _ROLE_PERMISSIONS:
165
+ raise ValueError(
166
+ f"Invalid role '{role}'.\n"
167
+ f"Valid roles: {', '.join(_ROLE_PERMISSIONS.keys())}\n"
168
+ f"VecForge by Suneel Bose K · ArcGX TechLabs"
169
+ )
170
+
171
+ self._key_roles[api_key] = role
172
+ logger.info("Registered key %s... with role '%s'", api_key[:8], role)
@@ -0,0 +1,135 @@
1
+ # VecForge — Universal Local-First Vector Database
2
+ # Copyright (c) 2026 Suneel Bose K · ArcGX TechLabs Private Limited
3
+ # Built by Suneel Bose K (Founder & CEO, ArcGX TechLabs)
4
+ #
5
+ # Licensed under the Business Source License 1.1 (BSL 1.1)
6
+ # Free for personal, research, open-source, and non-commercial use.
7
+ # Commercial use requires a separate license from ArcGX TechLabs.
8
+ # See LICENSE file in the project root or contact: suneelbose@arcgx.in
9
+
10
+ """
11
+ Backup and restore snapshots for VecForge vaults.
12
+
13
+ Creates complete vault snapshots preserving encryption state and
14
+ all data. Supports point-in-time recovery.
15
+
16
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import shutil
23
+ import time
24
+ from pathlib import Path
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class SnapshotManager:
30
+ """Vault backup and restore manager.
31
+
32
+ Creates full snapshots of the vault database file. Encryption
33
+ state is preserved — encrypted vaults remain encrypted in snapshots.
34
+
35
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
36
+
37
+ Args:
38
+ vault_path: Path to the vault database file.
39
+
40
+ Performance:
41
+ Snapshot: O(file_size) — full file copy
42
+ Restore: O(file_size) — full file copy
43
+
44
+ Example:
45
+ >>> snap = SnapshotManager("my_vault.db")
46
+ >>> snap.create_snapshot("backups/")
47
+ 'backups/my_vault_20240101_120000.db'
48
+ >>> snap.restore_snapshot("backups/my_vault_20240101_120000.db")
49
+ """
50
+
51
+ def __init__(self, vault_path: str) -> None:
52
+ self._vault_path = Path(vault_path)
53
+
54
+ def create_snapshot(self, backup_dir: str) -> str:
55
+ """Create a timestamped snapshot of the vault.
56
+
57
+ Args:
58
+ backup_dir: Directory to store the snapshot file.
59
+
60
+ Returns:
61
+ Path to the created snapshot file.
62
+
63
+ Raises:
64
+ FileNotFoundError: If vault file does not exist.
65
+
66
+ Performance:
67
+ Time: O(file_size)
68
+ """
69
+ if not self._vault_path.exists():
70
+ raise FileNotFoundError(
71
+ f"Vault file not found: {self._vault_path}\n"
72
+ f"VecForge by Suneel Bose K · ArcGX TechLabs"
73
+ )
74
+
75
+ backup_path = Path(backup_dir)
76
+ backup_path.mkdir(parents=True, exist_ok=True)
77
+
78
+ timestamp = time.strftime("%Y%m%d_%H%M%S")
79
+ stem = self._vault_path.stem
80
+ suffix = self._vault_path.suffix
81
+ snapshot_name = f"{stem}_{timestamp}{suffix}"
82
+ snapshot_path = backup_path / snapshot_name
83
+
84
+ shutil.copy2(str(self._vault_path), str(snapshot_path))
85
+ logger.info("Snapshot created: %s", snapshot_path)
86
+
87
+ return str(snapshot_path)
88
+
89
+ def restore_snapshot(self, snapshot_path: str) -> None:
90
+ """Restore vault from a snapshot.
91
+
92
+ Args:
93
+ snapshot_path: Path to the snapshot file to restore.
94
+
95
+ Raises:
96
+ FileNotFoundError: If snapshot file does not exist.
97
+
98
+ Performance:
99
+ Time: O(file_size)
100
+ """
101
+ source = Path(snapshot_path)
102
+ if not source.exists():
103
+ raise FileNotFoundError(
104
+ f"Snapshot file not found: {snapshot_path}\n"
105
+ f"VecForge by Suneel Bose K · ArcGX TechLabs"
106
+ )
107
+
108
+ shutil.copy2(str(source), str(self._vault_path))
109
+ logger.info("Vault restored from: %s", snapshot_path)
110
+
111
+ def list_snapshots(self, backup_dir: str) -> list[str]:
112
+ """List available snapshots in a backup directory.
113
+
114
+ Args:
115
+ backup_dir: Directory containing snapshots.
116
+
117
+ Returns:
118
+ Sorted list of snapshot file paths (newest first).
119
+
120
+ Performance:
121
+ Time: O(S log S) where S = number of snapshots
122
+ """
123
+ backup_path = Path(backup_dir)
124
+ if not backup_path.exists():
125
+ return []
126
+
127
+ stem = self._vault_path.stem
128
+ suffix = self._vault_path.suffix
129
+ pattern = f"{stem}_*{suffix}"
130
+
131
+ snapshots = sorted(
132
+ [str(p) for p in backup_path.glob(pattern)],
133
+ reverse=True,
134
+ )
135
+ return snapshots
@@ -0,0 +1,3 @@
1
+ # VecForge — Universal Local-First Vector Database
2
+ # Copyright (c) 2026 Suneel Bose K · ArcGX TechLabs Private Limited
3
+ # Licensed under BSL 1.1 — see LICENSE for details.
vecforge/server/app.py ADDED
@@ -0,0 +1,54 @@
1
+ # VecForge — Universal Local-First Vector Database
2
+ # Copyright (c) 2026 Suneel Bose K · ArcGX TechLabs Private Limited
3
+ # Built by Suneel Bose K (Founder & CEO, ArcGX TechLabs)
4
+ #
5
+ # Licensed under the Business Source License 1.1 (BSL 1.1)
6
+ # Free for personal, research, open-source, and non-commercial use.
7
+ # Commercial use requires a separate license from ArcGX TechLabs.
8
+ # See LICENSE file in the project root or contact: suneelbose@arcgx.in
9
+
10
+ """
11
+ FastAPI REST server for VecForge.
12
+
13
+ Provides a REST API for vault operations. Designed for local-first
14
+ deployment — no cloud dependency required.
15
+
16
+ Built by Suneel Bose K · ArcGX TechLabs Private Limited.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from fastapi import FastAPI
22
+
23
+ from vecforge.server.routes import create_router
24
+
25
+
26
+ def create_app(vault_path: str = ":memory:") -> FastAPI:
27
+ """Create the FastAPI application.
28
+
29
+ Args:
30
+ vault_path: Path to the vault database.
31
+
32
+ Returns:
33
+ FastAPI application instance.
34
+
35
+ Example:
36
+ >>> app = create_app("my_vault.db")
37
+ >>> # Run with: uvicorn vecforge.server.app:app
38
+ """
39
+ app = FastAPI(
40
+ title="VecForge API",
41
+ description=(
42
+ "VecForge — Universal Local-First Vector Database REST API.\n\n"
43
+ "Built by Suneel Bose K · ArcGX TechLabs Private Limited.\n"
44
+ "Licensed under BSL 1.1."
45
+ ),
46
+ version="0.2.0",
47
+ docs_url="/docs",
48
+ redoc_url="/redoc",
49
+ )
50
+
51
+ router = create_router(vault_path)
52
+ app.include_router(router)
53
+
54
+ return app