1,952 olvasmányok
1,952 olvasmányok

AI-engedélyek átruházása humán felhasználóknak a Permit.io hozzáférési kérelmével

által Permit.io15m2025/06/25
Read on Terminal Reader

Túl hosszú; Olvasni

Ismerje meg, hogyan lehet biztonságos, ember-in-the-loop AI ügynököket építeni a Permit.io Access Request MCP, LangGraph és LangChain MCP adapterekkel.
featured image - AI-engedélyek átruházása humán felhasználóknak a Permit.io hozzáférési kérelmével
Permit.io HackerNoon profile picture
0-item

Mivel az AI-ügynökök egyre autonómabbá és képessé válnak, szerepük a passzív asszisztensektől a proaktív szereplőkig terjed.A mai nagy nyelvi modellek (LLM-ek) nem csak szöveget generálnak – feladatokat hajtanak végre, API-kat érhetnek el, adatbázisokat módosítanak és még az infrastruktúrát is szabályozzák.

Az AI-ügynökök olyan tevékenységeket hajtanak végre, amelyek egykor szigorúan az emberi felhasználók számára voltak fenntartva, függetlenül attól, hogy egy találkozót ütemeznek, egy szolgáltatást telepítenek, vagy hozzáférnek egy érzékeny dokumentumhoz.

When agents operate without guardrailsaz ,they can inadvertently make harmful or unauthorized decisionsEgyetlen hallucinált parancs, félreértett utasítás vagy túlságosan széles körű engedély adatszivárgást, megfelelőségi jogsértést vagy törött rendszert eredményezhet.

Ezért az integrációhuman-in-the-loop (HITL)A munkafolyamatok elengedhetetlenek az ügynökök biztonságához és elszámoltathatóságához.

Permit.io’s Access Request MCPegy olyan keretrendszer, amely lehetővé teszi az AI ügynökök számára, hogyrequestérzékeny cselekvés, miközben lehetővé teszi ahumans to remain the final decision-makers.

A Permit.io hozzáférési kérelme MCP

épültEngedélyezzeés integrálva a népszerű ügynöki keretekbe, mint példáulLangChainésLangGraphEz a rendszer lehetővé teszi a jóváhagyási munkafolyamatok közvetlen beillesztését az LLM-alapú alkalmazásokba.

Ebben a tutorialban megtanulod:

  • Miért döntő fontosságú az érzékeny engedélyek átruházása az emberekre a megbízható AI számára?
  • Hogyan teszi lehetővé a Permit.io Model Context Protocol (MCP) a hozzáférési kérelmek munkafolyamatát,
  • Hogyan lehet létrehozni egy valós rendszert, amely egyesíti az LLM intelligenciát az emberi felügyelettel - a LangGraph interrupt() funkciójával.

Mielőtt belemerülnénk a demo alkalmazási és végrehajtási lépéseinkbe, röviden megvitatjuk az AI-engedélyek embereknek való átruházásának fontosságát.

Miért kritikus az AI-engedélyek átruházása az embereknek

Az AI-ügynökök erősek, de mint mindannyian tudjuk, nem hibátlanok.

Utasításokat követnek, de nem értik a kontextust, mint az emberek. válaszokat generálnak, de nem tudják megítélni a következményeket. És amikor ezek az ügynökök integrálódnak a valódi rendszerekbe – banki eszközök, belső eszköztárak, infrastruktúra-vezérlők – ez veszélyes szakadék.

Ebben az összefüggésben minden, ami tévedhet, meglehetősen világos:

  • Túl engedélyező ügynökök: Az LLM-k hozzáférést kaphatnak olyan eszközökhöz, amelyeket nem szabad megérinteni, akár tervezés, akár véletlenül.
  • Hallucinált eszközhívások: Az ügynökök olyan parancsokat, argumentumokat vagy azonosítókat készíthetnek, amelyek soha nem léteztek.
  • Az auditálhatóság hiánya: emberi ellenőrző pontok nélkül nincs egyértelmű nyilvántartás arról, hogy ki jóváhagyta mit, vagy miért.

Delegation is the solution.

Ahelyett, hogy ellenőrizetlen hatalmat adnánk az ügynököknek, protokollt adunk nekik:„Kérdezhetsz, de az ember dönt.”

