Code Monkey home page Code Monkey logo

mostro's People

Contributors

arkanoider avatar bilthon avatar catrya avatar cipherchabon avatar diazemiliano avatar grunch avatar satoshisound avatar shuoer86 avatar yukibtc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mostro's Issues

When a dispute begins, Mostro shows those involved the solver's npub

When a dispute begins, the Solver who takes it must write a DM to those involved. The buyer and seller must wait for the Solver to contact them and in the meantime they do not know who will assist them (unless there is only 1 possible solver).

Since the buyer and seller do not know who will handle the dispute, it could happen that a malicious user who manages to learn that they are both in dispute could pose as a solver and write to them asking for personal information or money to "resolve" the dispute, or any other trick.

Describe the solution you would like
That Mostro shows the buyer and seller involved in a dispute the npub of the solver who will handle their dispute so that they can open DM to the solver, or so that they know who will assist them and can verify that the person writing to them is the real solver and not an imposter.

Add filter by custom tag

Mostro send nip33 events to port 30078, currently we are sending events related with different data, those have a data_label custom tag, right now we have these possible values order, dispute and rating, in order to have a more efficient subscription we should add a filter rule to get only rating events

Here an example of an event with that custom tag

[
  "EVENT",
  "RAND",
  {
    "id": "44cad49530f45150dee36a8c3e5e11e94c8ba2f44ba9960013f0ec6f84e50c2d",
    "pubkey": "dbe0b1be7aafd3cfba92d7463edbd4e33b2969f61bd554d37ac56f032e13355a",
    "created_at": 1701977550,
    "kind": 30078,
    "tags": [
      [
        "d",
        "npub1qqqxssz4k6swex94zdg5s4pqx3uqlhwsc2vdzvhjvzk33pcypkhqe9aeq2"
      ],
      [
        "total_reviews",
        "6"
      ],
      [
        "total_rating",
        "3.6"
      ],
      [
        "last_rating",
        "4"
      ],
      [
        "max_rate",
        "1"
      ],
      [
        "min_rate",
        "5"
      ],
      [
        "data_label",
        "rating"
      ]
    ],
    "content": "",
    "sig": "225f73bae421b4f19eb3797ea20b6549afc2e498ae13876e24e79e7b83e2d95bbf0bc278ae89023bdb2d333bec4e564002f3e6e316ff41a7864c4c5e6f48ff1c"
  }
]

We should implement that filter here:

https://github.com/MostroP2P/mostro/blob/main/src/app/rate_user.rs#L23-L26

Handle orders with negative amounts

Orders with negative fiat and sats amounts are allowed to be created, and in the case of the negative sats amount a panic is generated when a hodl invoice with negative amount is requested.

Here's a log dump for that event:

2024-01-07T17:02:33.766035Z  INFO mostrod::util: DM content: "{\"Order\":{\"version\":1,\"id\":\"d43d4753-618e-416a-b9fc-9ddd57c97c9d\",\"pubkey\":null,\"action\":\"NewOrder\",\"content\":{\"Order\":{\"id\":\"d43d4753-618e-416a-b9fc-9ddd57c97c9d\",\"kind\":\"Sell\",\"status\":\"Pending\",\"amount\":-2000,\"fiat_code\":\"USD\",\"fiat_amount\":5,\"payment_method\":\"Cash\",\"premium\":0,\"created_at\":1704646953}}}}"
2024-01-07T17:02:33.771251Z  INFO mostrod::util: Sending event: Event {
    id: EventId(
        0xf5bc660d3dcd7758418346bdf950682b0e280f6a8650ae4254e25f00d4c5192c,
    ),
    pubkey: XOnlyPublicKey(
        d9725eb47dc4441cec52c21b3fa63f630dbdf6458dc3bd091c75b5faf3fecb2e28bf42c5978329f2d144e3e4cedd8d25e94b9fa8ded22a708c4219cb5336e099,
    ),
    created_at: Timestamp(
        1704646953,
    ),
    kind: EncryptedDirectMessage,
    tags: [
        PubKey(
            XOnlyPublicKey(
                128642fc8945af582de87e69c7d763626a89435cda6ee38ff8e06a0cf21f39498e20aa1b9dd72f537fcb4f4eb0431eeb0bca780cc9934575e7e2d4fb9b77bf8a,
            ),
            None,
        ),
    ],
    content: "hj/s1pICaVwpRsW3rux8wlSSw2G1JI6iiMQFxCFzdk5EVwL/lhNJJyFnO5IB8anD77TvpDi4FFmEMuhkz6X1/iQ8Xjiq1heAVIYMpDf3QAKRGm7L9++oaK8eLY902I9RGNULfyO5H6r1LTm8V4vvnWcLUb6OtfP7yu+tsm1pFmKAZVl5R9IJO2tvs29oXmtl9EFiwuR89K0iMv1/Wa8WSVb+A92M2aYHDXS7Ro7H91hEdQ6CQlQmWecd26s0lQn9nHCeEYC0bRxT9MhfQPZk57niM1EcGk9axdcCU0C5/ULREfWvBjq9HbWhowJDqLkoXrK6hTYuipqY3Nmpql/6P+Gq36+ulQcVq2xHN5bI6p6+YY54rkctkv0lbgTl92dn1K5JCgpIRuQHSXevRm32T0Ngo+yJyyyNKV1Zst3Keqs=?iv=vlrORIjU6t6JD0k/GORwDA==",
    sig: Signature(bb6fa6714b2a64cdeacaa97cf00714925f637de4b7b3bd16fc4cbabbefb7840527e369ec38927d26933a6072a0fef1005fd61562f50b9ba81cedb67c6728d434),
}
thread 'main' panicked at src/lightning/mod.rs:77:14:
Failed to add hold invoice: Status { code: Unknown, message: "payments of negative value are not allowed, value is -2000000", metadata: MetadataMap { headers: {"content-type": "application/grpc"} }, source: None }

