chuk-artifacts 0.1.0__py3-none-any.whl → 0.1.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chuk-artifacts
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Add your description here
5
5
  License: MIT
6
6
  Requires-Python: >=3.11
@@ -27,18 +27,20 @@ Dynamic: license-file
27
27
  [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://python.org)
28
28
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
29
29
 
30
- **Asynchronous, multi-backend artifact storage with metadata caching and presigned URLs**
30
+ **Asynchronous, multi-backend artifact storage with session-based security and presigned URLs**
31
31
 
32
- Chuk Artifacts provides a production-ready, modular artifact storage system that works seamlessly across multiple storage backends (memory, filesystem, AWS S3, IBM Cloud Object Storage) with Redis or memory-based metadata caching.
32
+ Chuk Artifacts provides a production-ready, modular artifact storage system that works seamlessly across multiple storage backends (memory, filesystem, AWS S3, IBM Cloud Object Storage) with Redis or memory-based metadata caching and **strict session-based security**.
33
33
 
34
34
  ## ✨ Key Features
35
35
 
36
- - 🏗️ **Modular Architecture**: 5 specialized operation modules for clean separation of concerns
36
+ - 🏗️ **Modular Architecture**: 6 specialized operation modules for clean separation of concerns
37
+ - 🔒 **Session-Based Security**: Strict isolation with no cross-session operations allowed
37
38
  - 🔄 **Multi-Backend Support**: Memory, filesystem, S3, IBM COS with seamless switching
38
- - ⚡ **Fully Async**: Built with async/await for high performance
39
+ - ⚡ **Fully Async**: Built with async/await for high performance (3,000+ ops/sec)
39
40
  - 🔗 **Presigned URLs**: Secure, time-limited access without credential exposure
40
41
  - 📊 **Batch Operations**: Efficient multi-file uploads and processing
41
42
  - 🗃️ **Metadata Caching**: Fast lookups with Redis or memory-based sessions
43
+ - 📁 **Directory-Like Operations**: Organize files with path-based prefixes
42
44
  - 🔧 **Zero Configuration**: Works out of the box with sensible defaults
43
45
  - 🌍 **Production Ready**: Battle-tested with comprehensive error handling
44
46
 
@@ -65,7 +67,8 @@ artifact_id = await store.store(
65
67
  data=b"Hello, world!",
66
68
  mime="text/plain",
67
69
  summary="A simple greeting",
68
- filename="hello.txt"
70
+ filename="hello.txt",
71
+ session_id="user_123" # Session-based isolation
69
72
  )
70
73
 
71
74
  # Retrieve it
@@ -76,6 +79,41 @@ print(data.decode()) # "Hello, world!"
76
79
  download_url = await store.presign_medium(artifact_id) # 1 hour
77
80
  ```
78
81
 
82
+ ### Session-Based File Management
83
+
84
+ ```python
85
+ # Create files in user sessions
86
+ doc_id = await store.write_file(
87
+ content="# User's Document\n\nPrivate content here.",
88
+ filename="docs/private.md",
89
+ mime="text/markdown",
90
+ session_id="user_alice"
91
+ )
92
+
93
+ # List files in a session
94
+ files = await store.list_by_session("user_alice")
95
+ print(f"Alice has {len(files)} files")
96
+
97
+ # List directory-like contents
98
+ docs = await store.get_directory_contents("user_alice", "docs/")
99
+ print(f"Alice's docs: {len(docs)} files")
100
+
101
+ # Copy within same session (allowed)
102
+ backup_id = await store.copy_file(
103
+ doc_id,
104
+ new_filename="docs/private_backup.md"
105
+ )
106
+
107
+ # Cross-session operations are BLOCKED for security
108
+ try:
109
+ await store.copy_file(
110
+ doc_id,
111
+ target_session_id="user_bob" # This will fail
112
+ )
113
+ except ArtifactStoreError:
114
+ print("Cross-session operations blocked!")
115
+ ```
116
+
79
117
  ### With Configuration
80
118
 
81
119
  ```python
@@ -104,7 +142,8 @@ Chuk Artifacts uses a modular architecture with specialized operation modules:
104
142
  ArtifactStore (Main Coordinator)
105
143
  ├── CoreStorageOperations # store() and retrieve()
106
144
  ├── PresignedURLOperations # URL generation and upload workflows
107
- ├── MetadataOperations # metadata, exists, delete, update
145
+ ├── MetadataOperations # metadata, exists, delete, update, list_by_session
146
+ ├── SessionOperations # session-based file operations (NEW)
108
147
  ├── BatchOperations # store_batch() for multiple files
109
148
  └── AdminOperations # validate_configuration, get_stats
110
149
  ```
@@ -114,6 +153,43 @@ This design provides:
114
153
  - **Enhanced maintainability**: Clear separation of concerns
115
154
  - **Easy extensibility**: Add new operation types without touching core
116
155
  - **Improved debugging**: Isolated functionality for easier troubleshooting
156
+ - **Session security**: Dedicated module for secure session operations
157
+
158
+ ## 🔒 Session-Based Security
159
+
160
+ ### Strict Session Isolation
161
+ ```python
162
+ # Users can only access their own files
163
+ alice_files = await store.list_by_session("user_alice")
164
+ bob_files = await store.list_by_session("user_bob")
165
+
166
+ # Cross-session operations are blocked
167
+ await store.copy_file(alice_file_id, target_session_id="user_bob") # ❌ Blocked
168
+ await store.move_file(alice_file_id, new_session_id="user_bob") # ❌ Blocked
169
+ ```
170
+
171
+ ### Multi-Tenant Safe
172
+ ```python
173
+ # Perfect for SaaS applications
174
+ company_a_files = await store.list_by_session("company_a")
175
+ company_b_files = await store.list_by_session("company_b")
176
+
177
+ # Companies cannot access each other's data
178
+ # Compliance-ready: GDPR, SOX, HIPAA
179
+ ```
180
+
181
+ ### Directory-Like Organization
182
+ ```python
183
+ # Organize files with path-like prefixes
184
+ await store.write_file(content, filename="docs/reports/q1_sales.pdf", session_id="user_123")
185
+ await store.write_file(content, filename="docs/contracts/client_a.pdf", session_id="user_123")
186
+ await store.write_file(content, filename="images/profile.jpg", session_id="user_123")
187
+
188
+ # List by directory
189
+ docs = await store.get_directory_contents("user_123", "docs/")
190
+ reports = await store.get_directory_contents("user_123", "docs/reports/")
191
+ images = await store.get_directory_contents("user_123", "images/")
192
+ ```
117
193
 
118
194
  ## 📦 Storage Providers
119
195
 
@@ -124,7 +200,7 @@ store = ArtifactStore(storage_provider="memory")
124
200
  - Perfect for development and testing
125
201
  - Zero configuration required
126
202
  - Non-persistent (data lost on restart)
127
- - Isolation between async contexts
203
+ - Session listing returns empty (graceful degradation)
128
204
 
129
205
  ### Filesystem Provider
130
206
  ```python
@@ -135,6 +211,7 @@ os.environ["ARTIFACT_FS_ROOT"] = "./my-artifacts"
135
211
  - Local disk storage
136
212
  - Persistent across restarts
137
213
  - `file://` URLs for local access
214
+ - **Full session listing support**
138
215
  - Great for development and small deployments
139
216
 
140
217
  ### AWS S3 Provider
@@ -151,6 +228,7 @@ os.environ.update({
151
228
  - Industry-standard cloud storage
152
229
  - Native presigned URL support
153
230
  - Highly scalable and durable
231
+ - **Full session listing support**
154
232
  - Perfect for production workloads
155
233
 
156
234
  ### IBM Cloud Object Storage
@@ -192,6 +270,40 @@ os.environ["SESSION_REDIS_URL"] = "redis://localhost:6379/0"
192
270
 
193
271
  ## 🎯 Common Use Cases
194
272
 
273
+ ### MCP Server Integration
274
+
275
+ ```python
276
+ from chuk_artifacts import ArtifactStore
277
+
278
+ # Initialize for MCP server
279
+ store = ArtifactStore(
280
+ storage_provider="filesystem", # or "s3" for production
281
+ session_provider="redis"
282
+ )
283
+
284
+ # MCP tool: Upload file
285
+ async def upload_file(data_base64: str, filename: str, mime: str, session_id: str):
286
+ data = base64.b64decode(data_base64)
287
+ artifact_id = await store.store(
288
+ data=data,
289
+ mime=mime,
290
+ summary=f"Uploaded: {filename}",
291
+ filename=filename,
292
+ session_id=session_id # Session isolation
293
+ )
294
+ return {"artifact_id": artifact_id}
295
+
296
+ # MCP tool: List session files
297
+ async def list_session_files(session_id: str, prefix: str = ""):
298
+ files = await store.list_by_prefix(session_id, prefix)
299
+ return {"files": files}
300
+
301
+ # MCP tool: Copy file (within session only)
302
+ async def copy_file(artifact_id: str, new_filename: str):
303
+ new_id = await store.copy_file(artifact_id, new_filename=new_filename)
304
+ return {"new_artifact_id": new_id}
305
+ ```
306
+
195
307
  ### Web Framework Integration
196
308
 
197
309
  ```python
@@ -203,13 +315,14 @@ store = ArtifactStore(
203
315
  session_provider="redis"
204
316
  )
205
317
 
206
- async def upload_file(file_content: bytes, filename: str, content_type: str):
207
- """Handle file upload in FastAPI/Flask"""
318
+ async def upload_file(file_content: bytes, filename: str, content_type: str, user_id: str):
319
+ """Handle file upload in FastAPI/Flask with user isolation"""
208
320
  artifact_id = await store.store(
209
321
  data=file_content,
210
322
  mime=content_type,
211
323
  summary=f"Uploaded: {filename}",
212
- filename=filename
324
+ filename=filename,
325
+ session_id=f"user_{user_id}" # User-specific session
213
326
  )
214
327
 
215
328
  # Return download URL
@@ -218,6 +331,73 @@ async def upload_file(file_content: bytes, filename: str, content_type: str):
218
331
  "artifact_id": artifact_id,
219
332
  "download_url": download_url
220
333
  }
334
+
335
+ async def list_user_files(user_id: str, directory: str = ""):
336
+ """List files for a specific user"""
337
+ return await store.get_directory_contents(f"user_{user_id}", directory)
338
+ ```
339
+
340
+ ### Multi-Tenant SaaS Application
341
+
342
+ ```python
343
+ # Tenant isolation
344
+ async def create_tenant_workspace(tenant_id: str):
345
+ """Create isolated workspace for tenant"""
346
+
347
+ # Create tenant directory structure
348
+ directories = ["documents/", "images/", "reports/", "config/"]
349
+
350
+ for directory in directories:
351
+ # Create a marker file for the directory
352
+ await store.write_file(
353
+ content=f"# {directory.rstrip('/')} Directory\n\nTenant workspace created.",
354
+ filename=f"{directory}README.md",
355
+ session_id=f"tenant_{tenant_id}"
356
+ )
357
+
358
+ return {"tenant_id": tenant_id, "directories": directories}
359
+
360
+ async def get_tenant_usage(tenant_id: str):
361
+ """Get storage usage for a tenant"""
362
+ files = await store.list_by_session(f"tenant_{tenant_id}")
363
+ total_bytes = sum(file.get('bytes', 0) for file in files)
364
+
365
+ return {
366
+ "tenant_id": tenant_id,
367
+ "file_count": len(files),
368
+ "total_bytes": total_bytes,
369
+ "total_mb": round(total_bytes / 1024 / 1024, 2)
370
+ }
371
+ ```
372
+
373
+ ### Advanced File Operations
374
+
375
+ ```python
376
+ # Read file content directly
377
+ content = await store.read_file(artifact_id, as_text=True)
378
+ print(f"File content: {content}")
379
+
380
+ # Write file with content
381
+ new_id = await store.write_file(
382
+ content="# New Document\n\nThis is a new file.",
383
+ filename="documents/new_doc.md",
384
+ mime="text/markdown",
385
+ session_id="user_123"
386
+ )
387
+
388
+ # Move/rename file within session
389
+ await store.move_file(
390
+ artifact_id,
391
+ new_filename="documents/renamed_doc.md"
392
+ )
393
+
394
+ # Update file content (overwrite)
395
+ updated_id = await store.write_file(
396
+ content="# Updated Document\n\nThis content replaces the old file.",
397
+ filename="documents/updated_doc.md",
398
+ session_id="user_123",
399
+ overwrite_artifact_id=old_artifact_id
400
+ )
221
401
  ```
222
402
 
223
403
  ### Batch Processing
@@ -229,45 +409,18 @@ items = [
229
409
  "data": file1_content,
230
410
  "mime": "image/png",
231
411
  "summary": "Product image 1",
232
- "filename": "product1.png"
412
+ "filename": "images/product1.png"
233
413
  },
234
414
  {
235
415
  "data": file2_content,
236
416
  "mime": "image/png",
237
417
  "summary": "Product image 2",
238
- "filename": "product2.png"
418
+ "filename": "images/product2.png"
239
419
  }
240
420
  ]
241
421
 
242
- # Store all at once
243
- artifact_ids = await store.store_batch(items, session_id="product-images")
244
- ```
245
-
246
- ### Advanced Metadata Management
247
-
248
- ```python
249
- # Store with custom metadata
250
- artifact_id = await store.store(
251
- data=image_data,
252
- mime="image/png",
253
- summary="Product photo",
254
- filename="product.png",
255
- meta={
256
- "product_id": "12345",
257
- "photographer": "John Doe",
258
- "category": "electronics"
259
- }
260
- )
261
-
262
- # Update metadata later
263
- await store.update_metadata(
264
- artifact_id,
265
- summary="Updated product photo",
266
- meta={"edited": True, "version": 2}
267
- )
268
-
269
- # Extend TTL
270
- await store.extend_ttl(artifact_id, 3600) # Add 1 hour
422
+ # Store all at once with session isolation
423
+ artifact_ids = await store.store_batch(items, session_id="product-catalog")
271
424
  ```
272
425
 
273
426
  ### Context Manager Usage
@@ -277,11 +430,52 @@ async with ArtifactStore() as store:
277
430
  artifact_id = await store.store(
278
431
  data=b"Temporary data",
279
432
  mime="text/plain",
280
- summary="Auto-cleanup example"
433
+ summary="Auto-cleanup example",
434
+ session_id="temp_session"
281
435
  )
282
436
  # Store automatically closed on exit
283
437
  ```
284
438
 
439
+ ## 🧪 Testing
440
+
441
+ ### Run All Tests
442
+ ```bash
443
+ # Comprehensive smoke test (64+ test scenarios)
444
+ uv run examples/artifact_smoke_test.py
445
+
446
+ # Usage examples with all providers
447
+ uv run examples/artifact_usage_examples.py
448
+
449
+ # Session operations demo (NEW)
450
+ uv run examples/session_operations_demo.py
451
+ ```
452
+
453
+ ### Session Security Testing
454
+ ```bash
455
+ # Run secure session operations demo
456
+ uv run examples/session_operations_demo.py
457
+
458
+ # Output shows:
459
+ # ✅ Cross-session copy correctly blocked
460
+ # ✅ Cross-session move correctly blocked
461
+ # ✅ Cross-session overwrite correctly blocked
462
+ # 🛡️ ALL SECURITY TESTS PASSED!
463
+ ```
464
+
465
+ ### Development Setup
466
+ ```python
467
+ from chuk_artifacts.config import development_setup
468
+
469
+ store = development_setup() # Uses memory providers
470
+ ```
471
+
472
+ ### Testing Setup
473
+ ```python
474
+ from chuk_artifacts.config import testing_setup
475
+
476
+ store = testing_setup("./test-artifacts") # Uses filesystem
477
+ ```
478
+
285
479
  ## 🔧 Configuration
286
480
 
287
481
  ### Environment Variables
@@ -328,91 +522,41 @@ configure_redis_session("redis://prod-redis:6379/1")
328
522
  store = ArtifactStore()
329
523
  ```
330
524
 
331
- ## 🧪 Testing
332
-
333
- ### Run All Tests
334
- ```bash
335
- # Comprehensive smoke test (64 test scenarios)
336
- uv run examples/artifact_smoke_test.py
337
-
338
- # Usage examples
339
- uv run examples/artifact_usage_examples.py
340
- ```
341
-
342
- ### Development Setup
343
- ```python
344
- from chuk_artifacts.config import development_setup
345
-
346
- store = development_setup() # Uses memory providers
347
- ```
348
-
349
- ### Testing Setup
350
- ```python
351
- from chuk_artifacts.config import testing_setup
352
-
353
- store = testing_setup("./test-artifacts") # Uses filesystem
354
- ```
355
-
356
525
  ## 🚀 Performance
357
526
 
527
+ - **High Throughput**: 3,000+ file operations per second
358
528
  - **Async/Await**: Non-blocking I/O for high concurrency
359
529
  - **Connection Pooling**: Efficient resource usage with aioboto3
360
- - **Metadata Caching**: Fast lookups with Redis
530
+ - **Metadata Caching**: Sub-millisecond lookups with Redis
361
531
  - **Batch Operations**: Reduced overhead for multiple files
362
532
  - **Streaming**: Large file support with streaming reads/writes
533
+ - **Session Listing**: Optimized prefix-based queries
534
+
535
+ ### Performance Benchmarks
536
+ ```
537
+ ✅ Created 50 files in 0.02 seconds (2,933 files/sec)
538
+ ✅ Listed 50 files in 0.006 seconds
539
+ ✅ Listed directory (50 files) in 0.005 seconds
540
+ ✅ Read 10 files in 0.002 seconds (5,375 reads/sec)
541
+ ```
363
542
 
364
543
  ## 🔒 Security
365
544
 
545
+ - **Session Isolation**: Strict boundaries prevent cross-session access
546
+ - **No Cross-Session Operations**: Copy, move, overwrite blocked across sessions
366
547
  - **Presigned URLs**: Time-limited access without credential sharing
367
548
  - **Secure Defaults**: Conservative TTL and expiration settings
368
549
  - **Credential Isolation**: Environment-based configuration
369
550
  - **Error Handling**: No sensitive data in logs or exceptions
551
+ - **Multi-Tenant Ready**: Perfect for SaaS applications
370
552
 
371
- ## 🛠️ Advanced Features
372
-
373
- ### Custom Providers
374
- ```python
375
- # Create custom storage provider
376
- def my_custom_factory():
377
- @asynccontextmanager
378
- async def _ctx():
379
- client = MyCustomClient()
380
- try:
381
- yield client
382
- finally:
383
- await client.close()
384
- return _ctx
385
-
386
- store = ArtifactStore(s3_factory=my_custom_factory())
387
- ```
388
-
389
- ### Error Handling
390
- ```python
391
- from chuk_artifacts import (
392
- ArtifactNotFoundError,
393
- ArtifactExpiredError,
394
- ProviderError
395
- )
396
-
397
- try:
398
- data = await store.retrieve("invalid-id")
399
- except ArtifactNotFoundError:
400
- print("Artifact not found or expired")
401
- except ProviderError as e:
402
- print(f"Storage provider error: {e}")
403
- ```
404
-
405
- ### Validation and Monitoring
553
+ ### Security Validation
406
554
  ```python
407
- # Validate configuration
408
- config_status = await store.validate_configuration()
409
- print(f"Storage: {config_status['storage']['status']}")
410
- print(f"Session: {config_status['session']['status']}")
411
-
412
- # Get statistics
413
- stats = await store.get_stats()
414
- print(f"Provider: {stats['storage_provider']}")
415
- print(f"Bucket: {stats['bucket']}")
555
+ # All these operations are blocked for security
556
+ await store.copy_file(user_a_file, target_session_id="user_b") # ❌ Blocked
557
+ await store.move_file(user_a_file, new_session_id="user_b") # ❌ Blocked
558
+ await store.write_file(content, session_id="user_b",
559
+ overwrite_artifact_id=user_a_file) # ❌ Blocked
416
560
  ```
417
561
 
418
562
  ## 📝 API Reference
@@ -422,39 +566,37 @@ print(f"Bucket: {stats['bucket']}")
422
566
  #### `store(data, *, mime, summary, meta=None, filename=None, session_id=None, ttl=900)`
423
567
  Store artifact data with metadata.
424
568
 
425
- **Parameters:**
426
- - `data` (bytes): The artifact data
427
- - `mime` (str): MIME type (e.g., "text/plain", "image/png")
428
- - `summary` (str): Human-readable description
429
- - `meta` (dict, optional): Additional metadata
430
- - `filename` (str, optional): Original filename
431
- - `session_id` (str, optional): Session identifier for organization
432
- - `ttl` (int, optional): Metadata TTL in seconds (default: 900)
433
-
434
- **Returns:** `str` - Unique artifact identifier
435
-
436
569
  #### `retrieve(artifact_id)`
437
570
  Retrieve artifact data by ID.
438
571
 
439
- **Parameters:**
440
- - `artifact_id` (str): The artifact identifier
441
-
442
- **Returns:** `bytes` - The artifact data
443
-
444
572
  #### `metadata(artifact_id)`
445
573
  Get artifact metadata.
446
574
 
447
- **Returns:** `dict` - Metadata including size, MIME type, timestamps, etc.
575
+ #### `exists(artifact_id)` / `delete(artifact_id)`
576
+ Check existence or delete artifacts.
577
+
578
+ ### Session Operations (NEW)
579
+
580
+ #### `list_by_session(session_id, limit=100)`
581
+ List all artifacts in a session.
582
+
583
+ #### `list_by_prefix(session_id, prefix="", limit=100)`
584
+ List artifacts with filename prefix (directory-like).
585
+
586
+ #### `get_directory_contents(session_id, directory_prefix="", limit=100)`
587
+ Get files in a directory-like structure.
448
588
 
449
- #### `exists(artifact_id)`
450
- Check if artifact exists and hasn't expired.
589
+ #### `copy_file(artifact_id, *, new_filename=None, target_session_id=None, new_meta=None)`
590
+ Copy file within same session only (cross-session blocked).
451
591
 
452
- **Returns:** `bool`
592
+ #### `move_file(artifact_id, *, new_filename=None, new_session_id=None, new_meta=None)`
593
+ Move/rename file within same session only (cross-session blocked).
453
594
 
454
- #### `delete(artifact_id)`
455
- Delete artifact and its metadata.
595
+ #### `read_file(artifact_id, *, encoding="utf-8", as_text=True)`
596
+ Read file content directly as text or binary.
456
597
 
457
- **Returns:** `bool` - True if deleted, False if not found
598
+ #### `write_file(content, *, filename, mime="text/plain", session_id=None, overwrite_artifact_id=None)`
599
+ Write content to new file or overwrite existing (within same session).
458
600
 
459
601
  ### Presigned URLs
460
602
 
@@ -467,18 +609,11 @@ Generate URLs with predefined durations (15min/1hr/24hr).
467
609
  #### `presign_upload(session_id=None, filename=None, mime_type="application/octet-stream", expires=3600)`
468
610
  Generate presigned URL for upload.
469
611
 
470
- **Returns:** `tuple[str, str]` - (upload_url, artifact_id)
471
-
472
612
  ### Batch Operations
473
613
 
474
614
  #### `store_batch(items, session_id=None, ttl=900)`
475
615
  Store multiple artifacts efficiently.
476
616
 
477
- **Parameters:**
478
- - `items` (list): List of dicts with keys: data, mime, summary, meta, filename
479
-
480
- **Returns:** `list[str]` - List of artifact IDs
481
-
482
617
  ### Admin Operations
483
618
 
484
619
  #### `validate_configuration()`
@@ -487,13 +622,64 @@ Validate storage and session provider connectivity.
487
622
  #### `get_stats()`
488
623
  Get storage statistics and configuration info.
489
624
 
625
+ ## 🛠️ Advanced Features
626
+
627
+ ### Custom Providers
628
+ ```python
629
+ # Create custom storage provider
630
+ def my_custom_factory():
631
+ @asynccontextmanager
632
+ async def _ctx():
633
+ client = MyCustomClient()
634
+ try:
635
+ yield client
636
+ finally:
637
+ await client.close()
638
+ return _ctx
639
+
640
+ store = ArtifactStore(s3_factory=my_custom_factory())
641
+ ```
642
+
643
+ ### Error Handling
644
+ ```python
645
+ from chuk_artifacts import (
646
+ ArtifactNotFoundError,
647
+ ArtifactExpiredError,
648
+ ProviderError,
649
+ ArtifactStoreError # NEW: for session security violations
650
+ )
651
+
652
+ try:
653
+ await store.copy_file(artifact_id, target_session_id="other_session")
654
+ except ArtifactStoreError as e:
655
+ print(f"Security violation: {e}")
656
+ except ArtifactNotFoundError:
657
+ print("Artifact not found or expired")
658
+ except ProviderError as e:
659
+ print(f"Storage provider error: {e}")
660
+ ```
661
+
662
+ ### Validation and Monitoring
663
+ ```python
664
+ # Validate configuration
665
+ config_status = await store.validate_configuration()
666
+ print(f"Storage: {config_status['storage']['status']}")
667
+ print(f"Session: {config_status['session']['status']}")
668
+
669
+ # Get statistics
670
+ stats = await store.get_stats()
671
+ print(f"Provider: {stats['storage_provider']}")
672
+ print(f"Bucket: {stats['bucket']}")
673
+ ```
674
+
490
675
  ## 🤝 Contributing
491
676
 
492
677
  1. Fork the repository
493
678
  2. Create a feature branch: `git checkout -b feature-name`
494
679
  3. Make your changes
495
680
  4. Run tests: `uv run examples/artifact_smoke_test.py`
496
- 5. Submit a pull request
681
+ 5. Test session operations: `uv run examples/session_operations_demo.py`
682
+ 6. Submit a pull request
497
683
 
498
684
  ## 📄 License
499
685
 
@@ -507,6 +693,9 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
507
693
 
508
694
  ## 🎯 Roadmap
509
695
 
696
+ - [x] **Session-based security** with strict isolation
697
+ - [x] **Directory-like operations** with prefix filtering
698
+ - [x] **High-performance operations** (3,000+ ops/sec)
510
699
  - [ ] Azure Blob Storage provider
511
700
  - [ ] Google Cloud Storage provider
512
701
  - [ ] Encryption at rest
@@ -517,3 +706,5 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
517
706
  ---
518
707
 
519
708
  **Made with ❤️ by the Chuk team**
709
+
710
+ *Secure, fast, and reliable artifact storage for modern applications*
@@ -5,19 +5,20 @@ chuk_artifacts/batch.py,sha256=I5WgWajuzrvqTCkQYYgpzrL1WduzxJchA3Ihv3Xvhcw,4264
5
5
  chuk_artifacts/config.py,sha256=MaUzHzKPoBUyERviEpv8JVvPybMzSksgLyj0b7AO3Sc,7664
6
6
  chuk_artifacts/core.py,sha256=rS5VWjen6aRlnAIJIhLi639VfZ2u8ORaFxrPJMfpPYE,8296
7
7
  chuk_artifacts/exceptions.py,sha256=f-s7Mg7c8vMXsbgqO2B6lMHdXcJQNvsESAY4GhJaV4g,814
8
- chuk_artifacts/metadata.py,sha256=CXC0j-zjKSOoACmyu6yKqcQpy35otwr-Y_3ogfcuKdE,9123
8
+ chuk_artifacts/metadata.py,sha256=cBMMwCuyRtmJHjqHWuKMbK1G-kg_G4QpfqW-7RxhIzw,11858
9
9
  chuk_artifacts/models.py,sha256=_foXlkr0DprqgztDw5WtlDc-s1OouLgYNp4XM1Ghp-g,837
10
10
  chuk_artifacts/presigned.py,sha256=qonNg7WMd7VmOEXAzF6GssXuPs5be2s8IJhtuFul7JM,9638
11
11
  chuk_artifacts/provider_factory.py,sha256=T0IXx1C8gygJzp417oB44_DxEaZoZR7jcdwQy8FghRE,3398
12
- chuk_artifacts/store.py,sha256=QdNyik5EXg2NthHnWxUnG5JMM1L7v2jO7phCL5WmvGg,16719
12
+ chuk_artifacts/session_operations.py,sha256=dolSkSnm4W_9ZElZJy25RmsKuLjUnnkfH8SvZWkbW5A,14232
13
+ chuk_artifacts/store.py,sha256=0RKSAkSfTM1v-qg3ERv3-8VaRiBVMxdGy2qxjFVz9MY,19749
13
14
  chuk_artifacts/providers/__init__.py,sha256=3lN1lAy1ETT1mQslJo1f22PPR1W4CyxmsqJBclzH4NE,317
14
15
  chuk_artifacts/providers/filesystem.py,sha256=F4EjE-_ItPg0RWe7CqameVpOMjU-b7AigEBkm_ZoNrc,15280
15
16
  chuk_artifacts/providers/ibm_cos.py,sha256=K1-VAX4UVV9tA161MOeDXOKloQ0hB77jdw1-p46FwmU,4445
16
17
  chuk_artifacts/providers/ibm_cos_iam.py,sha256=VtwvCi9rMMcZx6i9l21ob6wM8jXseqvjzgCnAA82RkY,3186
17
18
  chuk_artifacts/providers/memory.py,sha256=B1C-tR1PcNz-UuDfGm1bhjPz3oITVATIMPekVbE7nm4,10487
18
19
  chuk_artifacts/providers/s3.py,sha256=eWhBhFSaobpRbazn7ySfU_7D8rm_xCfdSVqRtzXzXRY,2858
19
- chuk_artifacts-0.1.0.dist-info/licenses/LICENSE,sha256=SG9BmgtPBagPV0d-Fep-msdAGl-E1CeoBL7-EDRH2qA,1066
20
- chuk_artifacts-0.1.0.dist-info/METADATA,sha256=G_DqvAOc5JzpuPDDdMqA1ULhTk6J588gjVVvQ9_5H9A,13847
21
- chuk_artifacts-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- chuk_artifacts-0.1.0.dist-info/top_level.txt,sha256=1_PVMtWXR0A-ZmeH6apF9mPaMtU0i23JE6wmN4GBRDI,15
23
- chuk_artifacts-0.1.0.dist-info/RECORD,,
20
+ chuk_artifacts-0.1.2.dist-info/licenses/LICENSE,sha256=SG9BmgtPBagPV0d-Fep-msdAGl-E1CeoBL7-EDRH2qA,1066
21
+ chuk_artifacts-0.1.2.dist-info/METADATA,sha256=RYa5j8aK37uuKQeqSp9OKpP1KGU4PoJXLUStrwq_pZY,20975
22
+ chuk_artifacts-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ chuk_artifacts-0.1.2.dist-info/top_level.txt,sha256=1_PVMtWXR0A-ZmeH6apF9mPaMtU0i23JE6wmN4GBRDI,15
24
+ chuk_artifacts-0.1.2.dist-info/RECORD,,