KyleStargarden
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # On-Chain Basic Base Frames Workshop ## Overview This is a comprehensive educational workshop for aspiring Frame developers. [Note: a tremendous amount of the subsequent writing is ripped directly from the Frame official documentation found here.](https://docs.farcaster.xyz/reference/frames/spec) ![RG_Death_hacker](https://hackmd.io/_uploads/Hk82MmtyR.png) ## Complete Frame Guide ### WTF is a Frame? <iframe width="560" height="315" src="https://www.youtube.com/embed/rp9X8rAPzPM?si=sqJyKybRyKY4yrHe" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> ### Frame Speedrun If you are already super comfortable with full-stack applications; check out the speedrun video: <iframe width="560" height="315" src="https://www.youtube.com/embed/JAIr8kyBxxU?si=yYQ3jnOgFyPFR8G3" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> ### Frame Repo Structure Frames have two key distinct code components; the front end app, back end api. Here's an example of a Frame's codebase structure: ### Folders - **app**: This folder likely contains the core front-end logic and components of the application. It's where the main user interface and client-side functionality are developed. - **api**: This folder is typically used for back-end services or the API layer of the application. It would contain server-side logic, including request handling, data processing, and interaction with databases or other services. ### Files - **route.ts**: This TypeScript file contains routing logic for the Frame. This is frame specific server-side routing (handling API endpoints and requests on the back-end). - **config.ts**: This file holds configuration settings for the application. This could include various environment settings (like API endpoints, database connection details, feature flags). - **layout.tsx**: The `.tsx` extension indicates this is a TypeScript file with JSX syntax. `layout.tsx` likely is the Frame's React layout component. It serves as a template or structure for the UI, including common elements like headers, body and footers. - **page.tsx**: The page file will contain the meat of our Frame. This is where buttons and user actions are defined. --- ### Initial Frame A frame is an HTML web application that lives at a URL (e.g. foo.com/app) on a web server. We'll refer to this web server as the "frame server." The frame server: - Must return a valid frame in the HTML head section. - Should return a valid HTML body, in case the user clicks through to the frame in a browser. - Should not include dynamic content in the initial frame, since it is cached by Farcaster clients. - Should not include an fc:frame:state tag. --- ### Response Frames When a user clicks a button on a frame, the app makes a POST request to the frame server with a frame signature which proves that the request came from the user. The server must respond with a new frame that is sent back to the user. When a frame server receives a POST request: - It must respond within 5 seconds. - It must respond with a 200 OK and another frame, on a post button click to indicate success. - It must respond with a 302 OK and a Location header, on a post_redirect button click to indicate success. - It may respond with 4XX status, content-type: application/json header, and JSON body containing a message property that is 90 or less characters to indicate an application-level error. - Any Location header provided must contain a URL that starts with http:// or https://. --- ### Best Practices Follow these best practices to work around the limitations of frames: - Start your initial frame with a load button if you must show dynamic content. - Add timestamps or UUIDs to image urls on subsequent frames to bust caches. Cache busting is a technique web developers employ to ensure that users receive the most recent version of a website or application. It “busts” the browser's cache, forcing it to download new files instead of using outdated ones. - Return a frame with a "refresh" button if your response takes > 5 seconds. --- ### Rendering Frames A frame enters Farcaster when a user creates a cast and embeds the frame URL in it. An app that wants to support frames must: - Check all call cast embed URLs for valid frames. - If the frame is valid, render the frame when the cast is viewed. - If the frame is malformed, fall back to treating it as an OpenGraph embed. - Follow the frame security model. --- ## Constructing A Frame A frame must include required properties and may contain optional properties. Frames can be validated using the Frame Validator tool provided by Warpcast. --- ### Frame Properties A frame property is a meta tag with a property and a content value. The properties are always prefixed with fc:frame. ```htmlembedded <!-- An example declaring a frame and supported version --> <meta property="fc:frame" content="vNext" /> ``` --- #### Required Properties - `fc:frame` A valid frame version string. The string must be a release date (e.g. 2020-01-01) or vNext. Apps must ignore versions they do not understand. Currently, the only valid version is vNext. - `fc:frame:image` An image which should have an aspect ratio of 1.91:1 or 1:1 - `og:image` An image which should have an aspect ratio of 1.91:1. Fallback for clients that do not support frames. --- #### Optional Properties - `fc:frame:post_url` A 256-byte string which contains a valid URL to send the Signature Packet to. - `fc:frame:button:$idx` A 256-byte string which is the label of the button at position $idx. A page may contain 0 to 4 buttons. If more than 1 button is present, the idx values must be in sequence starting from 1 (e.g., 1, 2, 3). If a broken sequence is present (e.g., 1, 2, 4), the frame is invalid.` - `fc:frame:button:$idx:action` Must be post, post_redirect, link, mint or tx. Defaults to post if not specified. See Button Actions for details on each action. - `fc:frame:button:$idx:target` A 256-byte string which determines the target of the action. - `fc:frame:button:$idx:post_url` A 256-byte string that defines a button-specific URL to send the Signature Packet to. If set, this overrides fc:frame:post_url. - `fc:frame:input:text` Adding this property enables the text field. The content is a 32-byte label that is shown to the user (e.g., Enter a message). - `fc:frame:image:aspect_ratio` Must be either 1.91:1 or 1:1. Defaults to 1.91:1 - `fc:frame:state` A string containing serialized state (e.g. JSON) passed to the frame server. May be up to 4096 bytes. Serialized state just means that an object like a JSON has been serialized into bytes; which is ready to transmit server for reconstruction into the same JSON object. --- ### Button Actions There are several different kinds of button actions that are used to drive the mechanics of a Frame. Here's a quick look at how Frame Button Actions are handled and authenticated: <iframe width="560" height="315" src="https://www.youtube.com/embed/L6qT5usMWsc?si=LqqI4w_IZXKRsvoj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> --- ### post [What is a post?](https://hackmd.io/@KyleStargarden/HJ5wbuN1C#What-is-an-HTTP-post) The `fc:frame:post_url` meta tag describes the content URL. ```html! <meta name="fc:frame:post_url" content="https://frame.example.com/start" /> <meta name="fc:frame:button:1" content="Start" /> ``` The post action sends an HTTP POST request to the frame or button post_url. This is the default button type. The frame server receives a Signature Packet in the POST body, which includes information about which button was clicked, text input, and the cast context. The frame server must respond with a 200 OK and another frame. --- ### post_redirect [What is a post redirect?](https://hackmd.io/@KyleStargarden/HJ5wbuN1C#What-is-a-post-redirect) ```html <meta name="fc:frame:post_url" content="https://frame.example.com/redirect" /> <meta name="fc:frame:button:1" content="Redirect" /> <meta name="fc:frame:button:1:action" content="post_redirect" /> ``` The post_redirect action sends an HTTP POST request to the frame or button post_url. You can use this action to redirect to a URL based on frame state or user input. The frame server receives a Signature Packet in the POST body. The frame server must respond with a 302 Found and Location header that starts with http:// or https://. --- ### link ```html <meta name="fc:frame:button:1" content="Farcaster Docs" /> <meta name="fc:frame:button:1:action" content="link" /> <meta name="fc:frame:button:1:target" content="https://docs.farcaster.xyz" /> ``` The link action redirects the user to an external URL. You can use this action to redirect to a URL without handling a POST request to the frame server. Clients do not make a request to the frame server for link actions. Instead, they redirect the user to the target URL. --- ### mint [What is a CAIP-10 address?](https://hackmd.io/@KyleStargarden/HJ5wbuN1C#What-is-CAIP-10) ```html <meta name="fc:frame:button:1" content="Mint" /> <meta name="fc:frame:button:1:action" content="mint" /> <meta name="fc:frame:button:1:target" content="eip155:8453:0xf5a3b6dee033ae5025e4332695931cadeb7f4d2b:1" /> ``` The mint action allows the user to mint an NFT. Clients that support relaying or initiating onchain transactions may enhance the mint button by relaying a transaction or interacting with the user's walletl. Clients that do not fall back to linking to an external URL. The target property must be a valid CAIP-10 address, plus an optional token ID. In this case the CAIP-10 address is the address of the NFT contract. [Crawling the provided contract address yields an easter egg.](https://basescan.org/address/0xf5a3b6dee033ae5025e4332695931cadeb7f4d2b#code) --- ### tx [What is eth_send huh?](https://hackmd.io/@KyleStargarden/HJ5wbuN1C#What-is-eth_sendTransaction) ```html <meta property="fc:frame:button:1" content="Transaction" /> <meta property="fc:frame:button:1:action" content="tx" /> <meta property="fc:frame:button:1:target" content="https://frame.example.com/get_tx_data" /> <meta property="fc:frame:button:1:post_url" content="https://frame.example.com/tx_callback" /> ``` The tx action allows a frame to send a transaction request to the user's connected wallet. Unlike other action types, tx actions have several steps. First, the client makes a POST request to the target URL to fetch data about the transaction. The frame server receives a Signature Packet in the POST body, including the address of the connected wallet. The frame server must respond with a 200 OK and a JSON response describing the transaction: ```jsonld { chainId: "eip155:10", method: "eth_sendTransaction", params: { abi: [...], // JSON ABI of the function selector and any errors to: "0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D", data: "0x783a112b0000000000000000000000000000000000000000000000000000000000000e250000000000000000000000000000000000000000000000000000000000000001", value: "984316556204476", }, }; ``` The client forwards this transaction data to the user's wallet. If the user signs the transaction, the client makes a POST request to the post_url with a Signature Packet that includes the transaction hash. The frame server must respond with a 200 OK and another frame. The frame server must monitor the transaction hash to determine if the transaction succeeds, reverts, or times out. #### Transaction Data Response Type A transaction data response must match the following TransactionTargetResponse type with: - chainId: A CAIP-2 chain ID to identify the tx network (e.g. Ethereum mainnet) - method: A method ID to identify the type of tx request. (e.g. "eth_sendTransaction") - attribution: Optional. Return false to omit the calldata attribution suffix. If this value is undefined or true, clients will append the attribution suffix. - params: Specific parameters for chainId and method ```typescript type TransactionTargetResponse { chainId: string; method: "eth_sendTransaction"; attribution?: boolean; params: EthSendTransactionParams; } ``` #### Ethereum Params If the method is eth_sendTransaction and the chain is an Ethereum EVM chain, the param must be of type EthSendTransactionParams: - to: transaction to address - abi: JSON ABI which must include encoded function type and should include potential error types. Can be empty. - value: value of ether to send with the transaction in wei. Optional. - data: transaction calldata. Optional. ```typescript type EthSendTransactionParams { abi: Abi | []; to: Hex; value?: string; data?: Hex; } ``` ### Images There are a few rules for serving images in fc:frame:image tags: - The size of the image must be < 10 MB. - The type of image must be jpg, png or gif. - The image source must either be an external resource with content headers or a data URI. - Clients may resize larger images or crop those that do not fit in their aspect ratio. SVG images are not because they can contain scripts and extra work must be done by clients to sanitize them. Frame servers can use cache headers to refresh images and offer more dynamic first frame experiences: Frame servers can use the max-age directive in the HTTP Cache-Control header to ensure images in the initial frame refresh automatically. A lower max-age ensures images update regularly without user interactions. App developers should respect cache headers set by the original frame image, and their image proxy implementations should not interfere with durations. --- ### Securing Frames There are important security concerns that must be addressed by both frame developers and apps that implement frames. - Sanitize all input received from the user via text inputs. - Verify the signature of a frame signature packet. - Validate the origin URL in the frame signature packet. - Load transaction calldata only from trusted origins. [WTF does this mean!?](https://hackmd.io/@KyleStargarden/HJ5wbuN1C) ### Data Structures #### Frame Signature [What is a protobuf?](https://hackmd.io/@KyleStargarden/HJ5wbuN1C#What-is-a-protobuf) A Frame signature proves that a user clicked a frame button. It is created by the Farcaster app, signed by the user's account and sent to the Frame server. When a frame button is clicked, the Farcaster app must generate a FrameAction protobuf. [A FrameAction is a new type of Farcaster message.](https://github.com/farcasterxyz/protocol/blob/main/docs/SPECIFICATION.md#2-message-specifications) Like all FC messages, it must be signed with an active Ed25519 account key (aka signer) that belongs to the user. ```protobuf message FrameActionBody { bytes frame_url = 1; // The URL of the frame app bytes button_index = 2; // The index of the button that was clicked CastId cast_id = 3; // The cast which contained the frame URĽ bytes input_text = 4; // The text from the user input (if any) bytes state = 5; // Serialized frame state value bytes transaction_id = 6; // Transaction ID bytes address = 7; // User's connected address } // MessageType and MessageData are extended to support the FrameAction enum MessageType { ..... MESSAGE_TYPE_FRAME_ACTION = 13; } message MessageData { oneof body { ... FrameActionBody frame_action_body = 16 } } ``` A FrameActionBody in a message m is valid only if it passes these validations: - m.signature_scheme must be SIGNATURE_SCHEME_ED25519. - m.data.type must be MESSAGE_TYPE_FRAME_ACTION - m.data.body.type must be a valid UserDataType - m.data.body.url must be <= 256 bytes - m.data.body.button_index index must be ≥1 and ≤4. - m.data.body.input_text must be <= 256 bytes - m.data.body.state must be <= 4096 bytes - m.data.body.transaction_id must be <= 256 bytes - m.data.body.address must be <= 64 bytes #### Frame Signature Packet A signature packet is a JSON object sent to the Frame server when a button is clicked. It contains two objects: - Signed Message — an authenticated protobuf that represents the user action. This message must be unpacked by a farcaster hub to read the data inside. - Unsigned Message — an unathenticated JSON object that represents the user action. can be read directly. Unsigned messages can be spoofed and should usually be ignored. It is only safe to use them if you are performing an unauthenticated request. If you are unsure, always read the signed message by sending it into the validateMessage endpoint on hubs and only trust the data it returns. ```json { "untrustedData": { "fid": 2, "url": "https://fcpolls.com/polls/1", "messageHash": "0xd2b1ddc6c88e865a33cb1a565e0058d757042974", "timestamp": 1706243218, "network": 1, "buttonIndex": 2, "inputText": "hello world", // "" if requested and no input, undefined if input not requested "state": "%7B%22counter%22%3A1%7D", "transactionId": "0x83afec0f72e32d2409ceb7443dc9e01443d0dec6d38ab454bf20918cf633a455", "address": "0xf6ea479f30a71cc8cb28dc28f9a94246e1edc492", "castId": { "fid": 226, "hash": "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9" } }, "trustedData": { "messageBytes": "d2b1ddc6c88e865a33cb1a565e0058d757042974..." } } ``` The Signed Message can be validated by calling the validateMessage API on Hubs. [See the Hub HTTP API for reference.](https://docs.farcaster.xyz/reference/hubble/httpapi/message#validatemessage) The hub (assuming it’s fully in sync) will validate the following: - the fid is a valid, registered farcaster fid - the signer is currently active and associated with the fid - the message hash is correct (contents match the hash) - the signature for the message is valid, and came from the signer - the FrameActionBody passes the above validations --- ## Onchain Frames Here's a nice tutorial for utilizing [CoinBase's onchainkit](https://onchainkit.xyz/) for building on-chain capable Frames: <iframe width="560" height="315" src="https://www.youtube.com/embed/YwlfgIJK0j8?si=97oDqYQfcl0vWDlx&amp;start=220" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> --- ## Workflow Notes [These are specific notes for the tutorial which can be found here.](https://docs.base.org/building-with-base/guides/nft-minting-frame/) - Deploy to Vercel directly from Github - Add a `.env` file and `.env*` to GitIgnore - Create .env.local and add: - BASESCAN_API_KEY - https://basescan.org/myapikey - NFT_CONTRACT_ADDRESS - As described above - WALLET_PRIVATE_KEY - the private key of the wallet that owns the above NFT contract and can mint it. This key is the same as your 12-word phrase - Images must have a 1.91 to 1 aspect ratio ## Raid Guild Frames Resources - [Everyone is using Frog Frame Framework from Paradigm](https://www.paradigm.xyz/2024/02/frames) - [Raid Guild Deux](https://hackmd.io/ZIOAnYnhT2Sc6Dj6Kr-EHg?both) - [Raid Guild Hacking Frames](https://hackmd.io/G3msHBgKR6yAXmmD8mS1hw?view) ## Resources: - [The Farcaster Youtube Channel, with all dev calls and a number of helpful developer oriented videos.](https://www.youtube.com/@farcasterxyz/videos) - [David Furlong's Frame Resources](https://github.com/davidfurlong/awesome-frames) ## Open Source Frames: - [Token Gating in a Frame](https://github.com/unlock-protocol/token-gated-frame)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully