Giter Site home page Giter Site logo

cs01 / termpair Goto Github PK

View Code? Open in Web Editor NEW
1.6K 18.0 71.0 8.05 MB

View and control terminals from your browser with end-to-end encryption ๐Ÿ”’

License: MIT License

Makefile 0.34% Python 46.07% HTML 0.73% CSS 0.40% Dockerfile 0.34% TypeScript 51.67% JavaScript 0.44%
terminal pty python websocket fastapi xtermjs e2ee end-to-end-encryption

termpair's Introduction

View and control remote terminals from your browser with end-to-end encryption

PyPI version PyPI version

What is TermPair?

TermPair lets developers securely share and control terminals in real time.

You can try it now at https://chadsmith.dev/termpair or check out the YouTube Demo.

Features

  • Share unix terminals in real time
  • Type from the terminal or browser; both are kept in sync
  • Multiple browsers can connect simultaneously
  • Browser permissions can be read/write or read only
  • Server cannot read terminal data even if it wanted to, since it is encrypted with AES 128 bit encryption
  • Secure web environment required (https)
  • Optional static-site hosting -- build the web app yourself to ensure the integrity of the web app (example)
  • Broadcasting terminal's dimensions are sent to the browser in realtime so rendering always matches

Usage

First start the TermPair server with termpair serve, or use the one already running at https://chadsmith.dev/termpair.

The server is used to route encrypted data between terminals and connected browsers โ€” it doesn't actually start sharing any terminals on its own.

> termpair serve

Now that you have the server running, you can share your terminal by running termpair share.

This connects your terminal to the server, and allows browsers to access the terminal through the server.

> termpair share
--------------------------------------------------------------------------------
Connection established with end-to-end encryption ๐Ÿ”’

Shareable link: http://localhost:8000/?terminal_id=d58ff4eed5aa9425e944abe63214382e#g8hSgHnDaBtiWKTeH4I0Ow==

Terminal ID: d58ff4eed5aa9425e944abe63214382e
Secret encryption key: g8hSgHnDaBtiWKTeH4I0Ow==
TermPair Server URL: http://localhost:8000/

Type 'exit' or close terminal to stop sharing.
--------------------------------------------------------------------------------

The URL printed contains a unique terminal ID and encryption key. You can share the URL with whoever you like. Anyone who has it can access your terminal while the termpair share process is running, so be sure you trust the person you are sharing the link with.

By default, the process that is shared is a new process running the current shell, determined by the $SHELL evironment variable.

The server multicasts terminal output to all browsers that connect to the session.

System Requirements

Python: 3.6+

Operating Systems: Linux, macOS

Installation

Option 1: Download executable

Download from the release page. You may have to run chmod +x before you can run it.

Option 2: Install Using pipx or pip

You can install using pipx, which installs Python applications in isolated environments (recommended):

> pipx install termpair

or install with pip

> pip install termpair

Note: Make sure the TermPair server you are broadcasting to is running the same major version as the broadcasting terminal (see termpair --version).

Run With Latest Version

You can also use pipx to directly run the latest version without installing:

Serve:

> pipx run termpair serve

Then share:

> pipx run termpair share

Note: Make sure the TermPair server you are broadcasting to is running the same major version as the broadcasting terminal (see pipx run termpair --version). You can specify the version with pipx run --spec termpair==$VERSION termpair ....

Security

TermPair uses end-to-end encryption for all terminal input and output, meaning the server never has access to the raw input or output of the terminal, nor does it have access to encryption keys (other than the https connection).

The browser must be running in a secure context. This typically means running with secure http traffic (https) or on localhost.

Running Client from Source

For extra assurance the source code is secure, you can broadcast your terminal from source. See CONTRIBUTING.md for more information.

Static Hosting

TermPair supports statically serving the JavaScript web app. This way you can easily run the web app from source for extra assurance the web app is secure.

In this arrangement, you can build the TermPair web app yourself and host on your computer, or statically host on something like GitHub pages or Vercel. That way you can guarantee the server is not providing a malicious JavaScript web app since you built it from the source.

When you open the web app without a TermPair server running, you specify the Terminal ID, encryption key, and TermPair server host to connect to.

You can try it out or just see what it looks like with a GitHub page from this project, https://cs01.github.io/termpair/connect/.

See CONTRIBUTING.md for more information.

How it Works

TermPair consists of three pieces:

  1. server
  2. terminal client
  3. JavaScript web app running in browser client(s)

Server

First, the termpair server is started (termpair serve). The server acts as a router that blindly forwards encrypted data between TermPair terminal clients and connected browsers. The server listens for termpair websocket connections from unix terminal clients, and maintains a mapping to any connected browsers.

Terminal Client

When a user wants to share their terminal, they run termpair share to start the client. The TermPair client registers this session with the server, then forks and starts a psuedo-terminal (pty) with the desired process, usually a shell like bash or zsh. TermPair reads data from the pty's file descriptor as it becomes available, then writes it to ther real terminal's stdout, where it is printed like normal. However, it also encrypts this output and sends it to the server via a websocket.

Encryption

The TermPair client creates three 128 bit AES encryption keys when it starts:

  • The first is used to encrypt the terminal's output before sending it to the server.
  • The second is used by the browser before sending user input to the server.
  • The third is a "bootstrap" key used by the browser to decrypt the initial connection response from the broadcasting terminal, which contains the above two keys encrypted with this third key. The browser obtains this bootstrap key via a part of the url that the server does not have access to, or via manual user input. A public key exchange like Diffie-Hellman was not used since multiple browsers can connect to the terminal, which would increase the complexity of TermPair's codebase. Still, DH in some form may be considered in the future.

Web App

The TermPair client provides the user with a unique URL for the duration of the shaing session. That URL points to the TermPair web application (TypeScript/React) that sets up a websocket connection to receive and send the encrypted terminal data. When data is received, it is decrypted and written to a browser-based terminal.

When a user types in the browser's terminal, it is encrypted in the browser with key #2, sent to the server, forwarded from the server to the terminal, then decrypted in the terminal by TermPair. Finally, the TermPair client writes it to the pty's file descriptor, as if it were being typed directly to the terminal.

AES keys #1 and #2 get rotated after either key has sent 2^20 (1048576) messages. The AES initialization vector (IV) values increment monotonically to ensure they are never reused.

Serving with NGINX

Running behind an nginx proxy can be done with the following configuration.

The TermPair server must be started already. This is usually done as a systemd service. The port being run on must be specified in the upstream configuration.

upstream termpair_app {
  # Make sure the port matches the port you are running on
  server 127.0.0.1:8000;
}

server {
    server_name myserver.com;

    # I recommend Certbot if you don't have SSL set up
    listen 443 ssl;
    ssl_certificate fullchain.pem;
    ssl_certificate_key privkey.pem;

    location /termpair/ {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;

        proxy_pass http://termpair_app/;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Running as a systemd service

If you use systemd to manage services, here is an example configuration you can start with.

This configuration assumes you've installed TermPair to /home/$USER/.local/bin/termpair and saved the file to /etc/systemd/system/termpair.service.

# /etc/systemd/system/termpair.service

# https://www.freedesktop.org/software/systemd/man/systemd.service.html
[Unit]
Description=
After=network.target

[Service]
User=$USER
Group=www-data
WorkingDirectory=/var/www/termpair/
PermissionsStartOnly=true
ExecStart=/home/$USER/.local/bin/termpair serve --port 8000
ExecStop=
Restart=on-failure
RestartSec=1s

[Install]
WantedBy=multi-user.target

After saving, you can use systemctl to start your systemd service:

sudo systemctl daemon-reload
sudo systemctl enable termpair.service
sudo systemctl restart termpair

CLI API

> termpair --help
usage: termpair [-h] [--version] {share,serve} ...

View and control remote terminals from your browser

positional arguments:
  {share,serve}

optional arguments:
  -h, --help     show this help message and exit
  --version

To start the TermPair server:

> termpair serve --help
usage: termpair serve [-h] [--port PORT] [--host HOST] [--certfile CERTFILE]
                      [--keyfile KEYFILE]

Run termpair server to route messages between unix terminals and browsers. Run
this before connecting any clients. It is recommended to encrypt communication
by using SSL/TLS. To generate an SSL certificate and private key, run `openssl
req -newkey rsa:2048 -nodes -keyout host.key -x509 -days 365 -out host.crt`.
To skip questions and use defaults, add the `-batch` flag. You can ignore
warnings about self-signed certificates since you know you just made it. Then
use them, pass the '--certfile' and '--keyfile' arguments.

optional arguments:
  -h, --help            show this help message and exit
  --port PORT, -p PORT  Port to run the server on (default: 8000)
  --host HOST           Host to run the server on (0.0.0.0 exposes publicly)
                        (default: localhost)
  --certfile CERTFILE, -c CERTFILE
                        Path to SSL certificate file (commonly .crt extension)
                        (default: None)
  --keyfile KEYFILE, -k KEYFILE
                        Path to SSL private key .key file (commonly .key
                        extension) (default: None)

To share a terminal using the TermPair client:

> termpair share --help
usage: termpair share [-h] [--cmd CMD] [--port PORT] [--host HOST] [--read-only]
                      [--open-browser]

Share your terminal session with one or more browsers. A termpair server must be
running before using this command.

optional arguments:
  -h, --help            show this help message and exit
  --cmd CMD             The command to run in this TermPair session. Defaults to
                        the SHELL environment variable (default: /bin/bash)
  --port PORT, -p PORT  port server is running on (default: 8000)
  --host HOST           host server is running on (default: http://localhost)
  --read-only, -r       Do not allow browsers to write to the terminal (default:
                        False)
  --open-browser, -b    Open a browser tab to the terminal after you start
                        sharing (default: False)

termpair's People

Contributors

cs01 avatar dependabot[bot] avatar florimondmanca 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

termpair's Issues

Is there an option for read-only sessions?

The web interface has a green light indicating that you can type, which implies there's an alternative, but I can't find a way to control that. Is there an option along the lines of termpair share --readonly that I just haven't found?

Perhaps a useful related feature could start the session in read only mode, but a viewer could enter a password to enable typing? termpair share --readonly --password=$foo?

add unit tests

Unit tests only have a placeholder at this time. Implement some unit tests.

--version commandline parsing error

Thanks for TermPair, it's an amazing project!

Describe the bug

$ termpair --version
usage: termpair [-h] [--version] {share,serve} ...
termpair: error: the following arguments are required: command

termpair doesn't parse correctly --version expecting other arguments and options

I've installed the latest version with pip, with python 3.9

SSH Not effective

Describe the bug

hello, I want to use ssh connect to start a termpair share, but got an error:
image

How to reproduce
ssh someone@anotherhost "termpair share -r --cmd "df -Th""

Expected behavior

I want to execute some command to show in the web for someone else, how can i do it ?

add CI tests

Run tests using github actions

  • linting
  • building
  • unit/e2e tests after they're added (#48)

No module named 'websockets'

Hi Chad,

forgotten dependency:

termpair is already at latest version 0.0.1.3 (location: /home/pixuser/.local/pipx/venvs/termpair)
[pixuser@gkfedora .local]$ termpair -h
Traceback (most recent call last):
  File "/home/pixuser/.local/bin/termpair", line 5, in <module>
    from termpair.main import main
  File "/home/pixuser/.local/pipx/venvs/termpair/lib64/python3.7/site-packages/termpair/main.py", line 10, in <module>
    from . import share, server
  File "/home/pixuser/.local/pipx/venvs/termpair/lib64/python3.7/site-packages/termpair/share.py", line 21, in <module>
    import websockets  # type: ignore
ModuleNotFoundError: No module named 'websockets'
[pixuser@gkfedora .local]$

[pixuser@gkfedora .local]$ which python
/usr/bin/python
[pixuser@gkfedora .local]$ python --version
Python 3.7.9

Workaround:

pipx  runpip termpair install websockets

PS: Thanks for both ;-)

Share on `0.0.0.0` over localhost

I was just wondering is there a possibility to share on 0.0.0.0 rather than localhost to see on other machines in the same network?

add instructions to host on nginx

How would this feature be useful?

Getting set up with nginx has a few gotchas. Adding to docs will help users save time setting up their own TermPair server with SSL behind nginx.

Describe the solution you'd like

Add a section to the README.

Describe alternatives you've considered

Let users search and figure it out via trial and error.

Running termpair serve defaults to port 80 but termpair share uses port 8000?

Seems that somehow termpair share defaults to port 80 somehow

Window 1

$ pipx run termpair serve

INFO: Started server process [218]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:8000 (Press CTRL+C to quit)

Window 2

$ pipx run termpair share -b

Traceback (most recent call last):
File "/home/mark/.local/pipx/.cache/b0d871b0f692059/bin/termpair", line 8, in
sys.exit(main())
File "/home/mark/.local/pipx/.cache/b0d871b0f692059/lib/python3.8/site-packages/termpair/main.py", line 109, in main
asyncio.get_event_loop().run_until_complete(
File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "/home/mark/.local/pipx/.cache/b0d871b0f692059/lib/python3.8/site-packages/termpair/share.py", line 231, in broadcast_terminal
async with websockets.connect(ws_endpoint, ssl=ssl_context) as ws:
File "/home/mark/.local/pipx/.cache/b0d871b0f692059/lib/python3.8/site-packages/websockets/client.py", line 517, in aenter
return await self
File "/home/mark/.local/pipx/.cache/b0d871b0f692059/lib/python3.8/site-packages/websockets/client.py", line 535, in await_impl
transport, protocol = await self._create_connection()
File "/usr/lib/python3.8/asyncio/base_events.py", line 1025, in create_connection
raise exceptions[0]
File "/usr/lib/python3.8/asyncio/base_events.py", line 1010, in create_connection
sock = await self._connect_sock(
File "/usr/lib/python3.8/asyncio/base_events.py", line 924, in _connect_sock
await self.sock_connect(sock, address)
File "/usr/lib/python3.8/asyncio/selector_events.py", line 494, in sock_connect
return await fut
File "/usr/lib/python3.8/asyncio/selector_events.py", line 526, in _sock_connect_cb
raise OSError(err, f'Connect call failed {address}')
ConnectionRefusedError: [Errno 111] Connect call failed ('127.0.0.1', 80)

use -p 8000 for both commands fixes the issue, but it makes me wonder if this is a bug or -p should be mandatory?

same issue after running pipx install termpair (so it's not to do with using pipx run)

$ pipx runpip termpair freeze

aiofiles==0.5.0
cffi==1.14.3
click==7.1.2
cryptography==3.1.1
fastapi==0.61.1
h11==0.11.0
pkg-resources==0.0.0
pycparser==2.20
pydantic==1.6.1
six==1.15.0
starlette==0.13.6
termpair==0.1.0.2
uvicorn==0.12.1
websockets==8.1

Unable to connect to session if not served by localhost

Hi,

if I start the server with termpair serve --host 192.168.x.x --port 8000 it successfully binds to the correct interface.

The shared session is successful initiated with termpair share --host "http://192.168.x.x/" --port 8000

Browser opens session and server sends HTTP 200 ok (304 for png & js) for every request but the session is always in "connection-pending" state.

If served/shared using localhost instead, it works without issue.

Do I miss something here? Any help appreciated.

add screencast

To better illustrate how termpair works, add a gif to the README showing how it's set up, and what it looks like when it runs.

Also create a YouTube video demonstrating installation and use.

convert to TypeScript

How would this feature be useful?

The codebase will be easier to work with, and bugs will be caught earlier

display version in browser UI

How would this feature be useful?

Users will know which version of TermPair is being served.

Describe the solution you'd like

Display in subtle text at bottom of window the version number, and possibly the git hash

Describe alternatives you've considered

N/A

Cryptographic suggestions

Termpair looks awesome and personally I am excited to start using it! I came to it via a thread on HN ... https://news.ycombinator.com/item?id=27338479 . That thread also includes some dissection of the cryptography in TermPair, some of the thoughts there are good, and some are off base. This issue is a summary of my own observations/suggestions in case they are interesting and useful:

  1. Termpair could use a key agreement protocol. Termpair is designed to avoid revealing keys to the server by encoding the key in the anchor fragment of the URL, which a server never sees. This is clever, but a server could still MITM by serving its own malicious HTML. It's actually very possible for two endpoints to share a key in a MITM proof way ... use a key agreement protocol such as ECDH. It's somewhat magical, but they can exchange material and agree a key without anyone in the middle ever being able to figure out what it is. There's some details to get right, like it's useful to have the ECDH be signed and verified by the parties involved, but it's all pretty straightforward.

  2. Keystroke timing is a practical attack. One of the perils of interactive terminal type applications is that human keystrokes can be timed and this timing can reveal what keys are being pressed. It's important to make sure that when a terminal is line-buffered, that the keys aren't sent one by one, but that the whole buffered line is encrypted and sent in one pass. Password inputs (like ssh and sudo) are expected to use line-buffering to mitigate keystroke timing issues. Additionally, it's a good idea to pad a line-buffered input up to a fixed size - this avoid leaking the length of the password entered.

  3. AES-GCM can safely use counters as nonces. This came from the HN thread, but they have a point; it's ok, and actually better to use the message count as the nonce for AES-GCM and it avoids the small odds of collisions occurring.

  4. It's good to use different keys in different directions. It's also a good idea to use a different encryption key in the direction of A to B than from B to A. These keys can be derived from a shared secret using HKDF.

Some of these may be bewildering, but I'm happy to answer questions. Again, Termpair looks awesome and I don't mean these suggestions as some kind of ding!

Secret encryption key is not valid

Describe the bug
I follow the instruction to run the programe , but at the last step , it says : secret encryption key is not valid ...
could you give me some advise?

image

Improve error message if server is not running

Traceback (most recent call last):
File "/Users/david/.local/bin/termpair", line 8, in
sys.exit(main())
File "/Users/david/.local/pipx/venvs/termpair/lib/python3.9/site-packages/termpair/main.py", line 109, in main
asyncio.get_event_loop().run_until_complete(
File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/Users/david/.local/pipx/venvs/termpair/lib/python3.9/site-packages/termpair/share.py", line 231, in broadcast_terminal
async with websockets.connect(ws_endpoint, ssl=ssl_context) as ws:
File "/Users/david/.local/pipx/venvs/termpair/lib/python3.9/site-packages/websockets/client.py", line 517, in aenter
return await self
File "/Users/david/.local/pipx/venvs/termpair/lib/python3.9/site-packages/websockets/client.py", line 535, in await_impl
transport, protocol = await self._create_connection()
File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 1064, in create_connection
raise OSError('Multiple exceptions: {}'.format(
OSError: Multiple exceptions: [Errno 61] Connect call failed ('::1', 8000, 0, 0), [Errno 61] Connect call failed ('127.0.0.1', 8000)

add end-to-end encryption

Right now data is viewable by the server. Add encryption/decryption in the client and in the browser so that the only data the server receives is already encrypted.

Trying to run termpair on LAN with SSL

Describe the bug
Trying to launch termpair on on LAN (192.168.31.234 on my domestic network), not localhost (127.0.0.1) finishes with message on browser, that i can not use non-secure connection:

termpair serve -p 8000 --host 192.168.31.234

and on sharing window:

termpair share -p 8000 --host 192.168.31.234

When opening browser with link (which is HTTP) I get from sharing terminal I see next message:

image

I decided to run it on LAN, with --keyfile and --certificate options to make secure connection (it is going to set up "HTTPS", right?). So I created SSL certificate with openssl library. The key, certificate are located in /etc/httpd/httpscertificate/ folder . But when I try command:

termpair serve -p 8000 --host 192.168.31.234 --certfile /etc/httpd/httpscertificate/192.168.31.234.crt --keyfile /etc/httpd/httpscertificate/192.168.31.234.key

I receive an error:

TermPair encountered an error. If you think this is a bug, it can be reported at https://github.com/cs01/termpair/issues

Traceback (most recent call last): File "/home/ooolledj/.local/lib/python3.8/site-packages/termpair/main.py", line 140, in main run_command(args) File "/home/ooolledj/.local/lib/python3.8/site-packages/termpair/main.py", line 124, in run_command uvicorn.run( File "/home/ooolledj/.local/lib/python3.8/site-packages/uvicorn/main.py", line 393, in run server.run() File "/home/ooolledj/.local/lib/python3.8/site-packages/uvicorn/server.py", line 50, in run loop.run_until_complete(self.serve(sockets=sockets)) File "uvloop/loop.pyx", line 1494, in uvloop.loop.Loop.run_until_complete File "/home/ooolledj/.local/lib/python3.8/site-packages/uvicorn/server.py", line 57, in serve config.load() File "/home/ooolledj/.local/lib/python3.8/site-packages/uvicorn/config.py", line 284, in load self.ssl = create_ssl_context( File "/home/ooolledj/.local/lib/python3.8/site-packages/uvicorn/config.py", line 115, in create_ssl_context ctx.load_cert_chain(certfile, keyfile, get_password) PermissionError: [Errno 13] Permission denied

Sudo command does not help with it

sudo: termpair: command not found

I created RSA key and SSL certificate with this guide:
https://www.rosehosting.com/blog/how-to-generate-a-self-signed-ssl-certificate-on-linux/
Then I just set path to files them with --keyfile and --certfile options in termpair serve.

Expected behavior
It should accept my .key and .crt files and run termpair on LAN ip-address, which with I can use termpair share for example on my mobile phone and see and type commands

I FOUND THE SOLUTION. UPDATE:
I thought it happens because i can not input certificate password and it does not let me use It. Truly, while writing report I tried to change access to .key file:

sudo chmod a+r /etc/httpd/httpscertificate/192.168.31.234.key

After that all my termpair commands with serve, share and --keyfile, --certfile options run perfectly (you can see HTTPS connection is established):

bkm

Now the question: how can I protect my .key file from unauthorized access and still be available to run termpair on HTTPS without using chmod a+r on .key file?

About ModuleNotFoundError in windows

When I'm using this project in windows ,I found that it cannot work because of there is no module "termios" supported in Windows and I have no idea on how to fix the problem. Is there anyone can make a fix on Windows? It's really helpful on changing it to useful on Windows.Thanks!!!

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.