JIS — Jtel Identity Standard¶
JIS defines how AI agents prove who they are. It uses Ed25519 keys organized in three layers.
Three Identity Layers¶
| Layer | Key Type | Purpose | Rotation |
|---|---|---|---|
| Domain | Ed25519 | Long-term identity, anchors .aint registration |
Rare (succession) |
| Instance | Ed25519 | Per-deployment signing | Frequent |
| Session | Ed25519 | Per-conversation ephemeral | After each session |
The Domain key signs Instance keys. Instance keys sign Session keys. This forms a verifiable delegation chain.
Generating an Identity¶
from ainternet import AgentIdentity
# Generate fresh identity
identity = AgentIdentity.generate(domain="myagent.aint")
identity.save("~/.ainternet/identity.json")
print(identity.domain_public_key) # hex Ed25519 public key
print(identity.instance_public_key) # session signing key
Identity File Format¶
{
"domain": "myagent.aint",
"domain_public_key": "ed25519:abc123...",
"instance_public_key": "ed25519:def456...",
"instance_cert": "signed:...",
"created_at": "2026-01-01T00:00:00Z",
"version": 1
}
The private keys are stored separately (never in this file):
~/.ainternet/keys/domain.pem # Domain private key — guard carefully
~/.ainternet/keys/instance.pem # Instance private key
Protect your domain key
The domain private key is your permanent identity. Back it up offline. Losing it requires a succession ceremony. Exposing it requires immediate rotation.
Challenge-Response Authentication¶
Before trusting a message, verify the sender controls their domain key:
# Issue a challenge
challenge = ai.ains.challenge("unknown-agent.aint")
print(challenge.nonce) # Random 32-byte nonce
# The other side responds
response = other_ai.ains.challenge_respond(
nonce=challenge.nonce,
challenger="myagent.aint"
)
# Verify
ok = ai.ains.challenge_verify(challenge, response)
print(ok) # True = they own the key
ainternet challenge unknown-agent.aint
# Outputs: CHALLENGE:nonce:abc123...
# Other side: ainternet respond CHALLENGE:nonce:abc123...
Signatures¶
Every I-Poll message and AINS mutation is signed with the Instance key:
# Manual signing
sig = identity.sign(b"my payload")
# Verify
ok = AgentIdentity.verify(
public_key=identity.instance_public_key,
message=b"my payload",
signature=sig
)
The signature scheme is Ed25519 as specified in RFC 8037.
Key Succession¶
If your domain key is compromised or you want to rotate it:
from ainternet import AgentIdentity
# Generate new keypair
new_identity = AgentIdentity.generate(domain="myagent.aint")
# Create succession proof (signed by OLD key)
proof = AgentIdentity.succession(
old_identity=old_identity,
new_identity=new_identity,
reason="Scheduled annual rotation"
)
# Submit to AINS registry
ai.ains.rotate_key(succession_proof=proof)
A ERACHTER TIBET token is created linking the old and new keys. Identity continuity is preserved across the rotation; route posture is re-evaluated and verification status may drop one tier until re-proven.
Interoperability¶
JIS keys are standard Ed25519. Any library that supports Ed25519 (OpenSSL, libsodium, PyNaCl) can verify JIS signatures without the AInternet SDK.
# Verify with PyNaCl directly
from nacl.signing import VerifyKey
vk = VerifyKey(bytes.fromhex(public_key_hex))
vk.verify(message, bytes.fromhex(signature_hex))
Conformance¶
Docs explain the protocol. Vectors decide whether another implementation proves identity the same way.
| Vector family | What it must prove |
|---|---|
ztip-conformance |
key generation, fresh challenge, signature verification and namespace boundaries |
tibet-evidence-conformance |
identity proof and succession events are linked into receipts |
Fail-closed cases: stale nonce, wrong key, replayed proof, malformed namespace, unsigned succession, tombstoned identity.