I'm not sure what the expected behavior is here, but I'd assume that such order creation requests should be rejected with an error.

Validate NewOrder with buyer_invoice

When an new buy order have buyer_invoice with a botl11 LN invoice we should validate:

  1. If amount = 0, the invoice MUST be amountless
  2. If amount != 0, the invoice amount MUST be equal to the order amount or amountless

In all cases we allow users to enter a lightning address instead of a bolt11 invoice.

This validation should be added on actions new-order and add-invoice.

Build fails

I get this when compiling from scratch on a linux VPS with rustc 1.71.0 (8ede3aae2 2023-07-12).

Oddly, when compiling locally (mac os) with rustc 1.68.0 (2c8cc3432 2023-03-06) this error is not thrown.

   Compiling mostro v0.7.9 (/home/bilthon/mostro)
error[E0308]: mismatched types
   --> src/app/rate_user.rs:82:33
    |
82  |     relay.send_msg(msg.clone(), false).await.unwrap();
    |           --------              ^^^^^ expected `Option<Duration>`, found `bool`
    |           |
    |           arguments to this method are incorrect
    |
    = note: expected enum `std::option::Option<std::time::Duration>`
               found type `bool`
note: method defined here
   --> /home/bilthon/.cargo/git/checkouts/nostr-7fe31184c4acbae8/8261bff/crates/nostr-sdk/src/relay/mod.rs:694:18
    |
694 |     pub async fn send_msg(&self, msg: ClientMessage, wait: Option<Duration>) -> Result<(), Error> {
    |                  ^^^^^^^^

error[E0308]: mismatched types
   --> src/app/rate_user.rs:111:46
    |
111 |     relay.send_msg(ClientMessage::close(id), false).await.ok()?;
    |           --------                           ^^^^^ expected `Option<Duration>`, found `bool`
    |           |
    |           arguments to this method are incorrect
    |
    = note: expected enum `std::option::Option<std::time::Duration>`
               found type `bool`
note: method defined here
   --> /home/bilthon/.cargo/git/checkouts/nostr-7fe31184c4acbae8/8261bff/crates/nostr-sdk/src/relay/mod.rs:694:18
    |
694 |     pub async fn send_msg(&self, msg: ClientMessage, wait: Option<Duration>) -> Result<(), Error> {
    |                  ^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `mostro` (bin "mostro") due to 2 previous errors

This is with v0.7.9 commit 6f2c043212a68d5da0f7115dad726974d5a1d026.

Set fee for order trades

In order to have incentive to run a Mostro node we need to set a fee for every order, this should be an small value and can be set on .env file as a percent, i.g.: FEE=0.006 is 0.6%, and this should be splitted and charge 0.3% to each part.

Disputes

  1. Seller creates an order
  2. Buyer takes it and adds an invoice
  3. Seller pays the HI
  4. Buyer initiates a dispute
  5. The order status changes to Dispute
  6. A new dispute is created with status Pending

To be done on this issue:

  • A message needs to be sent to the person who initiated the dispute indicating that it was started
  • A message needs to be sent to the counterparty so that they know that they started a dispute against him/her
  • A nip33 event needs to be sent to indicate that there is a dispute, this can only be taken by solvers

Send JSON objects with relevant data instead of an actual message

I can see that now when a potential buyer takes an order, mostro will send a DM to the seller, something like this:

🧌 Order Id: f6204501-6868-4919-8b8b-7b15a7746e4f

        npub1kk30z9gus7xk3kf3m8zxh9paw4ngw023he9037y2jd7y7msfvvdskj8e60 has taken your order and wants to buy your sats. Get in touch and tell him/her how to send you brl 36 through ibk.

        Once you verify you have received the full amount you have to release the sats

This is fine, but works only for english. A better approach IMO would be for mostro to just send a JSON object with the relevant data of this message, and let the client construct the final message, which can also then be localized depending on the user's settings.

{
  "order_id": "f6204501-6868-4919-8b8b-7b15a7746e4f",
  "buyer": "npub1kk30z9gus7xk3kf3m8zxh9paw4ngw023he9037y2jd7y7msfvvdskj8e60",
}

I removed fiat_amount, fiat_code and payment_method because that's information that the client can obtain just by querying old messages by order id. But they could be included anyways.

This is more flexible and also more in line with the nostr approach of moving the logic to the client.

Remove 11th parameter

Hi i think there's typo boss, you are inserting 10 parameters, but using 11. I think you have to remove one.

) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)

Send message to buyer after the payment fails

Message should have a new action payment-failed and indicate the payment attempts left.

With this information clients could create a message like this:

I tried to send you the sats but the payment of your invoice failed, I will try ${attempts} more times in ${pendingPaymentWindow} minutes window, please check your node/wallet is online

halt on dispute action

Error: error returned from database: (code: 2067) UNIQUE constraint failed: disputes.order_id

Caused by:
    (code: 2067) UNIQUE constraint failed: disputes.order_id

Privacy in mostro

Discussed in #56

Originally posted by bilthon April 24, 2023

Summary

Nostr's operation results in the leakage of some metadata about trades to external observers. Although identities in Nostr are pseudonymous, making an effort to cover these tracks would be beneficial.

The Problem

Mostro currently communicates with a mix of messages of kind 3000 and kind 4. Kind 3000 are replaceable notes used to post orders. These are akin to posting messages in a public board. The information in them is public and they're always issued by mostro so there's not much to protect there. But these order notes are preceded by direct messages (DMs) of kind 4 that carry the order information. Similarly when an order is taken, a DM to mostro is issued by the taker, and subsequently more DMs are sent from mostro to the order maker.

The kind 4 messages are meant to be direct messages, and while the contents of the message are encrypted, an external observer does get the public key information, meaning it is known who communicated to whom. A simple time correlation between these DMs and the order state changes would allow an attacker to infer the Nostr identities of buyer and seller.

Using only relays that support NIP-42 (authenticated messages) could minimize this information leak. However, a motivated attacker could still gather information by setting up one or multiple relays.

Ephemeral Identities

A proposed solution involves creating ephemeral identities for both the buyer and seller. These identities would last for the duration of the trade and be discarded afterward. By taking advantage of the fact that identities in Nostr are simply private/public key pairs and relatively cheap to create, the real traders' public keys can be masked.

The root identity of each party can still be communicated to Mostro to maintain the reputation system, but this information would be contained within the encrypted payload of each message.

Stop trying re-connect with unresponsive relays

Mostro is always trying to re-connect unresponsive relays, this is a waste of server resources and also we are generating a lot of useful log lines, I just got 600 MB on logs for only 4 days, we need to stop connecting again and again to unresponsive relays.

Not sure how to do it because I can see on logs that fully responsive relays also get disconnected and connected again, so we can't just stop connecting a relay after a disconnection, we can discuss here different ways of solve this problem.

dockerized relay panicked (ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db)

Here is the output

docker-compose up             
Creating network "relay_default" with the default driver
Pulling nostr-relay (scsibug/nostr-rs-relay:)...
latest: Pulling from scsibug/nostr-rs-relay
7bc891379e23: Pull complete
df4d78e13fae: Pull complete
3a6c16603983: Pull complete
5d8c1e92d83d: Pull complete
536d78aa3c67: Pull complete
Digest: sha256:6bd15c99ecf5bcba66438d5f9a1e5b8c0941a359fc7c0a18693b403fd3d56451
Status: Downloaded newer image for scsibug/nostr-rs-relay:latest
Creating nostr-relay ... done
Attaching to nostr-relay
nostr-relay    | Mar 21 15:29:31.873  INFO nostr_rs_relay: Starting up from main
nostr-relay    | Mar 21 15:29:31.875  INFO nostr_rs_relay::server: listening on: 0.0.0.0:8080
nostr-relay    | Mar 21 15:29:31.878 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | Mar 21 15:29:32.278 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | Mar 21 15:29:33.078 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | Mar 21 15:29:34.679 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | Mar 21 15:29:37.879 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | Mar 21 15:29:44.279 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | Mar 21 15:29:57.080 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db    
nostr-relay    | thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Error(Some("unable to open database file: /usr/src/app/db/nostr.db"))', src/repo/sqlite.rs:949:10
nostr-relay    | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
nostr-relay    | thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Any { .. }', src/main.rs:51:19
nostr-relay exited with code 101

Mostro crashes if yadio API request fails

This happened after I took a sell order. Maybe issue schedule a retry? if the API is down we should eventually give up and let the clients know about this.

 DEBUG mostro::app                    > message: Ok("{\"version\":0,\"pubkey\":\"npub1gwfevg324vdpgdnjr8jjl7cmeyrkg2xd4gv5jsgjvw34c7323yxqux3cul\",\"order_id\":\"db00977a-e4bd-46de-807e-f07ba513bb6f\",\"action\":\"TakeSell\",\"content\":null}")
 INFO  sqlx::query                    > UPDATE orders SET master_buyer_pubkey …; rows affected: 1, rows returned: 0, elapsed: 645.750µs

UPDATE
  orders
SET
  master_buyer_pubkey = ?1
WHERE
  id = ?2

 INFO  sqlx::query                    > UPDATE orders SET buyer_pubkey …; rows affected: 1, rows returned: 0, elapsed: 403.125µs

UPDATE
  orders
SET
  buyer_pubkey = ?1
WHERE
  id = ?2

 INFO  sqlx::query                    > PRAGMA foreign_keys = ON; …; rows affected: 0, rows returned: 0, elapsed: 36.000µs

PRAGMA foreign_keys = ON;

 INFO  sqlx::query                    > UPDATE orders SET taken_at …; rows affected: 1, rows returned: 0, elapsed: 1.041ms

UPDATE
  orders
SET
  taken_at = ?1
WHERE
  id = ?2

 DEBUG reqwest::connect               > starting new connection: https://api.yadio.io/
 INFO  sqlx::query                    > SELECT "orders"."id", "orders"."kind", "orders"."event_id", …; rows affected: 1, rows returned: 1, elapsed: 704.209µs

SELECT
  "orders"."id",
  "orders"."kind",
  "orders"."event_id",
  "orders"."hash",
  "orders"."preimage",
  "orders"."creator_pubkey",
  "orders"."cancel_initiator_pubkey",
  "orders"."buyer_pubkey",
  "orders"."master_buyer_pubkey",
  "orders"."seller_pubkey",
  "orders"."master_seller_pubkey",
  "orders"."status",
  "orders"."price_from_api",
  "orders"."premium",
  "orders"."payment_method",
  "orders"."amount",
  "orders"."min_amount",
  "orders"."max_amount",
  "orders"."buyer_dispute",
  "orders"."seller_dispute",
  "orders"."buyer_cooperativecancel",
  "orders"."seller_cooperativecancel",
  "orders"."fee",
  "orders"."routing_fee",
  "orders"."fiat_code",
  "orders"."fiat_amount",
  "orders"."buyer_invoice",
  "orders"."range_parent_id",
  "orders"."invoice_held_at",
  "orders"."taken_at",
  "orders"."created_at",
  "orders"."buyer_sent_rate",
  "orders"."seller_sent_rate"
FROM
  "orders"
WHERE
  "orders"."id" = ?
LIMIT
  1

Error: Something went wrong with API request, try again!

