Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
69a2d8f
docs(plan): add TalkingBytes 2.1 Foundation integration hardening plan
abmmhasan Sep 22, 2026
11ba8d5
docs: expand TalkingBytes 2.1 hardening plan after full rescan
abmmhasan Sep 22, 2026
67ce953
feat: isolate runtime events and add cooperative cancellation
abmmhasan Sep 22, 2026
abad84c
ci: update PHPForge reusable workflow contract
abmmhasan Sep 22, 2026
3eae5c3
cleanup
abmmhasan Sep 22, 2026
3f8ec9a
fix: resolve Batch 1 CI and review findings
abmmhasan Sep 22, 2026
dd03e72
feat: add host-controlled inbound gRPC boundary
abmmhasan Sep 22, 2026
2ac106f
fix: satisfy Batch 2 static quality gates
abmmhasan Sep 22, 2026
97de8c6
style: order inbound dispatcher methods
abmmhasan Sep 22, 2026
f0b636c
style: normalize inbound dispatcher separation
abmmhasan Sep 22, 2026
2a6f019
feat: harden sendmail and mailbox runtime lifecycle
abmmhasan Sep 22, 2026
2dc1633
fix: normalize sendmail process lifecycle state
abmmhasan Sep 22, 2026
899b070
style: order IMAP private lifecycle helpers
abmmhasan Sep 22, 2026
908dc99
fix: satisfy sendmail static lifecycle analysis
abmmhasan Sep 22, 2026
aa44572
docs: sync plan through green Batch 3
abmmhasan Sep 22, 2026
df70218
feat: add resolved protocol composition builders
abmmhasan Sep 22, 2026
c268aae
fix: satisfy Batch 4 safety and quality gates
abmmhasan Sep 22, 2026
e44c037
fix: align Batch 4 with PHPForge conventions
abmmhasan Sep 22, 2026
fead8b9
fix: tighten resolved composition types
abmmhasan Sep 22, 2026
2e67cff
docs: mark green Batch 4 composition work
abmmhasan Sep 22, 2026
c9db21c
test: harden webhook replay acceptance contract
abmmhasan Sep 22, 2026
4d0be69
docs: mark green Batch 5 replay acceptance
abmmhasan Sep 22, 2026
527c145
feat: add rolling cancellable HTTP multi scheduler
abmmhasan Sep 22, 2026
68058ff
fix: satisfy rolling scheduler quality gates
abmmhasan Sep 22, 2026
9726f94
fix: finalize rolling scheduler gate cleanup
abmmhasan Sep 22, 2026
6f11be8
test: prove rolling HTTP multi scheduling and cancellation
abmmhasan Sep 22, 2026
79db70c
fix: clean concurrent HTTP fixture quality gates
abmmhasan Sep 22, 2026
0d19f25
docs: mark green Batch 6 rolling scheduler
abmmhasan Sep 22, 2026
d9c9ef8
feat: make generated grpc stream adaptation deterministic
abmmhasan Sep 22, 2026
d61b028
fix: align grpc method-map validation types
abmmhasan Sep 22, 2026
9695a6a
docs: mark green Batch 7 grpc adapter work
abmmhasan Sep 22, 2026
e16778e
feat: harden protocol observability redaction
abmmhasan Sep 22, 2026
45c7dd1
fix: align observability tests with sanitized contract
abmmhasan Sep 22, 2026
9ffc337
docs: mark green Batch 8 observability hardening
abmmhasan Sep 22, 2026
b20b72a
test: enforce optional capability coldness
abmmhasan Sep 22, 2026
4f2fae4
bench: add native runtime and soak evidence
abmmhasan Sep 22, 2026
b947ce3
fix: finish monotonic transport timing
abmmhasan Sep 22, 2026
783b9c4
bench: cover host bridges and mailbox runtime
abmmhasan Sep 22, 2026
24f775d
ci: make optional coldness gate portable
abmmhasan Sep 22, 2026
4dcc924
test: enforce compiled capability boundaries in PHP
abmmhasan Sep 22, 2026
aee32d6
ci: restore valid security workflow
abmmhasan Sep 22, 2026
5328dfd
docs: clarify optional capability release evidence
abmmhasan Sep 22, 2026
b9c8590
test: allow DKIM public key sodium boundary
abmmhasan Sep 22, 2026
82022f6
style: normalize grpc benchmark stub
abmmhasan Sep 22, 2026
e3d0627
feat: add cooperative webhook retry cancellation
abmmhasan Sep 22, 2026
7e4a0c9
test: prove mutable runtime state isolation
abmmhasan Sep 22, 2026
1471407
fix: satisfy webhook cancellation gates
abmmhasan Sep 22, 2026
feb0854
ci: enforce warning-free documentation build
abmmhasan Sep 22, 2026
32ed1b1
docs: close TalkingBytes 2.1 implementation plan
abmmhasan Sep 22, 2026
c521a9a
docs: finalize TalkingBytes 2.1 plan
abmmhasan Sep 22, 2026
67ba1d3
cleanup
abmmhasan Sep 22, 2026
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
95 changes: 63 additions & 32 deletions .github/workflows/security-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,47 @@ on:
jobs:
phpforge:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@main
with:
integration_services: '[]'
service_topologies: '{}'
php_extensions: "curl, fileinfo, openssl"
permissions:
security-events: write
actions: read
contents: read
with:
php_versions: '["8.4","8.5"]'
dependency_versions: '["prefer-lowest","prefer-stable"]'
php_extensions: "curl, fileinfo, openssl"
composer_flags: ""
phpstan_memory_limit: "1G"
psalm_threads: "1"
run_analysis: true
run_svg_report: true
run_clean_install: true
benchmark_composer_script: ""
benchmark_result_file: ""
benchmark_baseline_file: ""
benchmark_max_regression_percent: 2
benchmark_stable_environment: false
fail_on_skipped_tests: true
enable_redis_service: false
enable_valkey_service: false
enable_memcached_service: false
enable_postgres_service: false
enable_mysql_service: false
enable_scylladb_service: false
enable_elasticsearch_service: false
enable_mongodb_service: false
service_db_name: "phpforge"
service_db_user: "phpforge"
service_db_password: "phpforge"
artifact_retention_days: 61

optional-capability-coldness:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup minimal PHP runtime
uses: shivammathur/setup-php@v2
with:
php-version: "8.5"
extensions: "curl, fileinfo, openssl, :grpc, :imap, :posix"
coverage: none

- name: Verify unloadable optional extensions are absent
run: |
php -r '
foreach (["grpc", "imap", "posix"] as $extension) {
if (extension_loaded($extension)) {
fwrite(STDERR, "Optional extension unexpectedly loaded: {$extension}\n");
exit(1);
}
}
'

- name: Install dependencies
run: composer install --no-interaction --prefer-dist --no-progress

- name: Run optional capability coldness gate
env:
TALKINGBYTES_OPTIONAL_COLDNESS: "1"
run: vendor/bin/pest --configuration=vendor/infocyph/phpforge/resources/pest.xml --bootstrap=vendor/autoload.php tests/OptionalCapabilityColdnessTest.php

mailpit-integration:
runs-on: ubuntu-latest
Expand All @@ -55,21 +64,43 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
php-version: "8.5"
extensions: curl, fileinfo, openssl
coverage: none

- name: Install dependencies
run: composer install --no-interaction --prefer-dist
run: composer install --no-interaction --prefer-dist --no-progress

- name: Run Mailpit integration test
env:
RUN_MAILPIT_INTEGRATION: '1'
RUN_MAILPIT_INTEGRATION: "1"
SMTP_HOST: 127.0.0.1
SMTP_PORT: 1025
MAILPIT_API_BASE: http://127.0.0.1:8025
run: vendor/bin/pest --configuration=vendor/infocyph/phpforge/resources/pest.xml --bootstrap=vendor/autoload.php --fail-on-skipped tests/MailpitIntegrationTest.php

docs-warning-free:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
cache: pip
cache-dependency-path: docs/requirements.txt

- name: Install documentation dependencies
run: python -m pip install --disable-pip-version-check -r docs/requirements.txt

- name: Build documentation with warnings as errors
run: sphinx-build -W --keep-going -b html docs build/docs

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ test.php
var
vendor
d2utmp*
/graphify-out
34 changes: 34 additions & 0 deletions benchmarks/EmailBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace Infocyph\TalkingBytes\Benchmarks;

use Infocyph\TalkingBytes\Email\Emailer;
use Infocyph\TalkingBytes\Email\EmailMessage;
use Infocyph\TalkingBytes\Email\Mailbox\FakeMailboxTransport;
use Infocyph\TalkingBytes\Email\Parser\RawEmailParser;
use Infocyph\TalkingBytes\Email\System\RawEmailBuilder;
use PhpBench\Attributes\BeforeMethods;
Expand All @@ -16,8 +18,14 @@ final class EmailBench
{
private RawEmailBuilder $builder;

private Emailer $fakeEmailer;

private FakeMailboxTransport $mailbox;

private EmailMessage $message;

private Emailer $nullEmailer;

private RawEmailParser $parser;

private string $rawMultipartEmail;
Expand All @@ -26,6 +34,8 @@ public function setUp(): void
{
$this->builder = new RawEmailBuilder();
$this->parser = new RawEmailParser();
$this->nullEmailer = Emailer::usingNull();
$this->fakeEmailer = Emailer::fake();
$this->message = EmailMessage::new()
->from('[email protected]', 'Sender Name')
->to('[email protected]', '[email protected]')
Expand All @@ -37,6 +47,7 @@ public function setUp(): void
->attachData(str_repeat('PDF-DATA-', 64), 'report.pdf', 'application/pdf')
->attachInlineData('<svg><rect width="8" height="8"/></svg>', 'logo.svg', 'logo-inline', 'image/svg+xml');
$this->rawMultipartEmail = $this->createRawMultipartEmail();
$this->mailbox = (new FakeMailboxTransport())->withMessage('INBOX', 1, $this->rawMultipartEmail);
}

#[Iterations(5)]
Expand All @@ -58,6 +69,29 @@ static function (string $chunk): void {
);
}

#[Iterations(5)]
#[Revs(100)]
public function benchFakeMailboxAdapter(): void
{
$this->mailbox->rawMessage('INBOX', 1);
$this->mailbox->rawHeaders('INBOX', 1);
$this->mailbox->status('INBOX');
}

#[Iterations(5)]
#[Revs(100)]
public function benchFakeSend(): void
{
$this->fakeEmailer->send($this->message);
}

#[Iterations(5)]
#[Revs(500)]
public function benchNullSend(): void
{
$this->nullEmailer->send($this->message);
}

#[Iterations(5)]
#[Revs(50)]
public function benchParseMultipartEmail(): void
Expand Down
121 changes: 121 additions & 0 deletions benchmarks/GrpcBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,120 @@

namespace Infocyph\TalkingBytes\Benchmarks;

use Infocyph\TalkingBytes\Grpc\GrpcClient;
use Infocyph\TalkingBytes\Grpc\GrpcInboundDispatcher;
use Infocyph\TalkingBytes\Grpc\GrpcMetadata;
use Infocyph\TalkingBytes\Grpc\GrpcStatus;
use Infocyph\TalkingBytes\Grpc\Receiver\GrpcInboundRequest;
use Infocyph\TalkingBytes\Grpc\Receiver\GrpcInboundResponse;
use Infocyph\TalkingBytes\Grpc\Retry\GrpcRetryPolicy;
use Infocyph\TalkingBytes\Grpc\Sender\GrpcRequest;
use Infocyph\TalkingBytes\Grpc\Sender\GrpcResponse;
use Infocyph\TalkingBytes\Grpc\Testing\FakeGrpcInboundSource;
use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;

#[BeforeMethods('setUp')]
final class GrpcBench
{
private GrpcClient $client;

private GrpcInboundDispatcher $dispatcher;

private GrpcClient $generatedClient;

private GrpcInboundRequest $inboundRequest;

private GrpcRequest $request;

private GrpcClient $retryClient;

public function setUp(): void
{
$caller = static fn(GrpcRequest $request): GrpcResponse => new GrpcResponse(
GrpcStatus::Ok,
$request->message,
);

$this->client = GrpcClient::using($caller);
$this->retryClient = GrpcClient::using($caller)
->withGrpcRetry(GrpcRetryPolicy::standard(attempts: 2, baseDelayMs: 0));

$stub = new class {
public function GetOrder(mixed $message, array $metadata = [], array $options = []): object
{
unset($metadata, $options);

$call = new class {
public mixed $message = null;

public function wait(): array
{
return [$this->message, (object) ['code' => GrpcStatus::Ok->value]];
}

public function getMetadata(): array
{
return [];
}

public function getTrailingMetadata(): array
{
return [];
}
};
$call->message = $message;

return $call;
}
};

$this->generatedClient = GrpcClient::usingGeneratedStub(
$stub,
['/orders.v1.OrderService/GetOrder' => 'GetOrder'],
);

$this->dispatcher = new GrpcInboundDispatcher([
'/orders.v1.OrderService/GetOrder' => static function (GrpcInboundRequest $request): GrpcInboundResponse {
return GrpcInboundResponse::ok($request->message);
},
]);
$this->request = new GrpcRequest(
'/orders.v1.OrderService/GetOrder',
['id' => 'order-1'],
new GrpcMetadata(['x-request-id' => ['bench-1']]),
1.5,
);
$this->inboundRequest = new GrpcInboundRequest(
'/orders.v1.OrderService/GetOrder',
['id' => 'order-1'],
);
}

#[Iterations(5)]
#[Revs(500)]
public function benchGeneratedStubInvocation(): void
{
$this->generatedClient->send($this->request);
}

#[Iterations(5)]
#[Revs(1000)]
public function benchHostAcceptedExchangeBridge(): void
{
$source = new FakeGrpcInboundSource();
$source->enqueue($this->inboundRequest);
$this->dispatcher->serveOne($source);
}

#[Iterations(5)]
#[Revs(1000)]
public function benchInboundDispatch(): void
{
$this->dispatcher->handle($this->inboundRequest);
}

#[Iterations(5)]
#[Revs(1000)]
public function benchRequestAndMetadata(): void
Expand All @@ -22,4 +129,18 @@ public function benchRequestAndMetadata(): void
1.5,
);
}

#[Iterations(5)]
#[Revs(1000)]
public function benchRetryMiddlewareSuccessPath(): void
{
$this->retryClient->send($this->request);
}

#[Iterations(5)]
#[Revs(1000)]
public function benchUnaryDispatch(): void
{
$this->client->send($this->request);
}
}
Loading
Loading