# What is TinyChain?

A brief overview

TinyChain is a single platform for cloud applications.

TinyChain provides a basic set of cloud data structures, like tables, tensors, and a graph database, as well as security using OAuth 2 and elliptic-key cryptography, in a single runtime that enables developers to prototype a new API in minutes and scale it without rewriting.

Some key features are:

* Automatic cross-service transactions
* Automatic replication
* Automatic concurrency (multithreading)
* Automatic GPU acceleration
* Stateful machine learning models with automatic memory management, even for large datasets
* GPU-accelerated graph database
* Hypothetical queries, which allow a developer to explore the consequences of a database update without committing the update
* Object-relational mapping

## Getting Started

### Guides: Jump right in

Follow our handy guides to get started on the basics as quickly as possible:

{% content-ref url="/pages/GhUlYKIfy1GNJwAnTlF1" %}
[Getting Started](/guides/getting-started)
{% endcontent-ref %}

{% content-ref url="/pages/oMr6pms8LIsrJzwcIsrn" %}
[Python client introduction](/guides/python-client-introduction)
{% endcontent-ref %}

{% content-ref url="/pages/VCsyUX1UcwDa1FfnEvLd" %}
[Code a compute graph](/guides/code-a-compute-graph)
{% endcontent-ref %}

### Fundamentals: Dive a little deeper

Learn the fundamentals of TinyChain to get a deeper understanding of its functionality:

{% content-ref url="/pages/9V5iT402OdO5liUbarJI" %}
[Install TinyChain](/fundamentals/install-tinychain)
{% endcontent-ref %}

{% content-ref url="/pages/wE7W8Y4ydmyI147g2VC7" %}
[Technical Details](/fundamentals/technical-details)
{% endcontent-ref %}


# Getting Started

## Hello, World!

TinyChain has one of the simplest imaginable "Hello, World!" programs— just open this link in your browser: [http://demo.tinychain.net/state/scalar/value/string?key="Hello, World!"](http://demo.tinychain.net/state/scalar/value/string?key=%22Hello,%20World!%22)

Some things to notice: TinyChain has four types of `Op`: `GET`, `PUT`, `POST`, and `DELETE`. These correspond to [HTTP methods ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). Every TinyChain `Op` is accessible over HTTP using the corresponding request method.

TinyChain supports object-oriented programming (OOP), including class inheritance. You can see the superclasses of a TinyChain `String` in its classpath: `/state/scalar/string`. So, `String` is a subclass of `Value`, which is a subclass of `Scalar`, which is a subclass of `State`, which is the parent of every TinyChain class. Any TinyChain `Value` can be constructed by `GET`ting its class.

## Run a TinyChain host

The TinyChain host at [demo.tinychain.net](http://demo.tinychain.net) is provided for convenience, but it may be slow due to the volume of requests it receives, and it may not always have the latest updates and bugfixes. For the best experience, you should run your own host. This is easy to do with Docker:

```bash
# the "docker build" command will output an image ID like "c0c251e99ef1"
docker build https://github.com/haydnv/tinychain.git

# paste the image ID into this command
docker run -it -p 8702:8702/tcp IMAGE_ID ./tinychain --data_dir=/tmp/data
                    
```

Check that it's working by running your own "Hello, World!" program: [http://127.0.0.1:8702/state/scalar/value/string?key="Hello, World!"](http://127.0.0.1:8702/state/scalar/value/string?key=%22Hello,%20World!%22)


# Python client introduction

Python API documentation: https\://tinychain.readthedocs.io/

## Install the client

The easy way to install TinyChain's Python client is using Pip:

```bash
# on newer operating systems this command may be "pip" instead of "pip3"
pip3 install tinychain
```

## Hello, World!

You can verify that you've installed the Python client correctly by running this Hello, World! program:

```python
import tinychain as tc

# This host is available for demonstration purposes, but may be slow
# due to the volume of requests it receives.
HOST = tc.host.Host("http://demo.tinychain.net")

# This endpoint will attempt to execute resolve whatever State it receives,
# without committing any write operations.
ENDPOINT = "/transact/hypothetical"

@tc.get_op
def hello(name: tc.String):
    return tc.String("Hello, {{name}}!").render(name=name)

if __name__ == "__main__":
    cxt = tc.Context()
    cxt.hello = hello
    cxt.result = cxt.hello("World")
    print(HOST.post(ENDPOINT, cxt))
```

If you have any trouble with the host at demo.tinychain.net, you can run your own host by following the steps in [Install TinyChain](/fundamentals/install-tinychain).


# Types and casting

TinyChain's Python client uses type declarations in an unusual way. Here's what to expect.

```python
import os
import tinychain as tc

# use the demo host at demo.tinychain.net, unless overridden by the environment variable `TC_HOST`
HOST = tc.host.Host(os.getenv("TC_HOST", "http://demo.tinychain.net"))
# this endpoint will attempt to resolve whatever state you send it, without committing any changes
ENDPOINT = "/transact/hypothetical"

# this assumes that `x` is of type `tc.tensor.Tensor`
def average(x):
    return x.sum() / x.size

if __name__ == "__main__":
    cxt = tc.Context()  # initialize a new Op context
    cxt.x = tc.tensor.Dense.ones([3])  # initialize a new Dense tensor
    cxt.result = average(cxt.x)  # call our custom function

    actual = HOST.post(ENDPOINT, cxt)  # execute the `Op` defined by `cxt`
    assert actual == 1  # verify the result
```

The `average` function in this example works well enough, but in a public library it might not handle every situation it should. For example, if a user calls `average(tc.URI("$x"))`, the `sum` method and `size` property won't be available because a `URI` doesn't have a `sum` method or `size` property. To handle cases like this, we can use TinyChain's built-in reflection annotations:

```python
# ...
 
# the `post_op` annotation tells TinyChain to reflect over this function
# and assume that it defines a POST Op 
@tc.post_op
# the annotation on `x` tells TinyChain to expect a `Tensor`
def average(x: tc.tensor.Tensor) -> tc.Number:
    # the return annotation tells the calling code to expect a `Number`
    return x.sum() / x.size

if __name__ == "__main__":
    cxt = tc.Context()  # initialize a new Op context
    # `average` is now a TinyChain `Op`, not a native Python function,
    # so it needs to be made addressable by the calling context
    cxt.average = average
    cxt.x = tc.tensor.Dense.ones([3])  # initialize a new Dense tensor
    cxt.result = cxt.average(x=tc.URI("$x"))  # call our custom `Op`
    actual = HOST.post(ENDPOINT, cxt)  # execute the `Op` defined by `cxt`
    assert actual == 1  # verify the result

```

Now, even though the type of `x` in the calling code is a `URI`, the `average` Op still works as the caller expects because the type annotations tell the code in `def average` what to expect.

{% hint style="warning" %}
**Important!** In the TinyChain Python client, types define *what to expect.* They don't necessarily instantiate or cast types, like they do in native Python code.
{% endhint %}

To illustrate this, let's change the return type of `average` to a `String`:

{% hint style="danger" %}

```python
# ...

@tc.post_op
def average(x: tc.tensor.Tensor) -> tc.String:  # <-- return type `String`
    return x.sum() / x.size

if __name__ == "__main__":
    # ...
    actual = HOST.post(ENDPOINT, cxt)
    assert actual == "1"  # this assertion fails!
```

{% endhint %}

Now, the return annotation `tc.String` tells the calling code to expect a `String` but the `Op` still actually returns a `Number`, so the assert fails. To fix this, we'll have to explicitly cast the return value:

```python
# ...

@tc.post_op
def average(x: tc.tensor.Tensor) -> tc.String:
    avg = x.sum() / x.size
    return avg.cast(tc.String)  # explicitly cast `avg` to a `String`

if __name__ == "__main__":
    # ...
    actual = HOST.post(ENDPOINT, cxt)
    assert actual == "1"  # assert that the result is in fact a string

```


# Anatomy of an Op

Understand when and how to use Contexts and type annotations

## Type annotations

```python
import tinychain as tc

# This is a native Python function.
# It doesn't need type annotations, although you can add them if you want,
# for example if they make the code more clear,
# or if you're using a linter which requires them.
#
# Since this is a native Python function, TinyChain code can only call it
# at compile-time.
def example1(b):
    a = 2
    return b + a, b * a

# This is a TinyChain Op. It can be evaluated at run-time.
# Every TinyChain Op has its own Context, which you can access
# by using the reserved names "cxt" or "txn" as the first argument of the Op.
#
# Since this is meant to be called at run-time, TinyChain doesn't know
# at compile-time what type of arguments to expect. So an Op needs
# type annotations in order to provide the correct API when compiled to JSON.
#
# For example, if you leave out the tc.Number annotation, TinyChain will
# provide a generic tc.State when this Op is compiled, and you'll get an
# error that says that tc.State doesn't support the "+" or "*" operators.
@tc.get_op
def example2(cxt, b: tc.Number) -> tc.Tuple:
    cxt.a = 2
    return b + cxt.a, b * cxt.a
```

Notice the type annotation `Number` on the parameter `b`. The actual value of parameter `b` may not be known when `example` is encoded as part of a graph configuration, so the type annotation is necessary in order to provide the correct API methods from inside the Python function definition. For example, if someone calls `example(URI("http://example.com/numeric_constant"))`, this is perfectly valid code, but it doesn't explicitly provide the type of the `Value` at `http://example.com/numeric_constant`.

Likewise, the return type annotation `Tuple` is provided for the benefit of the calling code. Without it, TinyChain would not know what type of `State` to return at encoding time (when the compute graph configuration is generated) and helper methods like `unpack` would be unavailable.

## The Op Context

The `cxt` parameter in the `example` function above is a `Context` object. It allows you to explicitly name the states which define your `Op`—in this case, there is one state called `b` passed in as an argument and one state called `a` defined in the body of the `Op`. TensorFlow users should find this familiar because it provides the same functionality as the `name_scope` function and `name=` keyword argument in TensorFlow.

{% hint style="danger" %}
Developers new to TinyChain sometimes find it confusing that the name "cxt" is used to refer to many different, independent contexts. Consider this (non-working!) example:

```python
import tinychain as tc

@tc.get_op
def my_constant() -> tc.Int:
    return 2

@tc.get_op
def example(cxt):
    return cxt.my_constant() * 5  # this is where the error happens!

if __name__ == "__main__":
    cxt = tc.Context()
    cxt.my_constant = my_constant
    cxt.example = example

    # note: here's a run-time call to the example op
    cxt.product = cxt.example()
```

This won't work! Calling `example` in this case will raise a `NotFoundException` because `example` has a *completely different and independent* `Op` context. You can verify this yourself by calling`tc.print_json(cxt)`from within the `example` function.

This should make intuitive sense. If `example` and `my_context` were hosted on different servers, would it make sense for the one to be able to access the other's internal state? What about from a security perspective?
{% endhint %}

## Automatic concurrency

Let's take another look at the `example` function:

```
@tc.get_op
def example(cxt, b: tc.Number) -> tc.Int:
    cxt.a = 2
    return cxt.a + b, cxt.a * b
```

The Python interpreter is imperative and single-threaded, so as a Python developer you're probably accustomed to the assumption that each line of your code will execute in exactly the order that it's written. TinyChain, however, is multi-threaded and features automatic concurrency (like TensorFlow). So, in the example above, `cxt.a + b` and `cxt.a * b` both execute simultaneously when a user executes your compute graph.

Automatic concurrency is crucial for a performant distributed runtime, but it comes with some trade-offs. For example, `Value`s are immutable:

{% hint style="danger" %}

```
@tc.get_op
def example(cxt, b: tc.Number) -> tc.Int:
    cxt.a = 1
    cxt.a += b  # this is where the error happens!
    return cxt.a
```

This won't work! If it did, it would be impossible for TinyChain to calculate the dependencies of each state in the `Op` context, and thus impossible to resolve independent states concurrently.
{% endhint %}

Only `Collections` like a `BTree`, `Table`, or `Tensor` are mutable. This introduces the need to handle *side-effects* with the `After` flow control, which we'll cover in the next section.


# Flow control: after, cond, while\_loop

## cond

When using Python to develop a TinyChain service, it’s important to remember that the output of your code is a compute graph which will be served by a TinyChain host; your Python code itself won’t be running in production. This means that you can’t use Python control flow operators like `if` or `while` the way that you’re used to. For example:

```python
@tc.get_op
def to_feet(txn, meters: tc.Number) -> tc.Number:
    # IMPORTANT! don't use Python's if statement! use tc.cond!
    return tc.cond(
        meters >= 0,
        meters * 3.28,
        tc.error.BadRequest("negative distance is not supported"))
```

## after

It’s also important to keep in mind that TinyChain by default resolves all dependencies concurrently, and does not resolve unused dependencies. Consider this function:

```python
@tc.post_op
def num_rows(txn):
    max_len = 100
    schema = tc.table.Schema(
        [tc.Column("user_id", tc.Number)],
        [tc.Column("name", tc.String, max_len), tc.Column("email", tc.String, max_len)])

    txn.table = tc.table.Table(schema)
    txn.table.insert((123,), ("Bob", "bob.roberts@example.com"))
    return txn.table.count()
```

This Op will *always* resolve to *zero*. This may seem counterintuitive at first, because you can obviously see the `table.insert` statement, but notice that the return value `table.count` does not actually depend on `table.insert`; `table.insert` is only intended to create a side-effect, so its result is unused. To handle situations like this, use the `after` flow control:

```python
@tc.post_op
def num_rows(txn):
    max_len = 100
    schema = tc.schema.Table(
        [tc.Column("user_id", tc.Number)],
        [tc.Column("name", tc.String, max_len), tc.Column("email", tc.String, max_len)])

    txn.table = tc.Table(schema)
    return tc.after(
        txn.table.insert((123,), ("Bob", "bob.roberts@example.com")),
        txn.table.count())
```

Now, since the program explicitly indicates that `table.count` depends on a side-effect of `table.insert`, TinyChain won’t execute `table.count` until after the call to `table.insert` has completed successfully.

## while\_loop

Loops are probably the most difficult part of TinyChain to get used to if you've never used a graph runtime before.

Consider this simple while loop:

```python
i = 0
while i < 10:
    i += 1
```

In a TinyChain compute graph, you have to account for the facts that a) you need the loop to run at execution time (when a user executes your graph), not encoding time (when the TinyChain Python client exports your graph configuration as JSON), and b) TinyChain `Value`s are immutable:

```python
import tinychain as tc

@tc.get_op
def loop(until: tc.Number) -> tc.Int:
    # the closure decorator captures referenced states from the outer scope,
    # in this case "until"
    @tc.closure
    @tc.post_op
    def cond(i: tc.Int):
        return i < until

    @tc.post_op
    def step(i: tc.Int) -> tc.Int:
        return tc.Map(i=i + 1)  # here we return the new state of the loop

    initial_state = tc.Map(i=0)  # here we set the initial state of the loop

    # return the loop itself
    return tc.while_loop(cond, step, initial_state)
```

## Nested conditionals

Consider this example:

{% hint style="danger" %}

```python
@tc.post_op
def maybe_delete(table: tc.table.Table, should_update: tc.Bool):
    a = tc.cond(should_update,
        table.update({"column": "value"}),
        table.delete())

    # this won't work!
    return tc.cond(table.is_empty(), tc.BadRequest("empty table"), a)
```

{% endhint %}

In this case, TinyChain is unable to resolve the dependencies of the state to `return` without executing *both* branches of `a`, which would make `a` no longer a conditional (the `table` would be deleted!). For this reason, nested conditionals are not allowed. The most foolproof way to handle a nested conditional is to use an `Op`:

{% hint style="success" %}

```python
@tc.post_op
def maybe_delete(table: tc.table.Table, should_update: tc.Bool):
    return tc.cond(should_update,
        table.update({"column": "value"}),
        table.delete())

@tc.post_op
def check_result(table: tc.table.Table, should_update: tc.Bool):
    return tc.cond(table.is_empty(),
        tc.error.BadRequest("empty table"),
        maybe_delete(table, should_update))
```

{% endhint %}

## Examples: updating a Tensor conditionally