bevezetésévelhuman-in-the-loop (HITL)A kulcsfontosságú döntéshozatali pontok jóváhagyásával:

  • Biztonság: Megakadályozza a visszafordíthatatlan cselekedeteket, mielőtt azok megtörténnek.
  • Elszámoltathatóság: Magas tételes műveletekhez kifejezett emberi bejelentkezést igényel.
  • Ellenőrzés: Hagyja, hogy az emberek szabályozzák a szabályokat – ki jóváhagyhatja, mi jóváhagyható, és mikor.

Ez a különbség egy ügynök közöttCsinálniValami és egy ügynökkérésHogy csináljunk valamit.

És ez pontosan az, amitEngedélyezzeAz Access Request MCP lehetővé teszi.

A Permit.io hozzáférési kérelme MCP

The Access Request MCPAz alapvető része aEngedélyezzeModel Context Protocol (MCP) – egy specifikáció, amely biztonságos, politika-tudatos hozzáférést biztosít az AI ügynököknek az eszközökhöz és erőforrásokhoz.

Think of it as a bridge between LLMs that want to actéshumans who need control.

Mit csinál az

A Permit's Access Request MCP lehetővé teszi az AI ügynökök számára, hogy:

  • Kérjen hozzáférést a korlátozott erőforrásokhoz (például: „Lehet-e hozzáférni ehhez az étteremhez?”)
  • Érzékeny műveletek elvégzéséhez kérjen engedélyt (például: „Megrendelhetem ezt a korlátozott ételeket?”)
  • Várjon emberi bevitelt a folytatás előtt – a LangGraph interrupt() mechanizmusán keresztül
  • Jelentkezzen be az ellenőrzés és a megfelelés iránti kérelemről és döntésről

A színfalak mögött pedig aEngedélyezzeAz engedélyezési képességek a következőket támogatják:

visszavágóFine Grained Authorization (FGA) szabályozásMunkafolyamatok jóváhagyásaA politika által támogatott elemek

Plug-and-play a LangChain és a LangGraph segítségével

Az MCP közvetlenül beépül aLangChain MCP AdapterésLangGraphAz ökoszisztéma:

  • Az Elemek engedélyezése LangGraph-kompatibilis eszközként jeleníthető meg.
  • Az interrupt() funkcióval leállíthatja az ügynököt, ha érzékeny műveletek történnek.
  • A végrehajtás a valódi emberi döntések alapján folytatható.

Ez a legegyszerűbb módja annak, hogyinject human judgment into AI behaviorNincs szükség backendre.

Megértve a végrehajtást és annak előnyeit, lépjünk be a demo alkalmazásunkba.

Mit fogunk építeni - Demo alkalmazás áttekintése

Ebben a tutoriálban egyreal-time approval workflowamelyben egyAI agent can request access or perform sensitive actions, but only a human can approveAz őket.

Címke: Családi élelmiszerrendszer

Annak megértéséhez, hogy a Permit MCP hogyan segíthet a HITL munkafolyamat engedélyezésében egy felhasználói alkalmazásban, modellezzük afood ordering systemEgy család számára:

  • A szülők hozzáférhetnek és irányíthatnak minden éttermet és ételeket.
  • A gyerekek megtekinthetik a nyilvános tárgyakat, de korlátozott éttermekhez vagy drága ételekhez kell hozzáférniük.
  • Amikor egy gyermek kérelmet nyújt be, a szülő megkapja azt felülvizsgálat céljából, és a cselekvés megkezdése előtt kifejezetten jóvá kell hagynia vagy el kell utasítania.

Ez a használati eset egy közös mintát tükröz:„Az ügynökök segíthetnek, de az emberek döntenek.”

Műszaki Stack

Ezt a HITL-képes ügynököt a következőkkel fogjuk felépíteni:

  • Permit.io - Felhatalmazások, szerepek, irányelvek és jóváhagyások kezelése
  • Engedélyezze az MCP-kiszolgálót - Megmutatja Engedélyezze a munkafolyamatokat eszközökként, amelyeket az ügynök használhat
  • LangChain MCP adapterek - A LangChain és a LangChain MCP eszközökhöz való csatlakoztatása
  • LangGraph - Az ügynök munkafolyamatának megszervezése interrupt() támogatással
  • Gemini 2.0 Flash - Könnyű, multimodális LLM, amelyet az ügynök érvelési motorjaként használnak
  • Python - A ragasztó, amely mindent együtt tart

You’ll end up with a working system where agents can collaborate with humans to ensure safe, intentional behavior—using real policies, real tools, and real-time approvals.

A repository with the full code for this application is available here.

Az alkalmazás teljes kódját tartalmazó adattár itt érhető el.

Lépésről lépésre Tutorial

Ebben a szakaszban megvizsgáljuk, hogyan lehet végrehajtani egy teljesen funkcionális humán-in-the-loop ügynöki rendszertEngedélyezzeés a Langgraph.

Mi fogjuk fedezni:

  • Modellezési engedélyek engedéllyel
  • Az MCP szerver engedélyének beállítása
  • LangGraph + LangChain MCP kliens létrehozása
  • Human-in-the-Loop hozzáadása interrupt() segítségével
  • A teljes munkafolyamat futtatása

Menjünk bele ebbe -

Modellezési engedélyek engedéllyel

Kezdjük azzal, hogy meghatározjuk a rendszer hozzáférési szabályait aLetöltés DashboardEz lehetővé teszi, hogy modellezze, hogy a felhasználók mit tehetnek, és milyen műveleteket kell kiváltani egy jóváhagyási folyamatot.

Create a ReBAC Resource

Navigáljon aPolicyAz oldal oldalról a sidebar, majd:

  • Kattintson az Erőforrások lapra
  • Kattintson egy erőforrás létrehozására
  • Forrás neve: Éttermek
  • Under ReBAC Options, define two roles:
    • parent
    • child-can-order
  • Kattintson a Save

Most menjünk aPolicy EditorKattintson és adja meg az engedélyeket:

  • szülő: teljes hozzáférés (teremtés, olvasás, frissítés, törlés)
  • Gyermekkönyv - Olvasás

Set Up Permit Elements

Menj aElementsA táblázat az oldalról. aUser ManagementRészletek, klikkCreate Element.

  • Configure the element as follows:
    • Name: Restaurant Requests
    • Configure elements based on: ReBAC Resource Roles
    • Resource Type: restaurants
    • Role permission levels
      • Level 1 – Workspace Owner: parent
      • Assignable Roles: child-can-order
  • Kattintson létre
  • Az újonnan létrehozott elemkártyán kattintson a Get Code gombra, és vegye figyelembe a config ID: restaurant-requests elemet.

Add Operation Approval Elements

  • Create a new Operation Approval element:
    • Name: Dish Approval
    • Resource Type: restaurants
  • Kattintson létre

  • Then create an Approval Management element:
    • Name: Dish Requests
  • Kattintson a Get Code elemre, és másolja a config ID: dish-requests elemet.

Add Test Users & Resource Instances

  • Navigáljon a Directory > Instances oldalra
  • Click Add Instance
    • Resource Type: restaurants
    • Instance Key: pizza-palace
    • Tenant: Default Tenant (or your working tenant)

  • Váltás a Felhasználók lapra
  • Click Add User
    • Key: joe
    • Instance Access: restaurants:pizza-palace#parent
    • Click Save
  • Create another user with the key henry
    • Don’t assign a role

Miután beállítottuk az Engedélyt, készen állunk az MCP szerver klónozására és a politikák csatlakoztatására egy működő ügynökhöz.

Az MCP szerver engedélyének beállítása

With your policies modeled in the Permit dashboard, it’s time to bring them to life by setting up the Permit MCP server— egy helyi szolgáltatás, amely a hozzáférési kérelmeket és a jóváhagyási folyamatokat olyan eszközökként tárja fel, amelyeket egy AI ügynök használhat.

Clone and Install the MCP Server

Kezdje az MCP-kiszolgáló repozitóriumának klónozásával és egy virtuális környezet létrehozásával.

git clone <https://github.com/permitio/permit-mcp>
cd permit-mcp

# Create virtual environment, activate it and install dependencies
uv venv
source .venv/bin/activate # For Windows: .venv\\Scripts\\activate
uv pip install -e .

Add Environment Configuration

létrehozni a.envfájl a projekt gyökerénél a rendelkezésre álló.env.example, és töltse ki a helyes értékekkel az Engedély beállításból:

bash
CopyEdit
RESOURCE_KEY=restaurants
ACCESS_ELEMENTS_CONFIG_ID=restaurant-requests
OPERATION_ELEMENTS_CONFIG_ID=dish-requests
TENANT= # e.g. default
LOCAL_PDP_URL=
PERMIT_API_KEY=
PROJECT_ID=
ENV_ID=

Ezeket az értékeket a következő erőforrások használatával visszaszerezheti:

  • Helyszín_PDP_URL
  • Engedélyezze az API-t
  • Projekt - azonosító
  • Azonosító / ID

✔️ Megjegyzés: HasználjukHelyi politikai döntéshozatali pont (Policy Decision Point – PDP)Ez a bemutató támogatja a ReBAC értékelést és az alacsony késleltetésű, offline tesztelést.

Start the Server

Ha minden a helyén van, most már helyi szinten is futtathatja az MCP szervert:

uv run -m src.permit_mcp

Miután a szerver fut, a beállított engedélyezési elemek (hozzáférési kérelem, jóváhagyási menedzsment stb.) az ügynök által az MCP protokollon keresztül hívható eszközökként lesznek feltüntetve.

LangGraph + LangChain MCP kliens létrehozása

Most, hogy az engedélyezési MCP-kiszolgáló be van állítva és fut, létrehozunk egy AI-ügynök klienst, amely kölcsönhatásba léphet vele.

  • Használja a Gemini által működtetett LLM-t annak eldöntéséhez, hogy milyen intézkedéseket kell tenniDynamically invoke MCP eszközök, mint például a request_access, approve_operation_approval, stb.
  • Teljesen egy LangGraph munkafolyamaton belül futtatható
  • Szünet az interrupt() használatával történő emberi felülvizsgálathoz (a következő szakaszban)

Csatlakoztassuk a pontokat!

Install Required Dependencies

Az MCP projekt könyvtárban telepítse a szükséges csomagokat:

uv add langchain-mcp-adapters langgraph langchain-google-genai

Ez ad neked:

  • langchain-mcp-adapterek: A Permit MCP eszközök automatikus átalakítása LangGraph-kompatibilis eszközökké
  • langgraph: grafon alapú munkafolyamatok megszervezéséhez
  • langchain-google-genai: interakció a Gemini 2.0 Flash

Add Google API Key

Szükséged lesz egy API kulcsraGoogle AI stúdióA Gemini használata.

Adja hozzá a kulcsot a.envA fájl:

GOOGLE_API_KEY=your-key-here

Build the MCP Client

Hozzon létre egy nevet viselő fájltclient.pyA projekt gyökere.

A fájlt logikai blokkokra bontjuk:

  • Imports and Setup

    Start by importing dependencies and loading environment variables:

    import os
    from typing_extensions import TypedDict, Literal, Annotated
    from dotenv import load_dotenv
    from langchain_google_genai import ChatGoogleGenerativeAI
    from langgraph.graph import StateGraph, START, END
    from langgraph.types import Command, interrupt
    from langgraph.checkpoint.memory import MemorySaver
    from langgraph.prebuilt import ToolNode
    from mcp import ClientSession, StdioServerParameters
    from mcp.client.stdio import stdio_client
    from langchain_mcp_adapters.tools import load_mcp_tools
    import asyncio
    from langgraph.graph.message import add_messages
    

    Then, load the environment and set up your Gemini LLM:


load_dotenv()

global_llm_with_tools = None

llm = ChatGoogleGenerativeAI(
    model="gemini-2.0-flash",
    google_api_key=os.getenv('GOOGLE_API_KEY')
)


  • Configure MCP Server ParametersTell LangGraph how to communicate with the running MCP server:

    server_params = StdioServerParameters( command="python", args=["src/permit_mcp/server.py"], )
    

A megosztott ügynök állapotának meghatározása:

class State(TypedDict):
    messages: Annotated[list, add_messages]


  • Define Workflow Nodes and the graph builder:

    Here’s the logic to route between calling the LLM and invoking tools:


    async def call_llm(state):
        response = await global_llm_with_tools.ainvoke(state["messages"])
        return {"messages": [response]}
    
    def route_after_llm(state) -> Literal[END, "run_tool"]:
        return END if len(state["messages"][-1].tool_calls) == 0 else "run_tool"
    
    async def setup_graph(tools):
        builder = StateGraph(State)
        run_tool = ToolNode(tools)
        builder.add_node(call_llm)
        builder.add_node('run_tool', run_tool)
    
        builder.add_edge(START, "call_llm")
        builder.add_conditional_edges("call_llm", route_after_llm)
        builder.add_edge("run_tool", "call_llm")
    
        memory = MemorySaver()
        return builder.compile(checkpointer=memory)
    

