tap-agent 0.3.0

Rust implementation of the Transaction Authorization Protocol (TAP)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
# TAP Agent

The `tap-agent` crate implements the agent functionality for the Transaction Authorization Protocol (TAP), providing a secure and extensible framework for handling TAP messages, managing cryptographic operations, and resolving decentralized identifiers (DIDs).

## Overview

The TAP Agent serves as the foundation for secure communication in the TAP ecosystem, enabling entities to:

- **Establish secure identities** using Decentralized Identifiers (DIDs)
- **Exchange authenticated and encrypted messages** with strong cryptographic guarantees
- **Process and validate** TAP protocol messages for compliant transfers and payments
- **Manage cryptographic keys** with support for multiple key types and algorithms
- **Resolve DIDs** across different methods with a pluggable resolver architecture
- **Automatically deliver messages** to service endpoints defined in DID documents
- **Generate, store, and retrieve** cryptographic keys for long-term use

## Architecture

The `tap-agent` crate is designed with a modular architecture that separates concerns and allows for extensibility:

```
tap-agent
├── agent        - Core agent implementation and traits
├── agent_key    - Key abstraction for signing, verification, encryption, and decryption
├── config       - Agent configuration parameters
├── cli          - Command-line interface for DID and key management
├── crypto       - Cryptographic operations and message security
├── did          - DID resolution, generation, and validation
├── error        - Error types and handling
├── key_manager  - Key generation and management
├── local_agent_key - Concrete implementation of AgentKey traits
├── message      - Message formatting and processing
├── message_packing - Message packing and unpacking utilities
├── storage      - Persistent storage for keys and DIDs
```

This architecture follows clean separation of concerns principles:
- Core agent functionality is independent from specific cryptographic implementations
- DID resolution is pluggable with support for multiple methods
- Key management is abstracted to support different security approaches
- CLI tools provide user-friendly access to core functionality

### Key Components

#### Agent

The `Agent` trait defines the core interface for TAP agents, with methods for:

- Retrieving the agent's DID
- Sending messages with appropriate security modes
- Finding service endpoints from DID documents
- Receiving and unpacking messages
- Validating message contents

The `Agent` implementation provides a production-ready implementation of this trait with:
- Multiple creation methods (builder pattern, from stored keys, ephemeral)
- Automatic service endpoint discovery and message delivery
- Configurable timeout and security settings
- Comprehensive logging for debugging
- Support for both native and WASM environments
- Integration with the `KeyManager` for cryptographic operations

#### AgentKey

The `AgentKey` trait hierarchy provides a flexible abstraction for cryptographic keys:

- `AgentKey` - Base trait with core properties (key ID, DID, key type)
- `SigningKey` - Extends `AgentKey` with capabilities for creating JWS signatures
- `VerificationKey` - Trait for verifying signatures (can be implemented by public keys)
- `EncryptionKey` - Extends `AgentKey` with capabilities for creating JWE encryptions
- `DecryptionKey` - Extends `AgentKey` with capabilities for decrypting JWEs

The `LocalAgentKey` implementation provides a concrete implementation that:
- Stores key material locally (in memory or persistent storage)
- Supports multiple cryptographic algorithms (Ed25519, P-256, Secp256k1)
- Implements all the AgentKey-related traits for complete cryptographic functionality
- Works with the JWS/JWE standards for signatures and encryption

This trait-based approach enables:
- Clean separation between key management and cryptographic operations
- Support for different key storage mechanisms (local, HSM, remote, etc.)
- Flexible algorithm selection based on key types
- Simple interface for common cryptographic operations

#### DID Resolution

The DID resolution system supports multiple DID methods through a pluggable architecture:

- `SyncDIDResolver` - A trait for resolving DIDs to DID documents
- `DIDMethodResolver` - A trait for method-specific resolvers
- `KeyResolver` - A resolver for the `did:key` method (Ed25519, P-256, Secp256k1)
- `WebResolver` - A resolver for the `did:web` method with HTTP resolution
- `MultiResolver` - A resolver that manages multiple method-specific resolvers

The system includes advanced features like:
- Automatic conversion between Ed25519 verification keys and X25519 key agreement keys
- Support for JWK, Multibase, and Base58 key formats
- Integration with W3C compliant DID Document formats
- Caching for improved performance

#### Cryptographic Operations

The cryptographic system provides:

- `MessagePacker` - A trait for packing and unpacking secure messages
- `DefaultMessagePacker` - Standards-compliant implementation of JWS/JWE formats
- `BasicSecretResolver` - A simple in-memory implementation for development
- `KeyManager` - A component for generating and managing cryptographic keys
- `KeyStorage` - Persistent storage for cryptographic keys and metadata

#### Security Modes

The agent supports different security modes for messages:

- `Plain` - No security (for testing only)
- `Signed` - Messages are digitally signed but not encrypted (integrity protection)
- `AuthCrypt` - Messages are authenticated and encrypted (confidentiality + integrity)

Each security mode uses standards-compliant cryptographic approaches:
- Signing uses JSON Web Signatures (JWS) with algorithm selection based on key type
- Encryption uses JSON Web Encryption (JWE) with AES-GCM and ECDH-ES key agreement
- Message formats are compatible with broader DIDComm ecosystem standards

## Features

- **Secure Identity Management**: Create and manage agent identities using DIDs
- **Message Processing**: Handle TAP message flows with proper validation
- **DID Resolution**: Resolve DIDs for message routing, key discovery, and service endpoints
- **Cryptographic Operations**: Sign, verify, encrypt, and decrypt messages
- **Key Management**: Generate, store, and retrieve cryptographic keys
- **Message Delivery**: Automatically deliver messages to service endpoints
- **DID Generation CLI**: Command-line tools for creating and managing DIDs and keys
- **Ephemeral DIDs**: Create temporary DIDs for testing or short-lived processes
- **Asynchronous Processing**: Process messages concurrently using Tokio
- **WASM Support**: Run in browser environments with WebAssembly
- **Extensible DID Methods**: Support for did:key and did:web, with architecture for adding more methods
- **Performance Optimized**: Benchmarked for high-throughput scenarios
- **Native Cryptography**: Direct implementation of cryptographic operations without external DIDComm dependencies
- **Persistent Storage**: Store keys securely for long-term use
- **Multiple Key Types**: Support for Ed25519, P-256, and Secp256k1 keys
- **Standards-Compliant**: Implementation follows W3C DID and IETF JWS/JWE standards

## Usage Examples

### Agent Creation

The TAP Agent can be created in multiple ways depending on your needs. Here are the main approaches:

#### 1. Using Agent Builder (Recommended)

The builder pattern provides a clean, fluent interface for creating agents:

```rust
use tap_agent::{Agent, AgentBuilder, DefaultKeyManager};
use std::sync::Arc;

// Create a key manager
let key_manager = Arc::new(DefaultKeyManager::new());

// Generate a new key or load an existing one
let key = key_manager.generate_key(DIDGenerationOptions {
    key_type: KeyType::Ed25519,
})?;

// Build the agent with the generated key
let agent = AgentBuilder::new(key.did)
    .with_debug(true)
    .with_timeout(30)
    .with_security_mode("SIGNED".to_string())
    .build(key_manager);
```

#### 2. Using Stored Keys

Load keys from the default storage location (~/.tap/keys.json). Keys can be referenced by their DID or label:

```rust
use tap_agent::Agent;

// Use a stored key with a specific DID
let agent = Agent::from_stored_keys(
    Some("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string()),
    true
).await?;

// Or use a stored key by its label
let agent = Agent::from_stored_keys(
    Some("production-key".to_string()),
    true
).await?;

// Or use the default key from storage
let agent = Agent::from_stored_keys(None, true).await?;
```

#### 3. Using Ephemeral Keys

Create an agent with a temporary key that is not persisted:

```rust
use tap_agent::{DefaultKeyManager, AgentBuilder};
use std::sync::Arc;

// Create a key manager
let key_manager = Arc::new(DefaultKeyManager::new());

// Generate a random key
let key = key_manager.generate_key(DIDGenerationOptions {
    key_type: KeyType::Ed25519,
})?;

// Create an agent with the ephemeral key
let agent = AgentBuilder::new(key.did.clone())
    .with_debug(true)
    .build(key_manager);

println!("Created ephemeral agent with DID: {}", key.did);
```

