Skip to content

Commit

Permalink
feat(backend): Routes to retrieve agent secret
Browse files Browse the repository at this point in the history
  • Loading branch information
RezaRahemtola committed Nov 5, 2024
1 parent c18578f commit 9edcd7a
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 243 deletions.
2 changes: 2 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ ALEPH_CHANNEL=libertai
# Type of the POST agent messages
ALEPH_POST_TYPE=libertai-agent

# Subscriptions backend base URL
SUBSCRIPTION_BACKEND_URL=
# Password used by the subscription backend for agent creation
SUBSCRIPTION_BACKEND_PASSWORD=
446 changes: 223 additions & 223 deletions backend/poetry.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool.poetry]
name = "libertai-agents-backend"
version = "0.1.0"
description = ""
description = "Backend to handle LibertAI agents registration and deployment"
authors = ["LibertAI.io team <[email protected]>"]
readme = "README.md"
homepage = "https://libertai.io"
Expand All @@ -13,8 +13,7 @@ package-mode = false
python = "^3.12"
fastapi = { extras = ["standard"], version = "^0.115.2" }
aleph-sdk-python = "^1.1.0"
eciespy = "^0.4.2"
libertai-utils = "^0.0.2"
libertai-utils = "^0.0.5"

[tool.poetry.group.dev.dependencies]
mypy = "^1.12.0"
Expand Down
2 changes: 2 additions & 0 deletions backend/src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class _Config:
ALEPH_CHANNEL: str
ALEPH_AGENT_POST_TYPE: str

SUBSCRIPTION_BACKEND_URL: str
SUBSCRIPTION_BACKEND_PASSWORD: str

def __init__(self):
Expand All @@ -26,6 +27,7 @@ def __init__(self):
"ALEPH_AGENT_POST_TYPE", "libertai-agent"
)

self.SUBSCRIPTION_BACKEND_URL = os.getenv("SUBSCRIPTION_BACKEND_URL")
self.SUBSCRIPTION_BACKEND_PASSWORD = os.getenv("SUBSCRIPTION_BACKEND_PASSWORD")


Expand Down
10 changes: 6 additions & 4 deletions backend/src/interfaces/agent.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from libertai_utils.interfaces.agent import BaseDeleteAgentBody
from libertai_utils.interfaces.subscription import SubscriptionAccount
from pydantic import BaseModel, validator

from src.config import config


class DeleteAgentBody(BaseModel):
subscription_id: str
password: str

class DeleteAgentBody(BaseDeleteAgentBody):
# noinspection PyMethodParameters
@validator("password")
def format_address(cls, password: str):
Expand Down Expand Up @@ -41,6 +39,10 @@ class FetchedAgent(Agent):
post_hash: str


class GetAgentSecretMessage(BaseModel):
message: str


class GetAgentResponse(PublicAgentData):
pass

Expand Down
61 changes: 48 additions & 13 deletions backend/src/main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import base64
import time
from http import HTTPStatus
from uuid import uuid4

import aiohttp
from aleph.sdk import AuthenticatedAlephHttpClient
from aleph.sdk.chains.ethereum import ETHAccount
from aleph_message.models.execution import Encoding
from ecies import encrypt, decrypt
from fastapi import FastAPI, HTTPException, UploadFile, File, Form
from libertai_utils.chains.index import is_signature_valid
from libertai_utils.interfaces.subscription import Subscription
from libertai_utils.utils.crypto import decrypt, encrypt
from starlette.middleware.cors import CORSMiddleware

from src.config import config
Expand All @@ -18,9 +20,11 @@
UpdateAgentResponse,
GetAgentResponse,
GetAgentSecretResponse,
GetAgentSecretMessage,
)
from src.interfaces.aleph import AlephVolume
from src.utils.agent import fetch_agents, fetch_agent_program_message
from src.utils.message import get_view_agent_secret_message
from src.utils.storage import upload_file

app = FastAPI(title="LibertAI agents")
Expand All @@ -43,16 +47,13 @@ async def setup(body: SetupAgentBody) -> None:
agent_id = str(uuid4())

secret = str(uuid4())
# Encrypting the secret ID with our public key
encrypted_secret = encrypt(config.ALEPH_SENDER_PK, secret.encode())
# Encoding it in base64 to avoid data loss when stored on Aleph
base64_encrypted_secret = base64.b64encode(encrypted_secret).decode()
encrypted_secret = encrypt(secret, config.ALEPH_SENDER_PK)

agent = Agent(
id=agent_id,
subscription_id=body.subscription_id,
vm_hash=None,
encrypted_secret=base64_encrypted_secret,
encrypted_secret=encrypted_secret,
last_update=int(time.time()),
tags=[agent_id, body.subscription_id, body.account.address],
)
Expand Down Expand Up @@ -93,9 +94,45 @@ async def get_agent_secret(agent_id: str, signature: str) -> GetAgentSecretRespo
status_code=HTTPStatus.NOT_FOUND,
detail=f"Agent with ID {agent_id} not found.",
)
# agent = agents[0]
# TODO: real implementation
return GetAgentSecretResponse(secret="")
agent = agents[0]

async with aiohttp.ClientSession() as session:
async with session.get(
url=f"{config.SUBSCRIPTION_BACKEND_URL}/subscriptions/{agent.subscription_id}"
) as response:
data = await response.json()
if response.status != HTTPStatus.OK:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail=f"Subscriptions API returned a non-200 code: {data}",
)
subscription = Subscription(**data)
print(subscription.account)

valid_signature = is_signature_valid(
subscription.account.chain,
get_view_agent_secret_message(agent_id),
signature,
subscription.account.address,
)

if not valid_signature:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="The signature doesn't match the owner of this agent subscription",
)

decrypted_secret = decrypt(agent.encrypted_secret, config.ALEPH_SENDER_SK)

return GetAgentSecretResponse(secret=decrypted_secret)


@app.get(
"/agent/{agent_id}/secret-message",
description="Get the message to fetch an agent secret",
)
def get_agent_secret_message(agent_id: str) -> GetAgentSecretMessage:
return GetAgentSecretMessage(message=get_view_agent_secret_message(agent_id))


@app.put("/agent/{agent_id}", description="Deploy an agent or update it")
Expand All @@ -119,10 +156,8 @@ async def update(
else None
)

# Decode the base64 secret
encrypted_secret = base64.b64decode(agent.encrypted_secret)
decrypted_secret = decrypt(agent.encrypted_secret, config.ALEPH_SENDER_SK)

decrypted_secret = decrypt(config.ALEPH_SENDER_SK, encrypted_secret).decode()
if secret != decrypted_secret:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
Expand Down
5 changes: 5 additions & 0 deletions backend/src/utils/message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from datetime import date


def get_view_agent_secret_message(agent_id: str) -> str:
return f"I confirm that I want to view the secret key of my agent {agent_id} today ({date.today()})."

0 comments on commit 9edcd7a

Please sign in to comment.