A fenti kódban definiáltunk egy LLM csomópontot és annak feltételes szélét, amely arun_toolcsomópont, ha van egy eszközhívás az állapot üzenetében, vagy a grafikon véget ér. Meghatároztunk egy funkciót is a grafikon beállításához és összeállításához egy memóriában lévő jelzővel.

Ezután adja hozzá a következő kódvonalat a válasz áramlásához a grafikonból, és adjon hozzá egy interaktív csevegőfolyót, amely addig fut, amíg kifejezetten kilép.


  • Stream Output and Handle Chat Input, and an infinite loop for user interaction:


         async def stream_responses(graph, config, invokeWith):
        async for event in graph.astream(invokeWith, config, stream_mode='updates'):
            for key, value in event.items():
                if key == 'call_llm':
                    content = value["messages"][-1].content
                    if content:
                        print('\\n' + ", ".join(content)
                              if isinstance(content, list) else content)
    
    async def chat_loop(graph):
        while True:
            try:
                user_input = input("\\nQuery: ").strip()
                if user_input in ["quit", "exit", "q"]:
                    print("Goodbye!")
                    break
    
                sys_m = """
                Always provide the resource instance key during tool calls, as the ReBAC authorization model is being used. To obtain the resource instance key, use the list_resource_instances tool to view available resource instances.
    
                Always parse the provided data before displaying it.
                If the user has initially provided their ID, use that for subsequent tool calls without asking them again.
                """
    
                invokeWith = {"messages": [
                    {"role": "user", "content": sys_m + '\\n\\n' + user_input}]}
                config = {"configurable": {"thread_id": "1"}}
    
                await stream_responses(graph, config, invokeWith)
    
            except Exception as e:
                print(f"Error: {e}")
    
  • Final Assembly

    Add the main entry point where we will convert the Permit MCP server tool to LangGraph-compatible tools, bind our LLM to the resulting tools, set up the graph, draw it to a file, and fire up the chat loop:


    python
    CopyEdit
    async def main():
        async with stdio_client(server_params) as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
    
                tools = await load_mcp_tools(session)
                llm_with_tools = llm.bind_tools(tools)
                graph = await setup_graph(tools)
    
                global global_llm_with_tools
                global_llm_with_tools = llm_with_tools
    
                with open("workflow_graph.png", "wb") as f:
                    f.write(graph.get_graph().draw_mermaid_png())
    
                await chat_loop(graph)
    
    if __name__ == "__main__":
        asyncio.run(main())
    
  • Lastly, Run the Client

Miután mindent mentett, indítsa el az ügyfelet:

uv run client.py

A futtatás után egy új képfájlworkflow_graph.pnglétrehozásra kerül, amely a grafikonot mutatja.

Mivel minden be van állítva, most megadhatunk ilyen lekérdezéseket:

Query: My user id is henry, request access to pizza palace with the reason: I am now 18, and the role child-can-order
Query: My user id is joe, list all access requests

Your agent is now able to call MCP tools dynamically!

Human-in-the-Loop hozzáadásainterrupt()

A LangGraph által működtetett MCP-ügyfél fel- és futtatásával az engedélyezési eszközök mostantól automatikusan igényelhetők.sensitive,például hozzáférést biztosít egy korlátozott erőforráshoz, vagy jóváhagy egy nagy kockázatú műveletet?

Ez az, ahol a LangGraphinterrupt() becomes useful.

Most hozzáadjuk ahuman approval nodea munkafolyamat megszakításához és felfüggesztéséhez, amikor az ügynök kritikus eszközöket próbál igénybe venni, mint például:

  • jóváhagyás_hozzáférés
  • approve_operation_approval

Az ember kérni fogja, hogymanually approve or denyaz eszköz hívása, mielőtt az ügynök elindulna.

Define the Human Review Node

A tetején a teclient.pyA fájl (előttesetup_graph), add the following function:


async def human_review_node(state) -> Command[Literal["call_llm", "run_tool"]]:
    """Handle human review process."""
    last_message = state["messages"][-1]
    tool_call = last_message.tool_calls[-1]

    high_risk_tools = ['approve_access_request', 'approve_operation_approval']
    if tool_call["name"] not in high_risk_tools:
        return Command(goto="run_tool")

    human_review = interrupt({
        "question": "Do you approve this tool call? (yes/no)",
        "tool_call": tool_call,
    })

    review_action = human_review["action"]

    if review_action == "yes":
        return Command(goto="run_tool")

    return Command(goto="call_llm", update={"messages": [{
        "role": "tool",
        "content": f"The user declined your request to execute the {tool_call.get('name', 'Unknown')} tool, with arguments {tool_call.get('args', 'N/A')}",
        "name": tool_call["name"],
        "tool_call_id": tool_call["id"],
    }]})

Ez a csomópont ellenőrzi, hogy a hívott eszköz „magas kockázatnak” minősül-e.

Update Graph Routing

Módosítsa aroute_after_llmA funkció úgy működik, hogy az eszköz az útvonalat az emberi felülvizsgálati csomóponthoz hívja, ahelyett, hogy azonnal futna:

def route_after_llm(state) -> Literal[END, "human_review_node"]:
    """Route logic after LLM processing."""
    return END if len(state["messages"][-1].tool_calls) == 0 else "human_review_node"

Wire in the HITL Node

Frissítve asetup_graphA funkció hozzáadásahuman_review_nodemint egy csomópont a grafikonban:

async def setup_graph(tools):
    builder = StateGraph(State)
    run_tool = ToolNode(tools)
    builder.add_node(call_llm)
    builder.add_node('run_tool', run_tool)
    builder.add_node(human_review_node)  # Add the interrupt node here

    builder.add_edge(START, "call_llm")
    builder.add_conditional_edges("call_llm", route_after_llm)
    builder.add_edge("run_tool", "call_llm")

    memory = MemorySaver()
    return builder.compile(checkpointer=memory)

Handle Human Input During Runtime

Végezetül növeljük astream_responsesfunkció, amely észleli, amikor a grafikon megszakad, felhívja a döntést, és folytatja az emberi bemenet használatávalCommand(resume={"action": user_input}).

A kliens futtatása után a grafikon nem így néz ki:

Miután elindította a kliens, a grafikon (workflow_graph.png) most egy emberi felülvizsgálati csomópontot tartalmaz az LLM és az eszköz végrehajtási szakaszai között:

Ez biztosítja, hogyyou remain in controlbármikor, amikor az ügynök olyan döntést próbál hozni, amely megváltoztathatja az engedélyeket vagy megkerülheti a korlátozásokat.

Ezzel sikeresen hozzáadta az emberi felügyeletet az AI ügynökéhez anélkül, hogy újraírná az eszközöket vagy a backend logikát.

következtetés

Ebben a bemutatóban egy biztonságos, ember-tudatos AI ügynököt építettünkPermit.io’s Access Request MCPaz ,LanggráfésLangChain MCP adapterek.

A Permit.io hozzáférési kérelme MCPLanggráfLangChain MCP adapterek

Ahelyett, hogy az ügynök ellenőrizetlenül működne, megadtuk neki a hatalmat, hogyrequestHozzáférés ésdefer critical decisions to human users,Akárcsak egy felelős csapattag.

We covered:

  • Engedélyezési elemek és ReBAC használatával engedélyek és jóváhagyási folyamatok modellezése
  • Hogyan lehet feltárni ezeket az áramlásokat a Permit MCP szerveren keresztül
  • Hogyan lehet létrehozni egy LangGraph-alapú klienst, amely ezeket az eszközöket természetes módon használja
  • És hogyan lehet valós idejű human-in-the-loop (HITL) ellenőrzéseket behelyezni a interrupt() használatával

Szeretné látni a teljes demo akcióban? nézd meg aGitHub Repo.

Further Reading -

  • Biztonságos AI együttműködés egy engedélyek kapuján keresztül
  • MCP GitHub Repo engedélyezése
  • LangChain MCP adapter dokumentumok
  • Engedélyezze a ReBAC politikákat
  • LangGraph interrupt() hivatkozás


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks