Metadata-Version: 2.4
Name: km-keybind
Version: 1.2.2
Summary: Stateless encrypted identity-bound token library
Author: Pravanjan Roy
License: Apache-2.0
Project-URL: Homepage, https://github.com/kingmon6996/keybind-py
Project-URL: Repository, https://github.com/kingmon6996/keybind-py
Project-URL: Issues, https://github.com/kingmon6996/keybind-py/issues
Keywords: km-keybind,keybind,encrypted-token,identity-bound,stateless-token,cryptography
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography
Requires-Dist: PyNaCl
Requires-Dist: zstandard
Requires-Dist: blake3
Requires-Dist: orjson
Requires-Dist: typing-extensions
Dynamic: license-file

# Keybind

Keybind is a broader secure token platform, and keybind-py is the Python library for turning structured values and files into compact, encrypted, identity-bound tokens. It is useful when you want to send or store self-contained payloads without keeping server-side state.

## What Keybind is for

You can use Keybind when you need to:

- create a compact token from a supported Python value such as a dictionary, list, string, bytes, tuple, set, or file path
- bind that token to a specific user or application context
- keep the payload encrypted and self-contained
- safely pass the token across systems or store it for later use

Typical use cases include:

- temporary session payloads
- encrypted user profile fragments
- backend-to-backend message transport
- short-lived access tokens with embedded data
- compact state handoff between services

## The master key

The master key is the secret value that unlocks and protects the token. It is supplied when you create a Keybind instance:

```python
from keybind import Keybind

chain = Keybind(b"master-key")
```

In real projects, use a strong secret instead of a sample string. A good master key should be:

- long enough to be unpredictable
- stored securely
- kept private
- reused consistently for the same application context

### How to generate a master key

A simple and safe approach is to generate a random 32-byte key:

```python
import os

master_key = os.urandom(32)
chain = Keybind(master_key)
```

You can also store it in an environment variable:

```python
import os

master_key = os.environ["KEYBIND_MASTER_KEY"].encode("utf-8")
chain = Keybind(master_key)
```

### Why the master key matters

- It is the root secret used to derive the encryption key.
- The same key must be used later when decoding the token.
- If the master key changes, the token cannot be decrypted correctly.

## Identities

Keybind also takes two identity values during encoding and decoding:

```python
from keybind.keybind import DICT

token = chain.encode("user123", "app", DICT, {"hello": "world"})
chain.decode("user123", "app", token)
```

These identities bind the token to a specific context. In practice:

- the first identity is often a user, account, or subject
- the second identity is often an app, service, or environment

This means the token is not only encrypted, but also tied to the identities used when it was created.

## Full example

Here is a complete example from start to finish:

```python
from keybind import Keybind
from keybind.keybind import DICT

master_key = b"example-master-key"
chain = Keybind(master_key)

payload = {
    "user": "alice",
    "role": "admin",
    "permissions": ["read", "write"],
    "active": True,
}

token = chain.encode("alice", "dashboard", DICT, payload)
print("Token:", token)

decoded, decoded_type = chain.decode("alice", "dashboard", token)
print("Decoded:", decoded)
print("Decoded type:", decoded_type)
```

### What happens in this example

1. A Keybind instance is created with a master key.
2. A supported payload value is prepared.
3. The payload is turned into an encrypted token.
4. The token is later decoded back into the original value using the same identities and the same Keybind instance in the same runtime.

If you start a new Python process or create a new runtime, you can still recover the payload as long as you use the same master key, the same token, and the same two identities. The token now carries the metadata and encrypted chunk data needed for decoding, so it behaves as a self-contained, stateless token.

## How the library works internally

Keybind supports file payloads via the `FILE` payload type. When you encode a file path, the raw file contents are encrypted and later decoded to a saved file path under `decoded_files/`, preserving the original filename.

```python
from keybind import Keybind
from keybind.keybind import FILE

chain = Keybind(b"example-master-key")

token = chain.encode("alice", "dashboard", FILE, "summary.txt")
file_path, decoded_type = chain.decode("alice", "dashboard", token)
print("Decoded file saved to:", file_path)
print("Decoded type:", decoded_type)
```

When you call encode, Keybind performs these steps:

1. Normalizes the two identities.
2. Wraps the payload into an internal representation and serializes it into bytes for most value types.
3. Applies optional compression when it improves size.
4. Derives an encryption key from the master key and identities.
5. Encrypts the payload.
6. Produces a compact token string.

When you call decode, it reverses this process:

1. Validates the token format.
2. Re-derives the key using the same master key and identities.
3. Decrypts the payload.
4. Reconstructs the original value and returns it together with its payload type.

> Note: The token is now self-contained, so decoding can happen later with a new Keybind instance in a different runtime/process as long as the same master key and identities are used.

## Supported payload types

Keybind supports the following payload types through the same encode/decode flow:

- `STR` for strings
- `INT` for integers
- `FLT` for floating-point numbers
- `BOOL` for booleans
- `NULL` for `None`
- `ARR` for lists
- `DICT` for dictionaries
- `BYTES` for raw byte strings
- `TUP` for tuples
- `SET` for sets
- `OBJ` for arbitrary Python objects via their `repr`
- `FILE` for files on disk

You can import these constants from `keybind.keybind` and pass them as the payload type argument to `encode`.

```python
from keybind import Keybind
from keybind.keybind import (
    STR,
    INT,
    FLT,
    BOOL,
    NULL,
    ARR,
    DICT,
    BYTES,
    TUP,
    SET,
    OBJ,
    FILE,
)

chain = Keybind(b"demo-master-key")

# Strings
chain.encode("alice", "app", STR, "hello world")

# Integers and floats
chain.encode("alice", "app", INT, 42)
chain.encode("alice", "app", FLT, 3.14159)

# Booleans and null
chain.encode("alice", "app", BOOL, True)
chain.encode("alice", "app", NULL, None)

# Lists and dictionaries
chain.encode("alice", "app", ARR, [1, 2, 3])
chain.encode("alice", "app", DICT, {"name": "alice", "active": True})

# Byte payloads
chain.encode("alice", "app", BYTES, b"\x00\x01\x02")

# Tuples and sets
chain.encode("alice", "app", TUP, ("a", 1, True))
chain.encode("alice", "app", SET, {"red", "green", "blue"})

# Arbitrary object payloads
class ExampleConfig:
    def __init__(self, retries: int = 3):
        self.retries = retries

    def __repr__(self) -> str:
        return f"ExampleConfig(retries={self.retries})"

chain.encode("alice", "app", OBJ, ExampleConfig(5))
```

### File payload demo

File payloads are a special case. Pass a path string to `encode` with `FILE` and Keybind will encrypt the file contents and later write the decoded bytes back to a file in `decoded_files/` using the original filename.

The `FILE` payload expects an existing filesystem path string. The original filename is preserved during decode, and the output file is created in the current working directory under `decoded_files/`.

```python
from pathlib import Path
from keybind import Keybind
from keybind.keybind import FILE

chain = Keybind(b"demo-master-key")

sample_path = Path("sample.txt")
sample_path.write_text("hello from a file payload", encoding="utf-8")

token = chain.encode("alice", "app", FILE, str(sample_path))
print("Token:", token)

output_path, decoded_type = chain.decode("alice", "app", token)
print("Decoded file saved to:", output_path)
print("Decoded type:", decoded_type)
```

### Decoding examples

```python
from keybind import Keybind
from keybind.keybind import DICT, FILE

chain = Keybind(b"demo-master-key")

payload_token = chain.encode("alice", "app", DICT, {"role": "admin"})
decoded_payload, decoded_type = chain.decode("alice", "app", payload_token)
print(decoded_payload)      # {'role': 'admin'}
print(decoded_type)         # DICT

file_token = chain.encode("alice", "app", FILE, "sample.txt")
file_path, file_type = chain.decode("alice", "app", file_token)
print(file_path)            # .../decoded_files/sample.txt
print(file_type)            # FILE
```

## Example use cases

### 1. Temporary user session payload

```python
import os
from keybind import Keybind
from keybind.keybind import DICT

chain = Keybind(os.urandom(32))

session_data = {"user_id": 42, "plan": "pro", "expires_in": 3600}
token = chain.encode("42", "web-app", DICT, session_data)
```

### 2. Cross-service message transport

```python
from keybind import Keybind
from keybind.keybind import DICT

chain = Keybind(b"shared-secret")

message = {"event": "user.created", "id": 101}
token = chain.encode("service-a", "service-b", DICT, message)
```

### 3. Compact state handoff

```python
from keybind import Keybind
from keybind.keybind import DICT

chain = Keybind(b"state-secret")

state = {"step": 3, "status": "pending", "retry": False}
token = chain.encode("job-17", "worker", DICT, state)
```

## Important security notes

- Keep the master key secret.
- Do not hard-code production secrets in source files.
- Use a secure secret store or environment variable in production.
- Reuse the same master key consistently for the same application context.
- Keep the identities meaningful and consistent with your system design.

## Installation

Install the package from PyPI with:

```bash
pip install km-keybind
```

Install it directly from GitHub as a package with:

```bash
pip install git+https://github.com/kingmon6996/keybind-py.git
```

## Summary

Keybind gives you a simple way to turn JSON-compatible data into a compact, encrypted, identity-bound token. The core idea is straightforward:

- provide a master key
- provide two identities
- encode data into a token
- decode later with the same key and identities

Thank you for using Keybind.
