Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

A headless web framework for .NET. Build a more robust frontend: whatever goes wrong, the backend gets it right, and
every run is recorded in your Postgres.
[Docs](https://traxsharp.net/docs) Β· [Getting started](https://traxsharp.net/docs/getting-started) Β·
[What Trax does](https://traxsharp.net/docs/what-trax-does) Β· [Docs](https://traxsharp.net/docs) Β·
[Getting started](https://traxsharp.net/docs/getting-started) Β·
[Samples](Trax.Samples) Β· [NuGet](https://www.nuget.org/profiles/Theauxm) Β·
[npm](https://www.npmjs.com/package/@traxsharp/state-machine)

Expand Down Expand Up @@ -52,7 +53,7 @@ folder depends only on folders above it in this table; take the layers you need,
| [Trax.Scheduler](Trax.Scheduler) | Cron and interval schedules, retries, dead letters, and workers on other machines or in Lambda |
| [Trax.Api](Trax.Api) | GraphQL generated from your trains and machines: a mutation per machine event, authorization checked on the server, persisted operations, audit, live subscriptions and the operations console your host serves |
| [Trax.Cli](Trax.Cli) | The `trax` tool: scaffold a hub and trains from an OpenAPI or GraphQL schema, and state-machine codegen |
| [Trax.Samples](Trax.Samples) | **Start here.** Complete sample apps, and the `trax-api`, `trax-scheduler` and `trax-hub` templates |
| [Trax.Samples](Trax.Samples) | Complete sample apps, and the `trax-api`, `trax-scheduler` and `trax-hub` templates |
| [Trax.Api.StateMachine](Trax.Api.StateMachine) | `@traxsharp/state-machine`, the TypeScript twin of the state-machine engine |
| [Trax.Docs](Trax.Docs) | The documentation published at [traxsharp.net/docs](https://traxsharp.net/docs), and the decision records |
| [Trax.Website](Trax.Website) | The source of [traxsharp.net](https://traxsharp.net) |
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Fixes ship against the latest version published to NuGet. Older major versions a

## Supply-chain posture

Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/supply-chain-security) guide.
Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/security/supply-chain) guide.
2 changes: 1 addition & 1 deletion Trax.Api/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Fixes ship against the latest version published to NuGet. Older major versions a

## Supply-chain posture

Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/supply-chain-security) guide.
Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/security/supply-chain) guide.
6 changes: 3 additions & 3 deletions Trax.Api/src/AUTH-PACKAGE-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The full disclaimer, including the consumer responsibility checklist, ships in t
| `Trax.Api.Auth.Jwt.Cognito.Issuer` | Mints Cognito-shaped RS256 tokens, with a refresh-token store contract. | [Cognito issuer](https://traxsharp.net/docs/sdk-reference/api-auth/cognito-token-issuer) |
| `Trax.Api.Auth.Jwt.Testing` | A self-hosted JWKS server and token minters for integration tests. | [JWT testing](https://traxsharp.net/docs/sdk-reference/api-auth/test-jwks-server) |
| `Trax.Api.Auth.Oidc` | OpenID Connect code flow with PKCE and a session cookie, for browser sign-in. | [AddTraxOidcAuth](https://traxsharp.net/docs/sdk-reference/api-auth/add-trax-oidc-auth) |
| `Trax.Api.GraphQL.Audit` | Records each GraphQL request to your `ITraxAuditSink` from a bounded channel and a background writer. | [API Security](https://traxsharp.net/docs/api-security) |
| `Trax.Api.GraphQL.Audit` | Records each GraphQL request to your `ITraxAuditSink` from a bounded channel and a background writer. | [API Security](https://traxsharp.net/docs/security/secure-a-graphql-host) |

## Installation

Expand Down Expand Up @@ -85,11 +85,11 @@ public sealed class MyAuditSink : ITraxAuditSink
}
```

Per-train authorization uses `[TraxAuthorize]` on the train class; see [Authorization](https://traxsharp.net/docs/authorization). Subscriptions carry credentials in the `connection_init` payload; see [API Security](https://traxsharp.net/docs/api-security).
Per-train authorization uses `[TraxAuthorize]` on the train class; see [Authorization](https://traxsharp.net/docs/security/authorization-rules). Subscriptions carry credentials in the `connection_init` payload; see [API Security](https://traxsharp.net/docs/security/secure-a-graphql-host).

## Documentation

- [API Security](https://traxsharp.net/docs/api-security): every scheme, subscription auth, auditing and hardening defaults
- [API Security](https://traxsharp.net/docs/security/secure-a-graphql-host): every scheme, subscription auth, auditing and hardening defaults
- [API Auth reference](https://traxsharp.net/docs/sdk-reference/api-auth)
- [Trax documentation](https://traxsharp.net/docs)
- Source: [github.com/TraxSharp/Trax/tree/main/Trax.Api](https://github.com/TraxSharp/Trax/tree/main/Trax.Api)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public static IEndpointConventionBuilder UseTraxApiDashboard(
+ "of the dashboard reads, and AddTraxGraphQL() does not expose it. Call "
+ "ExposeOperationQueries() on the GraphQL builder, and "
+ "ExposeOperationMutations() for the dashboard's actions, with the gate the "
+ "builder then asks for (RequireAuthorization() or GateOperations(...))."
+ "builder then asks for (RequireAuthorization() or GateOperations(...)). See "
+ "https://traxsharp.net/docs/cross-cutting/troubleshooting#usetraxapidashboard-requires-the-graphql-operations-namespace"
);

var assets = services.GetService<DashboardAssets>() ?? DashboardAssets.Embedded;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ Func<TraxGraphQLBuilder, TraxGraphQLBuilder> configure
if (!services.Any(sd => sd.ServiceType == typeof(TraxMarker)))
throw new InvalidOperationException(
"AddTraxGraphQL() requires AddTrax() to be called first. "
+ "Call services.AddTrax(trax => ...) before services.AddTraxGraphQL()."
+ "Call services.AddTrax(trax => ...) before services.AddTraxGraphQL(). See "
+ "https://traxsharp.net/docs/cross-cutting/troubleshooting#addtraxgraphql-requires-addtrax-to-be-called-first"
);

var builder = new TraxGraphQLBuilder(services);
Expand Down
2 changes: 1 addition & 1 deletion Trax.Cli/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Fixes ship against the latest version published to NuGet. Older major versions a

## Supply-chain posture

Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/supply-chain-security) guide.
Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/security/supply-chain) guide.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var builder = WebApplication.CreateBuilder(args);
// operator key holds. Both exist only in Development: Trax.Api refuses to start a host outside
// Development with a key containing "do-not-use-in-production" registered. Anywhere else, register real
// credentials (AddHashed keys from a secret store, or AddTraxJwtAuth) before removing the
// IsDevelopment() check: https://traxsharp.net/docs/api-security.
// IsDevelopment() check: https://traxsharp.net/docs/security/secure-a-graphql-host.
// AddAuthentication() stays outside the check. AddTraxApiKeyAuth registers authentication
// itself, so without this line a start outside Development fails in UseAuthentication()
// with "Unable to resolve service for type IAuthenticationSchemeProvider". AddAuthorization()
Expand Down
2 changes: 1 addition & 1 deletion Trax.Core/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Fixes ship against the latest version published to NuGet. Older major versions a

## Supply-chain posture

Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/supply-chain-security) guide.
Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/security/supply-chain) guide.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using AwesomeAssertions;
using Trax.Core.Functional;
using Trax.Core.Junction;
using Trax.Core.Train;

namespace Trax.Core.Tests.Integration.Examples.Caching;

// Shown on https://traxsharp.net/docs/sdk-reference/train-methods/short-circuit
//
// A price lookup with a cache in front. A cache hit becomes the result, but the steps after it
// still run. A cache miss throws, and the train carries on as if the step were not there.

public sealed record Product(string Sku);

public sealed record Price(decimal Amount);

public interface IPriceCache
{
Dictionary<string, decimal> Prices { get; }
}

public sealed class PriceCache : IPriceCache
{
public Dictionary<string, decimal> Prices { get; } = [];
}

public interface IAudit
{
List<string> Lines { get; }
}

public sealed class Audit : IAudit
{
public List<string> Lines { get; } = [];
}

public class CheckCache(IPriceCache cache) : Junction<Product, Price>
{
public override Task<Price> Run(Product product) =>
cache.Prices.TryGetValue(product.Sku, out var amount)
? Task.FromResult(new Price(amount))
: throw new KeyNotFoundException($"{product.Sku} is not cached");
}

public class LookUpPrice(IAudit audit) : Junction<Product, Price>
{
public override Task<Price> Run(Product product)
{
audit.Lines.Add($"looked up {product.Sku}");
return Task.FromResult(new Price(10m));
}
}

public class PriceTrain(IPriceCache cache, IAudit audit) : Train<Product, Price>
{
protected override Task<Either<Exception, Price>> Junctions() =>
AddServices(cache, audit).ShortCircuit<CheckCache>().Chain<LookUpPrice>().Resolve();
}

public class CachedPriceTests
{
[Test]
public async Task Run_CacheHit_ReturnsTheCachedPriceButStillRunsTheLookUp()
{
var cache = new PriceCache();
cache.Prices["tea"] = 4m;
var audit = new Audit();

var price = await new PriceTrain(cache, audit).Run(new Product("tea"));

price.Should().Be(new Price(4m));
audit.Lines.Should().Equal("looked up tea");
}

[Test]
public async Task Run_CacheMiss_IgnoresTheFailureAndReturnsTheLookUp()
{
var price = await new PriceTrain(new PriceCache(), new Audit()).Run(new Product("tea"));

price.Should().Be(new Price(10m));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace Trax.Core.Tests.Integration.Examples.Decisions;

// Shown on https://traxsharp.net/docs/core/decisions
//
// Moderating a post. Three questions about the same post are asked in one call: is it a threat,
// what should happen to it, and how severe is it. A threat is taken down whatever the verdict; the
// rest is routed by the verdict, and posts sent for review are routed again by severity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace Trax.Core.Tests.Integration.Examples.Decisions;

// Shown on https://traxsharp.net/docs/core/decisions
//
// A support desk triages each incoming message. A decider (in production, a model that returns
// typed decisions) reads the parsed ticket and picks a track. Refunds move money, so they need more
// confidence than the other tracks; anything the decider is unsure about goes to a person.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Trax.Core.Functional;
using Trax.Core.Junction;
using Trax.Core.Train;

namespace Trax.Core.Tests.Integration.Examples.Failures;

// Shown on https://traxsharp.net/docs/cross-cutting/handle-errors-in-trains
// Shown on https://traxsharp.net/docs/sdk-reference/trains-and-junctions/train
//
// A checkout whose card is always declined. The tests beside this file show what a failing
// junction does to the rest of the train, and what the caller gets back.

public sealed record Order(decimal Total);

public sealed record Payment(decimal Amount);

public sealed record Receipt(decimal Amount);

public interface IOutbox
{
List<string> Sent { get; }
}

public sealed class Outbox : IOutbox
{
public List<string> Sent { get; } = [];
}

public class ChargeCard : Junction<Order, Payment>
{
public override Task<Payment> Run(Order order) =>
throw new InvalidOperationException("Card declined");
}

public class SendReceipt(IOutbox outbox) : Junction<Payment, Receipt>
{
public override Task<Receipt> Run(Payment payment)
{
outbox.Sent.Add($"receipt for {payment.Amount}");
return Task.FromResult(new Receipt(payment.Amount));
}
}

public class CheckoutTrain(IOutbox outbox) : Train<Order, Receipt>
{
protected override Task<Either<Exception, Receipt>> Junctions() =>
AddServices(outbox).Chain<ChargeCard>().Chain<SendReceipt>().Resolve();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using AwesomeAssertions;

namespace Trax.Core.Tests.Integration.Examples.Failures;

// Shown on https://traxsharp.net/docs/cross-cutting/handle-errors-in-trains

public class FailedJunctionTests
{
[Test]
public async Task Run_ChargeCardThrows_SendsNoReceiptAndThrowsTheSameException()
{
var outbox = new Outbox();

var checkout = () => new CheckoutTrain(outbox).Run(new Order(20m));

var declined = await checkout.Should().ThrowAsync<InvalidOperationException>();
declined.Which.Message.Should().Be("Card declined");
outbox.Sent.Should().BeEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using AwesomeAssertions;

namespace Trax.Core.Tests.Integration.Examples.Failures;

// Shown on https://traxsharp.net/docs/sdk-reference/trains-and-junctions/train

public class RunEitherTests
{
[Test]
public async Task RunEither_ChargeCardThrows_ReturnsTheExceptionInsteadOfThrowing()
{
var result = await new CheckoutTrain(new Outbox()).RunEither(new Order(20m));

result.IsLeft.Should().BeTrue();
var message = result.Match(Right: receipt => "", Left: failure => failure.Message);
message.Should().Be("Card declined");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using AwesomeAssertions;
using Trax.Core.Functional;
using Trax.Core.Junction;
using Trax.Core.Train;

namespace Trax.Core.Tests.Integration.Examples.Memory;

// Shown on https://traxsharp.net/docs/core/memory
//
// Memory keeps one value per type. Both junctions return a string, so the second one replaces
// the first, and the train returns the last name only.

public sealed record Person(string FirstName, string LastName);

public class GetFirstName : Junction<Person, string>
{
public override Task<string> Run(Person person) => Task.FromResult(person.FirstName);
}

public class GetLastName : Junction<Person, string>
{
public override Task<string> Run(Person person) => Task.FromResult(person.LastName);
}

public class NameTrain : Train<Person, string>
{
protected override Task<Either<Exception, string>> Junctions() =>
Chain<GetFirstName>().Chain<GetLastName>().Resolve();
}

public class OneValuePerTypeTests
{
[Test]
public async Task Run_TwoJunctionsReturnAString_ReturnsTheSecond()
{
var name = await new NameTrain().Run(new Person("Ada", "Lovelace"));

name.Should().Be("Lovelace");
}
}
2 changes: 1 addition & 1 deletion Trax.Docs/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Fixes ship against the latest version published to NuGet. Older major versions a

## Supply-chain posture

Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/supply-chain-security) guide.
Trax builds and publishes through a defense-in-depth pipeline: SHA-pinned actions, isolated release credentials, OIDC trusted publishing with SLSA provenance, and locked + audited dependencies (a known-vulnerability advisory fails the build). See the [Supply Chain Security](https://traxsharp.net/docs/security/supply-chain) guide.
2 changes: 1 addition & 1 deletion Trax.Docs/about-these-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ it links to the page that has it rather than repeating it.

| Kind | What it is for | In Trax |
|---|---|---|
| Tutorial | Learning by doing. One path from an empty folder to something that runs, with a visible result at each step. | [Getting Started](/docs/getting-started) builds a server with a train, a schedule, the dashboard and a GraphQL API. |
| Tutorial | Learning by doing. One path from an empty folder to something that runs, with a visible result at each step. | [Getting Started](/docs/getting-started) builds a server with a train, a schedule, the operations console and a GraphQL API. |
| How-to guide | Getting a specific job done when you already know what you want. Steps, no teaching. | Setting up the scheduler, resolving a dead letter, securing a GraphQL host, testing a train. |
| Reference | Looking something up. Describes what exists and what it does, in a consistent shape, without advice. | Every public type and method under [SDK Reference](/docs/sdk-reference), the CLI, the packages, the glossary, the schema migrations. |
| Explanation | Understanding. Why a part of Trax works the way it does, what it trades off, how its pieces fit. | How a train runs its chain, what an effect is, how the scheduler decides when a manifest runs. |
Expand Down
Loading
Loading