For more detailed examples on how to use common flow controls, take a look at the [client tests](https://github.com/haydnv/tinychain/tree/main/tests/tctest/client).


# Closures and functional programming

How to use the @closure decorator for stream processing

TinyChain lets developers write code that mostly looks like regular Python, but executes in a distributed concurrent runtime. One important difference comes up when iterating over a `Map`, `Tuple`, or `Stream` (analogous to a Python `dict`, `tuple`, or generator). TinyChain handles these cases using [functional programming](https://en.wikipedia.org/wiki/Functional_programming) with the `filter`, `fold`, `for_each`, and `map` methods. An easy example is using `map` to create a new `Tuple` based on an existing `Tuple`:

```python
import os
import tinychain as tc

HOST = tc.host.Host(os.getenv("TC_HOST", "http://127.0.0.1:8702"))
ENDPOINT = "/transact/hypothetical"

# initialize a new execution context
cxt = tc.Context()

# instantiate a Tuple
cxt.tuple = tc.Tuple([1, 2, 3])

@tc.get_op
def pow(x: tc.Number):
    return x**2

# create a new Tuple by squaring the elements in `cxt.Tuple`
cxt.raised = cxt.tuple.map(pow)

if __name__ == "__main__":
    # check that the implementation works as expected
    assert HOST.post(ENDPOINT, cxt) == [1, 4, 9]
```

Often in these cases it's necessary to reference some state in the calling context in the function applied to the stream. You can do this using a closure:

```python
# ...
cxt.tuple = tc.Tuple([1, 2, 3])
cxt.exponent = 2

# capture `cxt.exponent` from the outer context
@tc.closure(cxt.exponent)
@tc.get_op
def pow(x: tc.Number):
    return x**cxt.exponent

cxt.raised = cxt.tuple.map(pow)
# ...
```

`Collection` types like `Table` and `Tensor` all support copying from and into a `Stream`. For example, you can create a `Tensor` by reading fields from a `Table`:

```python
# ...
# initialize a new execution context
cxt = tc.Context()

# initialize a new table
key = [tc.Column("order_id", tc.U64)]
values = [tc.Column("price", tc.U64)]
schema = tc.table.Schema(key, values)
cxt.table = tc.table.Table(schema)

# add a row
cxt.place_order = cxt.table.insert([1], [499])

# convert to a tensor
schema = [[1], tc.U64]
cxt.prices = tc.tensor.Dense.copy_from(schema, cxt.table.select(["price"]).rows())
cxt.result = tc.After(cxt.place_order, cxt.prices)

if __name__ == "__main__":
    # check that the implementation works as expected
    print(HOST.post(ENDPOINT, cxt))
```

Another common use-case for `Stream` is in place of a `for` loop. For example:

```python
cxt = tc.Context()
cxt.tensor = tc.tensor.Dense.constant([3], 1)

@tc.closure(cxt.tensor)
@tc.get_op
def pow(i: tc.UInt):
    return cxt.tensor[i].write(i**2)

cxt.update = tc.After(tc.Stream.range(cxt.tensor.size).for_each(pow), cxt.tensor)
```

### Examples

You can find more complex examples of functional programming in the codebase:

* the [Tensor client tests](https://github.com/haydnv/tinychain/blob/main/tests/tctest/client/test_tensor.py)
* [Table](https://github.com/haydnv/tinychain/blob/main/client/tinychain/collection/table.py)


# Install a Library or Service

Easily install TinyChain services on your host or cluster without restarting

Arguably TinyChain's most powerful feature is the ability to install a `Library` or `Service` at runtime without restarting. For example, TinyChain's linear algebra and machine learning services are distributed as part of the Python client (`math.linalg.LinearAlgebra`, `ml.NeuralNets`, and `ml.Optimizers`). Installing them is as easy as:

```python
import tinychain as tc

# the lead replica (can be a load balancer)
LEAD = tc.URI("http://your.tinychain.cluster")

host = tc.Host(LEAD)
host.install(tc.math.linalg.LinearAlgebra())

```

## Install your Service

Of course the built-in `Service`s aren't much use without being able to install your own!&#x20;

```python
# ...

class MyService(tc.service.Service):
    NS = "my_namespace"
    NAME = "my_service"
    VERSION = tc.Version("0.0.0")

    __uri__ = tc.service.service_uri(LEAD, NS, NAME, VERSION)

    @tc.get
    def hello(self, name: tc.String) -> tc.String:
        return tc.String("Hello, {{name}}!").render(name=name)


assert tc.URI(MyService).path() == "/service/my_namespace/my_service/0.0.0"

host.install(MyService())

assert host.get(tc.URI(MyService()).path() + "/hello", "World") == "Hello, World!"
```


# Code a compute graph

## Code

Let's now try writing our own function. This assumes you have a running TinyChain host, following the instructions in [Getting Started](/guides/getting-started):

```python
# new file: test.py

import tinychain as tc

# this is the local TinyChain host you just started with "docker run..."
HOST = tc.host.Host("http://127.0.0.1:8702")

# this endpoint will attempt to run whatever program you send it
# without committing any write operations
ENDPOINT = "/transact/hypothetical"

# define a GET Op
# the type annotations are important!
# without them, TinyChain doesn't know what type to expect the arguments to be
@tc.get_op
def hello(name: tc.String) -> tc.String:
    return tc.String("Hello, {{name}}").render(name=name)

if __name__ == "__main__":
    cxt = tc.Context() # construct a new execution context
    cxt.hello = hello # include our function definition in the context
    cxt.result = cxt.hello("Name") # call the function

    print(HOST.post(ENDPOINT, cxt)) # evaluate the context on the host
                    
```

## Test

Check that it works as expected:

```bash
$ python3 test.py
Hello, Name         
```

This requires some scaffolding in order to set up but it's a very convenient starting point for more advanced use cases.

## Understand

If you're curious about how this works under the hood, you can inspect the JSON that gets sent to the host:

```python
# ...
tc.print_json(cxt)  
```

You should see something like:

```javascript
[
    [
        "hello",
        {
            "/state/scalar/op/get": [
                "name",
                [
                    [
                        "String_7f8cf85223d0",
                        "Hello, {{name}}"
                    ],
                    [
                        "_return",
                        {
                            "$String_7f8cf85223d0/render": {
                                "name": {
                                    "$name": []
                                }
                            }
                        }
                    ]
                ]
            ]
        }
    ],
    [
        "result",
        {
            "$hello": [
                "Name"
            ]
        }
    ]
]
                    
```

In general you shouldn't have to worry about what the generated JSON looks like, but it can be convenient to give explicit names to the internal states of an `Op`. You can do this by using the `cxt` or `txn` keyword—just like Python treats the "self" keyword specially in a method declaration, assigning it a reference to the method's instance, TinyChain treats the `cxt` or `txn` keyword specially, assigning it a reference to the `Op`'s execution context. For example:

```python
# ...
@tc.get_op
def hello(cxt, name):
    cxt.template = tc.String("Hello, {{name}}")
    return cxt.template.render(name=name)
# ...
                    
```

If you again inspect the JSON representation of your compute graph, you'll see that the anonymous "String..." name has been replaced by "template."


# Debugging

How to debug TinyChain code

There is an interactive debugger planned but the current debugging experience relies entirely on naming and logging. Consider this example:

{% hint style="danger" %}

```python
import tinychain as tc

@tc.get_op
def add(a: tc.Int):
    b = tc.Int(2) + tc.Int("foo")
    return a + b

if __name__ == "__main__":
    cxt = tc.Context()
    cxt.op = add
    cxt.result = cxt.op(2)

    host = tc.host.Host("http://demo.tinychain.net")
    print(host.post("/transact/hypothetical", cxt))
```

{% endhint %}

Running this produces a `BadRequest`. This program is small enough that it's easy to search for "foo," but in larger programs the auto-generated names for intermediate variables can be very unhelpful when debugging.

### Step 1: assign descriptive names

&#x20;The first step to take in situations like this is to explicitly assign names in the `Op` context:

{% hint style="danger" %}

```python
# ...

@tc.get_op
def add(cxt, a: tc.Int):
    cxt.two = tc.Int(2)
    cxt.foo = tc.Int("foo")
    cxt.b = cxt.two + cxt.foo
    return a + cxt.b

# ...
```

{% endhint %}

Running this produces a slightly more helpful error message: `{'/error/bad_request': 'while resolving result: while resolving b: not a Number: foo'}`. Now you can at least tell that the error is happening with TinyChain tries to resolve state `b`.

Another reason why it's important to assign names to the individual states in an `Op` is to make sure that each step of your program is only executed once. For example:

```python
# ...

@tc.post_op
def increment(x: tc.tensor.Tensor):
    update = x.write(x + 1)
    return update, update # `x` will be incremented TWICE!

# ...
```

In the case above `updated` is a flow control, not a `State`, so it will be executed again every time it's referenced. Assigning a name solves this problem:

```python
# ...

@tc.post_op
def increment(cxt, x: tc.tensor.Tensor):
    cxt.update = x.write(x + 1)
    return cxt.update, cxt.update # `x` will be incremented ONLY ONCE!

# ...
```

In this case, we've assigned the result of `x.write(x + 1)` the name `update`, so each reference to `cxt.update` will resolve the *result* of updating x, not the update op itself.

### Step 2: add validation

The simplest way to inspect the state of a running TinyChain program is to add validation checks. For example:

```python
# ...

@tc.post_op
def percentages(x: tc.Tensor) -> tc.Number:
    return x / x.sum()

# ...
```

In this case you'll get a divide-by-zero error if `x` is zero-valued, but it may be confusing in the context of a larger program because you won't necessarily know where the error is happening. To get more information, you can add validation checks to your code:

```python
# ...
ERR_ZERO_PERCENT = tc.String("error in percentages: tensor sums to {{sum}}")

@tc.post_op
def percentages(cxt, x: tc.tensor.Tensor) -> tc.Number:
    # assign a name to `x.sum()` to make sure it will only be calculated once
    cxt.sum = x.sum()
    return tc.cond(cxt.sum == 0,
        tc.error.BadRequest(ERR_ZERO_PERCENT.render(sum=cxt.sum)),
        x / cxt.sum)
# ...
```

### Step 3: turn on logging

In larger programs, it may unclear which `Op` you need to inspect to begin with. To make this determination, you can turn on debug logging:

{% hint style="danger" %}

```python
import logging
import tinychain as tc

@tc.get_op
def add(cxt, a: tc.Int):
    b = tc.Int(2) + tc.Int("foo")
    return a + b

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG) # turn on debug logging
    # ...
```

{% endhint %}

With debug logging on, you should see some helpful debug output when you run your test script:

```
$ python3 test.py 
DEBUG:root:auto-assigning name result_subject_subject to Int(2) in execution context with data []
DEBUG:root:auto-assigning name result_subject to Int(GET Op ref $result_subject_subject/add (Int(foo),)) in execution context with data ['result_subject_subject']
# ...
```

As long as you have at least a few descriptive names explicitly set, this should give you a good idea where to look for more fine-grained debugging. You can also explicitly use `logging.debug` in your code to get information about the state of the program at compile-time.

### Step 4: inspect program code as JSON

If steps 1-3 don't help, or you suspect you may have found a bug in TinyChain itself, the final step to try on your own is to inspect the compiled JSON that's actually executed by a TinyChain host.

{% hint style="danger" %}

```python
# ...

if __name__ == "__main__":
    cxt = tc.Context()
    cxt.op = add
    cxt.result = cxt.op(2)
    
    # compile `cxt` to a JSON-encodable representation
    json_encodable = tc.to_json(cxt)
    tc.print_json(json_encodable)  # pretty-print JSON to stdout

    host = tc.host.Host("http://demo.tinychain.net")

    # important! make sure to send the same JSON to the host,
    # so the auto-generated names will be the same
    print(host.post("/transact/hypothetical", json_encodable))
```

{% endhint %}

When you run this, you'll see the structure of your program represented as JSON printed to stdout:

```json
[
    [
        "op",
        {
            "/state/scalar/op/get": [
                "a",
                [
                    [
                        "Int_7f7ee5844950",
                        2
                    ],
                    [
                        "Int_7f7ee49dec70",
                        {
                            "$Int_7f7ee5844950/add": [
                                "foo"
                            ]
                        }
                    ],
                    [
                        "_return",
                        {
                            "$a/add": [
                                {
                                    "$Int_7f7ee49dec70": []
                                }
                            ]
                        }
                    ]
                ]
            ]
        }
    ],
    [
        "result",
        {
            "$op": [
                2
            ]
        }
    ]
]
```

See [technical details](/fundamentals/technical-details#data-description) for the specification of the subset of JSON which TinyChain uses as an application language.

### Step 5: ask for help

If steps 1-4 didn't solve your problem, please email <support@tinychain.net> or [open an issue](https://github.com/haydnv/tinychain/issues/new)!

### Step 6: run the host in debug mode

The TinyChain host supports extensive debug logging, but for performance reasons it must be compiled in debug mode in order to enable this logging. If you're running TinyChain in a Docker container, you'll have to open an interactive terminal:

```bash
# the `-it` flag tells Docker to open an interactive terminal
docker run -it <your container ID>

# now, in the container
$ cd host

# optional but recommended: set a filter to specify what debug logs you want
$ export RUST_LOG=tinychain,tc_scalar=debug

# if you're not sure what value to give RUST_LOG, leave it unset
# and watch for module names that you're interested in

# leave `--release` out of the run command to run in debug mode
$ cargo run
# ...
HTTP server listening on 0.0.0.0:8702
```

Now, whenever you make a request to your local host, you'll see the host's internal debug logs in the terminal.


# Ask for help

TinyChain is a very new framework and is still evolving rapidly. Please ask for help if you need it!

If you run into any problems or just need more information in order to accomplish your goals using TinyChain, please ask for help! You can email <support@tinychain.net> or [start a discussion](https://www.github.com/haydnv/tinychain/discussions).


# Install TinyChain

Install the TinyChain host software

## Client

Most users will only need to run the TinyChain Python client, not a TinyChain host. See the [Python client introduction](/guides/python-client-introduction) for instructions to install the client.

## Host

### Easy install

The quick and easy way to get TinyChain up and running to try it out is to use Docker:

```
# build the Dockerfile from the GitHub repo, then run a new container with TinyChain listening on host port 8702
# the "-it" option also opens an interactive terminal
docker run -it -p 8702:8702/tcp $(docker build https://github.com/haydnv/tinychain.git -q) ./tinychain --data_dir=/tmp/data
```

You can check that your installation succeeded by loading [http://127.0.0.1:8702/state/scalar/value/string?key="Hello, World!"](http://127.0.0.1:8702/state/scalar/value/string?key=%22Hello,%20World!%22) in your browser.

### Automatic install (Ubuntu)

An install script is provided for Ubuntu (only tested on Ubuntu 20.04):

```
curl https://raw.githubusercontent.com/haydnv/tinychain/master/bin/install.sh -sSf | bash
```

### Manual install

1. (optional) If you need CUDA support for GPU acceleration, first install CUDA 11 by following the instructions here: <https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#ubuntu-installation>. If you're not sure, skip this step.
2. Install cargo by following the instructions here: <https://doc.rust-lang.org/cargo/getting-started/installation.html>
3. Install TinyChain by running `cargo install tinychain`

## systemd configuration

To use TinyChain in production, you'll probably want to configure [systemd](https://en.wikipedia.org/wiki/Systemd) so that TinyChain will automatically restart in the case of a crash or a host machine restart. You can find detailed instructions on how to configure a new service with systemd [here](https://www.linode.com/docs/guides/start-service-at-boot/). You can customize this example systemd config file for your use-case:

```toml
# This is an example systemd service config for TinyChain.

[Unit]
Description=Tinychain
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=2

# change this user name!
User=root

# change this path to match your Tinychain install path
ExecStart=/root/tinychain/host/target/release/tinychain --data_dir=/tmp/data --cache_size=4G

[Install]
WantedBy=multi-user.target
```


# Technical Details

This information is documented in case you want to develop your own general-purpose TinyChain client, or if you need to do an in-depth security or risk analysis.

## Data description

TinyChain exposes a JSON API over HTTP, and treats a subset of JSON as a Turing-complete application language. For example, this is a function to convert meters into feet:

```javascript
{"/state/scalar/op/get": ["meters", [
    {"/state/scalar/ref/if": [
        {"$meters/gte": 0},
        {"$meters/mul": 3.28},
        {"/error/bad_request": "Negative distance is not supported"}
    ]}
]]}
```

Obviously writing this by hand gets unwieldy very quickly, which is why the [Python client](https://github.com/haydnv/tinychain/tree/master/client) is provided. Here's the same function defined using the Python client:

```python
@tc.get_op
def to_feet(txn, meters: tc.Number) -> tc.Number:
    return tc.If(
        meters >= 0,
        meters * 3.28,
        tc.error.BadRequest("negative distance is not supported"))
```

## Replication

A TinyChain cluster uses a variation on [fast Byzantine multi-Paxos consensus](https://en.wikipedia.org/wiki/Paxos_\(computer_science\)#Message_flow:_Fast_Byzantine_Multi-Paxos,_steady_state).

The life of a replica *R* of a cluster *C* is:

1. Host loads cluster config
2. If any peer is hosting a cluster at the same path, a replication request is sent, and the state of *R* is updated to match the state of *C*; otherwise, *R* is assumed to be the latest state of *C*
3. *R* will handle transaction requests for *C* according to the flow below until its host is shut down or it attempts to commit a transaction and fails
4. If *R* attempts to commit a transaction and fails, it will stop accepting requests and attempt to re-join *C* by starting from step 1. above (assuming that automatic restarts are configured with systemd)

The flow of operations within a single transaction is:

1. A replica host *R* of cluster *C* with *N* total replicas receives a new transaction request *T*
2. *R* claims ownership of the transaction
3. For `PUT` and `DELETE` operations, the request *T* itself is replicated; for user-defined `GET` and `POST` requests, write operations which are part of *T* are replicated to all other hosts in *C*
4. Each dependent cluster *Cx* receives a request *Tx*, claims leadership of *Tx*, and replicates *Tx* to all hosts in *Cx*
5. If any host responds with error 409: conflict, the transaction is rolled back and error 409 is returned to the end-user
6. Otherwise, if at least (*N* / 2) + 1 hosts in each participating cluster respond with success, each cluster *C* removes the unsuccessful hosts from its replica set, commits the transaction, and responds to the end-user
7. Otherwise, the transaction is rolled back and an error is returned to the end-user

{% hint style="warning" %}
**Important note**: the TinyChain protocol does not support trustless replication. Do not allow untrusted replicas to join your cluster. A single malicious replica can significantly degrade performance, or even halt all updates entirely, by creating extra work to reach consensus; it can also report false information to your clients and the network as a whole.
{% endhint %}

## Authentication

TinyChain uses recursive JSON web tokens provided by the [rjwt](http://docs.rs/rjwt/) library. Each TinyChain host which handles part of a transaction must validate the auth token of the incoming request, sign it with its own private key, and forward it to any downstream dependencies. Note the unusual security consideration of a recursive token: a downstream dependency receives all of the upstream tokens, and therefore is authorized to take any action which an upstream dependency is authorized to take. For this reason, it's very important to use only a minimal scope to authenticate the end-user, and grant further scopes as narrowly as possible.

### Life of a transaction

Example:

```
|   user@example.com    |         retailer.com       |             factory.com          |          shipping.com        | bank.com
|-----------------------|----------------------------|----------------------------------|------------------------------|----------
| Buy 1 widget for $20 -> Debit user's account $20 -------------------------------------------------------------------->          
|                       | Make 1 widget -------------> Debit retailer's account $10 ----------------------------------->
|                       |                            | Ship 1 widget to user's address -> Debit retailer's account $5 ->          
```

* `user@example.com` initiates the transaction by sending a request to `retailer.com`, with an auth token signed by `example.com` whose scopes limit its use to the intended action
  * `retailer.com` claims ownership of the transaction by creating a new, signed request token which includes the original token
    * `retailer.com` sends a request to `bank.com` to debit $20 from the user's account
    * `retailer.com` sends a request to `factory.com` to manufacture 1 widget
      * `factory.com` sends a request to `bank.com` to debit $10 from `retailer.com`'s account
      * `factory.com` sends a request to `shipping.com` to schedule a shipment to the user's address, and charge `retailer.com`
        * `shipping.com` sends a request to `bank.com` to debit $5 from `retailer.com`'s account

Requests at the same indentation level above are executed concurrently, after validating the incoming request token. Acceptance of a request with transaction ID X forbids acceptance of any other request with transaction ID X, unless they share the same owner.

Note that this is only the case in a mutation (a write transaction). A read-only transaction does not need to perform any explicit synchronization with the transaction owner, only to lock the given transaction ID.

The concurrency control flow of this sequence of operations, starting from transaction number (X - 1) is:

1. Client initiates transaction X
2. Service endpoint validates auth token
3. Service endpoint claims transaction X, becoming the transaction owner
4. Service endpoint executes requested operation
   1. Dependency receives request
   2. Dependency validates auth token
      1. If the dependency is part of a different cluser than the transaction owner, it notifies the owner of its participation (this is necessary to synchronize dependencies further downstream)
   3. Dependency executes requested operation and replies to the transaction owner
5. Transaction owner notifies dependencies of success, updating their latest committed transaction to X
6. Transaction owner updates its latest committed transaction to X
7. Transaction owner replies to the client

This functionality is implemented automatically for any service using the TinyChain host software.

{% hint style="warning" %}
**Important note**: this cross-service consensus algorithm trusts each service to a) recover from a crash without losing state, and b) communicate that it has committed a transaction honestly and correctly. In other words, it is not a new and trustless protocol, but a new formalization of an existing ad-hoc procedure. An application which requires completely trustless transactions, like a distributed cryptocurrency, should use a single replicated cluster.
{% endhint %}

## More information

If you need some technical details which are not listed on this page, you can check the internal documentation for TinyChain's host software at [docs.rs/tinychain](https://docs.rs/tinychain/), or just [Ask for help](/guides/ask-for-help)!


# Client-side Class Inheritance

Learn how to inherit from and modify the behavior of TinyChain client classes

## Client-side methods

The recommended way to do object-oriented programming in TinyChain is to host `Model` classes as part of a `Service`. This minimizes the amount of work the developer has to do in terms of keeping track of the difference between the compile-time and run-time state of their program. However, in some cases, instance methods must be defined entirely client-side, meaning they are defined and executed only at compile-time in order to construct `Op`s that are executed at run-time.

{% hint style="warning" %}
In general, you should use hosted class definitions (`Model`s). The only situations where you should use this more advanced but much more challenging technique are when:

1. A trivial helper class is needed for convenience, such as assigning specific names and types to a `Map` or `Tuple`
2. Program efficiency depends strongly on compile-time parameters, or
3. The application cannot be distributed as a hosted service
   {% endhint %}

For these reasons, client-side instance methods are used extensively in the TinyChain Python client. For example, [`Table.insert`](https://github.com/haydnv/tinychain/blob/main/client/tinychain/collection/table.py) is only defined in the client (the host has no `/insert` handler) because `insert` is not idempotent and therefore is not safe when replaying the low-level write operations recorded in a `Block` of a `BlockChain`. Code like this is not straightforward to read or write.

## Easy: helper classes based on Map and Tuple

Consider this example:

```python
@tc.post_op
def force(mass: tc.Tuple, acceleration: tc.Tuple) -> tc.Tuple:
    mass_quantity = tc.Number(mass[0])
    mass_unit = Mass(mass[1])
    acceleration_quantity = tc.Number(acceleration[0])
    acceleration_unit = Velocity(acceleration[1])**2
    return mass_quantity * acceleration_quantity, mass_unit * acceleration_unit
```

Because `Tuple` by itself doesn't contain any type information, there's a lot of boilerplate destructuring code here and a lot of room for error. For example, is the `Unit` of `acceleration` already `Velocity**2`? It's hard to see if there's a bug. Readability and usability can be improved by a helper class:

```python
class UnitQuantity(tc.Tuple):
    # note: there's no __uri__ defined here
    # which means this class definition only exists in the Python client,
    # i.e. it's not exported by any hosted service

    @property
    def quantity(self):
        return tc.Number(self[0])

    @property
    def unit(self):
        return Unit(self[1])

    def __mul__(self):
        # use self.__class__ as the return type here to better support subclasses
        return self.__class__((
            self.quantity * other.quantity,
            self.unit * other.unit))
```

Now the business logic is much more readable:

```python
@tc.post_op
def force(mass: UnitQuantity, acceleration: UnitQuantity) -> UnitQuantity:
    return mass * acceleration
```

## Challenging: construct a deep neural net

```python
class NeuralNet(tc.Tuple):
    """abstract methods omitted here"""

class DNN(NeuralNet):
    @classmethod
    def load(cls, layers):
        # the network architecture, like this parameter `n`,
        # must be known at compile-time in order to construct
        # an Optimizer for this ML model
        n = len(layers)

        # because the form of `DNN.forward` depends on `n`,
        # the `DNN.forward` method must be defined at compile-time
        class DNN(cls):
            def forward(self, inputs):

                # `layers` is defined in the compile-time context
                # so even though we have access to `layers` here,
                # we still have to reference `self[i]` instead of `layers`

                # otherwise the ops could fail at run-time because
                # they might depend on a state that was only defined
                # at compile-time
                
                state = self[0].forward(inputs)
                for i in range(1, n):
                    state = self[i].forward(state)

                return state

        # here the form of the returned instance is set to `layers`
        return DNN(layers)
```

This example highlights the distinction between compile-time and run-time state which the developer must be mindful of in order to define a client-side instance method. As a general rule, a parameter which is a native Python state known at compile-time (like the integer `n` above) is safe to reference when constructing a class or method, but a TinyChain `State` in the calling context has to be referenced using `self`. The nested class idiom (where the `create` method defines a custom subclass of `cls`) is only necessary when the structure of a compute graph which the class defines depends on a compile-time parameter (like `n` in `DNN.forward`).


# For Engineers

TinyChain is an all-in-one backend host which allows you to rapidly prototype a complex application and scale it without rewriting. You can easily split a monolithic application into many microservices, or combine multiple services into one monolithic application. TinyChain optimizes your application's performance with automatic concurrency (multithreading) and GPU acceleration, with no extra code required. There is a Python client provided (try it out with `pip3 install tinychain`) but you can easily build your own client in any language by implementing TinyChain's JSON data description and protocol (see [Technical Details](/fundamentals/technical-details)). You can find more information in the [Guides](/guides/getting-started), the [client API documentation](https://tinychain.readthedocs.io/), the [tests](https://github.com/haydnv/tinychain/tree/main/tests), and the [tutorial videos](https://www.youtube.com/channel/UCC6brO3L3JR0wUiMSDoGjrw).


# For Data Scientists

With TinyChain, data scientists can easily analyze live replicas of a production database, eliminating the need to copy entire tables and databases out of the platform which tracks and enforces ownership of the data. This also eliminates the need to maintain a separate platform (e.g. TensorFlow Serving) in order to serve models. TinyChain also allows the construction of stateful models, like a recurrent neural network (RNN) with a per-user state in memory, or a model which updates a database when it encounters an unexpected input.


# For DevOps

TinyChain eliminates the need to manage an ever-growing "stack" of platform software which sometimes requires mutually-incompatible dependencies. With a TinyChain application, you no longer need tools like Docker and Kubernetes to package and deploy your application backend, because its "stack" is simply the TinyChain host software. You can easily deploy your application to a different cloud provider, or on-premises for a client, simply by installing a `Service`.


# For Product Owners & Executives

TinyChain is designed with many unique features to minimize the operational risk of hosting your customers' data. For example, TinyChain is the only database which supports *hypothetical queries*, which allow developers to examine the real consequences of potentially-destructive database updates without actually applying the updates. TinyChain is also the only database, and the only blockchain platform, designed from the ground up for compliance with data privacy laws like [GDPR](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation) and [CCPA](https://en.wikipedia.org/wiki/California_Consumer_Privacy_Act).

TinyChain's all-in-one approach can also reduce operational costs by removing the need for developers to maintain a broad familiarity with a wide variety of specific platform tools, and removing the operational separation between application development and data science: TinyChain is a single platform which is useful to both developers and data scientists.


# For End-Users

End users won't see TinyChain directly, but it makes the customer experience better by making cloud services faster, more reliable, and more cost-efficient to operate, as well as lowering the risk of a security breach by eliminating the need to make copies of customer data for analysis.