#### 4. Manual Creation (Advanced)

For complete control over the agent configuration:

```rust
use tap_agent::{Agent, AgentConfig, DefaultKeyManager, KeyManagerPacking, Secret, SecretMaterial, SecretType};
use std::sync::Arc;

// Create agent configuration
let config = AgentConfig::new("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string());

// Set up a key manager
let mut key_manager = DefaultKeyManager::new();

// Add a secret to the key manager
let secret = Secret {
    id: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string(),
    type_: SecretType::JsonWebKey2020,
    secret_material: SecretMaterial::JWK {
        private_key_jwk: serde_json::json!({
            "kty": "OKP",
            "crv": "Ed25519",
            "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
            "d": "nWGxne_9WmC6hEr-BQh-uDpW6n7dZsN4c4C9rFfIz3Yh",
            "kid": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#keys-1"
        }),
    },
};
key_manager.add_secret(&secret.id, secret)?;

// Create the agent
let agent = Agent::new(config, Arc::new(key_manager));
```

### Sending Messages

The agent provides a flexible API for sending messages to one or more recipients:

```rust
use tap_agent::Agent;
use tap_msg::message::Transfer;
use tap_caip::AssetId;
use tap_msg::Participant;
use std::str::FromStr;
use std::collections::HashMap;

// Create a transfer message
let transfer = Transfer {
    transaction_id: uuid::Uuid::new_v4().to_string(),
    asset: AssetId::from_str("eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(),
    originator: Participant {
        id: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string(),
        role: Some("originator".to_string()),
        policies: None,
        leiCode: None,
        name: None,
    },
    beneficiary: Some(Participant {
        id: "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k".to_string(),
        role: Some("beneficiary".to_string()),
        policies: None,
        leiCode: None,
        name: None,
    }),
    amount: "100.0".to_string(),
    agents: vec![],
    settlement_id: None,
    memo: Some("Test transfer".to_string()),
    metadata: HashMap::new(),
};

// Option 1: Pack a message without delivery (for manual handling)
let recipient_did = "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k";
let (packed_message, _) = agent.send_message(&transfer, vec![recipient_did], false).await?;

// You can now manually send the packed message however you want

// Option 2: Automatic delivery to service endpoints
let (packed_message, delivery_results) = agent.send_message(&transfer, vec![recipient_did], true).await?;

// Option 3: Send to multiple recipients
let recipients = vec![
    "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k",
    "did:web:example.com"
];
let (packed_message, delivery_results) = agent.send_message(&transfer, recipients, true).await?;

// Check delivery results
for result in delivery_results {
    if let Some(status) = result.status {
        println!("Delivery status: {}", status);
    } else if let Some(error) = &result.error {
        println!("Error delivering to {}: {}", result.did, error);
    }
}

// You can also look up service endpoints manually
if let Ok(Some(endpoint)) = agent.get_service_endpoint(recipient_did).await {
    println!("Found service endpoint: {}", endpoint);
    // Use the endpoint for custom delivery logic
}
```

The `send_message` method provides flexibility with three parameters:
1. The message to send (any type implementing `TapMessageBody`)
2. A list of recipient DIDs
3. A boolean indicating whether to attempt automatic delivery

The method returns:
1. The packed message as a string (ready for transport)
2. A vector of delivery results (when automatic delivery is requested)

### Receiving Messages

The agent provides a simple API for unpacking and validating received messages:

```rust
use tap_agent::Agent;
use tap_msg::message::Transfer;

// Receive a message from some transport mechanism
let packed_message = "..."; // Received from HTTP, WebSockets, etc.

// Unpack and validate the message, converting to the expected type
let transfer: Transfer = agent.receive_message(packed_message).await?;

// Now you can access the typed message content
println!("Received transfer:");
println!("  Transaction ID: {}", transfer.transaction_id);
println!("  Asset: {}", transfer.asset);
println!("  Amount: {}", transfer.amount);
println!("  From: {}", transfer.originator.id);
if let Some(beneficiary) = &transfer.beneficiary {
    println!("  To: {}", beneficiary.id);
}

// The agent automatically:
// 1. Verifies signatures (if the message is signed)
// 2. Decrypts content (if the message is encrypted)
// 3. Verifies the message is valid according to its type
// 4. Converts the message to the requested type
```

The agent handles the entire verification and decryption process, allowing you to focus on processing the message content rather than worrying about cryptographic details.

Both `TapAgent` and `DefaultAgent` implement the `Agent` trait, so the receiving API is the same regardless of which agent implementation you use.

### Using DID Resolvers

The agent provides flexible DID resolution capabilities:

```rust
use tap_agent::did::MultiResolver;
use std::sync::Arc;

// Create a resolver with built-in support for did:key and did:web
let resolver = MultiResolver::default();
let resolver = Arc::new(resolver);

// Resolve any supported DID to its DID document
let did_doc = resolver.resolve("did:web:example.com").await?;

if let Some(doc) = did_doc {
    println!("Resolved DID: {}", doc.id);

    // Check verification methods
    for vm in &doc.verification_method {
        println!("Verification method: {}", vm.id);
        println!("  Type: {:?}", vm.type_);
        println!("  Controller: {}", vm.controller);
    }

    // Check authentication methods
    if !doc.authentication.is_empty() {
        println!("Authentication methods:");
        for auth in &doc.authentication {
            println!("  {}", auth);
        }
    }

    // Check key agreement methods
    if !doc.key_agreement.is_empty() {
        println!("Key agreement methods:");
        for ka in &doc.key_agreement {
            println!("  {}", ka);
        }
    }

    // Check service endpoints
    if !doc.service.is_empty() {
        println!("Service endpoints:");
        for (i, service) in doc.service.iter().enumerate() {
            println!("  [{}] ID: {}", i+1, service.id);
            println!("      Type: {}", service.type_);
            println!("      Endpoint: {}", service.service_endpoint);
        }
    }
}
```

#### Supported DID Methods

The default resolver supports:

1. **did:key** - Self-contained DIDs with embedded public keys
   ```
   did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
   ```

2. **did:web** - DIDs associated with web domains
   ```
   did:web:example.com
   did:web:example.com:path:to:resource
   ```

You can extend the resolver with custom DID methods as shown in the next section.

### Custom DID Method Resolver

You can implement and register custom DID method resolvers to extend the agent's capabilities:

```rust
use tap_agent::did::{DIDMethodResolver, MultiResolver, DIDDoc, VerificationMethod};
use tap_agent::did::{VerificationMethodType, VerificationMaterial};
use async_trait::async_trait;
use tap_agent::error::Result;

#[derive(Debug)]
struct CustomResolver;

impl CustomResolver {
    fn new() -> Self {
        Self
    }
}

#[async_trait]
impl DIDMethodResolver for CustomResolver {
    fn method(&self) -> &str {
        "example" // For did:example:123
    }

    async fn resolve_method(&self, did: &str) -> Result<Option<DIDDoc>> {
        // Validate DID format
        if !did.starts_with("did:example:") {
            return Ok(None);
        }

        // Extract ID portion
        let id_part = &did[12..]; // Skip "did:example:"

        // Create a simple verification method
        let vm_id = format!("{}#keys-1", did);
        let vm = VerificationMethod {
            id: vm_id.clone(),
            type_: VerificationMethodType::Ed25519VerificationKey2018,
            controller: did.to_string(),
            verification_material: VerificationMaterial::Base58 {
                public_key_base58: format!("custom-key-for-{}", id_part),
            },
        };

        // Create a DID document
        let doc = DIDDoc {
            id: did.to_string(),
            verification_method: vec![vm],
            authentication: vec![vm_id.clone()],
            key_agreement: vec![],
            assertion_method: vec![],
            capability_invocation: vec![],
            capability_delegation: vec![],
            service: vec![],
        };

        Ok(Some(doc))
    }
}

// Create a resolver with the default resolvers
let mut resolver = MultiResolver::default();

// Register the custom resolver
resolver.register_method("example", CustomResolver::new());

// Now you can resolve did:example: DIDs
let doc = resolver.resolve("did:example:123").await?;
```

You can implement resolvers for any DID method, including:
- Blockchain-based DIDs (did:ethr, did:sol, etc.)
- Registry-based DIDs (did:ion, did:factom, etc.)
- Custom protocol DIDs for specific use cases

The resolver will automatically route DID resolution requests to the appropriate method resolver based on the DID prefix.

## Security Considerations

The `tap-agent` crate implements several security features:

- **Message Integrity**: All messages can be digitally signed to ensure integrity
- **Message Confidentiality**: Messages can be encrypted for confidentiality
- **Key Management**: Proper key handling with separation of concerns
- **DID Verification**: Validation of DIDs and DID documents
- **Secure Defaults**: Secure defaults for message security modes
- **Native Cryptography**: Direct implementation of cryptographic algorithms using well-tested libraries

For production use, it's recommended to:

1. Implement a custom `DebugSecretsResolver` that integrates with a secure key management system
2. Use proper key rotation and management practices
3. Ensure secure transport for message exchange
4. Regularly update dependencies to incorporate security fixes

## Integration with Other TAP Components

The `tap-agent` crate integrates with other components in the TAP ecosystem:

- **tap-msg**: Uses message types and validation from tap-msg
- **tap-caip**: Validates chain-agnostic identifiers in messages
- **tap-node**: Provides the agent functionality for tap-node
- **tap-http**: Can be used with tap-http for HTTP-based secure messaging
- **tap-wasm**: Supports WASM bindings for browser environments
- **tap-ts**: Provides TypeScript bindings for the agent functionality

## Performance

The `tap-agent` crate is designed for high performance, with benchmarks showing:

- Message packing/unpacking: Thousands of operations per second
- DID resolution: Fast caching of resolved DIDs
- Cryptographic operations: Optimized for common use cases

Benchmarks can be run with:

```bash
cargo bench --bench agent_benchmark
```

## DID Generation CLI

The `tap-agent` crate includes a command-line interface (CLI) for generating and managing DIDs and keys. This makes it easy to create DIDs for testing, development, or production use.

### Installation

The CLI tool can be installed in several ways:

```bash
# From crates.io (recommended for most users)
cargo install tap-agent

# From the repository (if you have it cloned)
cargo install --path tap-agent

# Build without installing
cargo build --package tap-agent
```

After installation, the following commands will be available:
- `tap-agent-cli` - Command-line tool for DID and key management

### Command Reference

After installation, you can use the `tap-agent-cli` command to manage DIDs and keys. Here's a complete reference of available commands:

#### Generate Command

The `generate` command creates new DIDs with different key types and methods:

```bash
# Generate a did:key with Ed25519 (default)
tap-agent-cli generate

# Specify method and key type
tap-agent-cli generate --method key --key-type ed25519
tap-agent-cli generate --method key --key-type p256
tap-agent-cli generate --method key --key-type secp256k1

# Generate a did:web for a domain
tap-agent-cli generate --method web --domain example.com

# Save outputs to files
tap-agent-cli generate --output did.json --key-output key.json

# Save key to default storage (~/.tap/keys.json) and set as default
tap-agent-cli generate --save --default

# Generate with a custom label (instead of default agent-1, agent-2, etc.)
tap-agent-cli generate --save --label "my-signing-key"
tap-agent-cli generate --save --label "production-key" --default
```

#### Lookup Command

The `lookup` command resolves DIDs to their DID documents:

```bash
# Look up a DID and display its DID document
tap-agent-cli lookup did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK

# Look up a DID and save the document to a file
tap-agent-cli lookup did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK --output did-document.json

# Look up WebDIDs
tap-agent-cli lookup did:web:example.com
tap-agent-cli lookup did:web:example.com:path:to:resource
```

The resolver supports the following DID methods by default:
- `did:key` - Resolves DIDs based on public keys
- `did:web` - Resolves DIDs from web domains

#### Keys Command

The `keys` command manages stored keys:

```bash
# List all stored keys (shows labels, DIDs, and key types)
tap-agent-cli keys list

# View details for a specific key (by DID or label)
tap-agent-cli keys view did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys view "my-signing-key"
tap-agent-cli keys view "agent-1"

# Set a key as the default (by DID or label)
tap-agent-cli keys set-default did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys set-default "production-key"

# Delete a key (by DID or label, with confirmation)
tap-agent-cli keys delete did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys delete "test-key"

# Delete a key (without confirmation)
tap-agent-cli keys delete "old-key" --force

# Relabel an existing key
tap-agent-cli keys relabel "agent-1" "development-key"
tap-agent-cli keys relabel did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK "new-label"
```

Keys are stored in `~/.tap/keys.json` by default. This storage location is shared with other TAP tools like `tap-http` for consistent key management.

#### Import Command

The `import` command imports existing keys:

```bash
# Import a key from a file
tap-agent-cli import key.json

# Import and set as default
tap-agent-cli import key.json --default
```

#### Pack Command

The `pack` command securely packs a plaintext DIDComm message for transmission:

```bash
# Pack a message with the default security mode (signed)
tap-agent-cli pack --input plaintext.json --output packed.json

# Pack using a specific security mode
tap-agent-cli pack --input plaintext.json --mode plain
tap-agent-cli pack --input plaintext.json --mode signed
tap-agent-cli pack --input plaintext.json --mode authcrypt --recipient did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK

# Specify sender and recipient DIDs
tap-agent-cli pack --input plaintext.json --sender did:key:z6Mky... --recipient did:key:z6Mkh...

# Display the packed message in the console (no output file)
tap-agent-cli pack --input plaintext.json
```

The `pack` command supports the following options:
- `--input, -i`: The input file containing the plaintext DIDComm message (required)
- `--output, -o`: The output file to save the packed message (optional, displays in console if not provided)
- `--sender, -s`: The DID of the sender (optional, uses default key if not provided)
- `--recipient, -r`: The DID of the recipient (required for authcrypt mode, optional for other modes)
- `--mode, -m`: The security mode to use: `plain`, `signed`, or `authcrypt` (default: `signed`)

Security modes:
- `plain`: No security, message is sent as plaintext (use for testing only)
- `signed`: Message is digitally signed but not encrypted (integrity protection)
- `authcrypt`: Message is authenticated and encrypted (confidentiality and integrity)

#### Unpack Command

The `unpack` command decrypts and verifies packed DIDComm messages:

```bash
# Unpack a message using the default key
tap-agent-cli unpack --input packed.json --output unpacked.json

# Specify a recipient DID (whose key should be used for decryption)
tap-agent-cli unpack --input packed.json --recipient did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK

# Display the unpacked message in the console (no output file)
tap-agent-cli unpack --input packed.json
```

The `unpack` command supports the following options:
- `--input, -i`: The input file containing the packed DIDComm message (required)
- `--output, -o`: The output file to save the unpacked message (optional, displays in console if not provided)
- `--recipient, -r`: The DID of the recipient whose key should be used for unpacking (optional, uses default key if not provided)

### Help and Documentation

```bash
# Display general help
tap-agent-cli --help

# Display help for a specific command
tap-agent-cli generate --help
tap-agent-cli lookup --help
tap-agent-cli keys --help
tap-agent-cli import --help
tap-agent-cli pack --help
tap-agent-cli unpack --help

# Display help for a subcommand
tap-agent-cli keys delete --help
```

### Using Generated DIDs

For `did:web`, you'll need to:
1. Generate the DID using the CLI: `tap-agent-cli generate --method web --domain yourdomain.com --output did.json`
2. Place the generated DID document at:
   - For `did:web:example.com`: `https://example.com/.well-known/did.json`
   - For `did:web:example.com:path:to:resource`: `https://example.com/path/to/resource/did.json`

## Working with Service Endpoints

Service endpoints in DID documents provide URLs where the DID subject can receive messages. TAP Agents can automatically discover and use these endpoints for message delivery.

```rust
// Example: Find and use a service endpoint for a DID
async fn work_with_service_endpoint(agent: &DefaultAgent, did: &str, message: &Transfer) -> Result<()> {
    // Method 1: Get the service endpoint URL directly
    match agent.get_service_endpoint(did).await? {
        Some(endpoint) => {
            println!("Found service endpoint for {}: {}", did, endpoint);

            // Pack the message first (if you want to handle delivery manually)
            let (packed_message, _) = agent.send_message(message, vec![did], false).await?;

            // Now you can manually send to the endpoint:
            // let client = reqwest::Client::new();
            // let response = client.post(endpoint)
            //     .header("Content-Type", "application/didcomm-encrypted+json")
            //     .body(packed_message)
            //     .send()
            //     .await?;
        },
        None => println!("No service endpoint found for {}", did),
    }

    // Method 2: Let the agent handle delivery automatically
    // The boolean parameter (true) tells the agent to attempt automatic delivery
    let (_, delivery_results) = agent.send_message(message, vec![did], true).await?;

    // Check the delivery results
    for result in delivery_results {
        if let Some(status) = result.status {
            println!("Delivery to {} resulted in status: {}", result.did, status);
        } else if let Some(error) = &result.error {
            println!("Delivery to {} failed: {}", result.did, error);
        }
    }

    Ok(())
}
```

### Service Endpoint Types and Message Delivery

The TAP Agent can work with different types of service endpoints defined in DID documents:

1. **DIDCommMessaging** endpoints (prioritized) - Specifically designed for secure message exchange:
   ```json
   {
     "service": [{
       "id": "did:example:123#didcomm",
       "type": "DIDCommMessaging",
       "serviceEndpoint": {
         "uri": "https://example.com/didcomm",
         "accept": ["didcomm/v2"],
         "routingKeys": []
       }
     }]
   }
   ```

2. **Other** endpoint types - Any service endpoint type that provides a URL:
   ```json
   {
     "service": [{
       "id": "did:example:123#agent",
       "type": "TapAgent",
       "serviceEndpoint": "https://agent.example.com/tap"
     }]
   }
   ```

3. **Simple string endpoints** - Basic URL string endpoints:
   ```json
   {
     "service": [{
       "id": "did:example:123#messaging",
       "type": "MessagingService",
       "serviceEndpoint": "https://example.com/messages"
     }]
   }
   ```

The agent supports multiple endpoint formats and follows these resolution rules:

1. Look for a service with `type = "DIDCommMessaging"` first
2. Fall back to the first available service endpoint if no DIDCommMessaging endpoint is found
3. Handle both object-style and string-style service endpoints

### Automatic Message Delivery

The TAP Agent provides a seamless way to automatically deliver messages to service endpoints:

```rust
// Example 1: Send a message to a single recipient with automatic delivery
let transfer = Transfer {
    asset: AssetId::from_str("eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(),
    originator: create_originator(),
    beneficiary: Some(create_beneficiary()),
    amount: "100.0".to_string(),
    // Other fields...
    transaction_id: uuid::Uuid::new_v4().to_string(),
};

// The third parameter (true) enables automatic delivery
let (packed_message, delivery_results) =
    agent.send_message(&transfer, vec![recipient_did], true).await?;

// Example 2: Send to multiple recipients
let recipients = vec!["did:web:example.com", "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"];
let (packed_message, delivery_results) =
    agent.send_message(&transfer, recipients, true).await?;

// Process delivery results
for result in &delivery_results {
    if let Some(status) = result.status {
        if status >= 200 && status < 300 {
            println!("✓ Successfully delivered to {} (status {})", result.did, status);
        } else {
            println!("! Delivery to {} failed with status {}", result.did, status);
        }
    } else if let Some(error) = &result.error {
        println!("✗ Error delivering to {}: {}", result.did, error);
    }
}
```

#### Delivery Process

When automatic delivery is enabled, the agent follows this process:

1. **Endpoint Resolution**: Resolves each recipient's DID document to find service endpoints
2. **Message Packing**: Packs the message with appropriate security for all recipients
3. **Delivery Attempts**: For each recipient with a service endpoint:
   - Sends the packed message via HTTP POST with `Content-Type: application/didcomm-encrypted+json`
   - Records success (with HTTP status) or failure (with error message)
4. **Result Collection**: Returns a vector of `DeliveryResult` structures containing:
   - `did`: The recipient DID
   - `endpoint`: The service endpoint URL used
   - `status`: HTTP status code (if successful)
   - `error`: Error message (if failed)

The delivery mechanism is designed to be resilient - failures with one recipient won't prevent delivery attempts to others, and the operation won't fail even if no service endpoints are found.

## Creating Ephemeral DIDs

For testing or short-lived processes, you can create ephemeral DIDs that exist only in memory:

```rust
// Option 1: Create an ephemeral agent with TapAgent (recommended, async API)
let (agent, did) = TapAgent::from_ephemeral_key().await?;
println!("TapAgent DID: {}", did);

// Option 2: Create an ephemeral agent with DefaultAgent
let (agent, did) = DefaultAgent::new_ephemeral()?;
println!("Agent DID: {}", did);

// This agent has a fully functional private key and can be used immediately
// for sending and receiving messages without any persistence
let (packed_message, _) = agent
    .send_message(&transfer, vec!["did:example:recipient"], false)
    .await?;
```

Ephemeral DIDs are useful for:
- Test environments where persistence isn't needed
- Short-lived agent instances that don't need to maintain state
- Situations where you want to minimize key management overhead
- Temporary identities for one-time operations

## Feature Flags

The crate provides several feature flags to customize functionality:

- **native** (default): Enables native platform features including:
  - Tokio runtime for async functionality
  - HTTP support for service endpoint delivery
  - Complete DID resolution with HTTP requests for did:web
  - File system access for key storage

- **wasm**: Enables WebAssembly support for browser environments:
  - Browser-compatible cryptography
  - JavaScript integration
  - Web-based service endpoint delivery
  - Browser storage for keys

When working with different environments:
- For server applications: Use the default **native** feature
- For browser applications: Use the **wasm** feature
- For CLI tools: Use the default **native** feature

Note that did:web resolution requires the **native** feature to be enabled, as it depends on HTTP requests to fetch DID documents.

## Cryptographic Support

The `tap-agent` crate implements comprehensive cryptographic operations with support for:

### Key Abstraction

- **AgentKey Trait Hierarchy** - Modular and extensible key capabilities:
  - `AgentKey` - Core trait with key ID, DID, and key type properties
  - `SigningKey` - For creating digital signatures (JWS)
  - `VerificationKey` - For verifying signatures
  - `EncryptionKey` - For encrypting data (JWE)
  - `DecryptionKey` - For decrypting data

- **LocalAgentKey Implementation** - Concrete implementation of the AgentKey traits:
  - Stores key material in memory or persistent storage
  - Implements all cryptographic operations locally
  - Supports multiple key types and algorithms
  - Compatible with JWS and JWE standards

### Key Types
- **Ed25519** - Fast digital signatures with small signatures (128 bits of security)
- **P-256** - NIST standardized elliptic curve for ECDSA signatures and ECDH
- **secp256k1** - Blockchain-compatible ECDSA signatures (used in Ethereum, Bitcoin)

### Encryption & Signing
- **JWS (JSON Web Signatures)** - Standards-compliant digital signatures:
  - EdDSA algorithm for Ed25519 keys
  - ES256 algorithm for P-256 keys
  - ES256K algorithm for secp256k1 keys

- **JWE (JSON Web Encryption)** - Standards-compliant encryption:
  - AES-GCM (A256GCM) for authenticated encryption
  - ECDH-ES+A256KW for key agreement and key wrapping
  - Per-recipient encrypted content encryption keys (CEKs)

### Security Modes
- **Plain** - No security (for testing only)
- **Signed** - Digital signatures without encryption (integrity protection)
- **AuthCrypt** - Authenticated encryption (confidentiality + integrity)

The cryptographic implementations align with industry standards, allowing interoperability with other systems that support JWS and JWE formats.

## License

This crate is licensed under the [MIT License](LICENSE).