Caused by:
    0: error sending request for url (https://api.yadio.io/convert/10/pen/BTC): error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known
    1: error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known
    2: dns error: failed to lookup address information: nodename nor servname provided, or not known
    3: failed to lookup address information: nodename nor servname provided, or not known

Feat request: set invoicing timer

I've been testing Mostro a little and I find the 15 minutes timer to send the LN invoice as a buyer is too short. I have failed to do it a bunch of times. It would be great having more time or, even better, having the option to choose that time at order making time.

I use Robosats all the time and I love that you can chose both the public duration of your order (up to 24 hours) as well as the escrow/invoice timer (up to 8 hours). I always set 24 and 8 hours respectively, what, together with their Telegram notifications, allows me to never miss when one of my orders gets taken, even when I fall asleep during the night (I always still have some time to do it in the morning). A similar way to choose those times (at least to increase some the 15 minutes timer for the invoice) on Mostro would be very beneficial, IMO.

Add cron job to cancel operation to takers after N seconds after they took order

This issue have two parts

  1. After a seller takes an order he is asked to paid a hold invoice, add a new cron job that cancel the hold invoice after N seconds and republish the order.

  2. After a buyer takes an order he is asked to add a lightning invoice to receive funds, add a new cron job that after N seconds republish the order.

We already have a job working, take a look to make this new ones

Create order with invalid code currency breaks mostro

This breaks mostrod

nostril --envelope --dm mostro-pubkey-hex --sec private-key-hex --content '{"version":0,"pubkey":"npub1qqqxssz4k6swex94zdg5s4pqx3uqlhwsc2vdzvhjvzk33pcypkhqe9aeq2","action":"Order","content":{"Order":{"kind":"Sell","status":"Pending","amount":0,"fiat_code":"xxxx","fiat_amount":8,"payment_method":"f2f","premium":0,"created_at":0}}}' | websocat ws://localhost:7000

Mostro response before panic

Error: Price API not answered - retry

Basic tasks to have a usable version

  • Seller creates a new order
  • Buyer creates a new order
  • Buyer send invoice to take seller's order
  • #8
  • Seller receives by DM a hold invoice that he must pay to take an order
  • Subscribe invoices to be aware when a hold invoice is paid
  • After both parties do what they need, mostro send a DM to each party letting them know the pubkey of the counterpart
  • Handle FiatSent message from buyer and send a message to seller to release funds
  • Handle Release message from seller, settle hold invoice and ask them to rate to counterparty
  • #22
  • #11
  • #12
  • Add fiat price API to orders where users don't add sats amount
  • #58
  • #13
  • #42
  • Validations
  • #16

Update order flow to work with market price orders

If users create orders with market price (without sats amount) we need to change the order flow but having in mind that don't break the order flow for orders with sats amount fixed.

Case 1: Sell order published

  1. buyer takes the order but the price is given by mostro
  2. mostro send a DM to buyer ask an invoice for n sats amount
  3. buyer use a new command sendinvoice to send the invoice to the bot, like mostro-cli sendinvoice -o id -i lnbc1...
  4. mostro ask to seller to pay the hold invoice
    the rest works like now

Case 2: Buy order published

  1. seller takes the order, mostro calculates the price and send the hold invoice to be paid by seller
  2. seller pays and mostro send a DM to buyer ask an invoice for n sats amount
  3. buyer use a new command sendinvoice to send the invoice to the bot, like mostro-cli sendinvoice -o id -i lnbc1...
    the rest works like now

Add reputation system

We need to add a reputation system like the one we use in @lnp2pbot but for this we should save user's reputation as a nip33 event on Nostr. Any time users can query other users reputation.

We should be able to send this fields on the event published

total_reviews: integer
total_rating: integer
last_rating: integer
min_rate: integer
max_rate: integer

The new reputation should be a new index from 1 to 5, calculated using the successful trades and volume in sats.

We can do it the same way we are posting orders, I mean serializing it on content field, and to calculate we should use the same logic on the lnp2pbot link above.

Tests breaks after commits

If you run cargo test on your local mostro environment it passes all tests, but after each commit on github we always have this errors related with the sqlite database.

 error: error returned from database: (code: 14) unable to open database file
   --> src/db.rs:98:25
    |
98  |       let rows_affected = sqlx::query!(
    |  _________________________^
99  | |         r#"
100 | |     UPDATE orders
101 | |     SET
...   |
114 | |         order_id
115 | |     )
    | |_____^
    |
    = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info)

Limit size of messages

Mostro should discard messages from users with content field with a size of 256 bytes in order to avoid DoS attacks.

Replace update_* functions with sqlx update()

We are getting full of db update functions, but there is an easy way of update with sqlx, let's start using it instead of create functions that we are using only once and are adding more complexity to the code, here an example:

if let Ok(mut d) = dispute {
// we update the dispute
d.status = DisputeStatus::SellerRefunded;
d.update(pool).await?;
}

Add local directory for settings and database

Mostro should have a local directory where we can place settings and database files, by default mostro should look files on $HOME/.mostro/, this local directory also can be set as a mostro binary parameter like mostro --dir /absolute/path

Not allow a disputed user to create or take any orders

I think that when a user is in dispute they should not be allowed to create or take any orders until the situation be resolved, to prevent possible scammers from trying to scam several people at the same time.

Disadvantages: Those who are innocent and also involved in the dispute will lose opportunities waiting for it to be resolved.

Allows me to take an order created by myself

I can take an order created by myself, it is removed from listorders. I continued with the process to see how far it went and when I entered the invoice to receive the sats it came out "Minimal expiration time on invoice"
It shouldn't be allowed to take your own orders.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.